mirror of https://github.com/golang/go.git
go/types: first steps toward type-checking individual contracts
This commit is contained in:
parent
66b509a402
commit
e21d47f346
|
|
@ -465,7 +465,7 @@ type (
|
|||
type Constraint struct {
|
||||
Param *Ident // constrained type parameter; or nil (for embedded constraints)
|
||||
MName *Ident // method name; or nil
|
||||
Type Expr // embedded constraint (CallExpr), constraint type, method type (*FuncType); or nil
|
||||
Type Expr // embedded constraint (CallExpr), constraint type, or method type (*FuncType)
|
||||
}
|
||||
|
||||
// Pos and End implementations for expression/type nodes.
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ var tests = [][]string{
|
|||
{"testdata/issue28251.src"},
|
||||
{"testdata/issue6977.src"},
|
||||
{"testdata/typeparams.src"},
|
||||
{"testdata/contracts.src"},
|
||||
}
|
||||
|
||||
var fset = token.NewFileSet()
|
||||
|
|
|
|||
|
|
@ -1513,7 +1513,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
goto Error
|
||||
|
||||
case *ast.ArrayType, *ast.StructType, *ast.FuncType,
|
||||
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
|
||||
*ast.InterfaceType, *ast.MapType, *ast.ChanType, *ast.ContractType:
|
||||
x.mode = typexpr
|
||||
x.typ = check.typ(e)
|
||||
// Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
// empty contracts
|
||||
contract _(){}
|
||||
contract _(A){}
|
||||
contract _(A, B, C){}
|
||||
contract _(A, B, A /* ERROR A redeclared */ ){}
|
||||
|
||||
// For now we also allow this form of contract declaration.
|
||||
type _ contract(){}
|
||||
type _ contract(A, B, A /* ERROR A redeclared */ ){}
|
||||
|
||||
// method constraints
|
||||
contract _(A) { A } /* ERROR expected type */
|
||||
contract _(A) { A m() }
|
||||
contract _(A) { B /* ERROR B not declared by contract */ m() }
|
||||
|
||||
// type constraints
|
||||
contract _(A) { A A }
|
||||
contract _(A) { A B /* ERROR undeclared name: B */ }
|
||||
|
|
@ -111,5 +111,3 @@ var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ )
|
|||
var _ = f8(int, float64)(1, 2.3, 3.4, 4)
|
||||
|
||||
var _ = f8(int, float64)(0, 0, nil...) // test case for #18268
|
||||
|
||||
// type P contract(C) {}
|
||||
|
|
@ -497,6 +497,17 @@ func (t *Named) AddMethod(m *Func) {
|
|||
}
|
||||
}
|
||||
|
||||
// A Contract represents a contract.
|
||||
type Contract struct {
|
||||
TParams []*TypeName
|
||||
}
|
||||
|
||||
func NewContract(tparams []*TypeName) *Contract {
|
||||
return &Contract{
|
||||
TParams: tparams,
|
||||
}
|
||||
}
|
||||
|
||||
// A TypeParam represents a type parameter type.
|
||||
type TypeParam struct {
|
||||
obj *TypeName
|
||||
|
|
@ -525,6 +536,7 @@ func (t *Interface) Underlying() Type { return t }
|
|||
func (m *Map) Underlying() Type { return m }
|
||||
func (c *Chan) Underlying() Type { return c }
|
||||
func (t *Named) Underlying() Type { return t.underlying }
|
||||
func (c *Contract) Underlying() Type { return c }
|
||||
func (c *TypeParam) Underlying() Type { return c }
|
||||
|
||||
func (b *Basic) String() string { return TypeString(b, nil) }
|
||||
|
|
@ -538,4 +550,5 @@ func (t *Interface) String() string { return TypeString(t, nil) }
|
|||
func (m *Map) String() string { return TypeString(m, nil) }
|
||||
func (c *Chan) String() string { return TypeString(c, nil) }
|
||||
func (t *Named) String() string { return TypeString(t, nil) }
|
||||
func (c *Contract) String() string { return TypeString(c, nil) }
|
||||
func (c *TypeParam) String() string { return TypeString(c, nil) }
|
||||
|
|
|
|||
|
|
@ -247,6 +247,17 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
|||
}
|
||||
buf.WriteString(s)
|
||||
|
||||
case *Contract:
|
||||
buf.WriteString("contract(")
|
||||
for i, p := range t.TParams {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(p.name)
|
||||
}
|
||||
buf.WriteString("){}")
|
||||
// TODO write contract body
|
||||
|
||||
case *TypeParam:
|
||||
var s string
|
||||
if t.obj != nil {
|
||||
|
|
|
|||
|
|
@ -107,6 +107,13 @@ var independentTestTypes = []testEntry{
|
|||
dup("chan (<-chan int)"),
|
||||
dup("chan<- func()"),
|
||||
dup("<-chan []func() int"),
|
||||
|
||||
// TODO(gri) These are not recognized as contracts as long as "contract"
|
||||
// is not a keyword.
|
||||
// contracts
|
||||
// dup("contract(){}"),
|
||||
// dup("contract(A){}"),
|
||||
// dup("contract(A, B, C){}"),
|
||||
}
|
||||
|
||||
// types that depend on other type declarations (src in TestTypes)
|
||||
|
|
|
|||
|
|
@ -149,8 +149,10 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, tpar *ast
|
|||
if tpar.NumFields() != 0 {
|
||||
check.scope = NewScope(check.scope, token.NoPos, token.NoPos, "function type parameters") // TODO(gri) replace with check.openScope call
|
||||
defer check.closeScope()
|
||||
// TODO(gri) record this scope
|
||||
}
|
||||
scope := NewScope(check.scope, token.NoPos, token.NoPos, "function")
|
||||
// TODO(gri) should we close this scope?
|
||||
scope.isFunc = true
|
||||
check.recordScope(ftyp, scope)
|
||||
|
||||
|
|
@ -345,6 +347,12 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
|
|||
typ.elem = check.typ(e.Value)
|
||||
return typ
|
||||
|
||||
case *ast.ContractType:
|
||||
typ := new(Contract)
|
||||
def.setUnderlying(typ)
|
||||
check.contractType(typ, e)
|
||||
return typ
|
||||
|
||||
default:
|
||||
check.errorf(e.Pos(), "%s is not a type", e)
|
||||
}
|
||||
|
|
@ -793,3 +801,57 @@ func embeddedFieldIdent(e ast.Expr) *ast.Ident {
|
|||
}
|
||||
return nil // invalid embedded field
|
||||
}
|
||||
|
||||
func (check *Checker) contractType(ctyp *Contract, e *ast.ContractType) {
|
||||
scope := NewScope(check.scope, token.NoPos, token.NoPos, "contract type parameters")
|
||||
check.scope = scope
|
||||
defer check.closeScope()
|
||||
check.recordScope(e, scope)
|
||||
|
||||
// collect type parameters
|
||||
var tparams []*TypeName
|
||||
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)
|
||||
tparams = append(tparams, tpar)
|
||||
}
|
||||
ctyp.TParams = tparams
|
||||
|
||||
// collect constraints
|
||||
for _, c := range e.Constraints {
|
||||
if c.Param != nil {
|
||||
// If a type name is present, it must be one of the contract's type parameters.
|
||||
tpar := scope.Lookup(c.Param.Name)
|
||||
if tpar == nil {
|
||||
check.errorf(c.Param.Pos(), "%s not declared by contract", c.Param.Name)
|
||||
continue // TODO(gri) should try fall through
|
||||
}
|
||||
if c.Type == nil {
|
||||
check.invalidAST(c.Param.Pos(), "missing method or type constraint")
|
||||
continue
|
||||
}
|
||||
typ := check.typ(c.Type)
|
||||
if c.MName != nil {
|
||||
// If a method name is present, it must be unique, and c.Type
|
||||
// must must be a method signature (guaranteed by AST).
|
||||
sig, _ := typ.(*Signature)
|
||||
if sig == nil {
|
||||
check.invalidAST(c.Type.Pos(), "invalid method type")
|
||||
}
|
||||
// TODO(gri) what requirements do we have for sig.scope, sig.recv?
|
||||
} else {
|
||||
// no method name => we have a type constraint
|
||||
check.typeConstraint(typ)
|
||||
}
|
||||
} else {
|
||||
// no type name => we have an embedded contract
|
||||
panic("embedded contracts unimplemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (check *Checker) typeConstraint(typ Type) {
|
||||
// TODO(gri) verify that we have a valid type constraint
|
||||
// - determine exact rules
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue