mirror of https://github.com/golang/go.git
go/types: interfaces as type bounds starting to work
With various loose ends. Change-Id: Idbcb4affc585f5bb14caa2045943a64a78d05d09
This commit is contained in:
parent
51d599efa2
commit
c47bad9a28
|
|
@ -684,7 +684,7 @@ func applyTypeFunc(f func(Type) Type, x Type) Type {
|
|||
TParams: []*TypeName{tpar},
|
||||
IFaces: map[*TypeName]*Interface{tpar: iface},
|
||||
}
|
||||
resTyp.contr = contr
|
||||
resTyp.bound = contr
|
||||
|
||||
return resTyp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ var tests = [][]string{
|
|||
{"testdata/map2.go2"},
|
||||
|
||||
// Go 2 prototype examples
|
||||
// {"examples/contracts.go2"}, // TODO(gri) enable
|
||||
{"examples/contracts.go2"},
|
||||
{"examples/functions.go2"},
|
||||
{"examples/types.go2"},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,7 +247,9 @@ func (check *Checker) satisfyContract(contr *Contract, targs []Type) bool {
|
|||
// with the respective type arguments.
|
||||
// TODO(gri) fix this
|
||||
if IsParameterized(iface) {
|
||||
panic("unimplemented")
|
||||
// check.dump("BEFORE iface(%s) => %s (%s)", targ, iface, fmt.Sprintf("%p", iface))
|
||||
iface = check.subst(iface, contr.TParams, targs).(*Interface)
|
||||
// check.dump("AFTER iface(%s) => %s (%s)", targ, iface, fmt.Sprintf("%p", iface))
|
||||
}
|
||||
// use interface type of type parameter, if any
|
||||
// targ must implement iface
|
||||
|
|
|
|||
|
|
@ -599,34 +599,58 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
|||
}
|
||||
|
||||
func (check *Checker) collectTypeParams(list *ast.FieldList) (tparams []*TypeName) {
|
||||
// Declare type parameters up-front.
|
||||
// If we use interfaces as type bounds, the scope of type parameters starts at
|
||||
// the beginning of the type parameter list (so we can have mutually recursive
|
||||
// parameterized interfaces). If we use contracts, it doesn't matter that the
|
||||
// type parameters are all declared early (it's not observable).
|
||||
index := 0
|
||||
for _, f := range list.List {
|
||||
var contr *Contract
|
||||
for _, name := range f.Names {
|
||||
tparams = append(tparams, check.declareTypeParam(name, index, nil))
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
index = 0
|
||||
for _, f := range list.List {
|
||||
var bound Type
|
||||
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) {
|
||||
switch b := typ.Underlying().(type) {
|
||||
case *Interface:
|
||||
bound = b
|
||||
case *Contract:
|
||||
if len(f.Names) != len(b.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
|
||||
check.errorf(f.Type.Pos(), "%d type parameters but contract expects %d", len(f.Names), len(b.TParams))
|
||||
break // cannot use this contract
|
||||
}
|
||||
} else {
|
||||
check.errorf(f.Type.Pos(), "%s is not a contract", typ)
|
||||
bound = b
|
||||
default:
|
||||
check.errorf(f.Type.Pos(), "%s is not an interface or contract", typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range f.Names {
|
||||
tparams = append(tparams, check.declareTypeParam(name, len(tparams), contr))
|
||||
// set the type parameter's bound
|
||||
if bound != nil {
|
||||
for _, name := range f.Names {
|
||||
tname := tparams[index]
|
||||
assert(name.Name == tname.name)
|
||||
tname.typ.(*TypeParam).bound = bound
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tparams
|
||||
}
|
||||
|
||||
func (check *Checker) declareTypeParam(name *ast.Ident, index int, contr *Contract) *TypeName {
|
||||
func (check *Checker) declareTypeParam(name *ast.Ident, index int, bound Type) *TypeName {
|
||||
tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
|
||||
NewTypeParam(tpar, index, contr) // assigns type to tpar as a side-effect
|
||||
NewTypeParam(tpar, index, bound) // assigns type to tpar as a side-effect
|
||||
check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
|
||||
return tpar
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,13 +45,14 @@ type Graph (type Node, Edge G) struct { /* ... */ }
|
|||
|
||||
func New (type Node, Edge G) (nodes []Node) *Graph(Node, Edge) { panic("unimplemented") }
|
||||
|
||||
func (g *Graph(Node, Edge)) ShortestPath(from, to Node) []Edge { panic("unimplemented") }
|
||||
// func (g *Graph(N, E)) ShortestPath(from, to N) []E { panic("unimplemented") }
|
||||
|
||||
// Same Graph using interface bounds instead of a contract.
|
||||
|
||||
/*
|
||||
type AltGraph (type Node NodeFace(Edge), Edge EdgeFace(Node)) struct { }
|
||||
|
||||
// func AltNew (type Node NodeFace(Edge), Edge EdgeFace(Node)) (nodes []Node) *AltGraph(Node, Edge) { panic("unimplemented") }
|
||||
|
||||
type NodeFace(type Edge) interface {
|
||||
Edges() []Edge
|
||||
}
|
||||
|
|
@ -59,4 +60,3 @@ type NodeFace(type Edge) interface {
|
|||
type EdgeFace(type Node) interface {
|
||||
Nodes() (from, to Node)
|
||||
}
|
||||
*/
|
||||
|
|
@ -106,7 +106,15 @@ func (s *subster) typ(typ Type) (res Type) {
|
|||
}
|
||||
|
||||
case *Interface:
|
||||
panic("subst not implemented for interfaces")
|
||||
// for now ignore embeddeds and types
|
||||
// TODO(gri) decide what to do
|
||||
assert(len(t.embeddeds) == 0)
|
||||
assert(len(t.types) == 0)
|
||||
if methods, copied := s.funcList(t.methods); copied {
|
||||
iface := &Interface{methods: methods}
|
||||
iface.Complete()
|
||||
return iface
|
||||
}
|
||||
|
||||
case *Map:
|
||||
key := s.typ(t.key)
|
||||
|
|
@ -222,6 +230,36 @@ func (s *subster) varList(in []*Var) (out []*Var, copied bool) {
|
|||
return
|
||||
}
|
||||
|
||||
func (s *subster) func_(f *Func) *Func {
|
||||
assert(len(f.tparams) == 0)
|
||||
if f != nil {
|
||||
if typ := s.typ(f.typ); typ != f.typ {
|
||||
copy := *f
|
||||
copy.typ = typ
|
||||
return ©
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (s *subster) funcList(in []*Func) (out []*Func, copied bool) {
|
||||
out = in
|
||||
for i, f := range in {
|
||||
if g := s.func_(f); g != f {
|
||||
if !copied {
|
||||
// first function that got substituted => allocate new out slice
|
||||
// and copy all functions
|
||||
new := make([]*Func, len(in))
|
||||
copy(new, out)
|
||||
out = new
|
||||
copied = true
|
||||
}
|
||||
out[i] = g
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func typeListString(targs []Type) string {
|
||||
var buf bytes.Buffer
|
||||
for i, arg := range targs {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ contract _() {
|
|||
// Contract implementation
|
||||
|
||||
// Type parameter type must be a contract.
|
||||
type _(type T int /* ERROR not a contract */ ) struct{}
|
||||
type _(type T int /* ERROR not an interface or contract */ ) struct{}
|
||||
|
||||
// The number of type parameters must match the number of contract parameters.
|
||||
contract C0() {}
|
||||
|
|
|
|||
|
|
@ -250,7 +250,6 @@ type Interface struct {
|
|||
|
||||
allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
|
||||
|
||||
// for contracts
|
||||
types []Type // for contracts
|
||||
}
|
||||
|
||||
|
|
@ -547,12 +546,12 @@ func (c *Contract) ifaceAt(index int) *Interface {
|
|||
type TypeParam struct {
|
||||
obj *TypeName
|
||||
index int
|
||||
contr *Contract // nil if no contract
|
||||
bound Type // either an *Interface or a *Contract
|
||||
}
|
||||
|
||||
// NewTypeParam returns a new TypeParam.
|
||||
func NewTypeParam(obj *TypeName, index int, contr *Contract) *TypeParam {
|
||||
typ := &TypeParam{obj, index, contr}
|
||||
func NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam {
|
||||
typ := &TypeParam{obj, index, bound}
|
||||
if obj.typ == nil {
|
||||
obj.typ = typ
|
||||
}
|
||||
|
|
@ -564,7 +563,16 @@ func NewTypeParam(obj *TypeName, index int, contr *Contract) *TypeParam {
|
|||
// the result is the empty interface.
|
||||
// TODO(gri) should this be Underlying instead?
|
||||
func (t *TypeParam) Interface() *Interface {
|
||||
return t.contr.ifaceAt(t.index)
|
||||
switch b := t.bound.(type) {
|
||||
case nil:
|
||||
return &emptyInterface
|
||||
case *Interface:
|
||||
return b
|
||||
case *Contract:
|
||||
return b.ifaceAt(t.index)
|
||||
default:
|
||||
panic("type parameter bound must be an interface or contract")
|
||||
}
|
||||
}
|
||||
|
||||
// Implementations for Type methods.
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
|||
}
|
||||
buf.WriteString(p.name)
|
||||
}
|
||||
buf.WriteString("){}")
|
||||
buf.WriteString("){...}")
|
||||
// TODO write contract body
|
||||
|
||||
case *TypeParam:
|
||||
|
|
|
|||
|
|
@ -495,10 +495,19 @@ func (check *Checker) parameterizedType(typ *Parameterized, e *ast.CallExpr) boo
|
|||
// 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)
|
||||
bound := tname.tparams[0].typ.(*TypeParam).bound // TODO(gri) This is incorrect (index 0) in general. FIX THIS.
|
||||
switch b := bound.(type) {
|
||||
case nil:
|
||||
// nothing to do (no bound)
|
||||
case *Interface:
|
||||
panic("unimplemented")
|
||||
case *Contract:
|
||||
if !check.satisfyContract(b, 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)
|
||||
}
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
// complete parameterized type
|
||||
|
|
|
|||
Loading…
Reference in New Issue