diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 34e0b5439f..9257dca3df 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -1,5 +1,4 @@ TODO -- allow recursive type parameterization without need to repeat type parameters - if type parameters are repeated in recursive instantiation, they must be the same order (not yet checked) - implement contract embedding - interface embedding doesn't take care of literal type constraints yet @@ -7,15 +6,15 @@ TODO OPEN ISSUES - using a contract and enumerating type arguments currently leads to an error (e.g. func f(type T C(T)) (x T) ... ) -git add - contracts slip through in places where only types are permitted +- contracts slip through in places where only types are permitted - parameterized interface methods (of type bounds) need to be customized (subst) for context DESIGN DECISIONS - 12/4/2019: do not allow parenthesized generic uninstantiated types (unless instantiated implicitly) In other words: generic types must always be instantiated before they can be used in any form More generally: Only permit type instantiation T(x) in type context, when the type is a named type. - Do not permit in in general in type context: e.g., disallow []T(x) because we consider that a + Do not permit it in general in type context: e.g., disallow []T(x) because we consider that a conversion, in general. Same for ([]T)(x). - 12/12/2019: represent type bounds always as (possibly unnamed) interfaces - (contracts are for user only) + (contracts are user syntactic sugar) diff --git a/src/go/types/decl.go b/src/go/types/decl.go index b8cd893115..bf0255b1fe 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -5,6 +5,7 @@ package types import ( + "fmt" "go/ast" "go/constant" "go/token" @@ -53,6 +54,9 @@ func pathString(path []Object) string { // For the meaning of def, see Checker.definedType, in typexpr.go. func (check *Checker) objDecl(obj Object, def *Named) { if check.conf.Trace && obj.Type() == nil { + if check.indent == 0 { + fmt.Println() // empty line between top-lebvel objects for readability + } check.trace(obj.Pos(), "-- checking %s (%s, objPath = %s)", obj, obj.color(), pathString(check.objPath)) check.indent++ defer func() { diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index bb8e5edad0..30ef4f942b 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -265,7 +265,7 @@ func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) { // x is of interface type V). // func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { - m, typ := (*Checker)(nil).missingMethod(V, T, static) + m, typ := (*Checker)(nil).missingMethod(V, T, static, nil) return m, typ != nil } @@ -273,11 +273,15 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b // 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. +// If a non-nil update function is provided, it is used to update +// the method types of V before comparing them with the methods +// of V (usually be renaming type parameters so they can be +// compared). // If the type has the correctly named method, but with the wrong // signature, the existing method is returned as well. // To improve error messages, also report the wrong signature // when the method exists on *V instead of V. -func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) { +func (check *Checker) missingMethod(V Type, T *Interface, static bool, update func(Type) Type) (method, wrongType *Func) { check.completeInterface(token.NoPos, T) // fast path for common case @@ -289,16 +293,26 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, check.completeInterface(token.NoPos, ityp) // TODO(gri) allMethods is sorted - can do this more efficiently for _, m := range T.allMethods { - _, obj := lookupMethod(ityp.allMethods, m.pkg, m.name) - switch { - case obj == nil: + _, f := lookupMethod(ityp.allMethods, m.pkg, m.name) + + if f == nil { if static { return m, nil } - case !check.identical(obj.Type(), m.typ): - return m, obj + continue + } + + // update (generic) method signatures before comparing them + ftyp := f.Type() + if update != nil { + ftyp = update(ftyp) + } + + if !check.identical(ftyp, m.typ) { + return m, f } } + return } @@ -326,7 +340,13 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, check.objDecl(f, nil) } - if !check.identical(f.typ, m.typ) { + // update (generic) method signatures before comparing them + ftyp := f.typ + if update != nil { + ftyp = update(ftyp) + } + + if !check.identical(ftyp, m.typ) { return m, f } } @@ -346,7 +366,7 @@ func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Fun if _, ok := T.Underlying().(*Interface); ok && !strict { return } - return check.missingMethod(T, V, false) + return check.missingMethod(T, V, false, nil) } // deref dereferences typ if it is a *Pointer and returns its base and true. diff --git a/src/go/types/operand.go b/src/go/types/operand.go index 1f5c968ea9..28fd040d08 100644 --- a/src/go/types/operand.go +++ b/src/go/types/operand.go @@ -268,7 +268,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool { // T is an interface type and x implements T if Ti, ok := Tu.(*Interface); ok { - if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ { + if m, wrongType := check.missingMethod(V, Ti, true, nil); m != nil /* Implements(V, Ti) */ { if reason != nil { if wrongType != nil { if check.identical(m.typ, wrongType.typ) { diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 12fbc42cea..5b03a23251 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -81,8 +81,16 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist iface = check.subst(pos, iface, tparams, targs).(*Interface) //check.dump(">>> %s: iface after : %s", pos, iface) + // update targ method signatures + update := func(typ Type) Type { + // check.dump(">>> %s: sig before: %s (tparams = %s)", pos, typ, typ.(*Signature).tparams) + typ = check.subst(pos, typ, tparams, targs) + // check.dump(">>> %s: sig after : %s", pos, typ) + return typ + } + // targ must implement iface (methods) - if m, _ := check.missingMethod(targ, iface, true); m != nil { + if m, _ := check.missingMethod(targ, iface, true, update); m != nil { check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m) break } diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index e97bd2edd7..188c4f0a5d 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -4,17 +4,34 @@ package p -contract C(T) { - T int - T Abs() T -} - -type B(type T) interface { - type int +type NumericAbs(type T) interface { Abs() T } -func AbsDifference(type T C)(a, b T) T { - d := a - b - return d.Abs() -} \ No newline at end of file +func AbsDifference(type T NumericAbs(T))(x T) + +type OrderedAbs(type T) T + +func (a OrderedAbs(T)) Abs() T + +func OrderedAbsDifference(type T)(x T) { + // TODO(gri) this should work + AbsDifference /* ERROR does not satisfy */ (OrderedAbs(T)(x)) +} + +// TODO(gri) Once the code above works, check the code below. +/* +type NumericAbs(type T) interface { + Abs() T +} + +func AbsDifference(type T NumericAbs(T))() + +type OrderedAbs(type T) T + +func (a OrderedAbs(T)) Abs() T + +func OrderedAbsDifference(type T)() { + AbsDifference(OrderedAbs(T))() +} +*/ \ No newline at end of file