R pkg/semantic/testdata/interface.snow => pkg/semantic/testdata/interface.snow.notyet +0 -0
M pkg/semantic/type.go => pkg/semantic/type.go +62 -5
@@ 24,6 24,15 @@ type Type interface {
fmt.Stringer
}
+// typeContainer is an optional interface that must be implemented by Types that
+// contain other identifiers. It defines the typeof method that must return
+// the type of the selected identifier inside the container type. It returns nil
+// as Type and Invalid as TypeContext if the selector does not exist.
+type typeContainer interface {
+ Type
+ typeof(sel *Ident, ctxHint TypeContext) (Type, TypeContext)
+}
+
// List of types implementing the Type interface.
type (
// BasicType represents a basic type such as int, bool or string. All basic types
@@ 56,7 65,7 @@ type (
lookup map[*GenericType]Type
}
- // InterfaceType represents a named interface type.
+ // InterfaceType represents an interface type.
InterfaceType struct {
// Decl represents the interface declaration of this type.
Decl *Interface
@@ 368,10 377,26 @@ func (t *TupleType) ResolveGeneric(resolve map[*GenericType]Type) Type {
return t
}
+func (t *TupleType) typeof(sel *Ident, ctxHint TypeContext) (Type, TypeContext) {
+ if sel.Index < 0 || sel.Index >= len(t.Fields) {
+ return nil, Invalid
+ }
+ return t.Fields[sel.Index], ctxHint
+}
+
// ========> implement Type for StructType
-func (s *StructType) AssignableTo(T Type) bool { return s.IdenticalTo(T) }
-func (s *StructType) Valid() bool { return true }
+func (s *StructType) AssignableTo(T Type) bool {
+ switch T.(type) {
+ case *StructType:
+ return s.IdenticalTo(T)
+ case *InterfaceType:
+ // TODO : struct can be assignable to an interface type if it satisfies it
+ }
+ return false
+}
+
+func (s *StructType) Valid() bool { return true }
func (s *StructType) String() string {
var buf strings.Builder
@@ 431,6 456,15 @@ func (s *StructType) ResolveGeneric(resolve map[*GenericType]Type) Type {
return s
}
+func (s *StructType) typeof(sel *Ident, _ TypeContext) (Type, TypeContext) {
+ decl := s.Decl.BodyScope.Lookup(sel.Name)
+ if decl == nil {
+ return nil, Invalid
+ }
+ sel.Ref = decl
+ return s.typeOfSel(decl.Type()), decl.TypeContext()
+}
+
// typeOfSel returns the actual type of a selection inside the struct.
// If the struct is not generic, then T is returned unchanged, otherwise
// if T is a GenericType it is replaced by the type of its corresponding
@@ 461,8 495,19 @@ func makeGenericResolveMap(gc *GenericClause, types []Type) map[*GenericType]Typ
// TODO: should be 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) }
-func (i *InterfaceType) Valid() bool { return true }
+func (i *InterfaceType) AssignableTo(T Type) bool {
+ return i.IdenticalTo(T)
+}
+
+// an interface type is valid if all of its methods' types are valid.
+func (i *InterfaceType) Valid() bool {
+ for _, m := range i.Decl.Methods {
+ if !m.Type().Valid() {
+ return false
+ }
+ }
+ return true
+}
func (i *InterfaceType) String() string {
var buf strings.Builder
@@ 522,6 567,18 @@ func (i *InterfaceType) ResolveGeneric(resolve map[*GenericType]Type) Type {
return i
}
+func (i *InterfaceType) typeof(sel *Ident, ctxHint 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 nil, Invalid
+}
+
// ========> implement Type for GenericType
func (g *GenericType) String() string { return g.Name }
M pkg/semantic/typeassign_pass.go => pkg/semantic/typeassign_pass.go +8 -24
@@ 144,9 144,6 @@ func (t *typeassignVisitor) Visit(n Node) Visitor {
n.typ = it
case *GenericElem:
- // TODO: when traits/capabilities are added, this would lookup the matching
- // generic clause on the parent, and add the specific traits constraints on
- // the type.
n.typ = &GenericType{
Name: n.Ident(),
ScopeID: n.Scope().ID,
@@ 335,6 332,7 @@ func (t *typeassignVisitor) instantiateGeneric(gi *GenericInst, gen GenericDecl,
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 {
@@ 378,7 376,7 @@ func (t *typeassignVisitor) typeAssignGenericInst(gi *GenericInst) {
ctx := genRef.TypeContext()
switch {
case ctx == Typ:
- gi.ctx = Typ // instantiated struct generics are types
+ gi.ctx = Typ // instantiated struct / interface generics are types
case ctx.isAnyOf(TypeContextValues...):
gi.ctx = Value // instantiated fn generics are values
}
@@ 390,27 388,13 @@ func (t *typeassignVisitor) typeAssignSelector(left Type, leftCtx TypeContext, s
// of the Selector expr. If Sel is marked unresolved, then the Selector
// expression will be unresolved too and will be properly added to deferred.
- switch left := left.(type) {
- case *StructType:
- decl := left.Decl.BodyScope.Lookup(sel.Name)
- if decl != nil {
- sel.ctx = decl.TypeContext()
- sel.typ = left.typeOfSel(decl.Type())
- sel.Ref = decl
- return
- }
- t.errUndefined(sel, left)
-
- case *TupleType:
- sel.ctx = leftCtx
- if sel.Index < 0 || sel.Index >= len(left.Fields) {
+ if tof, ok := left.(typeContainer); ok {
+ sel.typ, sel.ctx = tof.typeof(sel, leftCtx)
+ if sel.typ == nil {
t.errUndefined(sel, left)
- return
}
- sel.typ = left.Fields[sel.Index]
-
- default:
- sel.ctx = Invalid
- sel.typ = unresolvedType{}
+ return
}
+ sel.ctx = Invalid
+ sel.typ = unresolvedType{}
}