From 75897ab2703854a5e033a6c942e6da71081cd429 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Sun, 15 Dec 2019 17:56:42 -0800 Subject: [PATCH] go/types: instantiate contracts when used as type bounds Change-Id: I1b9ecf6007b485ca53096134fa875a9bf6fcfa73 --- src/go/types/NOTES | 3 +-- src/go/types/decl.go | 39 +++++++++++++++++++++----------- src/go/types/testdata/linalg.go2 | 4 +--- src/go/types/testdata/tmp.go2 | 3 +-- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 677c9a3b8b..468cad8d6d 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -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 diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 2763d9dd52..b8cd893115 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -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 diff --git a/src/go/types/testdata/linalg.go2 b/src/go/types/testdata/linalg.go2 index 3a8dfcc363..3239065afc 100644 --- a/src/go/types/testdata/linalg.go2 +++ b/src/go/types/testdata/linalg.go2 @@ -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. diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index 3c4dc575c8..e97bd2edd7 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -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() } \ No newline at end of file