~mna/snow unlisted

985e6c8681f086d329680b70ddf749e9d76681a9 — Martin Angers 9 months ago 361d647
pkg/semantic: work on refactoring type-assign in prep for interaces
3 files changed, 70 insertions(+), 29 deletions(-)

R pkg/semantic/testdata/{interface.snow => interface.snow.notyet}
M pkg/semantic/type.go
M pkg/semantic/typeassign_pass.go
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{}
}