~mna/snow unlisted

af9a050d71e169354b04f4e87d23e92425394567 — Martin Angers 9 months ago e75e506
pkg/codegen: test interface code generation
M cmd/goast/main.go => cmd/goast/main.go +4 -2
@@ 10,9 10,11 @@ func main() {
	src := `
package main

type I interface {
	foo(int) int
}

func main() { 
	s := map[string]bool{"a": true}
	_ = s
}
`


M pkg/codegen/mangle.go => pkg/codegen/mangle.go +12 -2
@@ 125,6 125,13 @@ func (r *nameResolver) NameForStructType(st *semantic.StructType, resolve map[*s
	return r.NameForGenericDeclAndTypes(st.Decl, st.Inst, resolve)
}

func (r *nameResolver) NameForInterfaceType(it *semantic.InterfaceType, resolve map[*semantic.GenericType]semantic.Type) string {
	if !it.Decl.IsGeneric() {
		return r.NameForDecl(it.Decl)
	}
	return r.NameForGenericDeclAndTypes(it.Decl, it.Inst, resolve)
}

func (r *nameResolver) NameForGenericInst(gi *semantic.GenericInst, resolve map[*semantic.GenericType]semantic.Type) string {
	gd := gi.GenericDecl.Ref.(semantic.GenericDecl)
	_, insts := gd.GenInsts()


@@ 200,7 207,9 @@ type fmtBuilder struct {
func (b *fmtBuilder) withPrefixFor(decl semantic.Decl) {
	switch decl := decl.(type) {
	case *semantic.Fn:
		if decl.Scope().IsTopLevel() || decl.MethodOf != nil {
		if decl.Scope().IsTopLevel() || decl.MethodOf != nil || decl.AbstractMethodOf != nil {
			// TODO: this will be an issue with pub/exported methods eventually: the mangling of
			// an interface method might not match the one of an otherwise matching struct method.
			b.withExport(false)
		}
	case *semantic.Var:


@@ 212,12 221,13 @@ func (b *fmtBuilder) withPrefixFor(decl semantic.Decl) {
		if !decl.Scope().IsTopLevel() {
			b.withScope(decl.Scope())
		}

	case *semantic.Interface:
		b.withExport(false)
		if !decl.Scope().IsTopLevel() {
			b.withScope(decl.Scope())
		}
	default:
		panic(fmt.Sprintf("invalid declaration type for mangler prefix: %T", decl))
	}
}


M pkg/codegen/position.go => pkg/codegen/position.go +2 -0
@@ 130,6 130,8 @@ func (p *positioner) Visit(n goast.Node) goast.Visitor {
		return nil
	case *goast.IfStmt:
		n.If = p.getIncByLen("if ")
	case *goast.InterfaceType:
		n.Interface = p.getIncByLen("interface ")
	}
	return p
}

M pkg/codegen/testdata/fn_assign_call_interface.snow => pkg/codegen/testdata/fn_assign_call_interface.snow +10 -1
@@ 8,14 8,23 @@ struct S {
  fn foo() {
    println(s)
  }

  struct S2 {
    fn foo() {
      println("s2")
    }
  }
}

@extern(import: "fmt", symbol: "Println")
fn println(s: string)

fn main() {
  let iface: I = S(s: "hello!")
  var iface: I = S(s: "hello!")
  iface.foo()
  iface = S.S2()
  iface.foo()
}

#=hello!
#=s2

A pkg/codegen/testdata/fn_assign_call_interface.snow.err => pkg/codegen/testdata/fn_assign_call_interface.snow.err +0 -0

A pkg/codegen/testdata/fn_assign_call_interface.snow.want => pkg/codegen/testdata/fn_assign_call_interface.snow.want +58 -0
@@ 0,0 1,58 @@
package main

import "fmt"

type _اS struct {
	_اs string
}

func (
	self _اS,
) _اfoo() {
	_اprintln(self._اs)
}

type _ا6اS2 struct {
}

func (
	self _ا6اS2,
) _اfoo() {
	_اprintln("s2")
}

func _ا6اS2اnew(
	s _ا6اS2,
	m map[string]bool,
) _ا6اS2 {
	var r _ا6اS2
	return r
}

func _اSاnew(
	s _اS,
	m map[string]bool,
) _اS {
	var r _اS
	if m["s"] {
		r._اs = s._اs
	}
	return r
}

func _اprintln(
	s string,
) {
	fmt.Println(s)
}

func main() {
	var iface _اI = _اSاnew(_اS{_اs: "hello!"}, map[string]bool{"s": true})
	iface._اfoo()
	iface = _ا6اS2اnew(_ا6اS2{}, map[string]bool{})
	iface._اfoo()
}

type _اI interface {
	_اfoo()
}

M pkg/codegen/translate.go => pkg/codegen/translate.go +70 -9
@@ 124,6 124,9 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
			for _, fn := range n.Fns {
				semantic.Walk(t, fn)
			}
			for _, iface := range n.Interfaces {
				semantic.Walk(t, iface)
			}
		})

		// ************** DECLARATIONS *****************


@@ 418,21 421,22 @@ func (t *translator) createFn(nm string, fn *semantic.Fn) {

	var body *goast.BlockStmt
	t.withContainer(cont, func() {
		if fn.Body == nil {
			body = createExternalFnBody(ft, t.fnImports[fn])
		} else {
		if fn.Body != nil {
			semantic.Walk(t, fn.Body)
		} else if fn.AbstractMethodOf == nil {
			// this is an external function
			body = createExternalFnBody(ft, t.fnImports[fn])
		}
	})

	// the function declaration is a top-level func statement if it is top-level
	// in snow or is a struct method.
	fd := &goast.FuncDecl{
		Name: &goast.Ident{Name: nm},
		Type: ft,
		Body: body,
	}
	if fn.Scope().IsTopLevel() || fn.MethodOf != nil {
		fd := &goast.FuncDecl{
			Name: &goast.Ident{Name: nm},
			Type: ft,
			Body: body,
		}
		if str := fn.MethodOf; str != nil {
			t.convertFuncToMethod(fd, str, fn.IsRef)
		}


@@ 444,6 448,14 @@ func (t *translator) createFn(nm string, fn *semantic.Fn) {
			t.topLevel.PushBack(cont)
		}
		return

	}
	if fn.AbstractMethodOf != nil {
		// if it is an interface method, create the function declaration inside the
		// interface's container. It cannot be a generic itself (only the interface can).
		cont.nodes = []goast.Node{fd}
		t.parentContainer.PushBack(cont)
		return
	}

	cont.nodes = createFuncLit(nm, ft, body)


@@ 509,13 521,16 @@ func (t *translator) createStruct(nm string, strTyp *semantic.StructType) {
	// generate struct initializer function
	strInit := t.createStructInit(nm, strTyp, propInit)

	// generate methods and nested structs
	// generate methods and nested structs and interfaces
	for _, meth := range strTyp.Decl.Fns {
		semantic.Walk(t, meth)
	}
	for _, str := range strTyp.Decl.Structs {
		semantic.Walk(t, str)
	}
	for _, iface := range strTyp.Decl.Interfaces {
		semantic.Walk(t, iface)
	}

	if mark := t.genDeclMarks[strTyp.Decl]; mark != nil {
		mark.list.InsertBefore(strInit, mark.elem)


@@ 524,6 539,40 @@ func (t *translator) createStruct(nm string, strTyp *semantic.StructType) {
	}
}

func (t *translator) createInterface(nm string, ifaceTyp *semantic.InterfaceType) {
	// all interface declarations are attached to the top-level scope of the file.
	fields := &goast.FieldList{List: make([]*goast.Field, len(ifaceTyp.Decl.Methods))}

	// create the container but don't set its Go node yet, it will be created
	// only after looping over the methods.
	cont := &container{snode: ifaceTyp.Decl, children: list.New()}

	t.withContainer(cont, func() {
		for i, m := range ifaceTyp.Decl.Methods {
			semantic.Walk(t, m)
			node := t.parentContainer.Back().Value
			fnDecl := node.(*container).nodes[0].(*goast.FuncDecl)
			fields.List[i] = &goast.Field{
				Names: []*goast.Ident{fnDecl.Name},
				Type:  fnDecl.Type,
			}
		}
	})

	decl := &goast.GenDecl{
		Tok: gotoken.TYPE,
		Specs: []goast.Spec{
			&goast.TypeSpec{Name: &goast.Ident{Name: nm}, Type: &goast.InterfaceType{Methods: fields}},
		},
	}
	cont.nodes = []goast.Node{decl}
	if mark := t.genDeclMarks[ifaceTyp.Decl]; mark != nil {
		mark.list.InsertBefore(cont, mark.elem)
	} else {
		t.topLevel.PushBack(cont)
	}
}

func (t *translator) addFileImports(f *semantic.File, gof *goast.File) {
	imports := t.fileImports[f]
	set := make(map[string]bool, len(imports))


@@ 590,6 639,10 @@ func (t *translator) goTypeExprFor(T semantic.Type) goast.Expr {
		name := t.resolver.NameForStructType(T, t.curGenInst)
		return &goast.Ident{Name: name}

	case *semantic.InterfaceType:
		name := t.resolver.NameForInterfaceType(T, t.curGenInst)
		return &goast.Ident{Name: name}

	default:
		panic(fmt.Sprintf("invalid type: %T", T))
	}


@@ 605,6 658,12 @@ func (t *translator) reconcileExprWith(LT, RT semantic.Type, expr goast.Expr) go
func (t *translator) convertExprFromTo(expr goast.Expr, fromT, toT semantic.Type) goast.Expr {
	var convertCallExpr goast.Expr

	// if toT is an interface, no explicit conversion needed, Go will be able to assign fromT
	// to it (as the rules for assignability are the same).
	if semantic.AsInterfaceType(toT) != nil {
		return expr
	}

	fromTupT, ok1 := fromT.(*semantic.TupleType)
	toTupT, ok2 := toT.(*semantic.TupleType)
	if ok1 && ok2 {


@@ 829,6 888,8 @@ func (t *translator) renderGenInst(gi *semantic.GenericInst, name string) {
		t.createFn(name, decl)
	case *semantic.Struct:
		t.createStruct(name, semantic.AsStructType(gi.Type()))
	case *semantic.Interface:
		t.createInterface(name, semantic.AsInterfaceType(gi.Type()))
	default:
		panic(fmt.Sprintf("invalid generic type to render: %T", decl))
	}

M pkg/codegen/treebuild.go => pkg/codegen/treebuild.go +3 -0
@@ 99,6 99,9 @@ func (tb *treebuilder) Visit(n semantic.Node) semantic.Visitor {
	case *semantic.Struct:
		// a struct container has a single Go node, the struct decl, and it is fully
		// formed, no need to append children, so nothing to do.
	case *semantic.Interface:
		// an interface container has a single Go node, the interface decl, and it is fully
		// formed, no need to append children, so nothing to do.

	case *semantic.Block:
		cont := tb.curContainer