go/types: instantiate contracts when used as type bounds

Change-Id: I1b9ecf6007b485ca53096134fa875a9bf6fcfa73
This commit is contained in:
Robert Griesemer 2019-12-15 17:56:42 -08:00
parent 67b83f1382
commit 75897ab270
4 changed files with 29 additions and 20 deletions

View File

@ -7,8 +7,7 @@ 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) ... )
- the above problem is the underlying problem why contracts are not properly "instantiated" when used as type bounds
- contracts slip through in places where only types are permitted
git add - contracts slip through in places where only types are permitted
- conversions against parameterized types are not implemented
- parameterized interface methods (of type bounds) need to be customized (subst) for context

View File

@ -606,16 +606,15 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
// type parameters are all declared early (it's not observable).
index := 0
for _, f := range list.List {
for _, name := range f.Names {
tparams = append(tparams, check.declareTypeParam(name, index, nil))
index++
for i, name := range f.Names {
tparams = append(tparams, check.declareTypeParam(name, index+i, nil))
}
index += len(f.Names)
}
index = 0
for _, f := range list.List {
var bound Type = &emptyInterface
var isContract bool
if f.Type != nil {
typ := check.typ(f.Type)
if typ != Typ[Invalid] {
@ -629,7 +628,6 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
break // cannot use this contract
}
bound = typ
isContract = true
default:
check.errorf(f.Type.Pos(), "%s is not an interface or contract", typ)
}
@ -637,17 +635,32 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam
}
// set the type parameter's bound
// (do this even if bound == nil to make sure index is correctly increasing)
var targs []Type // only needed for contract instantiation; lazily allocated
for i, name := range f.Names {
tname := tparams[index]
assert(name.Name == tname.name) // catch index errors
if isContract {
tname.typ.(*TypeParam).bound = bound.Underlying().(*Contract).boundsAt(i)
} else {
tname.typ.(*TypeParam).bound = bound
assert(name.Name == tparams[index+i].name) // catch index errors
// If we have a contract, use its matching type parameter bound
// and instantiate it with the actual type parameters (== arguments)
// present.
bound := bound
if b, _ := bound.Underlying().(*Contract); b != nil {
// TODO(gri) eliminate this nil test by ensuring that all
// contract parameters have an associated bound
if bat := b.boundsAt(i); bat != nil {
if targs == nil {
targs = make([]Type, len(f.Names))
for i, tparam := range tparams[index : index+len(f.Names)] {
targs[i] = tparam.typ
}
}
bound = check.instantiate(name.Pos(), bat, targs, nil)
} else {
bound = &emptyInterface
}
}
index++
tparams[index+i].typ.(*TypeParam).bound = bound
}
index += len(f.Names)
}
return tparams

View File

@ -42,9 +42,7 @@ contract NumericAbs(T) {
// a and b, where the absolute value is determined by the Abs method.
func AbsDifference(type T NumericAbs)(a, b T) T {
d := a - b
//TODO(gri) fix this
//return d.Abs()
return d
return d.Abs()
}
// OrderedNumeric matches numeric types that support the < operator.

View File

@ -16,6 +16,5 @@ type B(type T) interface {
func AbsDifference(type T C)(a, b T) T {
d := a - b
// TODO(gri) make this work
return d /* ERROR cannot use */ .Abs()
return d.Abs()
}