// Eventually, unify should return an error with cause.
var cause string
constraint := tpar.iface()
- if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
+ if !check.hasAllMethods(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause) {
// TODO(gri) better error message (see TODO above)
err.addf(pos, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
return nil
}
// V must implement T's methods, if any.
- if m, _ := check.missingMethod(V, T, true, Identical, cause); m != nil /* !Implements(V, T) */ {
+ if !check.hasAllMethods(V, T, true, Identical, cause) /* !Implements(V, T) */ {
if cause != nil {
*cause = check.sprintf("%s does not %s %s %s", V, verb, T, *cause)
}
return m, state == wrongSig || state == ptrRecv
}
+// hasAllMethods is similar to checkMissingMethod but instead reports whether all methods are present.
+// If V is not a valid type, or if it is a struct containing embedded fields with invalid types, the
+// result is true because it is not possible to say with certainty whether a method is missing or not
+// (an embedded field may have the method in question).
+// If the result is false and cause is not nil, *cause describes the error.
+// Use hasAllMethods to avoid follow-on errors due to incorrect types.
+func (check *Checker) hasAllMethods(V, T Type, static bool, equivalent func(x, y Type) bool, cause *string) bool {
+ if !isValid(V) {
+ return true // we don't know anything about V, assume it implements T
+ }
+ m, _ := check.missingMethod(V, T, static, equivalent, cause)
+ return m == nil || hasInvalidEmbeddedFields(V, nil)
+}
+
+// hasInvalidEmbeddedFields reports whether T is a struct (or a pointer to a struct) that contains
+// (directly or indirectly) embedded fields with invalid types.
+func hasInvalidEmbeddedFields(T Type, seen map[*Struct]bool) bool {
+ if S, _ := under(derefStructPtr(T)).(*Struct); S != nil && !seen[S] {
+ if seen == nil {
+ seen = make(map[*Struct]bool)
+ }
+ seen[S] = true
+ for _, f := range S.fields {
+ if f.embedded && (!isValid(f.typ) || hasInvalidEmbeddedFields(f.typ, seen)) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
func isInterfacePtr(T Type) bool {
p, _ := under(T).(*Pointer)
return p != nil && IsInterface(p.base)
return true
}
// TODO(gri) fix this for generalized interfaces
- m, _ := check.missingMethod(T, V, false, Identical, cause)
- return m == nil
+ return check.hasAllMethods(T, V, false, Identical, cause)
}
// newAssertableTo reports whether a value of type V can be asserted to have type T.
// Eventually, unify should return an error with cause.
var cause string
constraint := tpar.iface()
- if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
+ if !check.hasAllMethods(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause) {
// TODO(gri) better error message (see TODO above)
err.addf(posn, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
return nil
}
// V must implement T's methods, if any.
- if m, _ := check.missingMethod(V, T, true, Identical, cause); m != nil /* !Implements(V, T) */ {
+ if !check.hasAllMethods(V, T, true, Identical, cause) /* !Implements(V, T) */ {
if cause != nil {
*cause = check.sprintf("%s does not %s %s %s", V, verb, T, *cause)
}
return m, state == wrongSig || state == ptrRecv
}
+// hasAllMethods is similar to checkMissingMethod but instead reports whether all methods are present.
+// If V is not a valid type, or if it is a struct containing embedded fields with invalid types, the
+// result is true because it is not possible to say with certainty whether a method is missing or not
+// (an embedded field may have the method in question).
+// If the result is false and cause is not nil, *cause describes the error.
+// Use hasAllMethods to avoid follow-on errors due to incorrect types.
+func (check *Checker) hasAllMethods(V, T Type, static bool, equivalent func(x, y Type) bool, cause *string) bool {
+ if !isValid(V) {
+ return true // we don't know anything about V, assume it implements T
+ }
+ m, _ := check.missingMethod(V, T, static, equivalent, cause)
+ return m == nil || hasInvalidEmbeddedFields(V, nil)
+}
+
+// hasInvalidEmbeddedFields reports whether T is a struct (or a pointer to a struct) that contains
+// (directly or indirectly) embedded fields with invalid types.
+func hasInvalidEmbeddedFields(T Type, seen map[*Struct]bool) bool {
+ if S, _ := under(derefStructPtr(T)).(*Struct); S != nil && !seen[S] {
+ if seen == nil {
+ seen = make(map[*Struct]bool)
+ }
+ seen[S] = true
+ for _, f := range S.fields {
+ if f.embedded && (!isValid(f.typ) || hasInvalidEmbeddedFields(f.typ, seen)) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
func isInterfacePtr(T Type) bool {
p, _ := under(T).(*Pointer)
return p != nil && IsInterface(p.base)
return true
}
// TODO(gri) fix this for generalized interfaces
- m, _ := check.missingMethod(T, V, false, Identical, cause)
- return m == nil
+ return check.hasAllMethods(T, V, false, Identical, cause)
}
// newAssertableTo reports whether a value of type V can be asserted to have type T.
--- /dev/null
+// Copyright 2024 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
+
+import "io"
+
+// test using struct with invalid embedded field
+var _ io.Writer = W{} // no error expected here because W has invalid embedded field
+
+type W struct {
+ *bufio /* ERROR "undefined: bufio" */ .Writer
+}
+
+// test using an invalid type
+var _ interface{ m() } = &M{} // no error expected here because M is invalid
+
+type M undefined // ERROR "undefined: undefined"
+
+// test using struct with invalid embedded field and containing a self-reference (cycle)
+var _ interface{ m() } = &S{} // no error expected here because S is invalid
+
+type S struct {
+ *S
+ undefined // ERROR "undefined: undefined"
+}
+
+// test using a generic struct with invalid embedded field and containing a self-reference (cycle)
+var _ interface{ m() } = &G[int]{} // no error expected here because S is invalid
+
+type G[P any] struct {
+ *G[P]
+ undefined // ERROR "undefined: undefined"
+}