diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index d93805e9c7..bd62e825af 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -531,41 +531,51 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) if obj == nil { - switch { - case index != nil: + if index != nil { // TODO(gri) should provide actual type where the conflict happens check.errorf(e.Sel, "ambiguous selector %s.%s", x.expr, sel) - case indirect: - check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ) - default: - var why string - if tpar, _ := x.typ.(*TypeParam); tpar != nil { - // Type parameter bounds don't specify fields, so don't mention "field". - if tname := tpar.iface().obj; tname != nil { - why = check.sprintf("interface %s has no method %s", tname.name, sel) - } else { - why = check.sprintf("type bound for %s has no method %s", x.typ, sel) - } - } else { - why = check.sprintf("type %s has no field or method %s", x.typ, sel) - } - - // Check if capitalization of sel matters and provide better error message in that case. - if len(sel) > 0 { - var changeCase string - if r := rune(sel[0]); unicode.IsUpper(r) { - changeCase = string(unicode.ToLower(r)) + sel[1:] - } else { - changeCase = string(unicode.ToUpper(r)) + sel[1:] - } - if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { - why += ", but does have " + changeCase - } - } - - check.errorf(e.Sel, "%s.%s undefined (%s)", x.expr, sel, why) - + goto Error } + + if indirect { + check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ) + goto Error + } + + if isInterfacePtr(x.typ) { + check.errorf(e.Sel, "%s.%s undefined (type %s is pointer to interface, not interface)", x.expr, sel, x.typ) + goto Error + } + + var why string + if tpar, _ := x.typ.(*TypeParam); tpar != nil { + // Type parameter bounds don't specify fields, so don't mention "field". + // TODO(gri) Type constraints may have accessible fields now. Revisit this. + if tname := tpar.iface().obj; tname != nil { + why = check.sprintf("interface %s has no method %s", tname.name, sel) + } else { + why = check.sprintf("type bound for %s has no method %s", x.typ, sel) + } + } else { + why = check.sprintf("type %s has no field or method %s", x.typ, sel) + } + + // Check if capitalization of sel matters and provide better error message in that case. + // TODO(gri) This code only looks at the first character but LookupFieldOrMethod has an + // (internal) mechanism for case-insensitive lookup. Should use that instead. + if len(sel) > 0 { + var changeCase string + if r := rune(sel[0]); unicode.IsUpper(r) { + changeCase = string(unicode.ToLower(r)) + sel[1:] + } else { + changeCase = string(unicode.ToUpper(r)) + sel[1:] + } + if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { + why += ", but does have " + changeCase + } + } + + check.errorf(e.Sel, "%s.%s undefined (%s)", x.expr, sel, why) goto Error } diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 0cce3fdc3f..aa1ab8ac98 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -448,12 +448,12 @@ func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string // an extra formatting option for types2.Type that doesn't print out // 'func'. r = strings.Replace(r, "^^func", "", -1) - } else if IsInterface(T) && !isTypeParam(T) { + } else if IsInterface(T) { if isInterfacePtr(V) { - r = fmt.Sprintf("(%s is pointer to interface, not interface)", V) + r = fmt.Sprintf("(type %s is pointer to interface, not interface)", V) } - } else if isInterfacePtr(T) && !isTypeParam(T) { - r = fmt.Sprintf("(%s is pointer to interface, not interface)", T) + } else if isInterfacePtr(T) { + r = fmt.Sprintf("(type %s is pointer to interface, not interface)", T) } if r == "" { r = fmt.Sprintf("(missing %s)", mname) @@ -463,7 +463,7 @@ func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string func isInterfacePtr(T Type) bool { p, _ := under(T).(*Pointer) - return p != nil && IsInterface(p.base) && !isTypeParam(p.base) + return p != nil && IsInterface(p.base) } // assertableTo reports whether a value of type V can be asserted to have type T. diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go index f6bd0291ec..69e3a0a832 100644 --- a/src/cmd/compile/internal/types2/operand.go +++ b/src/cmd/compile/internal/types2/operand.go @@ -317,7 +317,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er if check != nil && check.conf.CompilerErrorMessages { if isInterfacePtr(Tu) { if reason != nil { - *reason = check.sprintf("%s does not implement %s (%s is pointer to interface, not interface)", x.typ, T, T) + *reason = check.sprintf("%s does not implement %s (type %s is pointer to interface, not interface)", x.typ, T, T) } return false, _InvalidIfaceAssign } diff --git a/src/cmd/compile/internal/types2/testdata/check/methodsets.src b/src/cmd/compile/internal/types2/testdata/check/methodsets.src index 9fb10deb9a..b0eb14cf50 100644 --- a/src/cmd/compile/internal/types2/testdata/check/methodsets.src +++ b/src/cmd/compile/internal/types2/testdata/check/methodsets.src @@ -196,9 +196,9 @@ func issue5918() { _ func(error) string = error.Error perr = &err - _ = perr.Error /* ERROR "no field or method" */ () - _ func() string = perr.Error /* ERROR "no field or method" */ - _ func(*error) string = (*error).Error /* ERROR "no field or method" */ + _ = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ () + _ func() string = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ + _ func(*error) string = (*error).Error /* ERROR "type \*error is pointer to interface, not interface" */ ) type T *interface{ m() int } @@ -207,8 +207,8 @@ func issue5918() { _ = (*x).m() _ = (*x).m - _ = x.m /* ERROR "no field or method" */ () - _ = x.m /* ERROR "no field or method" */ - _ = T.m /* ERROR "no field or method" */ + _ = x.m /* ERROR "type T is pointer to interface, not interface" */ () + _ = x.m /* ERROR "type T is pointer to interface, not interface" */ + _ = T.m /* ERROR "type T is pointer to interface, not interface" */ ) } diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2 index 6a2e787bf9..edde497f5a 100644 --- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2 +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2 @@ -20,7 +20,7 @@ func _[P interface{ m() }](x P) { x.m() // (&x).m doesn't exist because &x is of type *P // and pointers to type parameters don't have methods - (&x).m /* ERROR \*P has no field or method m */ () + (&x).m /* ERROR type \*P is pointer to interface, not interface */ () } @@ -29,7 +29,7 @@ type T2 interface{ m() } func _(x *T2) { // x.m doesn't exists because x is of type *T2 // and pointers to interfaces don't have methods - x.m /* ERROR \*T2 has no field or method m */() + x.m /* ERROR type \*T2 is pointer to interface, not interface */() } // Test case 1 from issue diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48312.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48312.go2 new file mode 100644 index 0000000000..6e5911d0aa --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48312.go2 @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T interface{ m() } +type P *T + +func _(p *T) { + p.m /* ERROR type \*T is pointer to interface, not interface */ () +} + +func _(p P) { + p.m /* ERROR type P is pointer to interface, not interface */ () +} + +func _[P T](p *P) { + p.m /* ERROR type \*P is pointer to interface, not interface */ () +} diff --git a/src/go/types/call.go b/src/go/types/call.go index ec6efd2379..a904b3df91 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -533,40 +533,52 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) if obj == nil { - switch { - case index != nil: + if index != nil { // TODO(gri) should provide actual type where the conflict happens check.errorf(e.Sel, _AmbiguousSelector, "ambiguous selector %s.%s", x.expr, sel) - case indirect: - check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ) - default: - var why string - if tpar, _ := x.typ.(*TypeParam); tpar != nil { - // Type parameter bounds don't specify fields, so don't mention "field". - if tname := tpar.iface().obj; tname != nil { - why = check.sprintf("interface %s has no method %s", tname.name, sel) - } else { - why = check.sprintf("type bound for %s has no method %s", x.typ, sel) - } - } else { - why = check.sprintf("type %s has no field or method %s", x.typ, sel) - } - - // Check if capitalization of sel matters and provide better error message in that case. - if len(sel) > 0 { - var changeCase string - if r := rune(sel[0]); unicode.IsUpper(r) { - changeCase = string(unicode.ToLower(r)) + sel[1:] - } else { - changeCase = string(unicode.ToUpper(r)) + sel[1:] - } - if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { - why += ", but does have " + changeCase - } - } - - check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why) + goto Error } + + if indirect { + check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ) + goto Error + } + + if isInterfacePtr(x.typ) { + check.errorf(e.Sel, _InvalidMethodExpr, "%s.%s undefined (type %s is pointer to interface, not interface)", x.expr, sel, x.typ) + goto Error + } + + var why string + if tpar, _ := x.typ.(*TypeParam); tpar != nil { + // Type parameter bounds don't specify fields, so don't mention "field". + // TODO(gri) Type constraints may have accessible fields now. Revisit this. + if tname := tpar.iface().obj; tname != nil { + why = check.sprintf("interface %s has no method %s", tname.name, sel) + } else { + why = check.sprintf("type bound for %s has no method %s", x.typ, sel) + } + } else { + why = check.sprintf("type %s has no field or method %s", x.typ, sel) + } + + // Check if capitalization of sel matters and provide better error message in that case. + // TODO(gri) This code only looks at the first character but LookupFieldOrMethod should + // have an (internal) mechanism for case-insensitive lookup that we should use + // instead (see types2). + if len(sel) > 0 { + var changeCase string + if r := rune(sel[0]); unicode.IsUpper(r) { + changeCase = string(unicode.ToLower(r)) + sel[1:] + } else { + changeCase = string(unicode.ToUpper(r)) + sel[1:] + } + if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { + why += ", but does have " + changeCase + } + } + + check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why) goto Error } diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index e593351804..1b820d5403 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -422,12 +422,12 @@ func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string // an extra formatting option for types2.Type that doesn't print out // 'func'. r = strings.Replace(r, "^^func", "", -1) - } else if IsInterface(T) && !isTypeParam(T) { + } else if IsInterface(T) { if isInterfacePtr(V) { - r = fmt.Sprintf("(%s is pointer to interface, not interface)", V) + r = fmt.Sprintf("(type %s is pointer to interface, not interface)", V) } - } else if isInterfacePtr(T) && !isTypeParam(T) { - r = fmt.Sprintf("(%s is pointer to interface, not interface)", T) + } else if isInterfacePtr(T) { + r = fmt.Sprintf("(type %s is pointer to interface, not interface)", T) } if r == "" { r = fmt.Sprintf("(missing %s)", mname) @@ -437,7 +437,7 @@ func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string func isInterfacePtr(T Type) bool { p, _ := under(T).(*Pointer) - return p != nil && IsInterface(p.base) && !isTypeParam(T) + return p != nil && IsInterface(p.base) } // assertableTo reports whether a value of type V can be asserted to have type T. diff --git a/src/go/types/operand.go b/src/go/types/operand.go index 06ecbf1410..d669981cf2 100644 --- a/src/go/types/operand.go +++ b/src/go/types/operand.go @@ -308,7 +308,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er if check != nil && compilerErrorMessages { if isInterfacePtr(Tu) { if reason != nil { - *reason = check.sprintf("%s does not implement %s (%s is pointer to interface, not interface)", x.typ, T, T) + *reason = check.sprintf("%s does not implement %s (type %s is pointer to interface, not interface)", x.typ, T, T) } return false, _InvalidIfaceAssign } diff --git a/src/go/types/testdata/check/methodsets.src b/src/go/types/testdata/check/methodsets.src index 9fb10deb9a..b0eb14cf50 100644 --- a/src/go/types/testdata/check/methodsets.src +++ b/src/go/types/testdata/check/methodsets.src @@ -196,9 +196,9 @@ func issue5918() { _ func(error) string = error.Error perr = &err - _ = perr.Error /* ERROR "no field or method" */ () - _ func() string = perr.Error /* ERROR "no field or method" */ - _ func(*error) string = (*error).Error /* ERROR "no field or method" */ + _ = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ () + _ func() string = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ + _ func(*error) string = (*error).Error /* ERROR "type \*error is pointer to interface, not interface" */ ) type T *interface{ m() int } @@ -207,8 +207,8 @@ func issue5918() { _ = (*x).m() _ = (*x).m - _ = x.m /* ERROR "no field or method" */ () - _ = x.m /* ERROR "no field or method" */ - _ = T.m /* ERROR "no field or method" */ + _ = x.m /* ERROR "type T is pointer to interface, not interface" */ () + _ = x.m /* ERROR "type T is pointer to interface, not interface" */ + _ = T.m /* ERROR "type T is pointer to interface, not interface" */ ) } diff --git a/src/go/types/testdata/fixedbugs/issue47747.go2 b/src/go/types/testdata/fixedbugs/issue47747.go2 index 6a2e787bf9..edde497f5a 100644 --- a/src/go/types/testdata/fixedbugs/issue47747.go2 +++ b/src/go/types/testdata/fixedbugs/issue47747.go2 @@ -20,7 +20,7 @@ func _[P interface{ m() }](x P) { x.m() // (&x).m doesn't exist because &x is of type *P // and pointers to type parameters don't have methods - (&x).m /* ERROR \*P has no field or method m */ () + (&x).m /* ERROR type \*P is pointer to interface, not interface */ () } @@ -29,7 +29,7 @@ type T2 interface{ m() } func _(x *T2) { // x.m doesn't exists because x is of type *T2 // and pointers to interfaces don't have methods - x.m /* ERROR \*T2 has no field or method m */() + x.m /* ERROR type \*T2 is pointer to interface, not interface */() } // Test case 1 from issue diff --git a/src/go/types/testdata/fixedbugs/issue48312.go2 b/src/go/types/testdata/fixedbugs/issue48312.go2 new file mode 100644 index 0000000000..6e5911d0aa --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue48312.go2 @@ -0,0 +1,20 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T interface{ m() } +type P *T + +func _(p *T) { + p.m /* ERROR type \*T is pointer to interface, not interface */ () +} + +func _(p P) { + p.m /* ERROR type P is pointer to interface, not interface */ () +} + +func _[P T](p *P) { + p.m /* ERROR type \*P is pointer to interface, not interface */ () +} diff --git a/test/fixedbugs/issue48471.go b/test/fixedbugs/issue48471.go index 88caeede15..ba6245ab41 100644 --- a/test/fixedbugs/issue48471.go +++ b/test/fixedbugs/issue48471.go @@ -32,7 +32,7 @@ func g() { i = new(T2) // ERROR "cannot use new\(T2\) \(.*type \*T2\) as type I in assignment:\n\t\*T2 does not implement I \(missing M method\)\n\t\thave m\(int\)\n\t\twant M\(int\)" i = new(T3) // ERROR "cannot use new\(T3\) \(.*type \*T3\) as type I in assignment:\n\t\*T3 does not implement I \(wrong type for M method\)\n\t\thave M\(string\)\n\t\twant M\(int\)" i = T4{} // ERROR "cannot use T4\{\} \(.*type T4\) as type I in assignment:\n\tT4 does not implement I \(M method has pointer receiver\)" - i = new(I) // ERROR "cannot use new\(I\) \(.*type \*I\) as type I in assignment:\n\t\*I does not implement I \(\*I is pointer to interface, not interface\)" + i = new(I) // ERROR "cannot use new\(I\) \(.*type \*I\) as type I in assignment:\n\t\*I does not implement I \(type \*I is pointer to interface, not interface\)" _ = i.(*T2) // ERROR "impossible type assertion: i.\(\*T2\)\n\t\*T2 does not implement I \(missing M method\)\n\t\thave m\(int\)\n\t\twant M\(int\)" _ = i.(*T3) // ERROR "impossible type assertion: i.\(\*T3\)\n\t\*T3 does not implement I \(wrong type for M method\)\n\t\thave M\(string\)\n\t\twant M\(int\)" var t *T4 diff --git a/test/method2.go b/test/method2.go index 2a92136d6c..0a497b4b84 100644 --- a/test/method2.go +++ b/test/method2.go @@ -28,7 +28,7 @@ type Val interface { val() int } -var _ = (*Val).val // ERROR "method" +var _ = (*Val).val // ERROR "method|type \*Val is pointer to interface, not interface" var v Val var pv = &v