diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 566aee75c1..3f2758c91a 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -12,6 +12,7 @@ TODO - use []*TypeParam for tparams in subst? (unclear) OPEN ISSUES +- a contract that is used earlier than its declaration may not be set up yet - instantiating a parameterized function type w/o value or result parameters may have unexpected side-effects (we don't make a copy of the signature in some cases) - investigate - using a contract and enumerating type arguments currently leads to an error (e.g. func f(type T C(T)) (x T) ... ) diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index db99113318..63458a8f1b 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -126,10 +126,42 @@ func (check *Checker) contractDecl(obj *Contract, cdecl *ast.ContractSpec) { if econtr == nil { check.invalidAST(c.Types[0].Pos(), "invalid embedded contract %s", econtr) } - etyp := check.typ(c.Types[0]) - _ = etyp - // TODO(gri) complete this - check.errorf(c.Types[0].Pos(), "%s: contract embedding not yet implemented", c.Types[0]) + // Handle contract lookup so we don't need to set up a special contract mode + // for operands just to carry its information through in form of some contract Type. + // TODO(gri) this code is also in collectTypeParams (decl.go) - factor out! + if ident, ok := unparen(econtr.Fun).(*ast.Ident); ok { + if eobj, _ := check.lookup(ident.Name).(*Contract); eobj != nil { + // TODO(gri) must set up contract if not yet done! + // eobj is a valid contract + // TODO(gri) look for contract cycles! + // contract arguments must match the embedded contract's parameters + if len(econtr.Args) != len(eobj.TParams) { + check.errorf(c.Types[0].Pos(), "%d type parameters but contract expects %d", len(econtr.Args), len(eobj.TParams)) + continue + } + // contract arguments must be type parameters + // For now, they must be from the enclosing contract. + // TODO(gri) can we allow any type parameter? + targs := make([]Type, len(econtr.Args)) + for i, arg := range econtr.Args { + targ := check.typ(arg) + if parg, _ := targ.(*TypeParam); parg != nil { + // TODO(gri) check that targ is a parameter from the enclosing contract + targs[i] = targ + } else { + check.errorf(arg.Pos(), "%s is not a type parameter", arg) + } + } + // TODO(gri) - implement the steps below + // - for each eobj type parameter, determine its (interface) bound + // - substitute that type parameter with the actual type argument in that interface + // - add the interface as am embedded interface to the bound matching the actual type argument + // - tests! (incl. overlapping methods, etc.) + check.errorf(c.Types[0].Pos(), "%s: contract embedding not yet implemented", c.Types[0]) + continue + } + } + check.errorf(c.Types[0].Pos(), "%s is not a contract", c.Types[0]) } } diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 4477b27ff1..54a71834af 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -636,6 +636,7 @@ func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeNam goto next } // obj is a valid contract + // TODO(gri) must set up contract if not yet done! // Use contract's matching type parameter bound and // instantiate it with the actual type parameters // (== targs) present. diff --git a/src/go/types/testdata/tmp.go2 b/src/go/types/testdata/tmp.go2 index cbb358b144..5deb0e4605 100644 --- a/src/go/types/testdata/tmp.go2 +++ b/src/go/types/testdata/tmp.go2 @@ -3,3 +3,13 @@ // license that can be found in the LICENSE file. package p + +contract C1(a, b, c) { + a a() + b b() + c c() +} + +contract C2(a, b) { + C1 /* ERROR contract embedding */ (a, b, a) +}