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