M pkg/semantic/semantic.go => pkg/semantic/semantic.go +32 -0
@@ 93,6 93,11 @@ type GenericDecl interface {
// values. This is so that the caller can loop over the sorted slice and
// process the map in deterministic order.
GenInsts() ([]*GenericInst, map[*GenericInst][]Type)
+
+ // Instantiate returns the type of the GenericDecl on which it is called once
+ // instantiated by the GenericInst, with the specified Types and the map of
+ // generic type to resolved type.
+ Instantiate(*GenericInst, []Type, map[*GenericType]Type) Type
}
type commonDecl struct {
@@ 155,6 160,7 @@ type Fn struct {
func (fn *Fn) TypeContext() TypeContext { return Immutable }
func (fn *Fn) IsGeneric() bool { return fn.GenericParams != nil }
func (fn *Fn) GenClause() *GenericClause { return fn.GenericParams }
+
func (fn *Fn) GenInsts() ([]*GenericInst, map[*GenericInst][]Type) {
gis := make([]*GenericInst, 0, len(fn.GenericInsts))
for gi := range fn.GenericInsts {
@@ 167,6 173,14 @@ func (fn *Fn) GenInsts() ([]*GenericInst, map[*GenericInst][]Type) {
return gis, fn.GenericInsts
}
+func (fn *Fn) Instantiate(gi *GenericInst, types []Type, resolve map[*GenericType]Type) Type {
+ if fn.GenericInsts == nil {
+ fn.GenericInsts = make(map[*GenericInst][]Type)
+ }
+ fn.GenericInsts[gi] = types
+ return fn.Type().ResolveGeneric(resolve)
+}
+
type Var struct {
Ctx TypeContext // only mutable or immutable for Var, set during translation pass
TypeExpr Expr // possibly nil, not a resolved Type, but the Expr assigned to var in `var x: int`
@@ 200,6 214,7 @@ type Struct struct {
func (s *Struct) TypeContext() TypeContext { return Typ }
func (s *Struct) IsGeneric() bool { return s.GenericParams != nil }
func (s *Struct) GenClause() *GenericClause { return s.GenericParams }
+
func (s *Struct) GenInsts() ([]*GenericInst, map[*GenericInst][]Type) {
gis := make([]*GenericInst, 0, len(s.GenericInsts))
for gi := range s.GenericInsts {
@@ 212,6 227,14 @@ func (s *Struct) GenInsts() ([]*GenericInst, map[*GenericInst][]Type) {
return gis, s.GenericInsts
}
+func (s *Struct) Instantiate(gi *GenericInst, types []Type, resolve map[*GenericType]Type) Type {
+ if s.GenericInsts == nil {
+ s.GenericInsts = make(map[*GenericInst][]Type)
+ }
+ s.GenericInsts[gi] = types
+ return &StructType{Decl: s, Inst: types}
+}
+
type Interface struct {
GenericParams *GenericClause // set during translation pass
GenericInsts map[*GenericInst][]Type // set during type-assign pass, each distinct instantiation is recorded here
@@ 223,6 246,7 @@ type Interface struct {
func (i *Interface) TypeContext() TypeContext { return Typ }
func (i *Interface) IsGeneric() bool { return i.GenericParams != nil }
func (i *Interface) GenClause() *GenericClause { return i.GenericParams }
+
func (i *Interface) GenInsts() ([]*GenericInst, map[*GenericInst][]Type) {
gis := make([]*GenericInst, 0, len(i.GenericInsts))
for gi := range i.GenericInsts {
@@ 235,6 259,14 @@ func (i *Interface) GenInsts() ([]*GenericInst, map[*GenericInst][]Type) {
return gis, i.GenericInsts
}
+func (i *Interface) Instantiate(gi *GenericInst, types []Type, resolve map[*GenericType]Type) Type {
+ if i.GenericInsts == nil {
+ i.GenericInsts = make(map[*GenericInst][]Type)
+ }
+ i.GenericInsts[gi] = types
+ return &InterfaceType{Decl: i, Inst: types}
+}
+
type GenericElem struct {
commonDecl
}
M pkg/semantic/type.go => pkg/semantic/type.go +67 -10
@@ 72,6 72,10 @@ type (
// Inst is the list of types used in a generic instantiation, if the
// interface declaration is generic.
Inst []Type
+
+ // lookup cache of GenericType to instantiated type, nil until the first
+ // call to typeOfSel.
+ lookup map[*GenericType]Type
}
// GenericType is the type of a generic placeholder, e.g. $T.
@@ 387,11 391,11 @@ func (t *TupleType) typeof(sel *Ident, ctxHint TypeContext) (Type, TypeContext)
// ========> implement Type for StructType
func (s *StructType) AssignableTo(T Type) bool {
- switch T.(type) {
+ switch T := T.(type) {
case *StructType:
return s.IdenticalTo(T)
case *InterfaceType:
- // TODO : struct can be assignable to an interface type if it satisfies it
+ return T.isSatisfiedBy(s)
}
return false
}
@@ 493,10 497,13 @@ func makeGenericResolveMap(gc *GenericClause, types []Type) map[*GenericType]Typ
// ========> implement Type for InterfaceType
-// TODO: should be assignable to any interface type that is a subset
-// of this interface (or identical).
+// Assignable to any interface type that is a subset of this interface (or
+// identical).
func (i *InterfaceType) AssignableTo(T Type) bool {
- return i.IdenticalTo(T)
+ if i2 := AsInterfaceType(T); i2 != nil {
+ return i2.isSatisfiedBy(i)
+ }
+ return false
}
// an interface type is valid if all of its methods' types are valid.
@@ 567,18 574,68 @@ func (i *InterfaceType) ResolveGeneric(resolve map[*GenericType]Type) Type {
return i
}
-func (i *InterfaceType) typeof(sel *Ident, ctxHint TypeContext) (Type, TypeContext) {
+func (i *InterfaceType) typeof(sel *Ident, _ TypeContext) (Type, TypeContext) {
for _, m := range i.Decl.Methods {
if m.Ident() == sel.Name {
- // TODO: first, the type must be generic-resolved much like typeOfSel for SturctType,
- // then the TypeContext might not be ok, this would always give immutable (the method
- // is a func decl). Maybe it should be ctxHint? TBD...
- return m.Type(), m.TypeContext()
+ return i.typeOfSel(m.Type()), m.TypeContext()
}
}
return nil, Invalid
}
+// typeOfSel returns the actual type of a selection inside the interface.
+// If the method is not generic, then T is returned unchanged, otherwise
+// if T is a GenericType it is replaced by the type of its corresponding
+// instantiation.
+func (i *InterfaceType) typeOfSel(T Type) Type {
+ if !i.Decl.IsGeneric() {
+ return T
+ }
+
+ if i.lookup == nil {
+ i.lookup = makeGenericResolveMap(i.Decl.GenericParams, i.Inst)
+ }
+ return T.ResolveGeneric(i.lookup)
+}
+
+// TODO: eventually, for better error reporting, should return the missing method names
+// or the invalid method types. Could also return the mapping of functions on T that
+// satisfy each required method of i, might be useful in later stages.
+func (i *InterfaceType) isSatisfiedBy(T Type) bool {
+ var (
+ tfns []*Fn
+ resolve func(Type) Type
+ )
+
+ switch T := T.(type) {
+ case *StructType:
+ tfns = T.Decl.Fns
+ resolve = T.typeOfSel
+ case *InterfaceType:
+ tfns = T.Decl.Methods
+ resolve = T.typeOfSel
+ default:
+ return false
+ }
+
+ // build the map of required fn names to corresponding type
+ req := make(map[string]Type, len(i.Decl.Methods))
+ for _, m := range i.Decl.Methods {
+ req[m.Ident()] = i.typeOfSel(m.Type())
+ }
+
+ for _, fn := range tfns {
+ // if this fn is part of the required methods set, check if the types match
+ if reqt := req[fn.Ident()]; reqt != nil {
+ if !reqt.IdenticalTo(resolve(fn.Type())) {
+ return false
+ }
+ delete(req, fn.Ident())
+ }
+ }
+ return len(req) == 0
+}
+
// ========> implement Type for GenericType
func (g *GenericType) String() string { return g.Name }
M pkg/semantic/typeassign_pass.go => pkg/semantic/typeassign_pass.go +1 -19
@@ 331,25 331,7 @@ func (t *typeassignVisitor) instantiateGeneric(gi *GenericInst, gen GenericDecl,
if count := len(types); count < len(gc.Elems) || len(types) > len(gc.Elems) {
t.errh(gi.Pos(), fmt.Sprintf("wrong number of types provided in generic instantiation, want %d, got %d", len(gc.Elems), len(types)))
}
-
- // TODO: need to support Interface too, could be methods on the semantic.Decl instead
- switch gen := gen.(type) {
- case *Fn:
- if gen.GenericInsts == nil {
- gen.GenericInsts = make(map[*GenericInst][]Type)
- }
- gen.GenericInsts[gi] = types
- return gen.Type().ResolveGeneric(resolve)
- case *Struct:
- if gen.GenericInsts == nil {
- gen.GenericInsts = make(map[*GenericInst][]Type)
- }
- gen.GenericInsts[gi] = types
- return &StructType{Decl: gen, Inst: types}
- default:
- t.errh(gi.Pos(), fmt.Sprintf("invalid generic declaration type: %T", gen))
- }
- return unresolvedType{}
+ return gen.Instantiate(gi, types, resolve)
}
func (t *typeassignVisitor) typeAssignGenericInst(gi *GenericInst) {
M pkg/semantic/typecheck_pass.go => pkg/semantic/typecheck_pass.go +0 -2
@@ 422,8 422,6 @@ func (t *typecheckVisitor) Visit(n Node) Visitor {
case *GenericInst:
var T Type
t.expectTypeCtx(n, &T, Typ, Value)
- // TODO: Number of types already validated in type-assign, only check
- // remaining would be the constraints, when we add that.
// TODO: also, should the number of types be validated here instead? Conceptually would make more sense.
case *Ident: