mirror of https://github.com/golang/go.git
go/types: more steps towards contract type checking
- moved type parameter collection out of resolver and into decl phase - collect (meta-) type information (contracts) for type parameters - first cut at checking contract satisfaction (methods only for now) Change-Id: I46707969a172423738171aaea9d5282fb4b25a44
This commit is contained in:
parent
590e4ea3f0
commit
b87cfd9558
|
|
@ -2763,7 +2763,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.
|
|||
lbrack := p.pos
|
||||
p.next()
|
||||
if p.tok == token.TYPE {
|
||||
// parametrized type
|
||||
// parameterized type
|
||||
p.next()
|
||||
p.openScope()
|
||||
tparams := p.parseTypeParams(p.topScope)
|
||||
|
|
@ -2789,7 +2789,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.
|
|||
lparen := p.pos
|
||||
p.next()
|
||||
if p.tok == token.TYPE {
|
||||
// parametrized type
|
||||
// parameterized type
|
||||
p.next()
|
||||
p.openScope()
|
||||
tparams := p.parseTypeParams(p.topScope)
|
||||
|
|
|
|||
|
|
@ -15,21 +15,15 @@ import (
|
|||
// won't exclude a contract where we only permit a type. Investigate.
|
||||
|
||||
func (check *Checker) contractType(contr *Contract, e *ast.ContractType) {
|
||||
pos := e.Lbrace
|
||||
if len(e.TParams) > 0 {
|
||||
pos = e.TParams[0].Pos()
|
||||
}
|
||||
scope := NewScope(check.scope, pos, e.Rbrace, "contract type parameters")
|
||||
check.scope = scope
|
||||
check.openScope(e, "contract")
|
||||
defer check.closeScope()
|
||||
check.recordScope(e, scope)
|
||||
|
||||
// collect type parameters
|
||||
tparams := make([]*TypeName, len(e.TParams))
|
||||
for index, name := range e.TParams {
|
||||
tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
|
||||
NewTypeParam(tpar, index) // assigns type to tpar as a side-effect
|
||||
check.declare(scope, name, tpar, scope.pos)
|
||||
NewTypeParam(tpar, index, nil) // assigns type to tpar as a side-effect
|
||||
check.declare(check.scope, name, tpar, check.scope.pos)
|
||||
tparams[index] = tpar
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +44,7 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) {
|
|||
if c.Param != nil {
|
||||
// If a type name is present, it must be one of the contract's type parameters.
|
||||
pos := c.Param.Pos()
|
||||
obj := scope.Lookup(c.Param.Name)
|
||||
obj := check.scope.Lookup(c.Param.Name)
|
||||
if obj == nil {
|
||||
check.errorf(pos, "%s not declared by contract", c.Param.Name)
|
||||
continue
|
||||
|
|
@ -159,13 +153,20 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) {
|
|||
ifaces[tpar] = &emptyInterface
|
||||
} else {
|
||||
var mset objset
|
||||
i := 0
|
||||
for _, m := range iface.methods {
|
||||
if m0 := mset.insert(m); m0 != nil {
|
||||
// A method with the same name exists already.
|
||||
check.errorf(m.Pos(), "method %s already declared", m.name)
|
||||
check.reportAltDecl(m0)
|
||||
} else {
|
||||
// only keep unique methods
|
||||
// TODO(gri) revisit this code - introduced to fix large rebase
|
||||
iface.methods[i] = m
|
||||
i++
|
||||
}
|
||||
}
|
||||
iface.methods = iface.methods[:i]
|
||||
sort.Sort(byUniqueMethodName(iface.methods))
|
||||
iface.Complete()
|
||||
}
|
||||
|
|
@ -233,3 +234,44 @@ func (check *Checker) typeConstraint(typ Type, why *string) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// satisfyContract reports whether the given type arguments satisfy a contract.
|
||||
// The contract may be nil, in which case it is always satisfied.
|
||||
// The number of type arguments must match the number of contract type parameters.
|
||||
// TODO(gri) missing: good error reporting
|
||||
func (check *Checker) satisfyContract(contr *Contract, targs []Type) bool {
|
||||
if contr == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
assert(len(contr.TParams) == len(targs))
|
||||
|
||||
// A contract is represented by a list of interfaces, one for each
|
||||
// contract type parameter. Each of those interfaces may be parameterized
|
||||
// with any of the other contract type parameters.
|
||||
// We need to verify that each type argument implements its respective
|
||||
// contract interface, but only after substituting any contract type parameters
|
||||
// in that interface with the respective type arguments.
|
||||
//
|
||||
// TODO(gri) This implementation strategy (and implementation) would be
|
||||
// much more direct if we were to replace contracts simply with (parameterized)
|
||||
// interfaces. All the existing machinery would simply fall into place.
|
||||
|
||||
for i, targ := range targs {
|
||||
iface := contr.ifaceAt(i)
|
||||
if iface == nil {
|
||||
continue // no constraints
|
||||
}
|
||||
// If iface is parameterized, we need to replace the type parameters
|
||||
// with the respective type arguments.
|
||||
if isParameterized(iface) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
// targ must implement iface
|
||||
if m, _ := check.missingMethod(targ, iface, true); m != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -546,14 +546,14 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
|||
check.validType(obj.typ, nil)
|
||||
})
|
||||
|
||||
if obj.IsParameterized() {
|
||||
assert(obj.scope != nil)
|
||||
check.scope = obj.scope // push type parameter scope
|
||||
}
|
||||
|
||||
if tdecl.Assign.IsValid() {
|
||||
// type alias declaration
|
||||
|
||||
if tdecl.TParams != nil {
|
||||
check.errorf(tdecl.TParams.Pos(), "type alias cannot be parameterized")
|
||||
// continue but ignore type parameters
|
||||
}
|
||||
|
||||
obj.typ = Typ[Invalid]
|
||||
obj.typ = check.typ(tdecl.Type)
|
||||
|
||||
|
|
@ -564,6 +564,11 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
|||
def.setUnderlying(named)
|
||||
obj.typ = named // make sure recursive type declarations terminate
|
||||
|
||||
if tdecl.TParams != nil {
|
||||
check.openScope(tdecl, "type parameters")
|
||||
obj.tparams = check.collectTypeParams(tdecl.TParams)
|
||||
}
|
||||
|
||||
// determine underlying type of named
|
||||
named.orig = check.definedType(tdecl.Type, named)
|
||||
|
||||
|
|
@ -582,17 +587,46 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
|||
// any forward chain.
|
||||
named.underlying = check.underlying(named)
|
||||
|
||||
}
|
||||
// this must happen before addMethodDecls - cannot use defer
|
||||
// TODO(gri) consider refactoring this
|
||||
if tdecl.TParams != nil {
|
||||
check.closeScope()
|
||||
}
|
||||
|
||||
// this must happen before addMethodDecls - cannot use defer
|
||||
// TODO(gri) consider refactoring this
|
||||
if obj.IsParameterized() {
|
||||
check.closeScope()
|
||||
}
|
||||
|
||||
check.addMethodDecls(obj)
|
||||
}
|
||||
|
||||
func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeName) {
|
||||
for _, f := range list.List {
|
||||
var contr *Contract
|
||||
if f.Type != nil {
|
||||
typ := check.typ(f.Type)
|
||||
if typ != Typ[Invalid] {
|
||||
if contr, _ = typ.Underlying().(*Contract); contr != nil {
|
||||
if len(f.Names) != len(contr.TParams) {
|
||||
// TODO(gri) improve error message
|
||||
check.errorf(f.Type.Pos(), "%d type parameters but contract expects %d", len(f.Names), len(contr.TParams))
|
||||
contr = nil // cannot use this contract
|
||||
}
|
||||
} else {
|
||||
check.errorf(f.Type.Pos(), "%s is not a contract", typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names {
|
||||
tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
|
||||
NewTypeParam(tpar, len(tparams), contr) // assigns type to tpar as a side-effect
|
||||
check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) verify scope pos is correct
|
||||
tparams = append(tparams, tpar)
|
||||
}
|
||||
}
|
||||
|
||||
return tparams
|
||||
}
|
||||
|
||||
func (check *Checker) addMethodDecls(obj *TypeName) {
|
||||
// get associated methods
|
||||
// (Checker.collectObjects only collects methods with non-blank names;
|
||||
|
|
@ -659,18 +693,18 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
|||
// func declarations cannot use iota
|
||||
assert(check.iota == nil)
|
||||
|
||||
if obj.IsParameterized() {
|
||||
assert(obj.scope != nil)
|
||||
check.scope = obj.scope // push type parameter scope
|
||||
fdecl := decl.fdecl
|
||||
if fdecl.TParams != nil {
|
||||
check.openScope(fdecl, "type parameters")
|
||||
obj.tparams = check.collectTypeParams(fdecl.TParams)
|
||||
}
|
||||
|
||||
sig := new(Signature)
|
||||
obj.typ = sig // guard against cycles
|
||||
fdecl := decl.fdecl
|
||||
check.funcType(sig, fdecl.Recv, fdecl.Type)
|
||||
sig.tparams = obj.tparams
|
||||
|
||||
if obj.IsParameterized() {
|
||||
if fdecl.TParams != nil {
|
||||
check.closeScope()
|
||||
}
|
||||
|
||||
|
|
@ -795,9 +829,6 @@ func (check *Checker) declStmt(decl ast.Decl) {
|
|||
|
||||
case *ast.TypeSpec:
|
||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||
if s.TParams != nil {
|
||||
obj.scope, obj.tparams = check.collectTypeParams(check.scope, s, s.TParams)
|
||||
}
|
||||
// spec: "The scope of a type identifier declared inside a function
|
||||
// begins at the identifier in the TypeSpec and ends at the end of
|
||||
// the innermost containing block."
|
||||
|
|
|
|||
|
|
@ -123,12 +123,22 @@ func isParameterized(typ Type) bool {
|
|||
}
|
||||
|
||||
case *Signature:
|
||||
assert(t.tparams == nil) // TODO(gri) is this correct?
|
||||
assert(t.recv == nil || !isParameterized(t.recv.typ)) // interface method receiver may not be nil
|
||||
assert(t.tparams == nil) // TODO(gri) is this correct?
|
||||
// TODO(gri) Rethink check below: contract interfaces
|
||||
// have methods where the receiver is a contract type
|
||||
// parameter, by design.
|
||||
//assert(t.recv == nil || !isParameterized(t.recv.typ))
|
||||
return isParameterized(t.params) || isParameterized(t.results)
|
||||
|
||||
case *Interface:
|
||||
panic("unimplemented")
|
||||
if t.allMethods == nil {
|
||||
panic("incomplete method")
|
||||
}
|
||||
for _, m := range t.allMethods {
|
||||
if isParameterized(m.typ) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *Map:
|
||||
return isParameterized(t.key) || isParameterized(t.elem)
|
||||
|
|
|
|||
|
|
@ -213,7 +213,6 @@ func (*Const) isDependency() {} // a constant may be a dependency of an initiali
|
|||
// A TypeName represents a name for a (defined or alias) type.
|
||||
type TypeName struct {
|
||||
object
|
||||
scope *Scope // type parameter scope; or nil
|
||||
tparams []*TypeName // type parameters from left to right; or nil
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +224,7 @@ type TypeName struct {
|
|||
// argument for NewNamed, which will set the TypeName's type as a side-
|
||||
// effect.
|
||||
func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, nil, nil}
|
||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, nil}
|
||||
}
|
||||
|
||||
// IsParameterized reports whether obj is a parametrized type.
|
||||
|
|
@ -312,7 +311,6 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa
|
|||
type Func struct {
|
||||
object
|
||||
hasPtrRecv bool // only valid for methods that don't have a type yet
|
||||
scope *Scope // type parameter scope; or nil
|
||||
tparams []*TypeName // type parameters from left to right; or nil
|
||||
}
|
||||
|
||||
|
|
@ -324,7 +322,7 @@ func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
|
|||
if sig != nil {
|
||||
typ = sig
|
||||
}
|
||||
return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, false, nil, nil}
|
||||
return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}, false, nil}
|
||||
}
|
||||
|
||||
// FullName returns the package- or receiver-type-qualified name of
|
||||
|
|
|
|||
|
|
@ -393,9 +393,6 @@ func (check *Checker) collectObjects() {
|
|||
|
||||
case *ast.TypeSpec:
|
||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||
if s.TParams != nil {
|
||||
obj.scope, obj.tparams = check.collectTypeParams(pkg.scope, s, s.TParams)
|
||||
}
|
||||
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})
|
||||
|
||||
default:
|
||||
|
|
@ -428,9 +425,6 @@ func (check *Checker) collectObjects() {
|
|||
check.softErrorf(obj.pos, "missing function body")
|
||||
}
|
||||
} else {
|
||||
if d.TParams != nil {
|
||||
obj.scope, obj.tparams = check.collectTypeParams(pkg.scope, d, d.TParams)
|
||||
}
|
||||
check.declare(pkg.scope, d.Name, obj, token.NoPos)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -446,11 +440,12 @@ func (check *Checker) collectObjects() {
|
|||
// - if the receiver type is parameterized but we don't need the parameters, we permit leaving them away
|
||||
// - this is a effectively a declaration, and thus a receiver type parameter may be the blank identifier (_)
|
||||
// - since methods cannot have other type parameters, we store receiver type parameters where function type parameters would be
|
||||
// TODO(gri) move this into decl phase? (like we did for type and func type parameters?)
|
||||
ptr, recv, tparams := check.unpackRecv(d.Recv.List[0].Type)
|
||||
if tparams != nil {
|
||||
obj.scope = NewScope(pkg.scope, d.Pos(), d.End(), "receiver type parameters")
|
||||
check.recordScope(d, obj.scope)
|
||||
obj.tparams = check.declareTypeParams(obj.scope, tparams)
|
||||
scope := NewScope(pkg.scope, d.Pos(), d.End(), "receiver type parameters")
|
||||
check.recordScope(d, scope)
|
||||
obj.tparams = check.declareTypeParams(scope, tparams)
|
||||
}
|
||||
|
||||
// (Methods with invalid receiver cannot be associated to a type, and
|
||||
|
|
@ -509,25 +504,11 @@ func (check *Checker) collectObjects() {
|
|||
}
|
||||
}
|
||||
|
||||
func (check *Checker) collectTypeParams(parent *Scope, node ast.Node, list *ast.FieldList) (scope *Scope, tparams []*TypeName) {
|
||||
scope = NewScope(parent, node.Pos(), node.End(), "type parameters")
|
||||
check.recordScope(node, scope)
|
||||
|
||||
var names []*ast.Ident
|
||||
for _, f := range list.List {
|
||||
for _, name := range f.Names {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
|
||||
return scope, check.declareTypeParams(scope, names)
|
||||
}
|
||||
|
||||
func (check *Checker) declareTypeParams(scope *Scope, list []*ast.Ident) []*TypeName {
|
||||
tparams := make([]*TypeName, len(list))
|
||||
for i, name := range list {
|
||||
tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
|
||||
NewTypeParam(tpar, i) // assigns type to tpar as a side-effect
|
||||
NewTypeParam(tpar, i, nil) // assigns type to tpar as a side-effect
|
||||
check.declare(scope, name, tpar, scope.pos)
|
||||
tparams[i] = tpar
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,9 +152,9 @@ func (check *Checker) multipleDefaults(list []ast.Stmt) {
|
|||
}
|
||||
}
|
||||
|
||||
func (check *Checker) openScope(s ast.Stmt, comment string) {
|
||||
scope := NewScope(check.scope, s.Pos(), s.End(), comment)
|
||||
check.recordScope(s, scope)
|
||||
func (check *Checker) openScope(node ast.Node, comment string) {
|
||||
scope := NewScope(check.scope, node.Pos(), node.End(), comment)
|
||||
check.recordScope(node, scope)
|
||||
check.scope = scope
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,17 +59,45 @@ contract _() {
|
|||
// --------------------------------------------------------------------------------------
|
||||
// Contract implementation
|
||||
|
||||
// Type parameter type must be a contract.
|
||||
type _(type T int /* ERROR not a contract */ ) struct{}
|
||||
|
||||
// The number of type parameters must match the number of contract parameters.
|
||||
contract C0() {}
|
||||
|
||||
type _(type A C0 /* ERROR 1 type parameters */ ) struct{}
|
||||
|
||||
contract C2(A, B) {
|
||||
A a()
|
||||
}
|
||||
|
||||
type _(type A C2 /* ERROR 1 type parameters */ ) struct{}
|
||||
type _(type A, B C2) struct{}
|
||||
type _(type A, B, C C2 /* ERROR 3 type parameters */ ) struct{}
|
||||
|
||||
// Type instantiation must satisfy the contract.
|
||||
|
||||
type T1(type _, _ C2) struct{}
|
||||
|
||||
type A struct{}
|
||||
|
||||
func (A) a() {}
|
||||
|
||||
var _ T1 /* ERROR not satisfied */ (int, int)
|
||||
var _ T1(A, int)
|
||||
|
||||
contract Stringer(T) {
|
||||
T String() string
|
||||
}
|
||||
|
||||
type List(type T Stringer) struct{
|
||||
data T
|
||||
link *List(T)
|
||||
// TODO(gri) cannot handle this yet
|
||||
//link *List(T)
|
||||
}
|
||||
|
||||
var _ List(MyData)
|
||||
var _ List(int) // TODO(gri) should get an error here: int doesn't implement the Stringer contract
|
||||
var _ List /* ERROR not satisfied */ (int)
|
||||
|
||||
type MyData string
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package p
|
||||
|
||||
func f(type T)([]*T) int
|
||||
// Type instantiation must satisfy the contract.
|
||||
type Stringer contract(T) {
|
||||
T String() string
|
||||
}
|
||||
|
||||
var _ = f(nil) // ERROR cannot infer
|
||||
type List(type T Stringer) struct{}
|
||||
|
||||
func g(type T)(T) int
|
||||
|
||||
var _ = g(nil) // ERROR cannot infer
|
||||
|
||||
func h(type T)(T, T) int
|
||||
|
||||
var _ = h(nil /* ERROR cannot convert */, uintptr(1))
|
||||
var _ List /* ERROR not satisfied */ (int)
|
||||
|
|
@ -17,17 +17,8 @@ type T2(type P) struct {
|
|||
|
||||
type List(type P) []P
|
||||
|
||||
type A1(type P) = P
|
||||
|
||||
type A2(type P) = struct {
|
||||
f P
|
||||
g int
|
||||
}
|
||||
|
||||
type A3(type P) = struct {
|
||||
f P
|
||||
g myInt // myInt should still be in scope chain
|
||||
}
|
||||
// Alias type declarations cannot have parameters.
|
||||
type A1( /* ERROR cannot be parameterized */ type P) = P /* ERROR undeclared */
|
||||
|
||||
// Parametrized type instantiations
|
||||
|
||||
|
|
@ -42,12 +33,6 @@ type _ T1 /* ERROR got 0 arguments but 1 type parameters */ ()
|
|||
type _ T1(x /* ERROR not a type */ )
|
||||
type _ T1 /* ERROR got 2 arguments but 1 type parameters */ (int, float32)
|
||||
|
||||
// TODO(gri) parameterized alias types don't work correctly
|
||||
// var _ A1(int) = x
|
||||
// var _ A1(float32) = x // ERROR cannot use x .* as float32
|
||||
|
||||
// var _ T2(*int) = A2(*int){}
|
||||
// var _ T2(*int) = A2 /* ERROR cannot use */ (int){}
|
||||
var _ T2(int) = T2(int){}
|
||||
|
||||
var _ List(int) = []int{1, 2, 3}
|
||||
|
|
|
|||
|
|
@ -513,15 +513,26 @@ type Contract struct {
|
|||
IFaces map[*TypeName]*Interface
|
||||
}
|
||||
|
||||
// ifaceAt returns the interface matching for the respective
|
||||
// contract type parameter with the given index. If c is nil
|
||||
// the result is nil.
|
||||
func (c *Contract) ifaceAt(index int) *Interface {
|
||||
if c != nil {
|
||||
return c.IFaces[c.TParams[index]]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A TypeParam represents a type parameter type.
|
||||
type TypeParam struct {
|
||||
obj *TypeName
|
||||
index int
|
||||
contr *Contract // nil if no contract
|
||||
}
|
||||
|
||||
// NewTypeParam returns a new TypeParam.
|
||||
func NewTypeParam(obj *TypeName, index int) *TypeParam {
|
||||
typ := &TypeParam{obj, index}
|
||||
func NewTypeParam(obj *TypeName, index int, contr *Contract) *TypeParam {
|
||||
typ := &TypeParam{obj, index, contr}
|
||||
if obj.typ == nil {
|
||||
obj.typ = typ
|
||||
}
|
||||
|
|
|
|||
|
|
@ -457,8 +457,6 @@ func (check *Checker) typeList(list []ast.Expr) []Type {
|
|||
}
|
||||
|
||||
func (check *Checker) parameterizedType(typ *Parameterized, e *ast.CallExpr) bool {
|
||||
// TODO(gri) This code cannot handle type aliases at the moment.
|
||||
// Probably need to do the name lookup here.
|
||||
t := check.typ(e.Fun)
|
||||
if t == Typ[Invalid] {
|
||||
return false // error already reported
|
||||
|
|
@ -485,6 +483,23 @@ func (check *Checker) parameterizedType(typ *Parameterized, e *ast.CallExpr) boo
|
|||
return false
|
||||
}
|
||||
|
||||
// TODO(gri) quick hack - clean this up
|
||||
// Also, it looks like contract should be part of the parameterized type,
|
||||
// not its individual type parameters, at least as long as we only permit
|
||||
// one contract. If we permit multiple contracts C1, C2 as in
|
||||
//
|
||||
// type _(type A, B C1, B C2, ...)
|
||||
//
|
||||
// the current approach may be the right one. The current approach also
|
||||
// lends itself more easily to a design where we just use interfaces
|
||||
// rather than contracts.
|
||||
assert(len(tname.tparams) > 0)
|
||||
contr := tname.tparams[0].typ.(*TypeParam).contr
|
||||
if !check.satisfyContract(contr, args) {
|
||||
// TODO(gri) need to put in some work for really good error messages here
|
||||
check.errorf(e.Pos(), "contract for %s is not satisfied", tname)
|
||||
}
|
||||
|
||||
// complete parameterized type
|
||||
typ.tname = tname
|
||||
typ.targs = args
|
||||
|
|
|
|||
Loading…
Reference in New Issue