~mna/snow

10c981ad97e179fb5835e9f75ed408fd622ff00b — Martin Angers 1 year, 8 months ago a34f9b4 + 52bcc1d
Merge branch 'wip-struct-generics'
77 files changed, 1621 insertions(+), 528 deletions(-)

M pkg/codegen/codegen.go
M pkg/codegen/mangle.go
M pkg/codegen/testdata/fn_generic_decl_after_inst.snow.want
M pkg/codegen/testdata/fn_generic_fn_uses_generic_fn.snow.want
M pkg/codegen/testdata/fn_generic_identity.snow.want
M pkg/codegen/testdata/fn_nested_generic_fn.snow.want
M pkg/codegen/testdata/fn_nested_struct_method.snow.want
M pkg/codegen/testdata/fn_struct_method.snow.want
A pkg/codegen/testdata/struct_complex_generic.snow
A pkg/codegen/testdata/struct_complex_generic.snow.err
A pkg/codegen/testdata/struct_complex_generic.snow.want
A pkg/codegen/testdata/struct_simple_generic.snow
A pkg/codegen/testdata/struct_simple_generic.snow.err
A pkg/codegen/testdata/struct_simple_generic.snow.want
M pkg/codegen/translate.go
A pkg/codegen/treebuild.go
M pkg/grammar/grammar.ebnf
M pkg/parser/parser.go
A pkg/parser/testdata/struct_complex_generic.snow
A pkg/parser/testdata/struct_complex_generic.snow.err
A pkg/parser/testdata/struct_complex_generic.snow.want
A pkg/parser/testdata/struct_simple_generic.snow
A pkg/parser/testdata/struct_simple_generic.snow.err
A pkg/parser/testdata/struct_simple_generic.snow.want
M pkg/semantic/semantic.go
M pkg/semantic/testdata/check/fn_generic_unused_in_signature.snow.err
M pkg/semantic/testdata/check/fn_generic_unused_in_signature.snow.want
A pkg/semantic/testdata/check/fn_invalid_generic_use_in_fn.snow.err
A pkg/semantic/testdata/check/fn_invalid_generic_use_in_fn.snow.want
A pkg/semantic/testdata/check/generic_struct_instantiation.snow.err
A pkg/semantic/testdata/check/generic_struct_instantiation.snow.want
A pkg/semantic/testdata/check/struct_complex_generic.snow.err
A pkg/semantic/testdata/check/struct_complex_generic.snow.want
M pkg/semantic/testdata/check/struct_generic.snow.err
M pkg/semantic/testdata/check/struct_generic.snow.want
A pkg/semantic/testdata/check/struct_simple_generic.snow.err
A pkg/semantic/testdata/check/struct_simple_generic.snow.want
M pkg/semantic/testdata/fn_generic_unused_in_signature.snow
A pkg/semantic/testdata/fn_invalid_generic_use_in_fn.snow
R pkg/semantic/testdata/{generic_struct_instantiation.snow.notyet => generic_struct_instantiation.snow}
A pkg/semantic/testdata/scopes/fn_invalid_generic_use_in_fn.snow.err
A pkg/semantic/testdata/scopes/fn_invalid_generic_use_in_fn.snow.want
M pkg/semantic/testdata/scopes/generic_struct_instantiation.snow.want
A pkg/semantic/testdata/scopes/struct_complex_generic.snow.err
A pkg/semantic/testdata/scopes/struct_complex_generic.snow.want
M pkg/semantic/testdata/scopes/struct_generic.snow.err
A pkg/semantic/testdata/scopes/struct_simple_generic.snow.err
A pkg/semantic/testdata/scopes/struct_simple_generic.snow.want
M pkg/semantic/testdata/static/fn_generic_unused_in_signature.snow.err
M pkg/semantic/testdata/static/fn_generic_unused_in_signature.snow.want
A pkg/semantic/testdata/static/fn_invalid_generic_use_in_fn.snow.err
A pkg/semantic/testdata/static/fn_invalid_generic_use_in_fn.snow.want
A pkg/semantic/testdata/static/generic_struct_instantiation.snow.err
A pkg/semantic/testdata/static/generic_struct_instantiation.snow.want
A pkg/semantic/testdata/static/struct_complex_generic.snow.err
A pkg/semantic/testdata/static/struct_complex_generic.snow.want
M pkg/semantic/testdata/static/struct_generic.snow.err
A pkg/semantic/testdata/static/struct_simple_generic.snow.err
A pkg/semantic/testdata/static/struct_simple_generic.snow.want
A pkg/semantic/testdata/struct_complex_generic.snow
R pkg/semantic/testdata/{struct_generic.snow.notyet => struct_generic.snow}
A pkg/semantic/testdata/struct_simple_generic.snow
M pkg/semantic/testdata/types/fn_generic_unused_in_signature.snow.err
M pkg/semantic/testdata/types/fn_generic_unused_in_signature.snow.want
A pkg/semantic/testdata/types/fn_invalid_generic_use_in_fn.snow.err
A pkg/semantic/testdata/types/fn_invalid_generic_use_in_fn.snow.want
M pkg/semantic/testdata/types/generic_struct_instantiation.snow.want
A pkg/semantic/testdata/types/struct_complex_generic.snow.err
A pkg/semantic/testdata/types/struct_complex_generic.snow.want
M pkg/semantic/testdata/types/struct_generic.snow.err
M pkg/semantic/testdata/types/struct_generic.snow.want
A pkg/semantic/testdata/types/struct_simple_generic.snow.err
A pkg/semantic/testdata/types/struct_simple_generic.snow.want
M pkg/semantic/translate_pass.go
M pkg/semantic/type.go
M pkg/semantic/typeassign_pass.go
M pkg/semantic/typecheck_pass.go
M pkg/codegen/codegen.go => pkg/codegen/codegen.go +16 -9
@@ 55,9 55,11 @@ func ExecUnit(outDir string, unit *semantic.Unit) ([]string, error) {
	// Code generation works in steps:
	// 1. do name mangling of all declarations that require it
	// 2. extract all external imports required per file
	// 3. generate the in-memory, position-less Go AST
	// 4. walk the Go AST and assign valid positions
	// 5. use the go/printer package to generate Go code from the AST
	// 3. generate the in-memory, position-less Go AST nodes, associated to Snow nodes
	// 4. build the Go AST tree from the Snow-associated nodes so that relative
	//    position of nodes respects the snow source
	// 5. walk the Go AST and assign valid positions to nodes
	// 6. use the go/printer package to generate Go code from the AST

	// 1. do name mangling of all declarations that require it
	resolver := mangle(unit)


@@ 65,7 67,7 @@ func ExecUnit(outDir string, unit *semantic.Unit) ([]string, error) {
	// 2. extract all external imports required per file
	fileImps := imports(unit)

	// 3. generate the in-memory, position-less Go AST
	// 3. generate the in-memory, position-less Go AST nodes, associated to Snow nodes
	fnImps := make(map[*semantic.Fn]_import)
	for _, imps := range fileImps {
		for _, imp := range imps {


@@ 76,13 78,18 @@ func ExecUnit(outDir string, unit *semantic.Unit) ([]string, error) {
		resolver:    resolver,
		fileImports: fileImps,
		fnImports:   fnImps,
		convertFns:  make(map[convertFnKey]string),
		genDecls:    make(map[semantic.Decl]genDeclInfo),
	}
	semantic.Walk(t, unit)
	gofiles := t.files

	// 4. walk the Go AST and assign valid positions
	// 4. build the Go AST tree from the Snow-associated nodes so that relative
	//    position of nodes respects the snow source
	tb := &treebuilder{
		filesList: t.filesList,
	}
	semantic.Walk(tb, unit)
	gofiles := tb.gofiles

	// 5. walk the Go AST and assign valid positions to nodes
	fset := token.NewFileSet()
	for i, f := range unit.Files {
		pos := &positioner{


@@ 97,7 104,7 @@ func ExecUnit(outDir string, unit *semantic.Unit) ([]string, error) {
		}
	}

	// 5. use the go/printer package to generate Go code from the AST
	// 6. use the go/printer package to generate Go code from the AST
	var el scanner.ErrorList
	w := &writer{dir: outDir, fset: fset}
	outFiles := make([]string, len(gofiles))

M pkg/codegen/mangle.go => pkg/codegen/mangle.go +28 -16
@@ 12,6 12,7 @@ import (
const (
	exportedChar   = 'X'
	unexportedChar = '_'
	invalidChar    = '🤦'
)

// mangle mangles all identifiers declared in decls. The logic is as follows.


@@ 45,7 46,7 @@ func mangle(unit *semantic.Unit) *nameResolver {
		}

		for _, decl := range s.Symbols {
			// first, process the generic cases for all decls
			// first, process the standard cases for all decls
			if decl == unit.Main {
				names[decl] = basicIdentsToGo[decl.Ident()]
				continue


@@ 58,7 59,13 @@ func mangle(unit *semantic.Unit) *nameResolver {
			}

			if decl.IsGeneric() {
				// ignore, will be mangled on-the-fly as the Go AST is generated
				// the actual name will be mangled on-the-fly as the Go AST is generated,
				// but a mangled name can be required if a generic is used as left-hand side
				// of a selector, e.g. A.B where A is a generic struct and B is a struct.
				// In the final AST, the mangled name of A will not be used, but during the
				// translate pass it will be accessed, so register an invalid name so that
				// it is easy to tell (fail noisily) if it does end up in the generated code.
				names[decl] = string(invalidChar) + decl.Ident()
				continue
			}



@@ 87,16 94,13 @@ func mangle(unit *semantic.Unit) *nameResolver {
		return true
	})

	genNames := make(map[*semantic.GenericInst]string)
	typesSets := make(map[semantic.Decl][][]semantic.Type)
	return &nameResolver{names: names, genNames: genNames, typesSets: typesSets}
	return &nameResolver{names: names, typesSets: typesSets}
}

type nameResolver struct {
	// mangled names for declarations
	names map[semantic.Decl]string
	// mangled names for generic instantiations
	genNames map[*semantic.GenericInst]string
	// map of generic declaration to list of instantiation types, to mangle unique
	// names per identical instantiation.
	typesSets map[semantic.Decl][][]semantic.Type


@@ 110,24 114,32 @@ func (r *nameResolver) NameForDecl(n semantic.Decl) string {
	return nm
}

func (r *nameResolver) NameForGenericInst(gi *semantic.GenericInst) string {
	if nm, ok := r.genNames[gi]; ok {
		return nm
func (r *nameResolver) NameForStructType(st *semantic.StructType, resolve map[*semantic.GenericType]semantic.Type) string {
	if !st.Decl.IsGeneric() {
		return r.NameForDecl(st.Decl)
	}
	return r.NameForGenericDeclAndTypes(st.Decl, st.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()
	types := insts[gi]
	return r.NameForGenericDeclAndTypes(gd, types, resolve)
}

func (r *nameResolver) NameForGenericDeclAndTypes(decl semantic.Decl, types []semantic.Type, resolve map[*semantic.GenericType]semantic.Type) string {
	for i, typ := range types {
		types[i] = typ.ResolveGeneric(resolve)
	}

	var b fmtBuilder
	b.withPrefixFor(gd)
	typesID := r.idForGenericInst(gd, types)
	b.withPrefixFor(decl)
	typesID := r.idForGenericInst(decl, types)

	r.genNames[gi] = b.get(gd.Ident() +
	return b.get(decl.Ident() +
		string(scanner.NameManglingSeparator) +
		strconv.Itoa(typesID))

	return r.genNames[gi]
}

func (r *nameResolver) idForGenericInst(genDecl semantic.Decl, types []semantic.Type) int {


@@ 158,13 170,13 @@ func (r *nameResolver) NameForTuple(index int, exported bool) string {
	return b.get(strconv.Itoa(index))
}

func (r *nameResolver) NameForStructInit(str *semantic.Struct) string {
func (r *nameResolver) NameForStructInit(baseName string) string {
	// TODO: for now, just append the separator and "new", so it gets exported if
	// the struct is exported. This will require more thought when pub gets added,
	// as some struct fields might be exported but others not, and the struct
	// still has to be fully initialized on return. Possibly a static analysis
	// that makes sure all fields are assigned in the initializer.
	return r.NameForDecl(str) + string(scanner.NameManglingSeparator) + "new"
	return baseName + string(scanner.NameManglingSeparator) + "new"
}

// NameForConv returns the Go name for an automatically- generated function to

M pkg/codegen/testdata/fn_generic_decl_after_inst.snow.want => pkg/codegen/testdata/fn_generic_decl_after_inst.snow.want +5 -5
@@ 2,17 2,17 @@ package main

import "fmt"

func main() {
	var a int = _اAا0(1)
	_اprintln(a)
}

func _اAا0(
	v int,
) int {
	return v
}

func main() {
	var a int = _اAا0(1)
	_اprintln(a)
}

func _اprintln(
	i int,
) {

M pkg/codegen/testdata/fn_generic_fn_uses_generic_fn.snow.want => pkg/codegen/testdata/fn_generic_fn_uses_generic_fn.snow.want +6 -6
@@ 2,6 2,12 @@ package main

import "fmt"

func _اAا0(
	v int,
) int {
	return v
}

func _اBا0(
	x int,
) int {


@@ 11,12 17,6 @@ func _اBا0(
	return a(x)
}

func _اAا0(
	v int,
) int {
	return v
}

func main() {
	var b int = _اBا0(1)
	_اprintln(b)

M pkg/codegen/testdata/fn_generic_identity.snow.want => pkg/codegen/testdata/fn_generic_identity.snow.want +6 -6
@@ 2,18 2,18 @@ package main

import "fmt"

func _اidا1(
	v int64,
) int64 {
	return v
}

func _اidا0(
	v string,
) string {
	return v
}

func _اidا1(
	v int64,
) int64 {
	return v
}

func main() {
	var s string = _اidا0("a")
	var i int64 = _اidا1(int64(1))

M pkg/codegen/testdata/fn_nested_generic_fn.snow.want => pkg/codegen/testdata/fn_nested_generic_fn.snow.want +12 -12
@@ 12,12 12,12 @@ func _اcall(
}

func _اouter() {
	var innerا2 func(
		w bool,
	) bool
	innerا2 = func(
		w bool,
	) bool {
	var innerا0 func(
		w int,
	) int
	innerا0 = func(
		w int,
	) int {
		return w
	}
	var innerا1 func(


@@ 28,12 28,12 @@ func _اouter() {
	) string {
		return w
	}
	var innerا0 func(
		w int,
	) int
	innerا0 = func(
		w int,
	) int {
	var innerا2 func(
		w bool,
	) bool
	innerا2 = func(
		w bool,
	) bool {
		return w
	}
	var _ int = innerا0(1)

M pkg/codegen/testdata/fn_nested_struct_method.snow.want => pkg/codegen/testdata/fn_nested_struct_method.snow.want +17 -17
@@ 6,17 6,6 @@ type _ا4اS struct {
	_اx string
}

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

func (
	self *_ا4اS,
) _اinit() {


@@ 27,6 16,14 @@ type _ا5اT struct {
	_اy string
}

func (
	self *_ا5اT,
) _اappend(
	s string,
) {
	self._اy = self._اy + s
}

func _ا5اTاnew(
	s _ا5اT,
	m map[string]bool,


@@ 38,12 35,15 @@ func _ا5اTاnew(
	return r
}

func (
	self *_ا5اT,
) _اappend(
	s string,
) {
	self._اy = self._اy + s
func _ا4اSاnew(
	s _ا4اS,
	m map[string]bool,
) _ا4اS {
	var r _ا4اS
	if m["x"] {
		r._اx = s._اx
	}
	return r
}

func main() {

M pkg/codegen/testdata/fn_struct_method.snow.want => pkg/codegen/testdata/fn_struct_method.snow.want +11 -11
@@ 12,17 12,6 @@ type _ا5اS struct {
	_اx int
}

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

func (
	self *_ا5اS,
) _اinc() {


@@ 35,6 24,17 @@ func (
	self._اx = self._اx + 1
}

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

func main() {
	var s _ا5اS
	s._اx = 3

A pkg/codegen/testdata/struct_complex_generic.snow => pkg/codegen/testdata/struct_complex_generic.snow +31 -0
@@ 0,0 1,31 @@
struct A [$T, $U] {
  struct B [$V] {
    var ffb: ($V) -> $V
  }

  var bb: B[$U]
  var ffa: ($T) -> $T
}

fn str(s: string) -> string {
  return s
}

fn integer(i: int) -> int {
  return i
}

fn main() {
  var a = A[int, string] (bb: A.B[string](ffb: str), ffa: integer)
  println(a.bb.ffb("ok"))
  printiln(a.ffa(3))
}

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

@extern(import: "fmt", symbol: "Println")
fn printiln(v: int)

#=ok
#=3

A pkg/codegen/testdata/struct_complex_generic.snow.err => pkg/codegen/testdata/struct_complex_generic.snow.err +0 -0
A pkg/codegen/testdata/struct_complex_generic.snow.want => pkg/codegen/testdata/struct_complex_generic.snow.want +71 -0
@@ 0,0 1,71 @@
package main

import "fmt"

type _اAا0 struct {
	_اbb  _ا4اBا0
	_اffa func(
		int,
	) int
}

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

func _اstr(
	s string,
) string {
	return s
}

func _اinteger(
	i int,
) int {
	return i
}

type _ا4اBا0 struct {
	_اffb func(
		string,
	) string
}

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

func main() {
	var a _اAا0 = _اAا0اnew(_اAا0{_اbb: _ا4اBا0اnew(_ا4اBا0{_اffb: _اstr}, map[string]bool{"ffb": true}), _اffa: _اinteger}, map[string]bool{"bb": true, "ffa": true})
	_اprintln(a._اbb._اffb("ok"))
	_اprintiln(a._اffa(3))
}

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

func _اprintiln(
	v int,
) {
	fmt.Println(v)
}

A pkg/codegen/testdata/struct_simple_generic.snow => pkg/codegen/testdata/struct_simple_generic.snow +13 -0
@@ 0,0 1,13 @@
struct A[$T] {
  var x: $T
}

fn main() {
  let a = A[int](x: 1)
  println(a.x)
}

@extern(import: "fmt", symbol: "Println")
fn println(i: int)

#=1

A pkg/codegen/testdata/struct_simple_generic.snow.err => pkg/codegen/testdata/struct_simple_generic.snow.err +0 -0
A pkg/codegen/testdata/struct_simple_generic.snow.want => pkg/codegen/testdata/struct_simple_generic.snow.want +29 -0
@@ 0,0 1,29 @@
package main

import "fmt"

type _اAا0 struct {
	_اx int
}

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

func main() {
	var a _اAا0 = _اAا0اnew(_اAا0{_اx: 1}, map[string]bool{"x": true})
	_اprintln(a._اx)
}

func _اprintln(
	i int,
) {
	fmt.Println(i)
}

M pkg/codegen/translate.go => pkg/codegen/translate.go +274 -265
@@ 1,6 1,7 @@
package codegen

import (
	"container/list"
	"fmt"
	goast "go/ast"
	gotoken "go/token"


@@ 14,48 15,70 @@ import (
// The translator translates a Snow abstract semantic graph to a Go abstract
// syntax tree that can then be written to file.
type translator struct {
	// resolver of mangled names for declarations and other nodes (provided as input)
	resolver *nameResolver

	curFile *goast.File
	files   []*goast.File

	// list of external imports per file (provided as input)
	fileImports map[*semantic.File][]_import
	fnImports   map[*semantic.Fn]_import
	convertFns  map[convertFnKey]string
	// list of external import per function (provided as input)
	fnImports map[*semantic.Fn]_import

	// current file being processed in the Unit
	curFile *semantic.File
	// map of already generated conversion functions (value is the associated mangled name)
	convertFns map[convertFnKey]string
	// map of active replacement types for generic placeholder types
	curGenInst map[*semantic.GenericType]semantic.Type
	// map of generic declarations render information, keyed by the
	// generic declaration.
	genDecls map[semantic.Decl]genDeclInfo
	// set of already rendered generic instantiations, based on the mangled name
	renderedGenInsts map[string]bool

	// expressions are generated and returned to the parent node
	generatedExpr goast.Expr

	filesList *list.List
	// the following two lists represent the current topLevel list of nodes (the file),
	// and the current container list.
	topLevel        *list.List
	parentContainer *list.List
	// map associating placeholder marks in their container where generic
	// instantiations should be inserted. The associated Element is a container
	// with no associated Go nodes.
	genDeclMarks map[semantic.Decl]*mark
}

	// when processing children, this is the child node (or children nodes) that
	// was generated by the call to goast.Walk with that Visitor.
	generated interface{}
type convertFnKey struct {
	from, to semantic.Type
}

// sentinel type for t.generated to indicate that the insertion position
// must be saved for that generic declaration.
type genericDeclInsertPoint struct{}
type mark struct {
	list *list.List
	elem *list.Element
}

type genDeclInfo struct {
	slice         interface{}     // *[]goast.Stmt or *[]goast.Decl
	index         int             // index in slice where the instantiations must be inserted
	renderedNames map[string]bool // set of generic instantiation names already rendered
// file, block, if, guard, struct and fn are containers
type container struct {
	snode    semantic.Node
	nodes    []goast.Node
	children *list.List
}

	// pendingRenders is a map of GenericInst to generic declaration mangled name
	// for instantiations that are encountered before the generic declaration they
	// use. The rendering of the instantiations is actually done when the generic
	// declaration is processed.
	pendingRenders map[*semantic.GenericInst]string
func newContainer(snode semantic.Node, ns ...goast.Node) *container {
	return &container{
		snode:    snode,
		nodes:    ns,
		children: list.New(),
	}
}

type convertFnKey struct {
	from, to semantic.Type
func (t *translator) withContainer(c *container, fn func()) {
	old := t.parentContainer
	t.parentContainer = c.children
	defer func() { t.parentContainer = old }()
	fn()
}

// TODO: do a with genInst like withContainer for this?
// merges the new generic instantiation replacement types into a cloned lookup map,
// sets it as the current replacement lookup map and returns the previus map, that
// sets it as the current replacement lookup map and returns the previous map, that
// should be set back in place once done with this generic instantiation.
func (t *translator) cloneGenInst(clause *semantic.GenericClause, types []semantic.Type) map[*semantic.GenericType]semantic.Type {
	old := t.curGenInst


@@ 74,53 97,61 @@ func (t *translator) cloneGenInst(clause *semantic.GenericClause, types []semant
func (t *translator) Visit(n semantic.Node) semantic.Visitor {
	switch n := n.(type) {
	case *semantic.Unit:
		t.convertFns = make(map[convertFnKey]string)
		t.renderedGenInsts = make(map[string]bool)
		t.genDeclMarks = make(map[semantic.Decl]*mark)
		t.filesList = list.New()
		for _, f := range n.Files {
			t.curFile = new(goast.File)
			t.curFile = f
			semantic.Walk(t, f)
			t.files = append(t.files, t.curFile)
		}

	case *semantic.File:
		// create package clause, imports if any
		t.curFile.Name = &goast.Ident{Name: "main"}
		t.addFileImports(n)

		decls := make([]semantic.Decl, 0, len(n.Vars)+len(n.Structs)+len(n.Fns))
		for _, v := range n.Vars {
			decls = append(decls, v)
		}
		for _, str := range n.Structs {
			decls = append(decls, str)
		}
		for _, fn := range n.Fns {
			decls = append(decls, fn)
		}
		for _, decl := range decls {
			semantic.Walk(t, decl)
			if t.generated != nil {
				switch gen := t.generated.(type) {
				case goast.Decl:
					t.curFile.Decls = append(t.curFile.Decls, gen)
				case genericDeclInsertPoint:
					t.recordGenDeclInfo(decl, &t.curFile.Decls, len(t.curFile.Decls))
				default:
					panic(fmt.Sprintf("unexpected generated type for file declarations: %T", gen))
				}
		gof := &goast.File{Name: &goast.Ident{Name: "main"}}
		t.addFileImports(n, gof)
		cont := newContainer(n, gof)
		t.filesList.PushBack(cont)
		t.topLevel = cont.children

		t.withContainer(cont, func() {
			for _, v := range n.Vars {
				semantic.Walk(t, v)
			}
		}
			for _, str := range n.Structs {
				semantic.Walk(t, str)
			}
			for _, fn := range n.Fns {
				semantic.Walk(t, fn)
			}
		})

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

	case *semantic.Fn:
		if n.IsGeneric() {
			// record insertion position of future instantiations of this generic fn
			t.generated = genericDeclInsertPoint{}
			// go nodes will be generated only on instantiation, create an empty container for now
			var m *mark
			if n.Scope().IsTopLevel() || n.MethodOf != nil {
				m = &mark{list: t.topLevel, elem: t.topLevel.PushBack(newContainer(n))}
			} else {
				m = &mark{list: t.parentContainer, elem: t.parentContainer.PushBack(newContainer(n))}
			}
			t.genDeclMarks[n] = m
			break
		}
		name := t.resolver.NameForDecl(n)
		t.createFn(name, n)

		// render a non-generic function
	case *semantic.Struct:
		if n.IsGeneric() {
			// go nodes will be generated only on instantiation, create an empty container for now
			m := &mark{list: t.topLevel, elem: t.topLevel.PushBack(newContainer(n))}
			t.genDeclMarks[n] = m
			break
		}
		name := t.resolver.NameForDecl(n)
		t.generated = t.createFn(name, n)
		t.createStruct(name, semantic.AsStructType(n.Type()))

	case *semantic.Var:
		name := t.resolver.NameForDecl(n)


@@ 130,131 161,74 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
		}
		if n.Value != nil {
			semantic.Walk(t, n.Value)
			expr := t.generated.(goast.Expr)
			expr := t.generatedExpr
			vs.Values = []goast.Expr{expr}
		}
		t.generated = &goast.GenDecl{
		t.parentContainer.PushBack(&goast.GenDecl{
			Tok:   gotoken.VAR,
			Specs: []goast.Spec{vs},
		}

	case *semantic.Struct:
		// all struct declarations are attached to the top-level scope of the
		// file, so the t.generated returned is nil (as the case takes care of
		// adding the struct to the file.Decls).
		name := t.resolver.NameForDecl(n)
		fields := &goast.FieldList{List: make([]*goast.Field, len(n.Vars))}

		// keep a map of values, will be used to generate the struct initializer
		propInit := make(map[*semantic.Var][]goast.Expr)
		for i, prop := range n.Vars {
			semantic.Walk(t, prop)
			vs := t.generated.(*goast.GenDecl).Specs[0].(*goast.ValueSpec)
			fields.List[i] = &goast.Field{
				Names: vs.Names,
				Type:  vs.Type,
			}
			if prop.Value != nil {
				propInit[prop] = vs.Values
			}
		}
		t.curFile.Decls = append(t.curFile.Decls, &goast.GenDecl{
			Tok: gotoken.TYPE,
			Specs: []goast.Spec{
				&goast.TypeSpec{Name: &goast.Ident{Name: name}, Type: &goast.StructType{Fields: fields}},
			},
		})

		// generate struct initializer function
		strInit := t.createStructInit(n, propInit)
		t.curFile.Decls = append(t.curFile.Decls, strInit)

		// generate methods and nested structs
		for _, meth := range n.Fns {
			semantic.Walk(t, meth)
			switch fd := t.generated.(type) {
			case *goast.FuncDecl:
				t.curFile.Decls = append(t.curFile.Decls, fd)
			case genericDeclInsertPoint:
				t.recordGenDeclInfo(meth, &t.curFile.Decls, len(t.curFile.Decls))
			}
		}
		for _, str := range n.Structs {
			semantic.Walk(t, str)
			// structs are added on top-level file, so no t.generated is returned
		}
		t.generated = nil

		// ************** STATEMENTS *****************

	case *semantic.Block:
		bs := &goast.BlockStmt{
			List: make([]goast.Stmt, 0, len(n.Stmts)),
		}
		for _, stmt := range n.Stmts {
			semantic.Walk(t, stmt)
			switch gen := t.generated.(type) {
			case goast.Stmt:
				bs.List = append(bs.List, gen)
			case goast.Decl:
				bs.List = append(bs.List, &goast.DeclStmt{Decl: gen})
			case []goast.Stmt:
				bs.List = append(bs.List, gen...)
			case genericDeclInsertPoint:
				t.recordGenDeclInfo(stmt.(semantic.Decl), &bs.List, len(bs.List))
			case nil:
				// ok, ignore (can happen for struct declarations)
			default:
				panic(fmt.Sprintf("unexpected generated node returned to Block statement: %T", gen))
		cont := newContainer(n, bs)
		t.parentContainer.PushBack(cont)
		t.withContainer(cont, func() {
			for _, stmt := range n.Stmts {
				semantic.Walk(t, stmt)
			}
		}
		t.generated = bs
		})

	case *semantic.Return:
		rs := &goast.ReturnStmt{}
		if n.Value != nil {
			semantic.Walk(t, n.Value)
			rs.Results = []goast.Expr{t.generated.(goast.Expr)}
			rs.Results = []goast.Expr{t.generatedExpr}
		}
		t.generated = rs
		t.parentContainer.PushBack(rs)

	case *semantic.Assign:
		semantic.Walk(t, n.Left)
		as := &goast.AssignStmt{
			Lhs: []goast.Expr{t.generated.(goast.Expr)},
			Lhs: []goast.Expr{t.generatedExpr},
			Tok: tokenToGoToken[token.Assign],
		}
		semantic.Walk(t, n.Right)
		as.Rhs = []goast.Expr{t.generated.(goast.Expr)}
		t.generated = as
		as.Rhs = []goast.Expr{t.generatedExpr}
		t.parentContainer.PushBack(as)

	case *semantic.ExprStmt:
		semantic.Walk(t, n.Value)
		t.generated = &goast.ExprStmt{X: t.generated.(goast.Expr)}
		t.parentContainer.PushBack(&goast.ExprStmt{X: t.generatedExpr})

	case *semantic.If:
		is := &goast.IfStmt{}
		for _, cond := range n.Conds {
			semantic.Walk(t, cond)
			if is.Cond == nil {
				is.Cond = t.generated.(goast.Expr)
				is.Cond = t.generatedExpr
				continue
			}
			and := &goast.BinaryExpr{
				X:  is.Cond,
				Op: tokenToGoToken[token.And],
				Y:  t.generated.(goast.Expr),
				Y:  t.generatedExpr,
			}
			is.Cond = and
		}
		semantic.Walk(t, n.Body)
		is.Body = t.generated.(*goast.BlockStmt)

		if n.Else != nil {
			semantic.Walk(t, n.Else)
			is.Else = t.generated.(goast.Stmt)
		}
		t.generated = is
		cont := newContainer(n, is)
		t.parentContainer.PushBack(cont)
		t.withContainer(cont, func() {
			semantic.Walk(t, n.Body)
			if n.Else != nil {
				semantic.Walk(t, n.Else)
			}
		})

	case *semantic.Guard:
		// use an empty if and an else


@@ 262,30 236,32 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
		for _, cond := range n.Conds {
			semantic.Walk(t, cond)
			if is.Cond == nil {
				is.Cond = t.generated.(goast.Expr)
				is.Cond = t.generatedExpr
				continue
			}
			and := &goast.BinaryExpr{
				X:  is.Cond,
				Op: tokenToGoToken[token.And],
				Y:  t.generated.(goast.Expr),
				Y:  t.generatedExpr,
			}
			is.Cond = and
		}

		// empty "if" body
		is.Body = &goast.BlockStmt{}

		semantic.Walk(t, n.Else)
		is.Else = t.generated.(goast.Stmt)
		t.generated = is
		cont := newContainer(n, is)
		t.parentContainer.PushBack(cont)
		t.withContainer(cont, func() {
			semantic.Walk(t, n.Else)
		})

		// ************** EXPRESSIONS *****************

	case *semantic.FnTypeExpr:
		t.generated = t.goTypeExprFor(n.Type())
		t.generatedExpr = t.goTypeExprFor(n.Type())

	case *semantic.TupleTypeExpr:
		t.generated = t.goTypeExprFor(n.Type())
		t.generatedExpr = t.goTypeExprFor(n.Type())

	case *semantic.TupleVal:
		cl := &goast.CompositeLit{


@@ 294,61 270,61 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
		}
		for i, val := range n.Values {
			semantic.Walk(t, val)
			expr := t.generated.(goast.Expr)
			cl.Elts[i] = expr
			cl.Elts[i] = t.generatedExpr
		}
		t.generated = cl
		t.generatedExpr = cl

	case *semantic.Binary:
		semantic.Walk(t, n.Left)
		be := &goast.BinaryExpr{
			X:  t.generated.(goast.Expr),
			X:  t.generatedExpr,
			Op: tokenToGoToken[n.Op],
		}
		semantic.Walk(t, n.Right)
		be.Y = t.generated.(goast.Expr)
		t.generated = be
		be.Y = t.generatedExpr
		t.generatedExpr = be

	case *semantic.Unary:
		semantic.Walk(t, n.Right)
		t.generated = &goast.UnaryExpr{
		t.generatedExpr = &goast.UnaryExpr{
			Op: tokenToGoToken[n.Op],
			X:  t.generated.(goast.Expr),
			X:  t.generatedExpr,
		}

	case *semantic.Paren:
		semantic.Walk(t, n.Value)
		t.generated = &goast.ParenExpr{X: t.generated.(goast.Expr)}
		t.generatedExpr = &goast.ParenExpr{X: t.generatedExpr}

	case *semantic.Call:
		semantic.Walk(t, n.Fun)

		if n.InitOf != nil {
			t.generated = t.callStructInit(n)
			t.generatedExpr = t.callStructInit(n)
			break
			// TODO: once modules exist, the n.Fun might be a qualified struct type, and the initializer
			// might be in a different module, will need to address that...
		}

		semantic.Walk(t, n.Fun)
		ce := &goast.CallExpr{
			Fun:  t.generated.(goast.Expr),
			Fun:  t.generatedExpr,
			Args: make([]goast.Expr, len(n.Args)),
		}
		for i, arg := range n.Args {
			semantic.Walk(t, arg)
			ce.Args[i] = t.generated.(goast.Expr)
			ce.Args[i] = t.generatedExpr
		}
		t.generated = ce
		t.generatedExpr = ce

	case *semantic.Selector:
		semantic.Walk(t, n.Left)
		left := t.generated.(goast.Expr)
		left := t.generatedExpr
		semantic.Walk(t, n.Sel)
		sel := t.generated.(goast.Expr)
		sel := t.generatedExpr

		if n.TypeContext() == semantic.Typ {
			// if this is a nested type, use just the type of Sel, as it is hoisted to the
			// top-level in Go and mangled.
			t.generated = sel
			t.generatedExpr = sel
			break
		}



@@ 362,33 338,32 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
		case *goast.Ident:
			rident = gen
		}
		t.generated = &goast.SelectorExpr{
		t.generatedExpr = &goast.SelectorExpr{
			X:   left,
			Sel: rident,
		}

	case *semantic.LitString:
		t.generated = &goast.BasicLit{
		t.generatedExpr = &goast.BasicLit{
			Kind:  tokenToGoToken[token.String],
			Value: n.Repr,
		}

	case *semantic.LitInt:
		t.generated = &goast.BasicLit{
		t.generatedExpr = &goast.BasicLit{
			Kind:  tokenToGoToken[token.Int],
			Value: n.Repr,
		}

	case *semantic.GenericInst:
		// render this generic instantiation if not already rendered
		name := t.resolver.NameForGenericInst(n)
		name := t.resolver.NameForGenericInst(n, t.curGenInst)
		t.renderGenInst(n, name)
		expr := &goast.Ident{Name: name}
		t.generated = expr
		t.generatedExpr = &goast.Ident{Name: name}

	case *semantic.Ident:
		if n.Index >= 0 {
			t.generated = &goast.Ident{Name: t.resolver.NameForTuple(n.Index, false)}
			t.generatedExpr = &goast.Ident{Name: t.resolver.NameForTuple(n.Index, false)}
			break
		}



@@ 407,12 382,11 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
				Sel: expr.(*goast.Ident),
			}
		}
		t.generated = expr
		t.generatedExpr = expr

	case *semantic.ImplicitConv:
		semantic.Walk(t, n.Value)
		expr := t.generated.(goast.Expr)
		t.generated = t.reconcileExprWith(n.Type(), n.Value.Type(), expr)
		t.generatedExpr = t.reconcileExprWith(n.Type(), n.Value.Type(), t.generatedExpr)

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


@@ 420,7 394,7 @@ func (t *translator) Visit(n semantic.Node) semantic.Visitor {
	return nil
}

func (t *translator) createFn(nm string, fn *semantic.Fn) interface{} {
func (t *translator) createFn(nm string, fn *semantic.Fn) {
	ft := t.goTypeExprFor(fn.Type()).(*goast.FuncType)
	// function type has no name for parameters, add them in
	for i, p := range ft.Params.List {


@@ 428,13 402,18 @@ func (t *translator) createFn(nm string, fn *semantic.Fn) interface{} {
		p.Names = []*goast.Ident{{Name: nm}}
	}

	// create the container but don't set its Go nodes yet, we don't know
	// if it will be a func literal or declaration.
	cont := &container{snode: fn, children: list.New()}

	var body *goast.BlockStmt
	if fn.Body == nil {
		body = createExternalFnBody(ft, t.fnImports[fn])
	} else {
		semantic.Walk(t, fn.Body)
		body = t.generated.(*goast.BlockStmt)
	}
	t.withContainer(cont, func() {
		if fn.Body == nil {
			body = createExternalFnBody(ft, t.fnImports[fn])
		} else {
			semantic.Walk(t, fn.Body)
		}
	})

	// the function declaration is a top-level func statement if it is top-level
	// in snow or is a struct method.


@@ 447,12 426,95 @@ func (t *translator) createFn(nm string, fn *semantic.Fn) interface{} {
		if str := fn.MethodOf; str != nil {
			t.convertFuncToMethod(fd, str, fn.IsRef)
		}
		return fd

		cont.nodes = []goast.Node{fd}
		if mark := t.genDeclMarks[fn]; mark != nil {
			mark.list.InsertBefore(cont, mark.elem)
		} else {
			t.topLevel.PushBack(cont)
		}
		return
	}

	cont.nodes = createFuncLit(nm, ft, body)
	if mark := t.genDeclMarks[fn]; mark != nil {
		mark.list.InsertBefore(cont, mark.elem)
	} else {
		t.parentContainer.PushBack(cont)
	}
}

func (t *translator) convertFuncToMethod(fn *goast.FuncDecl, str *semantic.Struct, isRef bool) {
	typ := t.goTypeExprFor(str.Type())
	if isRef {
		typ = &goast.StarExpr{X: typ}
	}
	fn.Recv = &goast.FieldList{
		List: []*goast.Field{{
			Names: []*goast.Ident{{Name: semantic.SelfVarName}},
			Type:  typ,
		}},
	}
}

func (t *translator) createStruct(nm string, strTyp *semantic.StructType) {
	// all struct declarations are attached to the top-level scope of the file.
	fields := &goast.FieldList{List: make([]*goast.Field, len(strTyp.Decl.Vars))}

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

	// keep a map of field values, will be used to generate the struct
	// initializer
	propInit := make(map[*semantic.Var][]goast.Expr)
	t.withContainer(cont, func() {
		for i, prop := range strTyp.Decl.Vars {
			semantic.Walk(t, prop)
			node := t.parentContainer.Back().Value
			vs := node.(*goast.GenDecl).Specs[0].(*goast.ValueSpec)
			fields.List[i] = &goast.Field{
				Names: vs.Names,
				Type:  vs.Type,
			}
			if prop.Value != nil {
				propInit[prop] = vs.Values
			}
		}
	})

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

	// generate struct initializer function
	strInit := t.createStructInit(nm, strTyp, propInit)

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

	if mark := t.genDeclMarks[strTyp.Decl]; mark != nil {
		mark.list.InsertBefore(strInit, mark.elem)
	} else {
		t.topLevel.PushBack(strInit)
	}
	return createFuncLit(nm, ft, body)
}

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


@@ 468,8 530,8 @@ func (t *translator) addFileImports(f *semantic.File) {
		if imp.Pkg != "" {
			impSpec.Name = &goast.Ident{Name: imp.Pkg}
		}
		t.curFile.Imports = append(t.curFile.Imports, impSpec)
		t.curFile.Decls = append(t.curFile.Decls, &goast.GenDecl{Tok: gotoken.IMPORT, Specs: []goast.Spec{impSpec}})
		gof.Imports = append(gof.Imports, impSpec)
		gof.Decls = append(gof.Decls, &goast.GenDecl{Tok: gotoken.IMPORT, Specs: []goast.Spec{impSpec}})
		set[imp.Pkg+"\n"+imp.Path] = true
	}
}


@@ 515,7 577,7 @@ func (t *translator) goTypeExprFor(T semantic.Type) goast.Expr {
		return st

	case *semantic.StructType:
		name := t.resolver.NameForDecl(T.Decl)
		name := t.resolver.NameForStructType(T, t.curGenInst)
		return &goast.Ident{Name: name}

	default:


@@ 552,7 614,7 @@ func (t *translator) convertExprFromTo(expr goast.Expr, fromT, toT semantic.Type
func (t *translator) getOrCreateConvertFn(from, to *semantic.TupleType) string {
	key := convertFnKey{from, to}
	if nm := t.convertFns[key]; nm != "" {
		return ""
		return nm
	}

	fnName := t.resolver.NameForConv(len(t.convertFns))


@@ 596,24 658,11 @@ func (t *translator) getOrCreateConvertFn(from, to *semantic.TupleType) string {
		Results: []goast.Expr{cl},
	}
	fd.Body.List[0] = ret
	t.curFile.Decls = append(t.curFile.Decls, fd)
	t.topLevel.PushBack(fd)

	return fnName
}

func (t *translator) convertFuncToMethod(fn *goast.FuncDecl, str *semantic.Struct, isRef bool) {
	typ := t.goTypeExprFor(str.Type())
	if isRef {
		typ = &goast.StarExpr{X: typ}
	}
	fn.Recv = &goast.FieldList{
		List: []*goast.Field{{
			Names: []*goast.Ident{{Name: semantic.SelfVarName}},
			Type:  typ,
		}},
	}
}

func (t *translator) callStructInit(c *semantic.Call) *goast.CallExpr {
	// map of struct labels to field decls
	// TODO: memoize this to optimize codegen a bit


@@ 625,21 674,22 @@ func (t *translator) callStructInit(c *semantic.Call) *goast.CallExpr {
	// The struct initializer has the signature (where S is the struct type):
	// func init(s S, m map[string]bool) S
	// The map m contains the labels (unmangled field names) set by the caller.
	strName := t.resolver.NameForStructType(semantic.AsStructType(c.Fun.Type()), t.curGenInst)
	ce := &goast.CallExpr{
		Fun:  &goast.Ident{Name: t.resolver.NameForStructInit(c.InitOf)},
		Fun:  &goast.Ident{Name: t.resolver.NameForStructInit(strName)},
		Args: make([]goast.Expr, 2),
	}

	// build the struct argument
	strLit := &goast.CompositeLit{
		Type: &goast.Ident{Name: t.resolver.NameForDecl(c.InitOf)},
		Type: &goast.Ident{Name: strName},
		Elts: make([]goast.Expr, len(c.Args)),
	}
	for i, arg := range c.Args {
		semantic.Walk(t, arg)
		kve := &goast.KeyValueExpr{
			Key:   &goast.Ident{Name: t.resolver.NameForDecl(lblToDecl[c.Labels[i]])},
			Value: t.generated.(goast.Expr),
			Value: t.generatedExpr,
		}
		strLit.Elts[i] = kve
	}


@@ 662,29 712,29 @@ func (t *translator) callStructInit(c *semantic.Call) *goast.CallExpr {
	return ce
}

func (t *translator) createStructInit(str *semantic.Struct, propInit map[*semantic.Var][]goast.Expr) *goast.FuncDecl {
func (t *translator) createStructInit(baseName string, strTyp *semantic.StructType, propInit map[*semantic.Var][]goast.Expr) *goast.FuncDecl {
	// The struct initializer has the signature (where S is the struct type):
	// func init(s S, m map[string]bool) S
	// The map m contains the labels (unmangled field names) set by the caller.

	strTyp := t.goTypeExprFor(str.Type())
	strTypExpr := t.goTypeExprFor(strTyp)
	// declare the return variable
	ret := &goast.GenDecl{
		Tok: gotoken.VAR,
		Specs: []goast.Spec{
			&goast.ValueSpec{
				Names: []*goast.Ident{{Name: "r"}},
				Type:  strTyp,
				Type:  strTypExpr,
			},
		},
	}

	// build the statements that make up the body of the function
	stmts := make([]goast.Stmt, 0, len(str.Vars)*2+1)
	stmts := make([]goast.Stmt, 0, len(strTyp.Decl.Vars)*2+1)
	stmts = append(stmts, &goast.DeclStmt{Decl: ret})

	// assign variables with initial values, and overwrite if value was provided
	for _, v := range str.Vars {
	for _, v := range strTyp.Decl.Vars {
		name := t.resolver.NameForDecl(v)

		if v.Value != nil {


@@ 724,10 774,10 @@ func (t *translator) createStructInit(str *semantic.Struct, propInit map[*semant

	// create the partial signature type, minus the map as this is not a concept
	// that exists in Snow yet.
	name := t.resolver.NameForStructInit(str)
	name := t.resolver.NameForStructInit(baseName)
	sig := &semantic.SignatureType{
		Params: []semantic.Type{str.Type()},
		Return: str.Type(),
		Params: []semantic.Type{strTyp},
		Return: strTyp,
	}
	ft := t.goTypeExprFor(sig).(*goast.FuncType)



@@ 750,23 800,13 @@ func (t *translator) createStructInit(str *semantic.Struct, propInit map[*semant
}

func (t *translator) renderGenInst(gi *semantic.GenericInst, name string) {
	decl := gi.GenericDecl.Ref
	info, ok := t.genDecls[decl]
	if !ok || info.slice == nil {
		// record the instantiation as pending, will be rendered when the declaration is processed.
		if info.pendingRenders == nil {
			info.pendingRenders = make(map[*semantic.GenericInst]string)
		}
		info.pendingRenders[gi] = name
		t.genDecls[decl] = info
		return
	}
	if info.renderedNames[name] {
	if t.renderedGenInsts[name] {
		return
	}

	info.renderedNames[name] = true
	t.renderedGenInsts[name] = true

	decl := gi.GenericDecl.Ref
	gd := decl.(semantic.GenericDecl)
	_, insts := gd.GenInsts()
	types := insts[gi]


@@ 774,50 814,19 @@ func (t *translator) renderGenInst(gi *semantic.GenericInst, name string) {
	old := t.cloneGenInst(gd.GenClause(), types)
	defer func() { t.curGenInst = old }()

	node := t.createFn(name, decl.(*semantic.Fn)) // TODO: for now, only fn
	switch node := node.(type) {
	case goast.Decl:
		target := info.slice.(*[]goast.Decl)
		if len(*target) < info.index {
			*target = append(*target, node)
		} else {
			*target = append((*target)[:info.index], append([]goast.Decl{node}, (*target)[info.index:]...)...)
		}
	case []goast.Stmt:
		target := info.slice.(*[]goast.Stmt)
		if len(*target) < info.index {
			*target = append(*target, node...)
		} else {
			*target = append((*target)[:info.index], append(node, (*target)[info.index:]...)...)
		}
	}
}

func (t *translator) recordGenDeclInfo(decl semantic.Decl, slice interface{}, index int) {
	pendingInfo := t.genDecls[decl]

	switch slice.(type) {
	case *[]goast.Decl:
	case *[]goast.Stmt:
	switch decl := decl.(type) {
	case *semantic.Fn:
		t.createFn(name, decl)
	case *semantic.Struct:
		t.createStruct(name, semantic.AsStructType(gi.Type()))
	default:
		panic(fmt.Sprintf("invalid type for slice for generic declaration container: %T", slice))
	}

	info := genDeclInfo{
		slice:         slice,
		index:         index,
		renderedNames: make(map[string]bool),
	}
	t.genDecls[decl] = info

	for gi, nm := range pendingInfo.pendingRenders {
		t.renderGenInst(gi, nm)
		panic(fmt.Sprintf("invalid generic type to render: %T", decl))
	}
}

func createFuncLit(name string, ft *goast.FuncType, body *goast.BlockStmt) []goast.Stmt {
func createFuncLit(name string, ft *goast.FuncType, body *goast.BlockStmt) []goast.Node {
	// need to declare first and assign second, so the func can be recursive
	return []goast.Stmt{
	return []goast.Node{
		&goast.DeclStmt{
			Decl: &goast.GenDecl{
				Tok: gotoken.VAR,

A pkg/codegen/treebuild.go => pkg/codegen/treebuild.go +197 -0
@@ 0,0 1,197 @@
package codegen

import (
	"container/list"
	"fmt"
	goast "go/ast"

	"git.sr.ht/~mna/snow/pkg/semantic"
)

// The treebuilder builds the Go AST from the nodes associated with the
// abstract semantic graph in the translator phase.
type treebuilder struct {
	filesList *list.List

	curGoFile *goast.File
	gofiles   []*goast.File

	curContainer *container
}

func (tb *treebuilder) Visit(n semantic.Node) semantic.Visitor {
	switch n := n.(type) {
	case *semantic.Unit:
		for _, f := range n.Files {
			semantic.Walk(tb, f)
			tb.gofiles = append(tb.gofiles, tb.curGoFile)
		}

	case *semantic.File:
		elem := tb.filesList.Front()
		tb.filesList.Remove(elem)
		cont := elem.Value.(*container)
		if len(cont.nodes) != 1 {
			panic(fmt.Sprintf("file element should have a single node, got %d", len(cont.nodes)))
		}

		tb.curGoFile = cont.nodes[0].(*goast.File)
		for n := cont.children.Front(); n != nil; n = n.Next() {
			switch v := n.Value.(type) {
			case goast.Decl:
				tb.curGoFile.Decls = append(tb.curGoFile.Decls, v)

			case *container:
				if len(v.nodes) > 0 {
					tb.curContainer = v
					semantic.Walk(tb, v.snode)
				}
				for _, node := range v.nodes {
					tb.curGoFile.Decls = append(tb.curGoFile.Decls, node.(goast.Decl))
				}

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

	case *semantic.Fn:
		cont := tb.curContainer
		if n.Body == nil {
			if count := cont.children.Len(); count != 0 {
				panic(fmt.Sprintf("external function should have no child, got %d", count))
			}
			break
		}

		// fn children should be a single container with a single Go node, the body block
		if count := cont.children.Len(); count != 1 {
			panic(fmt.Sprintf("function should have a single child, the body block, got %d", count))
		}
		var body *goast.BlockStmt
		for n := cont.children.Front(); n != nil; n = n.Next() {
			switch v := n.Value.(type) {
			case *container:
				if len(v.nodes) > 0 {
					tb.curContainer = v
					semantic.Walk(tb, v.snode)
				}
				if len(v.nodes) != 1 {
					panic(fmt.Sprintf("function body block should have a single Go node, got %d", len(v.nodes)))
				}
				body = v.nodes[0].(*goast.BlockStmt)

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

		// find the body target
		for _, node := range cont.nodes {
			switch node := node.(type) {
			case *goast.FuncDecl:
				node.Body = body
			case *goast.AssignStmt:
				node.Rhs[0].(*goast.FuncLit).Body = body
			}
		}

	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.Block:
		cont := tb.curContainer
		if len(cont.nodes) != 1 {
			panic(fmt.Sprintf("block should have a single Go node, got %d", len(cont.nodes)))
		}
		bs := cont.nodes[0].(*goast.BlockStmt)

		for n := cont.children.Front(); n != nil; n = n.Next() {
			switch v := n.Value.(type) {
			case goast.Stmt:
				bs.List = append(bs.List, v)

			case *container:
				if len(v.nodes) > 0 {
					tb.curContainer = v
					semantic.Walk(tb, v.snode)
				}
				for _, node := range v.nodes {
					bs.List = append(bs.List, node.(goast.Stmt))
				}

			case *goast.GenDecl:
				bs.List = append(bs.List, &goast.DeclStmt{Decl: v})

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

	case *semantic.If:
		cont := tb.curContainer
		if len(cont.nodes) != 1 {
			panic(fmt.Sprintf("if should have a single Go node, got %d", len(cont.nodes)))
		}
		is := cont.nodes[0].(*goast.IfStmt)

		wantChild := 1
		if n.Else != nil {
			wantChild = 2
		}
		if count := cont.children.Len(); count != wantChild {
			panic(fmt.Sprintf("if should have %d child node(s), got %d", wantChild, count))
		}

		for n := cont.children.Front(); n != nil; n = n.Next() {
			switch v := n.Value.(type) {
			case *container:
				if len(v.nodes) > 0 {
					tb.curContainer = v
					semantic.Walk(tb, v.snode)
				}
				if len(v.nodes) != 1 {
					panic(fmt.Sprintf("if block should have a single Go node, got %d", len(v.nodes)))
				}
				if is.Body == nil {
					is.Body = v.nodes[0].(*goast.BlockStmt)
				} else {
					is.Else = v.nodes[0].(goast.Stmt)
				}

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

	case *semantic.Guard:
		cont := tb.curContainer
		if len(cont.nodes) != 1 {
			panic(fmt.Sprintf("guard should have a single Go node, got %d", len(cont.nodes)))
		}
		is := cont.nodes[0].(*goast.IfStmt)

		if count := cont.children.Len(); count != 1 {
			panic(fmt.Sprintf("guard should have a single child, got %d", count))
		}

		for n := cont.children.Front(); n != nil; n = n.Next() {
			switch v := n.Value.(type) {
			case *container:
				if len(v.nodes) > 0 {
					tb.curContainer = v
					semantic.Walk(tb, v.snode)
				}
				if len(v.nodes) != 1 {
					panic(fmt.Sprintf("guard block should have a single Go node, got %d", len(v.nodes)))
				}
				is.Else = v.nodes[0].(*goast.BlockStmt)

			default:
				panic(fmt.Sprintf("invalid element value type: %T", v))
			}
		}
	}
	return nil
}

M pkg/grammar/grammar.ebnf => pkg/grammar/grammar.ebnf +1 -1
@@ 86,7 86,7 @@ Arguments = "(" [ ( ExprList | LabeledExprList ) [ "," ] ] ")" . // either all a
ExprList = Expr { "," Expr } .
LabeledExprList = LabeledExpr { "," LabeledExpr } .
LabeledExpr = Label ":" Expr .
Selector = "." ( ident | number ) .
Selector = "." ( ident [ GenericArgClause ] | number ) .
Label = ident .

// => Terminals

M pkg/parser/parser.go => pkg/parser/parser.go +3 -3
@@ 263,7 263,7 @@ func (p *parser) parseVarDefList() []*ast.VarDef {
	return list
}

func (p *parser) parseIdentOrTupleIndex() *ast.Ident {
func (p *parser) parseGenericIdentOrTupleIndex() ast.Expr {
	if p.tok == token.Int {
		// must be a base10 integer (i.e. not 0xabc nor 0b1101)
		lit := p.lit


@@ 276,7 276,7 @@ func (p *parser) parseIdentOrTupleIndex() *ast.Ident {
			}
		}
	}
	return p.parseIdent()
	return p.parseGenericIdent()
}

func (p *parser) parseGenericIdent() ast.Expr {


@@ 551,7 551,7 @@ loop:
				Left: expr,
				Dot:  p.expect(token.Dot),
			}
			sel.Sel = p.parseIdentOrTupleIndex()
			sel.Sel = p.parseGenericIdentOrTupleIndex()
			expr = sel

		default:

A pkg/parser/testdata/struct_complex_generic.snow => pkg/parser/testdata/struct_complex_generic.snow +20 -0
@@ 0,0 1,20 @@
struct A [$T, $U] {
  struct B [$V] {
    var ffb: ($V) -> $V
  }

  var bb: B[$U]
  var ffa: ($T) -> $T
}

fn str(s: string) -> string {
  return s
}

fn integer(i: int) -> int {
  return i
}

fn main() {
  var a = A[int, string] (bb: A.B[string](ffb: str), ffa: integer)
}

A pkg/parser/testdata/struct_complex_generic.snow.err => pkg/parser/testdata/struct_complex_generic.snow.err +0 -0
A pkg/parser/testdata/struct_complex_generic.snow.want => pkg/parser/testdata/struct_complex_generic.snow.want +86 -0
@@ 0,0 1,86 @@
file [4, #0] [0:274]
  struct [3] [0:106]
    ident [A] [7:8]
    generic [9:17]
      param [10:13]
        ident [$T] [10:12]
      param [14:16]
        ident [$U] [14:16]
    struct [1] [22:65]
      ident [B] [29:30]
      generic [31:35]
        param [32:34]
          ident [$V] [32:34]
      var [42:61]
        ffb: [46:61]
          ident [ffb] [46:49]
          sig [1->1] [51:61]
            param [52:54]
              ident [$V] [52:54]
            ident [$V] [59:61]
    var [69:82]
      bb: [73:82]
        ident [bb] [73:75]
        generic ident [77:82]
          ident [B] [77:78]
          generic [78:82]
            param [79:81]
              ident [$U] [79:81]
    var [85:104]
      ffa: [89:104]
        ident [ffa] [89:92]
        sig [1->1] [94:104]
          param [95:97]
            ident [$T] [95:97]
          ident [$T] [102:104]
  fn [108:150]
    ident [str] [111:114]
    sig [1->1] [114:135]
      param [115:124]
        ident [s] [115:116]
        ident [string] [118:124]
      ident [string] [129:135]
    block [1] [136:150]
      return [140:148]
        ident [s] [147:148]
  fn [152:192]
    ident [integer] [155:162]
    sig [1->1] [162:177]
      param [163:169]
        ident [i] [163:164]
        ident [int] [166:169]
      ident [int] [174:177]
    block [1] [178:192]
      return [182:190]
        ident [i] [189:190]
  fn [194:274]
    ident [main] [197:201]
    sig [0->0] [201:203]
    block [1] [204:274]
      var [208:272]
        a= [212:272]
          ident [a] [212:213]
          call [2] [216:272]
            generic ident [216:230]
              ident [A] [216:217]
              generic [217:230]
                param [218:222]
                  ident [int] [218:221]
                param [223:229]
                  ident [string] [223:229]
            item: [232:258]
              ident [bb] [232:234]
              call [1] [236:257]
                select [236:247]
                  ident [A] [236:237]
                  generic ident [238:247]
                    ident [B] [238:239]
                    generic [239:247]
                      param [240:246]
                        ident [string] [240:246]
                item: [248:256]
                  ident [ffb] [248:251]
                  ident [str] [253:256]
            item: [259:271]
              ident [ffa] [259:262]
              ident [integer] [264:271]

A pkg/parser/testdata/struct_simple_generic.snow => pkg/parser/testdata/struct_simple_generic.snow +8 -0
@@ 0,0 1,8 @@
struct A[$T] {
  var x: $T
}

fn main() {
  let a = A[int](x: 1)
}


A pkg/parser/testdata/struct_simple_generic.snow.err => pkg/parser/testdata/struct_simple_generic.snow.err +0 -0
A pkg/parser/testdata/struct_simple_generic.snow.want => pkg/parser/testdata/struct_simple_generic.snow.want +26 -0
@@ 0,0 1,26 @@
file [2, #0] [0:66]
  struct [1] [0:28]
    ident [A] [7:8]
    generic [8:12]
      param [9:11]
        ident [$T] [9:11]
    var [17:26]
      x: [21:26]
        ident [x] [21:22]
        ident [$T] [24:26]
  fn [30:66]
    ident [main] [33:37]
    sig [0->0] [37:39]
    block [1] [40:66]
      let [44:64]
        a= [48:64]
          ident [a] [48:49]
          call [1] [52:64]
            generic ident [52:58]
              ident [A] [52:53]
              generic [53:58]
                param [54:57]
                  ident [int] [54:57]
            item: [59:63]
              ident [x] [59:60]
              int [1] [62:63]

M pkg/semantic/semantic.go => pkg/semantic/semantic.go +2 -2
@@ 303,7 303,7 @@ type Call struct {

type Selector struct {
	Left Expr
	Sel  *Ident
	Sel  Expr // can be *Ident or *GenericInst
	commonExpr
}



@@ 357,7 357,7 @@ func TypeIdentString(n Node) string {
	case *Ident:
		lbl = n.Name
	case *Selector:
		lbl = "_." + n.Sel.Name
		return "_." + TypeIdentString(n.Sel)
	case *GenericInst:
		lbl = n.GenericDecl.Name + "[...]"
	}

M pkg/semantic/testdata/check/fn_generic_unused_in_signature.snow.err => pkg/semantic/testdata/check/fn_generic_unused_in_signature.snow.err +1 -0
@@ 3,3 3,4 @@ testdata/fn_generic_unused_in_signature.snow:7:4: unused generic type(s) in func
testdata/fn_generic_unused_in_signature.snow:24:4: invalid type for declaration g5: (unresolved) -> $T
testdata/fn_generic_unused_in_signature.snow:24:14: invalid type for identifier $U: unresolved
testdata/fn_generic_unused_in_signature.snow:24:14: undefined: $U
testdata/fn_generic_unused_in_signature.snow:30:3: wrong number of types provided in generic instantiation, want 1, got 2

M pkg/semantic/testdata/check/fn_generic_unused_in_signature.snow.want => pkg/semantic/testdata/check/fn_generic_unused_in_signature.snow.want +8 -1
@@ 59,4 59,11 @@ file testdata/fn_generic_unused_in_signature.snow [0, 6, 0]
      return
        ident a [let: $T]
  fn main [let: () -> void]
    block [0]
    block [1]
      expr
        call [1] [value: int]
          generic inst [2] [value: (int) -> int]
            ident g3 [let: (int) -> $T]
            ident int [type: int]
            ident string [type: string]
          int [1] [const: int]

A pkg/semantic/testdata/check/fn_invalid_generic_use_in_fn.snow.err => pkg/semantic/testdata/check/fn_invalid_generic_use_in_fn.snow.err +8 -0
@@ 0,0 1,8 @@
main function missing
testdata/fn_invalid_generic_use_in_fn.snow:2:11: expected type BasicType; got $T
testdata/fn_invalid_generic_use_in_fn.snow:2:11: invalid type: unresolved
testdata/fn_invalid_generic_use_in_fn.snow:3:6: expected type BasicType; got $T
testdata/fn_invalid_generic_use_in_fn.snow:3:6: expected type BasicType; got unresolved
testdata/fn_invalid_generic_use_in_fn.snow:4:13: expected type BasicType; got $U
testdata/fn_invalid_generic_use_in_fn.snow:6:3: expected type SignatureType; got $T
testdata/fn_invalid_generic_use_in_fn.snow:6:3: invalid type: unresolved

A pkg/semantic/testdata/check/fn_invalid_generic_use_in_fn.snow.want => pkg/semantic/testdata/check/fn_invalid_generic_use_in_fn.snow.want +24 -0
@@ 0,0 1,24 @@
file testdata/fn_invalid_generic_use_in_fn.snow [0, 1, 0]
  fn g [$2] [let: ($T, $U) -> void]
    generic type $T [type: $T]
    generic type $U [type: $U]
    let: v [let: $T]
      ident $T [type: $T]
    let: w [let: $U]
      ident $U [type: $U]
    block [3]
      let= a [let: unresolved]
        binary [+] [value: unresolved]
          ident v [let: $T]
          ident w [let: $U]
      if [1]
        binary [==] [value: unresolved]
          ident v [let: $T]
          ident w [let: $U]
        block [0]
        if [1]
          ident w [let: $U]
          block [0]
      expr
        call [0] [value: unresolved]
          ident v [let: $T]

A pkg/semantic/testdata/check/generic_struct_instantiation.snow.err => pkg/semantic/testdata/check/generic_struct_instantiation.snow.err +0 -0
A pkg/semantic/testdata/check/generic_struct_instantiation.snow.want => pkg/semantic/testdata/check/generic_struct_instantiation.snow.want +17 -0
@@ 0,0 1,17 @@
file testdata/generic_struct_instantiation.snow [0, 1, 1]
  fn main [let: () -> void]
    block [2]
      let= g1 [let: struct G[int]]
        call [x] [1] [value: struct G[int]]
          generic inst [1] [type: struct G[int]]
            ident G [type: struct G]
            ident int [type: int]
          int [1] [const: int]
      let: g2 [let: struct G[string]]
        generic inst [1] [type: struct G[string]]
          ident G [type: struct G]
          ident string [type: string]
  struct G [1, 0, 0, $1] [type: struct G]
    generic type $T [type: $T]
    var: x [var: $T]
      ident $T [type: $T]

A pkg/semantic/testdata/check/struct_complex_generic.snow.err => pkg/semantic/testdata/check/struct_complex_generic.snow.err +0 -0
A pkg/semantic/testdata/check/struct_complex_generic.snow.want => pkg/semantic/testdata/check/struct_complex_generic.snow.want +48 -0
@@ 0,0 1,48 @@
file testdata/struct_complex_generic.snow [0, 3, 1]
  fn str [let: (string) -> string]
    let: s [let: string]
      ident string [type: string]
    ident string [type: string]
    block [1]
      return
        ident s [let: string]
  fn integer [let: (int) -> int]
    let: i [let: int]
      ident int [type: int]
    ident int [type: int]
    block [1]
      return
        ident i [let: int]
  fn main [let: () -> void]
    block [1]
      var= a [var: struct A[int, string]]
        call [bb, ffa] [2] [value: struct A[int, string]]
          generic inst [2] [type: struct A[int, string]]
            ident A [type: struct A]
            ident int [type: int]
            ident string [type: string]
          call [ffb] [1] [value: struct B[string]]
            select [type: struct B[string]]
              ident A [type: struct A]
              generic inst [1] [type: struct B[string]]
                ident B [type: struct B]
                ident string [type: string]
            ident str [let: (string) -> string]
          ident integer [let: (int) -> int]
  struct A [2, 0, 1, $2] [type: struct A]
    generic type $T [type: $T]
    generic type $U [type: $U]
    var: bb [var: struct B[$U]]
      generic inst [1] [type: struct B[$U]]
        ident B [type: struct B]
        ident $U [type: $U]
    var: ffa [var: ($T) -> $T]
      sig [1->1] [type: ($T) -> $T]
        ident $T [type: $T]
        ident $T [type: $T]
    struct B [1, 0, 0, $1] [type: struct B]
      generic type $V [type: $V]
      var: ffb [var: ($V) -> $V]
        sig [1->1] [type: ($V) -> $V]
          ident $V [type: $V]
          ident $V [type: $V]

M pkg/semantic/testdata/check/struct_generic.snow.err => pkg/semantic/testdata/check/struct_generic.snow.err +13 -4
@@ 1,5 1,14 @@
main function missing
testdata/struct_generic.snow:15:19: declared generic type $V is unused
testdata/struct_generic.snow:23:19: generic type $T: symbol already declared in this scope
testdata/struct_generic.snow:32:10: invalid type for identifier $V: unresolved
testdata/struct_generic.snow:32:10: undefined: $V
testdata/struct_generic.snow:3:10: invalid type for identifier $T: unresolved
testdata/struct_generic.snow:3:10: undefined: $T
testdata/struct_generic.snow:4:10: invalid type for identifier $U: unresolved
testdata/struct_generic.snow:4:10: undefined: $U
testdata/struct_generic.snow:6:12: invalid type for identifier $U: unresolved
testdata/struct_generic.snow:6:12: undefined: $U
testdata/struct_generic.snow:7:12: invalid type for identifier $V: unresolved
testdata/struct_generic.snow:7:12: undefined: $V
testdata/struct_generic.snow:10:14: invalid type for identifier $W: unresolved
testdata/struct_generic.snow:10:14: undefined: $W
testdata/struct_generic.snow:24:19: generic type $T: symbol already declared in this scope
testdata/struct_generic.snow:33:10: invalid type for identifier $V: unresolved
testdata/struct_generic.snow:33:10: undefined: $V

M pkg/semantic/testdata/check/struct_generic.snow.want => pkg/semantic/testdata/check/struct_generic.snow.want +15 -19
@@ 1,24 1,20 @@
file testdata/struct_generic.snow [0, 0, 4]
  struct A [2, 1, 0, $3] [type: struct A [$T, $U, $V]]
    generic type $T [type: $T]
    generic type $U [type: $U]
    generic type $V [type: $V]
    var: x [var: $T]
      ident $T [type: $T]
    let: y [let: $U]
      ident $U [type: $U]
  struct A [2, 1, 0] [type: struct A]
    var: x [var: unresolved]
      ident $T [invalid: unresolved]
    let: y [let: unresolved]
      ident $U [invalid: unresolved]
    fn z [let: () -> void]
      block [3]
        let: a [let: $U]
          ident $U [type: $U]
        let: b [let: $V]
          ident $V [type: $V]
        fn inner [$1] [let: () -> void [$W]]
          generic type $W [type: $W]
        let: a [let: unresolved]
          ident $U [invalid: unresolved]
        let: b [let: unresolved]
          ident $V [invalid: unresolved]
        fn inner [let: () -> void]
          block [1]
            let: c [let: $W]
              ident $W [type: $W]
  struct B [1, 1, 0, $3] [type: struct B [$T, $U, $V]]
            let: c [let: unresolved]
              ident $W [invalid: unresolved]
  struct B [1, 1, 0, $3] [type: struct B]
    generic type $T [type: $T]
    generic type $U [type: $U]
    generic type $V [type: $V]


@@ 30,7 26,7 @@ file testdata/struct_generic.snow [0, 0, 4]
      block [1]
        var: z [var: $U]
          ident $U [type: $U]
  struct C [2, 0, 0, $3] [type: struct C [$T, $U, $T]]
  struct C [2, 0, 0, $3] [type: struct C]
    generic type $T [type: $T]
    generic type $U [type: $U]
    generic type $T [type: $T]


@@ 38,7 34,7 @@ file testdata/struct_generic.snow [0, 0, 4]
      ident $T [type: $T]
    let: y [let: $U]
      ident $U [type: $U]
  struct D [3, 0, 0, $2] [type: struct D [$T, $U]]
  struct D [3, 0, 0, $2] [type: struct D]
    generic type $T [type: $T]
    generic type $U [type: $U]
    var: v [var: $T]

A pkg/semantic/testdata/check/struct_simple_generic.snow.err => pkg/semantic/testdata/check/struct_simple_generic.snow.err +0 -0
A pkg/semantic/testdata/check/struct_simple_generic.snow.want => pkg/semantic/testdata/check/struct_simple_generic.snow.want +26 -0
@@ 0,0 1,26 @@
file testdata/struct_simple_generic.snow [0, 2, 1]
  fn main [let: () -> void]
    block [2]
      let= a [let: struct A[int]]
        call [x] [1] [value: struct A[int]]
          generic inst [1] [type: struct A[int]]
            ident A [type: struct A]
            ident int [type: int]
          int [1] [const: int]
      expr
        call [1] [value: void]
          ident println [let: (int) -> void]
          select [let: int]
            ident a [let: struct A[int]]
            ident x [var: int]
  fn println [let: (int) -> void]
    @ [import, symbol] [2] [value: struct extern]
      ident extern [type: struct extern]
      string ["fmt"] [const: string]
      string ["Println"] [const: string]
    let: i [let: int]
      ident int [type: int]
  struct A [1, 0, 0, $1] [type: struct A]
    generic type $T [type: $T]
    var: x [var: $T]
      ident $T [type: $T]

M pkg/semantic/testdata/fn_generic_unused_in_signature.snow => pkg/semantic/testdata/fn_generic_unused_in_signature.snow +3 -1
@@ 26,4 26,6 @@ fn g5[$T](v: $U) -> $T {
  return a
}

fn main() {}
fn main() {
  g3[int, string](1)
}

A pkg/semantic/testdata/fn_invalid_generic_use_in_fn.snow => pkg/semantic/testdata/fn_invalid_generic_use_in_fn.snow +7 -0
@@ 0,0 1,7 @@
fn g[$T, $U] (v: $T, w: $U) {
  let a = v + w
  if v == w {
  } else if w {
  }
  v()
}

R pkg/semantic/testdata/generic_struct_instantiation.snow.notyet => pkg/semantic/testdata/generic_struct_instantiation.snow +3 -2
@@ 1,8 1,9 @@
struct G {
# TODO: a more complex case would be if var x was ($T) -> $T, or a generic inner struct.
struct G[$T] {
  var x: $T
}

fn main() {
  let g1 = G [int] (1)
  let g1 = G [int] (x: 1)
  let g2: G [string]
}

A pkg/semantic/testdata/scopes/fn_invalid_generic_use_in_fn.snow.err => pkg/semantic/testdata/scopes/fn_invalid_generic_use_in_fn.snow.err +0 -0
A pkg/semantic/testdata/scopes/fn_invalid_generic_use_in_fn.snow.want => pkg/semantic/testdata/scopes/fn_invalid_generic_use_in_fn.snow.want +42 -0
@@ 0,0 1,42 @@
1 *semantic.Unit {
.  bool
.  extern
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  2 *semantic.Struct {
.  .  import
.  .  pkg
.  .  symbol
.  }
.  3 *semantic.File {
.  .  g
.  .  4 *semantic.Fn {
.  .  .  $T
.  .  .  $U
.  .  .  5 *semantic.Fn {
.  .  .  .  a
.  .  .  .  v
.  .  .  .  w
.  .  .  .  6 *semantic.If {
.  .  .  .  }
.  .  .  .  7 *semantic.If {
.  .  .  .  }
.  .  .  }
.  .  }
.  }
}

M pkg/semantic/testdata/scopes/generic_struct_instantiation.snow.want => pkg/semantic/testdata/scopes/generic_struct_instantiation.snow.want +1 -0
@@ 27,6 27,7 @@
.  .  G
.  .  main
.  .  4 *semantic.Struct {
.  .  .  $T
.  .  .  x
.  .  }
.  .  5 *semantic.Fn {

A pkg/semantic/testdata/scopes/struct_complex_generic.snow.err => pkg/semantic/testdata/scopes/struct_complex_generic.snow.err +0 -0
A pkg/semantic/testdata/scopes/struct_complex_generic.snow.want => pkg/semantic/testdata/scopes/struct_complex_generic.snow.want +52 -0
@@ 0,0 1,52 @@
1 *semantic.Unit {
.  bool
.  extern
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  2 *semantic.Struct {
.  .  import
.  .  pkg
.  .  symbol
.  }
.  3 *semantic.File {
.  .  A
.  .  integer
.  .  main
.  .  str
.  .  4 *semantic.Struct {
.  .  .  $T
.  .  .  $U
.  .  .  B
.  .  .  bb
.  .  .  ffa
.  .  .  5 *semantic.Struct {
.  .  .  .  $V
.  .  .  .  ffb
.  .  .  }
.  .  }
.  .  6 *semantic.Fn {
.  .  .  s
.  .  }
.  .  7 *semantic.Fn {
.  .  .  i
.  .  }
.  .  8 *semantic.Fn {
.  .  .  a
.  .  }
.  }
}

M pkg/semantic/testdata/scopes/struct_generic.snow.err => pkg/semantic/testdata/scopes/struct_generic.snow.err +1 -1
@@ 1,1 1,1 @@
testdata/struct_generic.snow:23:19: generic type $T: symbol already declared in this scope
testdata/struct_generic.snow:24:19: generic type $T: symbol already declared in this scope

A pkg/semantic/testdata/scopes/struct_simple_generic.snow.err => pkg/semantic/testdata/scopes/struct_simple_generic.snow.err +0 -0
A pkg/semantic/testdata/scopes/struct_simple_generic.snow.want => pkg/semantic/testdata/scopes/struct_simple_generic.snow.want +41 -0
@@ 0,0 1,41 @@
1 *semantic.Unit {
.  bool
.  extern
.  f32
.  f64
.  false
.  float
.  i16
.  i32
.  i64
.  i8
.  int
.  string
.  true
.  u16
.  u32
.  u64
.  u8
.  uint
.  void
.  2 *semantic.Struct {
.  .  import
.  .  pkg
.  .  symbol
.  }
.  3 *semantic.File {
.  .  A
.  .  main
.  .  println
.  .  4 *semantic.Struct {
.  .  .  $T
.  .  .  x
.  .  }
.  .  5 *semantic.Fn {
.  .  .  a
.  .  }
.  .  6 *semantic.Fn {
.  .  .  i
.  .  }
.  }
}

M pkg/semantic/testdata/static/fn_generic_unused_in_signature.snow.err => pkg/semantic/testdata/static/fn_generic_unused_in_signature.snow.err +1 -0
@@ 3,3 3,4 @@ testdata/fn_generic_unused_in_signature.snow:7:4: unused generic type(s) in func
testdata/fn_generic_unused_in_signature.snow:24:4: invalid type for declaration g5: (unresolved) -> $T
testdata/fn_generic_unused_in_signature.snow:24:14: invalid type for identifier $U: unresolved
testdata/fn_generic_unused_in_signature.snow:24:14: undefined: $U
testdata/fn_generic_unused_in_signature.snow:30:3: wrong number of types provided in generic instantiation, want 1, got 2

M pkg/semantic/testdata/static/fn_generic_unused_in_signature.snow.want => pkg/semantic/testdata/static/fn_generic_unused_in_signature.snow.want +3 -0
@@ 2,3 2,6 @@ testdata/fn_generic_unused_in_signature.snow:10:10: a
testdata/fn_generic_unused_in_signature.snow:15:10: a
testdata/fn_generic_unused_in_signature.snow:21:10: b
testdata/fn_generic_unused_in_signature.snow:26:10: a
testdata/fn_generic_unused_in_signature.snow:30:3: g3
testdata/fn_generic_unused_in_signature.snow:30:6: int
testdata/fn_generic_unused_in_signature.snow:30:11: string

A pkg/semantic/testdata/static/fn_invalid_generic_use_in_fn.snow.err => pkg/semantic/testdata/static/fn_invalid_generic_use_in_fn.snow.err +8 -0
@@ 0,0 1,8 @@
main function missing
testdata/fn_invalid_generic_use_in_fn.snow:2:11: expected type BasicType; got $T
testdata/fn_invalid_generic_use_in_fn.snow:2:11: invalid type: unresolved
testdata/fn_invalid_generic_use_in_fn.snow:3:6: expected type BasicType; got $T
testdata/fn_invalid_generic_use_in_fn.snow:3:6: expected type BasicType; got unresolved
testdata/fn_invalid_generic_use_in_fn.snow:4:13: expected type BasicType; got $U
testdata/fn_invalid_generic_use_in_fn.snow:6:3: expected type SignatureType; got $T
testdata/fn_invalid_generic_use_in_fn.snow:6:3: invalid type: unresolved

A pkg/semantic/testdata/static/fn_invalid_generic_use_in_fn.snow.want => pkg/semantic/testdata/static/fn_invalid_generic_use_in_fn.snow.want +6 -0
@@ 0,0 1,6 @@
testdata/fn_invalid_generic_use_in_fn.snow:2:11: v
testdata/fn_invalid_generic_use_in_fn.snow:2:15: w
testdata/fn_invalid_generic_use_in_fn.snow:3:6: v
testdata/fn_invalid_generic_use_in_fn.snow:3:11: w
testdata/fn_invalid_generic_use_in_fn.snow:4:13: w
testdata/fn_invalid_generic_use_in_fn.snow:6:3: v

A pkg/semantic/testdata/static/generic_struct_instantiation.snow.err => pkg/semantic/testdata/static/generic_struct_instantiation.snow.err +0 -0
A pkg/semantic/testdata/static/generic_struct_instantiation.snow.want => pkg/semantic/testdata/static/generic_struct_instantiation.snow.want +2 -0
@@ 0,0 1,2 @@
testdata/generic_struct_instantiation.snow:7:12: G
testdata/generic_struct_instantiation.snow:7:15: int

A pkg/semantic/testdata/static/struct_complex_generic.snow.err => pkg/semantic/testdata/static/struct_complex_generic.snow.err +0 -0
A pkg/semantic/testdata/static/struct_complex_generic.snow.want => pkg/semantic/testdata/static/struct_complex_generic.snow.want +10 -0
@@ 0,0 1,10 @@
testdata/struct_complex_generic.snow:11:10: s
testdata/struct_complex_generic.snow:15:10: i
testdata/struct_complex_generic.snow:19:11: A
testdata/struct_complex_generic.snow:19:13: int
testdata/struct_complex_generic.snow:19:18: string
testdata/struct_complex_generic.snow:19:31: A
testdata/struct_complex_generic.snow:19:33: B
testdata/struct_complex_generic.snow:19:35: string
testdata/struct_complex_generic.snow:19:48: str
testdata/struct_complex_generic.snow:19:59: integer

M pkg/semantic/testdata/static/struct_generic.snow.err => pkg/semantic/testdata/static/struct_generic.snow.err +13 -4
@@ 1,5 1,14 @@
main function missing
testdata/struct_generic.snow:15:19: declared generic type $V is unused
testdata/struct_generic.snow:23:19: generic type $T: symbol already declared in this scope
testdata/struct_generic.snow:32:10: invalid type for identifier $V: unresolved
testdata/struct_generic.snow:32:10: undefined: $V
testdata/struct_generic.snow:3:10: invalid type for identifier $T: unresolved
testdata/struct_generic.snow:3:10: undefined: $T
testdata/struct_generic.snow:4:10: invalid type for identifier $U: unresolved
testdata/struct_generic.snow:4:10: undefined: $U
testdata/struct_generic.snow:6:12: invalid type for identifier $U: unresolved
testdata/struct_generic.snow:6:12: undefined: $U
testdata/struct_generic.snow:7:12: invalid type for identifier $V: unresolved
testdata/struct_generic.snow:7:12: undefined: $V
testdata/struct_generic.snow:10:14: invalid type for identifier $W: unresolved
testdata/struct_generic.snow:10:14: undefined: $W
testdata/struct_generic.snow:24:19: generic type $T: symbol already declared in this scope
testdata/struct_generic.snow:33:10: invalid type for identifier $V: unresolved
testdata/struct_generic.snow:33:10: undefined: $V

A pkg/semantic/testdata/static/struct_simple_generic.snow.err => pkg/semantic/testdata/static/struct_simple_generic.snow.err +0 -0
A pkg/semantic/testdata/static/struct_simple_generic.snow.want => pkg/semantic/testdata/static/struct_simple_generic.snow.want +5 -0
@@ 0,0 1,5 @@
testdata/struct_simple_generic.snow:6:11: A
testdata/struct_simple_generic.snow:6:13: int
testdata/struct_simple_generic.snow:7:3: println
testdata/struct_simple_generic.snow:7:11: a
testdata/struct_simple_generic.snow:7:13: x

A pkg/semantic/testdata/struct_complex_generic.snow => pkg/semantic/testdata/struct_complex_generic.snow +20 -0
@@ 0,0 1,20 @@
struct A [$T, $U] {
  struct B [$V] {
    var ffb: ($V) -> $V
  }

  var bb: B[$U]
  var ffa: ($T) -> $T
}

fn str(s: string) -> string {
  return s
}

fn integer(i: int) -> int {
  return i
}

fn main() {
  var a = A[int, string] (bb: A.B[string](ffb: str), ffa: integer)
}

R pkg/semantic/testdata/struct_generic.snow.notyet => pkg/semantic/testdata/struct_generic.snow +1 -0
@@ 1,3 1,4 @@
# implicit does not work
struct A {
  var x: $T
  let y: $U

A pkg/semantic/testdata/struct_simple_generic.snow => pkg/semantic/testdata/struct_simple_generic.snow +13 -0
@@ 0,0 1,13 @@
struct A[$T] {
  var x: $T
}

fn main() {
  let a = A[int](x: 1)
  println(a.x)
}

@extern(import: "fmt", symbol: "Println")
fn println(i: int)

#=1

M pkg/semantic/testdata/types/fn_generic_unused_in_signature.snow.err => pkg/semantic/testdata/types/fn_generic_unused_in_signature.snow.err +1 -0
@@ 1,1 1,2 @@
testdata/fn_generic_unused_in_signature.snow:24:14: undefined: $U
testdata/fn_generic_unused_in_signature.snow:30:3: wrong number of types provided in generic instantiation, want 1, got 2

M pkg/semantic/testdata/types/fn_generic_unused_in_signature.snow.want => pkg/semantic/testdata/types/fn_generic_unused_in_signature.snow.want +8 -1
@@ 59,4 59,11 @@ file testdata/fn_generic_unused_in_signature.snow [0, 6, 0]
      return
        ident a [let: $T]
  fn main [let: () -> void]
    block [0]
    block [1]
      expr
        call [1] [value: int]
          generic inst [2] [value: (int) -> int]
            ident g3 [let: (int) -> $T]
            ident int [type: int]
            ident string [type: string]
          int [1] [const: int]

A pkg/semantic/testdata/types/fn_invalid_generic_use_in_fn.snow.err => pkg/semantic/testdata/types/fn_invalid_generic_use_in_fn.snow.err +0 -0
A pkg/semantic/testdata/types/fn_invalid_generic_use_in_fn.snow.want => pkg/semantic/testdata/types/fn_invalid_generic_use_in_fn.snow.want +24 -0
@@ 0,0 1,24 @@
file testdata/fn_invalid_generic_use_in_fn.snow [0, 1, 0]
  fn g [$2] [let: ($T, $U) -> void]
    generic type $T [type: $T]
    generic type $U [type: $U]
    let: v [let: $T]
      ident $T [type: $T]
    let: w [let: $U]
      ident $U [type: $U]
    block [3]
      let= a [let: unresolved]
        binary [+] [value: unresolved]
          ident v [let: $T]
          ident w [let: $U]
      if [1]
        binary [==] [value: unresolved]
          ident v [let: $T]
          ident w [let: $U]
        block [0]
        if [1]
          ident w [let: $U]
          block [0]
      expr
        call [0] [value: unresolved]
          ident v [let: $T]

M pkg/semantic/testdata/types/generic_struct_instantiation.snow.want => pkg/semantic/testdata/types/generic_struct_instantiation.snow.want +8 -8
@@ 1,17 1,17 @@
file testdata/generic_struct_instantiation.snow [0, 1, 1]
  fn main [let: () -> void]
    block [2]
      let= g1 [let: struct G [$T] [int]]
        call [1] [value: struct G [$T] [int]]
          generic inst [1] [type: struct G [$T] [int]]
            ident G [type: struct G [$T]]
      let= g1 [let: struct G[int]]
        call [x] [1] [value: struct G[int]]
          generic inst [1] [type: struct G[int]]
            ident G [type: struct G]
            ident int [type: int]
          int [1] [const: int]
      let: g2 [let: struct G [$T] [string]]
        generic inst [1] [type: struct G [$T] [string]]
          ident G [type: struct G [$T]]
      let: g2 [let: struct G[string]]
        generic inst [1] [type: struct G[string]]
          ident G [type: struct G]
          ident string [type: string]
  struct G [1, 0, 0, $1] [type: struct G [$T]]
  struct G [1, 0, 0, $1] [type: struct G]
    generic type $T [type: $T]
    var: x [var: $T]
      ident $T [type: $T]

A pkg/semantic/testdata/types/struct_complex_generic.snow.err => pkg/semantic/testdata/types/struct_complex_generic.snow.err +0 -0
A pkg/semantic/testdata/types/struct_complex_generic.snow.want => pkg/semantic/testdata/types/struct_complex_generic.snow.want +48 -0
@@ 0,0 1,48 @@
file testdata/struct_complex_generic.snow [0, 3, 1]
  fn str [let: (string) -> string]
    let: s [let: string]
      ident string [type: string]
    ident string [type: string]
    block [1]
      return
        ident s [let: string]
  fn integer [let: (int) -> int]
    let: i [let: int]
      ident int [type: int]
    ident int [type: int]
    block [1]
      return
        ident i [let: int]
  fn main [let: () -> void]
    block [1]
      var= a [var: struct A[int, string]]
        call [bb, ffa] [2] [value: struct A[int, string]]
          generic inst [2] [type: struct A[int, string]]
            ident A [type: struct A]
            ident int [type: int]
            ident string [type: string]
          call [ffb] [1] [value: struct B[string]]
            select [type: struct B[string]]
              ident A [type: struct A]
              generic inst [1] [type: struct B[string]]
                ident B [type: struct B]
                ident string [type: string]
            ident str [let: (string) -> string]
          ident integer [let: (int) -> int]
  struct A [2, 0, 1, $2] [type: struct A]
    generic type $T [type: $T]
    generic type $U [type: $U]
    var: bb [var: struct B[$U]]
      generic inst [1] [type: struct B[$U]]
        ident B [type: struct B]
        ident $U [type: $U]
    var: ffa [var: ($T) -> $T]
      sig [1->1] [type: ($T) -> $T]
        ident $T [type: $T]
        ident $T [type: $T]
    struct B [1, 0, 0, $1] [type: struct B]
      generic type $V [type: $V]
      var: ffb [var: ($V) -> $V]
        sig [1->1] [type: ($V) -> $V]
          ident $V [type: $V]
          ident $V [type: $V]

M pkg/semantic/testdata/types/struct_generic.snow.err => pkg/semantic/testdata/types/struct_generic.snow.err +7 -3
@@ 1,3 1,7 @@
testdata/struct_generic.snow:15:19: declared generic type $V is unused
testdata/struct_generic.snow:23:19: generic type $T: symbol already declared in this scope
testdata/struct_generic.snow:32:10: undefined: $V
testdata/struct_generic.snow:3:10: undefined: $T
testdata/struct_generic.snow:4:10: undefined: $U
testdata/struct_generic.snow:6:12: undefined: $U
testdata/struct_generic.snow:7:12: undefined: $V
testdata/struct_generic.snow:10:14: undefined: $W
testdata/struct_generic.snow:24:19: generic type $T: symbol already declared in this scope
testdata/struct_generic.snow:33:10: undefined: $V

M pkg/semantic/testdata/types/struct_generic.snow.want => pkg/semantic/testdata/types/struct_generic.snow.want +15 -19
@@ 1,24 1,20 @@
file testdata/struct_generic.snow [0, 0, 4]
  struct A [2, 1, 0, $3] [type: struct A [$T, $U, $V]]
    generic type $T [type: $T]
    generic type $U [type: $U]
    generic type $V [type: $V]
    var: x [var: $T]
      ident $T [type: $T]
    let: y [let: $U]
      ident $U [type: $U]
  struct A [2, 1, 0] [type: struct A]
    var: x [var: unresolved]
      ident $T [invalid: unresolved]
    let: y [let: unresolved]
      ident $U [invalid: unresolved]
    fn z [let: () -> void]
      block [3]
        let: a [let: $U]
          ident $U [type: $U]
        let: b [let: $V]
          ident $V [type: $V]
        fn inner [$1] [let: () -> void [$W]]
          generic type $W [type: $W]
        let: a [let: unresolved]
          ident $U [invalid: unresolved]
        let: b [let: unresolved]
          ident $V [invalid: unresolved]
        fn inner [let: () -> void]
          block [1]
            let: c [let: $W]
              ident $W [type: $W]
  struct B [1, 1, 0, $3] [type: struct B [$T, $U, $V]]
            let: c [let: unresolved]
              ident $W [invalid: unresolved]
  struct B [1, 1, 0, $3] [type: struct B]
    generic type $T [type: $T]
    generic type $U [type: $U]
    generic type $V [type: $V]


@@ 30,7 26,7 @@ file testdata/struct_generic.snow [0, 0, 4]
      block [1]
        var: z [var: $U]
          ident $U [type: $U]
  struct C [2, 0, 0, $3] [type: struct C [$T, $U, $T]]
  struct C [2, 0, 0, $3] [type: struct C]
    generic type $T [type: $T]
    generic type $U [type: $U]
    generic type $T [type: $T]


@@ 38,7 34,7 @@ file testdata/struct_generic.snow [0, 0, 4]
      ident $T [type: $T]
    let: y [let: $U]
      ident $U [type: $U]
  struct D [3, 0, 0, $2] [type: struct D [$T, $U]]
  struct D [3, 0, 0, $2] [type: struct D]
    generic type $T [type: $T]
    generic type $U [type: $U]
    var: v [var: $T]

A pkg/semantic/testdata/types/struct_simple_generic.snow.err => pkg/semantic/testdata/types/struct_simple_generic.snow.err +0 -0
A pkg/semantic/testdata/types/struct_simple_generic.snow.want => pkg/semantic/testdata/types/struct_simple_generic.snow.want +26 -0
@@ 0,0 1,26 @@
file testdata/struct_simple_generic.snow [0, 2, 1]
  fn main [let: () -> void]
    block [2]
      let= a [let: struct A[int]]
        call [x] [1] [value: struct A[int]]
          generic inst [1] [type: struct A[int]]
            ident A [type: struct A]
            ident int [type: int]
          int [1] [const: int]
      expr
        call [1] [value: void]
          ident println [let: (int) -> void]
          select [let: int]
            ident a [let: struct A[int]]
            ident x [var: int]
  fn println [let: (int) -> void]
    @ [import, symbol] [2] [value: struct extern]
      ident extern [type: struct extern]
      string ["fmt"] [const: string]
      string ["Println"] [const: string]
    let: i [let: int]
      ident int [type: int]
  struct A [1, 0, 0, $1] [type: struct A]
    generic type $T [type: $T]
    var: x [var: $T]
      ident $T [type: $T]

M pkg/semantic/translate_pass.go => pkg/semantic/translate_pass.go +9 -1
@@ 121,6 121,7 @@ func (t *translateVisitor) Visit(n ast.Node) ast.Visitor {
		tgen := t
		if gp := n.GenericParams; gp != nil {
			if fn.MethodOf != nil {
				// TODO: should that really be prevented?
				t.errh(n.GenericParams.Pos(), "invalid generic clause on struct method")
			} else {
				tgen = t.cloneNewScope(&fn)


@@ 426,7 427,14 @@ func (t *translateVisitor) Visit(n ast.Node) ast.Visitor {
		}
		if n.Sel != nil {
			ast.Walk(t, n.Sel)
			sel.Sel = t.generated.(*Ident)
			switch expr := t.generated.(type) {
			case *GenericInst:
				sel.Sel = expr
			case *Ident:
				sel.Sel = expr
			default:
				panic(fmt.Sprintf("invalid node type for right-hand side of selector: %T", expr))
			}
		}
		t.generated = &sel


M pkg/semantic/type.go => pkg/semantic/type.go +154 -18
@@ 17,6 17,9 @@ type Type interface {
	IdenticalTo(T Type) bool
	// Valid indicates if the type is valid. Unresolved types are invalid.
	Valid() bool
	// ResolveGeneric returns a Type where all generic types present in the
	// resolve map provided and resolved to their matching type.
	ResolveGeneric(map[*GenericType]Type) Type

	fmt.Stringer
}


@@ 44,6 47,13 @@ type (
	StructType struct {
		// Decl represents the struct declaration of this type.
		Decl *Struct
		// Inst is the list of types used in a generic instantiation, if the
		// struct 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. Its


@@ 118,18 128,18 @@ func AsType(T Type, v interface{}) bool {

// ========> implement Type for unresolvedType

func (u unresolvedType) AssignableTo(Type) bool              { return false }
func (u unresolvedType) IdenticalTo(T Type) bool             { _, ok := T.(unresolvedType); return ok }
func (u unresolvedType) Valid() bool                         { return false }
func (u unresolvedType) IsGeneric(gens *[]*GenericType) bool { return false }
func (u unresolvedType) String() string                      { return "unresolved" }
func (u unresolvedType) AssignableTo(Type) bool                            { return false }
func (u unresolvedType) IdenticalTo(T Type) bool                           { _, ok := T.(unresolvedType); return ok }
func (u unresolvedType) Valid() bool                                       { return false }
func (u unresolvedType) String() string                                    { return "unresolved" }
func (u unresolvedType) ResolveGeneric(resolve map[*GenericType]Type) Type { return u }

// ========> implement Type for BasicType

func (b *BasicType) String() string                      { return b.Kind.String() }
func (b *BasicType) IdenticalTo(T Type) bool             { return IsBasicOfKind(T, b.Kind) }
func (b *BasicType) Valid() bool                         { return b.Kind >= Void && b.Kind < kindEnd }
func (b *BasicType) IsGeneric(gens *[]*GenericType) bool { return false }
func (b *BasicType) String() string                                    { return b.Kind.String() }
func (b *BasicType) IdenticalTo(T Type) bool                           { return IsBasicOfKind(T, b.Kind) }
func (b *BasicType) Valid() bool                                       { return b.Kind >= Void && b.Kind < kindEnd }
func (b *BasicType) ResolveGeneric(resolve map[*GenericType]Type) Type { return b }
func (b *BasicType) AssignableTo(T Type) bool {
	// implicit type coercion works when all values of the type can be
	// represented by the target type, i.e. the target is a superset


@@ 236,9 246,36 @@ func (s *SignatureType) Valid() bool {
	return true
}

// ========> implement Type for TupleType
func (s *SignatureType) ResolveGeneric(resolve map[*GenericType]Type) Type {
	var diff bool

	sigt := &SignatureType{
		Params: make([]Type, len(s.Params)),
	}
	for i, pt := range s.Params {
		if gt := AsGenericType(pt); gt != nil {
			if newt := resolve[gt]; newt != nil {
				sigt.Params[i] = newt
				diff = true
				continue
			}
		}
		sigt.Params[i] = pt
	}
	sigt.Return = s.Return
	if gt := AsGenericType(s.Return); gt != nil {
		if newt := resolve[gt]; newt != nil {
			sigt.Return = newt
			diff = true
		}
	}
	if diff {
		return sigt
	}
	return s
}

func (t *TupleType) IsGeneric(gens *[]*GenericType) bool { return false }
// ========> implement Type for TupleType

func (t *TupleType) Valid() bool {
	for _, f := range t.Fields {


@@ 296,32 333,131 @@ func (t *TupleType) IdenticalTo(T Type) bool {
	return true
}

func (t *TupleType) ResolveGeneric(resolve map[*GenericType]Type) Type {
	var diff bool

	tt := &TupleType{
		Fields: make([]Type, len(t.Fields)),
	}
	for i, ft := range t.Fields {
		if gt := AsGenericType(ft); gt != nil {
			if newt := resolve[gt]; newt != nil {
				tt.Fields[i] = newt
				diff = true
				continue
			}
		}
		tt.Fields[i] = ft
	}
	if diff {
		return tt
	}
	return t
}

// ========> 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) String() string           { return "struct " + s.Decl.Ident() }

func (s *StructType) String() string {
	var buf strings.Builder
	buf.WriteString("struct " + s.Decl.Ident())
	if len(s.Inst) > 0 {
		buf.WriteString("[")
		for i, t := range s.Inst {
			if i > 0 {
				buf.WriteString(", ")
			}
			buf.WriteString(t.String())
		}
		buf.WriteString("]")
	}
	return buf.String()
}

func (s *StructType) IdenticalTo(T Type) bool {
	s2 := AsStructType(T)
	if s2 == nil {
		return false
	}
	return s.Decl == s2.Decl
	if s.Decl != s2.Decl {
		return false
	}
	if len(s.Inst) != len(s2.Inst) {
		return false
	}
	for i, t1 := range s.Inst {
		t2 := s2.Inst[i]
		if !t1.IdenticalTo(t2) {
			return false
		}
	}
	return true
}

func (s *StructType) ResolveGeneric(resolve map[*GenericType]Type) Type {
	var diff bool
	strt := &StructType{
		Decl: s.Decl,
		Inst: make([]Type, len(s.Inst)),
	}
	for i, it := range s.Inst {
		if gt := AsGenericType(it); gt != nil {
			if newt := resolve[gt]; newt != nil {
				strt.Inst[i] = newt
				diff = true
				continue
			}
		}
		strt.Inst[i] = it
	}
	if diff {
		return strt
	}
	return s
}

// 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
// instantiation.
func (s *StructType) typeOfSel(T Type) Type {
	if !s.Decl.IsGeneric() {
		return T
	}

	if s.lookup == nil {
		s.lookup = makeGenericResolveMap(s.Decl.GenericParams, s.Inst)
	}
	return T.ResolveGeneric(s.lookup)
}

func makeGenericResolveMap(gc *GenericClause, types []Type) map[*GenericType]Type {
	resolve := make(map[*GenericType]Type, len(types))
	for i, elem := range gc.Elems {
		if i >= len(types) {
			break
		}
		resolve[AsGenericType(elem.Type())] = types[i]
	}
	return resolve
}

// ========> implement Type for GenericType

func (g *GenericType) String() string { return g.Name }
func (g *GenericType) ResolveGeneric(resolve map[*GenericType]Type) Type {
	if newt := resolve[g]; newt != nil {
		return newt
	}
	return g
}

// a generic type is always valid by itself (i.e. it is not unresolved)
func (g *GenericType) Valid() bool { return true }

// this might seem a bit strange but GenericType is not generic itself - i.e. it can be
// used in places other than its declaration without being instantiated, that's what
// makes it possible to define a higher-level generic type.
func (g *GenericType) IsGeneric(gens *[]*GenericType) bool { return false }
func (g *GenericType) AssignableTo(T Type) bool            { return g.IdenticalTo(T) }
func (g *GenericType) AssignableTo(T Type) bool { return g.IdenticalTo(T) }
func (g *GenericType) IdenticalTo(T Type) bool {
	g2 := AsGenericType(T)
	if g2 == nil {

M pkg/semantic/typeassign_pass.go => pkg/semantic/typeassign_pass.go +53 -89
@@ 35,20 35,18 @@ type typeassignVisitor struct {
	errh     func(token.Pos, string)
	deferred []func()

	// flag indicating that the expression being visited is on the left of a Selector,
	// indicating that it will step further down into that expression's scope (important
	// for generics, as using a generic Struct identifier is not allowed unless it is just
	// to get to an inner scope).
	//leftOfSel bool

	// flag indicating that the Identifier is part of a generic instantiation expression.
	genericInstIdent bool
	// flag indicating that we are visiting the left-hand side of a selector.
	selectorLHS bool
}

func (t *typeassignVisitor) addDeferred(n Node) {
	genericInstIdent := t.genericInstIdent
	selectorLHS := t.selectorLHS
	t.deferred = append(t.deferred, func() {
		t.genericInstIdent = genericInstIdent
		t.selectorLHS = selectorLHS
		Walk(t, n)
	})
}


@@ 233,10 231,6 @@ func (t *typeassignVisitor) Visit(n Node) Visitor {
		// of n.Fun, if it is a signature type, or the type of the struct or
		// attribute if it is a struct initializer or func attribute. Otherwise
		// default to unresolved.

		// TODO: if n.Fun is a generic, try to infer the types from the call, and replace
		// n.Fun with a *GenericInst.

		lt := n.Fun.Type()
		n.ctx = Value
		switch lt := lt.(type) {


@@ 252,10 246,22 @@ func (t *typeassignVisitor) Visit(n Node) Visitor {
		}

	case *Selector:
		slhs := t.selectorLHS // flag can be recursive, do not set to false after Walk, set to old value
		t.selectorLHS = true
		Walk(t, n.Left)
		t.selectorLHS = slhs

		// must not call walk on n.Sel, as it will not find the right symbol in
		// the lookup chain - must look in n.Left.
		t.typeAssignSelector(n.Left.Type(), n.Left.TypeContext(), n.Sel)
		switch sel := n.Sel.(type) {
		case *Ident:
			t.typeAssignSelector(n.Left.Type(), n.Left.TypeContext(), sel)
		case *GenericInst:
			t.typeAssignSelector(n.Left.Type(), n.Left.TypeContext(), sel.GenericDecl)
			// at this point sel.Type() should be a generic type and sel.Ref a generic decl,
			// now instantiate it.
			t.typeAssignGenericInst(sel)
		}

		n.ctx = selectorTypeContext[n.Left.TypeContext()][n.Sel.TypeContext()]
		n.typ = n.Sel.Type()


@@ 263,48 269,18 @@ func (t *typeassignVisitor) Visit(n Node) Visitor {
	case *GenericInst:
		t.genericInstIdent = true
		Walk(t, n.GenericDecl)

		n.ctx = Invalid
		n.typ = unresolvedType{}
		if !n.GenericDecl.Type().Valid() {
			break
		}
		genRef := n.GenericDecl.Ref
		if genRef == nil || !genRef.Type().Valid() {
			break
		}
		if !genRef.IsGeneric() {
			t.errh(n.Pos(), "invalid instantiation of a non-generic declaration")
			break
		}

		types := make([]Type, len(n.TypeExprs))
		for i, te := range n.TypeExprs {
			Walk(t, te)
			types[i] = te.Type()
		}

		ctx := genRef.TypeContext()
		switch {
		case ctx == Typ:
			n.ctx = Typ // instantiated struct generics are types
		case ctx.isAnyOf(TypeContextValues...):
			n.ctx = Value // instantiated fn generics are values
		}
		n.typ = t.instantiateGeneric(n, genRef.(GenericDecl), types)
		t.genericInstIdent = false
		t.typeAssignGenericInst(n)

	case *Ident:
		// TODO: if this gets deferred, the genericInstIdent gets lost...
		genericInstIdent := t.genericInstIdent
		defer func() { t.genericInstIdent = false }()

		decl := n.Scope().LookupChain(n.Name, n.Pos())
		if decl == nil {
			t.errUndefined(n, nil)
			break
		}
		if (!genericInstIdent) && decl.IsGeneric() {
			// TODO: for struct, this might be valid on a lhs of a Selector
		if !t.genericInstIdent && !t.selectorLHS && decl.IsGeneric() {
			// cannot access a generic identifier without an instantiation, unless it is used
			// on the left-hand side of a selector to access an inner member.
			t.errh(n.Pos(), "cannot use generic declaration without instantiation")
		}



@@ 337,15 313,12 @@ func (t *typeassignVisitor) Visit(n Node) Visitor {
	return nil
}

// returns the concrete type of an instantiated generic declaration.
func (t *typeassignVisitor) instantiateGeneric(gi *GenericInst, gen GenericDecl, types []Type) Type {
	gc := gen.GenClause()
	genTypes := make([]*GenericType, len(gc.Elems))
	for i, elem := range gc.Elems {
		genTypes[i] = AsGenericType(elem.Type())
	}
	// make the map of generic type names to actual types
	count, resolve := makeGenericResolveMap(genTypes, types)
	if count < len(gc.Elems) || len(types) > len(gc.Elems) {
	gc := gen.GenClause()
	resolve := makeGenericResolveMap(gc, types)
	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)))
	}



@@ 355,57 328,48 @@ func (t *typeassignVisitor) instantiateGeneric(gi *GenericInst, gen GenericDecl,
			gen.GenericInsts = make(map[*GenericInst][]Type)
		}
		gen.GenericInsts[gi] = types
		return resolveGenericFn(gen, resolve)
		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{}
}

func resolveGenericFn(fn *Fn, resolve map[string]Type) Type {
	orit := AsSignatureType(fn.Type())
	sigt := &SignatureType{
		Params: make([]Type, len(orit.Params)),
func (t *typeassignVisitor) typeAssignGenericInst(gi *GenericInst) {
	gi.ctx = Invalid
	gi.typ = unresolvedType{}
	if !gi.GenericDecl.Type().Valid() {
		return
	}
	for i, pt := range orit.Params {
		if gt := AsGenericType(pt); gt != nil {
			if newt := resolve[gt.Name]; newt != nil {
				sigt.Params[i] = newt
				continue
			}
		}
		sigt.Params[i] = pt
	genRef := gi.GenericDecl.Ref
	if genRef == nil || !genRef.Type().Valid() {
		return
	}
	sigt.Return = orit.Return
	if gt := AsGenericType(orit.Return); gt != nil {
		if newt := resolve[gt.Name]; newt != nil {
			sigt.Return = newt
		}
	if !genRef.IsGeneric() {
		t.errh(gi.Pos(), "invalid instantiation of a non-generic declaration")
		return
	}
	return sigt
}

func makeGenericResolveMap(gens []*GenericType, types []Type) (int, map[string]Type) {
	var count int
	resolve := make(map[string]Type, len(types))
	for i, gen := range gens {
		if i >= len(types) {
			break
		}
		count++
		// at this point, the generic names are not type-checked and may contain
		// duplicates, so don't overwrite a resolved generic name if it already
		// exists.
		if _, ok := resolve[gen.Name]; !ok {
			resolve[gen.Name] = types[i]
		}
	types := make([]Type, len(gi.TypeExprs))
	for i, te := range gi.TypeExprs {
		Walk(t, te)
		types[i] = te.Type()
	}

	ctx := genRef.TypeContext()
	switch {
	case ctx == Typ:
		gi.ctx = Typ // instantiated struct generics are types
	case ctx.isAnyOf(TypeContextValues...):
		gi.ctx = Value // instantiated fn generics are values
	}
	return count, resolve
	gi.typ = t.instantiateGeneric(gi, genRef.(GenericDecl), types)
}

func (t *typeassignVisitor) typeAssignSelector(left Type, leftCtx TypeContext, sel *Ident) {


@@ 418,7 382,7 @@ func (t *typeassignVisitor) typeAssignSelector(left Type, leftCtx TypeContext, s
		decl := left.Decl.BodyScope.Lookup(sel.Name)
		if decl != nil {
			sel.ctx = decl.TypeContext()
			sel.typ = decl.Type()
			sel.typ = left.typeOfSel(decl.Type())
			sel.Ref = decl
			return
		}

M pkg/semantic/typecheck_pass.go => pkg/semantic/typecheck_pass.go +14 -4
@@ 197,6 197,8 @@ func (t *typecheckVisitor) Visit(n Node) Visitor {
		// All expressions within the struct body must not use any symbol in outer
		// scopes except for Universe and TopLevel ones.
		tt := t.cloneInStruct(n)
		// TODO: validate that all generic params are used in the struct, and only those (well, others
		// would not type-check as they would be undefined symbols)
		if n.GenericParams != nil {
			for _, ge := range n.GenericParams.Elems {
				Walk(tt, ge)


@@ 404,9 406,16 @@ func (t *typecheckVisitor) Visit(n Node) Visitor {

		// if the selector is a ref method, only valid if the left side is a mutable context
		// (i.e. must be a var struct to get a method that can mutate the struct).
		if fn := AsFnDecl(n.Sel.Ref); fn != nil && fn.IsRef {
		var ref Decl
		switch sel := n.Sel.(type) {
		case *Ident:
			ref = sel.Ref
		case *GenericInst:
			ref = sel.GenericDecl.Ref
		}
		if fn := AsFnDecl(ref); fn != nil && fn.IsRef {
			if lctx := n.Left.TypeContext(); lctx != Mutable {
				t.errh(n.Sel.Pos(), fmt.Sprintf("cannot access ref fn %s; left-hand side must be %s, is %s", n.Sel.Name, Mutable, lctx))
				t.errh(n.Sel.Pos(), fmt.Sprintf("cannot access ref fn %s; left-hand side must be %s, is %s", ref.Ident(), Mutable, lctx))
			}
		}



@@ 415,6 424,7 @@ func (t *typecheckVisitor) Visit(n Node) Visitor {
		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:
		// check that it has a valid type and context


@@ 583,7 593,7 @@ func (t *typecheckVisitor) typecheckStructInit(n *Call) {
	}

	// sanity check
	if !st.IdenticalTo(n.InitOf.Type()) {
	if !st.IdenticalTo(n.InitOf.Type()) && !(n.InitOf.IsGeneric() && st.Decl == n.InitOf) {
		panic(fmt.Sprintf("struct init with Fun type %s has unexpected InitOf.Type of %s", st, n.InitOf.Type()))
	}



@@ 592,7 602,7 @@ func (t *typecheckVisitor) typecheckStructInit(n *Call) {
	labelToType := make(map[string]Type, len(n.InitOf.Vars))
	required := make(map[string]bool, len(n.InitOf.Vars))
	for _, v := range n.InitOf.Vars {
		labelToType[v.Ident()] = v.Type()
		labelToType[v.Ident()] = st.typeOfSel(v.Type())
		if v.Value == nil {
			required[v.Ident()] = true
		}