// AssertableTo reports whether a value of type V can be asserted to have type T.
func AssertableTo(V *Interface, T Type) bool {
- m, _ := assertableTo(V, T)
+ m, _ := (*Checker)(nil).assertableTo(V, T)
return m == nil
}
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
func AssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V}
- return x.assignableTo(nil, T, nil) // config not needed for non-constant x
+ return x.assignableTo(nil, T, nil) // check not needed for non-constant x
}
// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
func ConvertibleTo(V, T Type) bool {
x := operand{mode: value, typ: V}
- return x.convertibleTo(nil, T) // config not needed for non-constant x
+ return x.convertibleTo(nil, T) // check not needed for non-constant x
}
// Implements reports whether type V implements interface T.
return
}
- if reason := ""; !x.assignableTo(check.conf, T, &reason) {
+ if reason := ""; !x.assignableTo(check, T, &reason) {
if reason != "" {
check.errorf(x.pos(), "cannot use %s as %s value in %s: %s", x, T, context, reason)
} else {
// spec: "As a special case, append also accepts a first argument assignable
// to type []byte with a second argument of string type followed by ... .
// This form appends the bytes of the string.
- if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(universeByte), nil) {
+ if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check, NewSlice(universeByte), nil) {
arg(x, 1)
if x.mode == invalid {
return
return
}
- if !x.assignableTo(check.conf, m.key, nil) {
+ if !x.assignableTo(check, m.key, nil) {
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
return
}
goto Error
}
+ // methods may not have a fully set up signature yet
+ if m, _ := obj.(*Func); m != nil {
+ check.objDecl(m, nil)
+ }
+
if x.mode == typexpr {
// method expression
m, _ := obj.(*Func)
case constArg && isConstType(T):
// constant conversion
switch t := T.Underlying().(*Basic); {
- case representableConst(x.val, check.conf, t, &x.val):
+ case representableConst(x.val, check, t, &x.val):
ok = true
case isInteger(x.typ) && isString(t):
codepoint := int64(-1)
x.val = constant.MakeString(string(codepoint))
ok = true
}
- case x.convertibleTo(check.conf, T):
+ case x.convertibleTo(check, T):
// non-constant conversion
x.mode = value
ok = true
// is tricky because we'd have to run updateExprType on the argument first.
// (Issue #21982.)
-func (x *operand) convertibleTo(conf *Config, T Type) bool {
+// convertibleTo reports whether T(x) is valid.
+// The check parameter may be nil if convertibleTo is invoked through an
+// exported API call, i.e., when all methods have been type-checked.
+func (x *operand) convertibleTo(check *Checker, T Type) bool {
// "x is assignable to T"
- if x.assignableTo(conf, T, nil) {
+ if x.assignableTo(check, T, nil) {
return true
}
}
// type-check
+ // TODO(gri): This call is not needed anymore because the code can handle
+ // method signatures that have not yet been type-checked.
+ // Remove in separate CL to make it easy to isolate issues
+ // that might be introduced by this change.
check.objDecl(m, nil)
if base != nil {
// representable floating-point and complex values, and to an Int
// value for integer values; it is left alone otherwise.
// It is ok to provide the addressof the first argument for rounded.
-func representableConst(x constant.Value, conf *Config, typ *Basic, rounded *constant.Value) bool {
+//
+// The check parameter may be nil if representableConst is invoked
+// (indirectly) through an exported API call (AssignableTo, ConvertibleTo)
+// because we don't need the Checker's config for those calls.
+func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *constant.Value) bool {
if x.Kind() == constant.Unknown {
return true // avoid follow-up errors
}
+ var conf *Config
+ if check != nil {
+ conf = check.conf
+ }
+
switch {
case isInteger(typ):
x := constant.ToInt(x)
// representable checks that a constant operand is representable in the given basic type.
func (check *Checker) representable(x *operand, typ *Basic) {
assert(x.mode == constant_)
- if !representableConst(x.val, check.conf, typ, &x.val) {
+ if !representableConst(x.val, check, typ, &x.val) {
var msg string
if isNumeric(x.typ) && isNumeric(typ) {
// numeric conversion : error msg
// spec: "In any comparison, the first operand must be assignable
// to the type of the second operand, or vice versa."
err := ""
- if x.assignableTo(check.conf, y.typ, nil) || y.assignableTo(check.conf, x.typ, nil) {
+ if x.assignableTo(check, y.typ, nil) || y.assignableTo(check, x.typ, nil) {
defined := false
switch op {
case token.EQL, token.NEQ:
// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) {
- method, wrongType := assertableTo(xtyp, T)
+ method, wrongType := check.assertableTo(xtyp, T)
if method == nil {
return
}
package types
+// Internal use of LookupFieldOrMethod: If the obj result is a method
+// associated with a concrete (non-interface) type, the method's signature
+// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
+// the method's type.
+
// LookupFieldOrMethod looks up a field or method with given package and name
// in T and returns the corresponding *Var or *Func, an index sequence, and a
// bool indicating if there were any pointer indirections on the path to the
// look for a matching attached method
if i, m := lookupMethod(named.methods, pkg, name); m != nil {
// potential match
- assert(m.typ != nil)
+ // caution: method may not have a proper signature yet
index = concat(e.index, i)
if obj != nil || e.multiples {
return nil, index, false // collision
// x is of interface type V).
//
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
+ return (*Checker)(nil).missingMethod(V, T, static)
+}
+
+// missingMethod is like MissingMethod but accepts a receiver.
+// The receiver may be nil if missingMethod is invoked through
+// an exported API call (such as MissingMethod), i.e., when all
+// methods have been type-checked.
+func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
// fast path for common case
if T.Empty() {
return
for _, m := range T.allMethods {
obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
+ // we must have a method (not a field of matching function type)
f, _ := obj.(*Func)
if f == nil {
return m, false
}
+ // methods may not have a fully set up signature yet
+ if check != nil {
+ check.objDecl(f, nil)
+ }
+
if !Identical(f.typ, m.typ) {
return m, true
}
// assertableTo reports whether a value of type V can be asserted to have type T.
// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
// method required by V and whether it is missing or just has the wrong type.
-func assertableTo(V *Interface, T Type) (method *Func, wrongType bool) {
+// The receiver may be nil if assertableTo is invoked through an exported API call
+// (such as AssertableTo), i.e., when all methods have been type-checked.
+func (check *Checker) assertableTo(V *Interface, T Type) (method *Func, wrongType bool) {
// no static check is required if T is an interface
// spec: "If T is an interface type, x.(T) asserts that the
// dynamic type of x implements the interface T."
if _, ok := T.Underlying().(*Interface); ok && !strict {
return
}
- return MissingMethod(T, V, false)
+ return check.missingMethod(T, V, false)
}
// deref dereferences typ if it is a *Pointer and returns its base and true.
// assignableTo reports whether x is assignable to a variable of type T.
// If the result is false and a non-nil reason is provided, it may be set
// to a more detailed explanation of the failure (result != "").
-func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool {
+// The check parameter may be nil if assignableTo is invoked through
+// an exported API call, i.e., when all methods have been type-checked.
+func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
if x.mode == invalid || T == Typ[Invalid] {
return true // avoid spurious errors
}
return true
}
if x.mode == constant_ {
- return representableConst(x.val, conf, t, nil)
+ return representableConst(x.val, check, t, nil)
}
// The result of a comparison is an untyped boolean,
// but may not be a constant.
// T is an interface type and x implements T
if Ti, ok := Tu.(*Interface); ok {
- if m, wrongType := MissingMethod(x.typ, Ti, true); m != nil /* Implements(x.typ, Ti) */ {
+ if m, wrongType := check.missingMethod(x.typ, Ti, true); m != nil /* Implements(x.typ, Ti) */ {
if reason != nil {
if wrongType {
*reason = "wrong type for method " + m.Name()
type Named struct {
obj *TypeName // corresponding declared object
underlying Type // possibly a *Named during setup; never a *Named once set up completely
- methods []*Func // methods declared for this type (not the method set of this type)
+ methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
}
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
}
if isUntyped(x.typ) || isInteger(x.typ) {
if val := constant.ToInt(x.val); val.Kind() == constant.Int {
- if representableConst(val, check.conf, Typ[Int], nil) {
+ if representableConst(val, check, Typ[Int], nil) {
if n, ok := constant.Int64Val(val); ok && n >= 0 {
return n
}