mirror of https://github.com/golang/go.git
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
This commit is contained in:
parent
e1203396e7
commit
118728b8c2
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
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))()
|
||||
}
|
||||
*/
|
||||
Loading…
Reference in New Issue