From 118728b8c2c12445a5986e63769d9cd7edb72512 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 16 Dec 2019 16:47:33 -0800 Subject: [PATCH] go/types: steps towards customization of methods in bounds checks (snapshot) This CL introduces an update mechanism to customize method signatures with the correct type parameters before comparing them for equality. The mechanism is not correctly used, yet. Change-Id: Ib88af88e151578a3fb3a648ed70d3f462a936c9b --- src/go/types/NOTES | 7 +++---- src/go/types/decl.go | 4 ++++ src/go/types/lookup.go | 38 ++++++++++++++++++++++++++-------- src/go/types/operand.go | 2 +- src/go/types/subst.go | 10 ++++++++- src/go/types/testdata/tmp.go2 | 39 +++++++++++++++++++++++++---------- 6 files changed, 74 insertions(+), 26 deletions(-) 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