tint: Add type constructors and converters support to intrinsic-gen

These are currently not used, but the first step towards moving type
constructors and converters over to using the intrinisc table.

This will simplify maintenance of type functions, and will greatly
simplify the [AbstractInt -> i32|u32] [AbstractFloat -> f32|f16] logic.

Bug: tint:1504
Change-Id: I15526670a6ff801e66551ab5adc37b1570ac49de
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/90242
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/tools/src/cmd/intrinsic-gen/ast/ast.go b/tools/src/cmd/intrinsic-gen/ast/ast.go
index da78d96..75b74c7 100644
--- a/tools/src/cmd/intrinsic-gen/ast/ast.go
+++ b/tools/src/cmd/intrinsic-gen/ast/ast.go
@@ -25,11 +25,13 @@
 
 // AST is the parsed syntax tree of the intrinsic definition file
 type AST struct {
-	Enums     []EnumDecl
-	Types     []TypeDecl
-	Matchers  []MatcherDecl
-	Builtins  []IntrinsicDecl
-	Operators []IntrinsicDecl
+	Enums        []EnumDecl
+	Types        []TypeDecl
+	Matchers     []MatcherDecl
+	Builtins     []IntrinsicDecl
+	Constructors []IntrinsicDecl
+	Converters   []IntrinsicDecl
+	Operators    []IntrinsicDecl
 }
 
 func (a AST) String() string {
@@ -50,6 +52,14 @@
 		fmt.Fprintf(&sb, "%v", b)
 		fmt.Fprintln(&sb)
 	}
+	for _, o := range a.Constructors {
+		fmt.Fprintf(&sb, "%v", o)
+		fmt.Fprintln(&sb)
+	}
+	for _, o := range a.Converters {
+		fmt.Fprintf(&sb, "%v", o)
+		fmt.Fprintln(&sb)
+	}
 	for _, o := range a.Operators {
 		fmt.Fprintf(&sb, "%v", o)
 		fmt.Fprintln(&sb)
@@ -103,7 +113,7 @@
 	m.Options.Format(w, verb)
 }
 
-// IntrinsicKind is either a Builtin or Operator
+// IntrinsicKind is either a Builtin, Operator, Constructor or Converter
 type IntrinsicKind string
 
 const (
@@ -113,6 +123,12 @@
 	// Operator is a unary or binary operator.
 	// Declared with 'op'.
 	Operator IntrinsicKind = "operator"
+	// Constructor is a type constructor function.
+	// Declared with 'ctor'.
+	Constructor IntrinsicKind = "constructor"
+	// Converter is a type conversion function.
+	// Declared with 'conv'.
+	Converter IntrinsicKind = "converter"
 )
 
 // IntrinsicDecl describes a builtin or operator declaration
@@ -133,6 +149,10 @@
 		fmt.Fprintf(w, "fn ")
 	case Operator:
 		fmt.Fprintf(w, "op ")
+	case Constructor:
+		fmt.Fprintf(w, "ctor ")
+	case Converter:
+		fmt.Fprintf(w, "conv ")
 	}
 	fmt.Fprintf(w, "%v", i.Name)
 	i.TemplateParams.Format(w, verb)
diff --git a/tools/src/cmd/intrinsic-gen/gen/builtin_table.go b/tools/src/cmd/intrinsic-gen/gen/builtin_table.go
index 8d6e4da..502b690 100644
--- a/tools/src/cmd/intrinsic-gen/gen/builtin_table.go
+++ b/tools/src/cmd/intrinsic-gen/gen/builtin_table.go
@@ -37,14 +37,15 @@
 	NMatchers     []sem.Named
 	NMatcherIndex map[sem.Named]int // [object -> index] in NMatchers
 
-	MatcherIndices  []int        // kMatcherIndices table content
-	OpenTypes       []OpenType   // kOpenTypes table content
-	OpenNumbers     []OpenNumber // kOpenNumbers table content
-	Parameters      []Parameter  // kParameters table content
-	Overloads       []Overload   // kOverloads table content
-	Builtins        []Intrinsic  // kBuiltins table content
-	UnaryOperators  []Intrinsic  // kUnaryOperators table content
-	BinaryOperators []Intrinsic  // kBinaryOperators table content
+	MatcherIndices            []int        // kMatcherIndices table content
+	OpenTypes                 []OpenType   // kOpenTypes table content
+	OpenNumbers               []OpenNumber // kOpenNumbers table content
+	Parameters                []Parameter  // kParameters table content
+	Overloads                 []Overload   // kOverloads table content
+	Builtins                  []Intrinsic  // kBuiltins table content
+	UnaryOperators            []Intrinsic  // kUnaryOperators table content
+	BinaryOperators           []Intrinsic  // kBinaryOperators table content
+	ConstructorsAndConverters []Intrinsic  // kConstructorsAndConverters table content
 }
 
 // OpenType is used to create the C++ OpenTypeInfo structure
@@ -372,6 +373,7 @@
 		{s.Builtins, &b.Builtins},
 		{s.UnaryOperators, &b.UnaryOperators},
 		{s.BinaryOperators, &b.BinaryOperators},
+		{s.ConstructorsAndConverters, &b.ConstructorsAndConverters},
 	} {
 		out := make([]Intrinsic, len(intrinsics.in))
 		for i, f := range intrinsics.in {
diff --git a/tools/src/cmd/intrinsic-gen/lexer/lexer.go b/tools/src/cmd/intrinsic-gen/lexer/lexer.go
index aeffe04..a81875e 100644
--- a/tools/src/cmd/intrinsic-gen/lexer/lexer.go
+++ b/tools/src/cmd/intrinsic-gen/lexer/lexer.go
@@ -97,6 +97,8 @@
 			case l.match("op", tok.Operator):
 			case l.match("enum", tok.Enum):
 			case l.match("type", tok.Type):
+			case l.match("ctor", tok.Constructor):
+			case l.match("conv", tok.Converter):
 			case l.match("match", tok.Match):
 			case unicode.IsLetter(l.peek(0)) || l.peek(0) == '_':
 				l.tok(l.count(alphaNumericOrUnderscore), tok.Identifier)
diff --git a/tools/src/cmd/intrinsic-gen/lexer/lexer_test.go b/tools/src/cmd/intrinsic-gen/lexer/lexer_test.go
index 1c6a2ae..f17eb23 100644
--- a/tools/src/cmd/intrinsic-gen/lexer/lexer_test.go
+++ b/tools/src/cmd/intrinsic-gen/lexer/lexer_test.go
@@ -58,6 +58,12 @@
 		{"type", tok.Token{Kind: tok.Type, Runes: []rune("type"), Source: tok.Source{
 			S: loc(1, 1, 0), E: loc(1, 5, 4),
 		}}},
+		{"ctor", tok.Token{Kind: tok.Constructor, Runes: []rune("ctor"), Source: tok.Source{
+			S: loc(1, 1, 0), E: loc(1, 5, 4),
+		}}},
+		{"conv", tok.Token{Kind: tok.Converter, Runes: []rune("conv"), Source: tok.Source{
+			S: loc(1, 1, 0), E: loc(1, 5, 4),
+		}}},
 		{"enum", tok.Token{Kind: tok.Enum, Runes: []rune("enum"), Source: tok.Source{
 			S: loc(1, 1, 0), E: loc(1, 5, 4),
 		}}},
diff --git a/tools/src/cmd/intrinsic-gen/parser/parser.go b/tools/src/cmd/intrinsic-gen/parser/parser.go
index 48c8a94..a93d966 100644
--- a/tools/src/cmd/intrinsic-gen/parser/parser.go
+++ b/tools/src/cmd/intrinsic-gen/parser/parser.go
@@ -71,6 +71,12 @@
 		case tok.Operator:
 			out.Operators = append(out.Operators, p.operatorDecl(decorations))
 			decorations = nil
+		case tok.Constructor:
+			out.Constructors = append(out.Constructors, p.constructorDecl(decorations))
+			decorations = nil
+		case tok.Converter:
+			out.Converters = append(out.Converters, p.converterDecl(decorations))
+			decorations = nil
 		default:
 			p.err = fmt.Errorf("%v unexpected token '%v'", t.Source, t.Kind)
 		}
@@ -195,6 +201,47 @@
 	}
 	return f
 }
+
+func (p *parser) constructorDecl(decos ast.Decorations) ast.IntrinsicDecl {
+	p.expect(tok.Constructor, "constructor declaration")
+	name := p.next()
+	f := ast.IntrinsicDecl{
+		Source:      name.Source,
+		Kind:        ast.Constructor,
+		Decorations: decos,
+		Name:        string(name.Runes),
+	}
+	if p.peekIs(0, tok.Lt) {
+		f.TemplateParams = p.templateParams()
+	}
+	f.Parameters = p.parameters()
+	if p.match(tok.Arrow) != nil {
+		ret := p.templatedName()
+		f.ReturnType = &ret
+	}
+	return f
+}
+
+func (p *parser) converterDecl(decos ast.Decorations) ast.IntrinsicDecl {
+	p.expect(tok.Converter, "converter declaration")
+	name := p.next()
+	f := ast.IntrinsicDecl{
+		Source:      name.Source,
+		Kind:        ast.Converter,
+		Decorations: decos,
+		Name:        string(name.Runes),
+	}
+	if p.peekIs(0, tok.Lt) {
+		f.TemplateParams = p.templateParams()
+	}
+	f.Parameters = p.parameters()
+	if p.match(tok.Arrow) != nil {
+		ret := p.templatedName()
+		f.ReturnType = &ret
+	}
+	return f
+}
+
 func (p *parser) parameters() ast.Parameters {
 	l := ast.Parameters{}
 	p.expect(tok.Lparen, "function parameter list")
diff --git a/tools/src/cmd/intrinsic-gen/parser/parser_test.go b/tools/src/cmd/intrinsic-gen/parser/parser_test.go
index 9ea76c3..0c01f7c 100644
--- a/tools/src/cmd/intrinsic-gen/parser/parser_test.go
+++ b/tools/src/cmd/intrinsic-gen/parser/parser_test.go
@@ -370,6 +370,254 @@
 					},
 					Parameters: ast.Parameters{},
 				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"ctor F()",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind:       ast.Constructor,
+					Name:       "F",
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"[[deco]] ctor F()",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind: ast.Constructor,
+					Name: "F",
+					Decorations: ast.Decorations{
+						{Name: "deco", Values: []string{}},
+					},
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"ctor F(a)",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind: ast.Constructor,
+					Name: "F",
+					Parameters: ast.Parameters{
+						{Type: ast.TemplatedName{Name: "a"}},
+					},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"ctor F(a: T)",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind: ast.Constructor,
+					Name: "F",
+					Parameters: ast.Parameters{
+						{Name: "a", Type: ast.TemplatedName{Name: "T"}},
+					},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"ctor F(a, b)",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind: ast.Constructor,
+					Name: "F",
+					Parameters: ast.Parameters{
+						{Type: ast.TemplatedName{Name: "a"}},
+						{Type: ast.TemplatedName{Name: "b"}},
+					},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"ctor F<A : B<C> >()",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind: ast.Constructor,
+					Name: "F",
+					TemplateParams: ast.TemplateParams{
+						{
+							Name: "A", Type: ast.TemplatedName{
+								Name: "B",
+								TemplateArgs: ast.TemplatedNames{
+									{Name: "C"},
+								},
+							},
+						},
+					},
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"ctor F<T>(a: X, b: Y<T>)",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind: ast.Constructor,
+					Name: "F",
+					TemplateParams: ast.TemplateParams{
+						{Name: "T"},
+					},
+					Parameters: ast.Parameters{
+						{Name: "a", Type: ast.TemplatedName{Name: "X"}},
+						{Name: "b", Type: ast.TemplatedName{
+							Name:         "Y",
+							TemplateArgs: []ast.TemplatedName{{Name: "T"}},
+						}},
+					},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"ctor F() -> X",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind:       ast.Constructor,
+					Name:       "F",
+					ReturnType: &ast.TemplatedName{Name: "X"},
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"ctor F() -> X<T>",
+			ast.AST{
+				Constructors: []ast.IntrinsicDecl{{
+					Kind: ast.Constructor,
+					Name: "F",
+					ReturnType: &ast.TemplatedName{
+						Name:         "X",
+						TemplateArgs: []ast.TemplatedName{{Name: "T"}},
+					},
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"conv F()",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind:       ast.Converter,
+					Name:       "F",
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"[[deco]] conv F()",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind: ast.Converter,
+					Name: "F",
+					Decorations: ast.Decorations{
+						{Name: "deco", Values: []string{}},
+					},
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"conv F(a)",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind: ast.Converter,
+					Name: "F",
+					Parameters: ast.Parameters{
+						{Type: ast.TemplatedName{Name: "a"}},
+					},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"conv F(a: T)",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind: ast.Converter,
+					Name: "F",
+					Parameters: ast.Parameters{
+						{Name: "a", Type: ast.TemplatedName{Name: "T"}},
+					},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"conv F(a, b)",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind: ast.Converter,
+					Name: "F",
+					Parameters: ast.Parameters{
+						{Type: ast.TemplatedName{Name: "a"}},
+						{Type: ast.TemplatedName{Name: "b"}},
+					},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"conv F<A : B<C> >()",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind: ast.Converter,
+					Name: "F",
+					TemplateParams: ast.TemplateParams{
+						{
+							Name: "A", Type: ast.TemplatedName{
+								Name: "B",
+								TemplateArgs: ast.TemplatedNames{
+									{Name: "C"},
+								},
+							},
+						},
+					},
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"conv F<T>(a: X, b: Y<T>)",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind: ast.Converter,
+					Name: "F",
+					TemplateParams: ast.TemplateParams{
+						{Name: "T"},
+					},
+					Parameters: ast.Parameters{
+						{Name: "a", Type: ast.TemplatedName{Name: "X"}},
+						{Name: "b", Type: ast.TemplatedName{
+							Name:         "Y",
+							TemplateArgs: []ast.TemplatedName{{Name: "T"}},
+						}},
+					},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"conv F() -> X",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind:       ast.Converter,
+					Name:       "F",
+					ReturnType: &ast.TemplatedName{Name: "X"},
+					Parameters: ast.Parameters{},
+				}},
+			},
+		}, { ///////////////////////////////////////////////////////////////////
+			utils.ThisLine(),
+			"conv F() -> X<T>",
+			ast.AST{
+				Converters: []ast.IntrinsicDecl{{
+					Kind: ast.Converter,
+					Name: "F",
+					ReturnType: &ast.TemplatedName{
+						Name:         "X",
+						TemplateArgs: []ast.TemplatedName{{Name: "T"}},
+					},
+					Parameters: ast.Parameters{},
+				}},
 			}},
 	} {
 		got, err := parser.Parse(test.src, "file.txt")
diff --git a/tools/src/cmd/intrinsic-gen/resolver/resolve.go b/tools/src/cmd/intrinsic-gen/resolver/resolve.go
index 52a8140..68b9dee 100644
--- a/tools/src/cmd/intrinsic-gen/resolver/resolve.go
+++ b/tools/src/cmd/intrinsic-gen/resolver/resolve.go
@@ -27,23 +27,25 @@
 	a *ast.AST
 	s *sem.Sem
 
-	globals           scope
-	builtins          map[string]*sem.Intrinsic
-	unaryOperators    map[string]*sem.Intrinsic
-	binaryOperators   map[string]*sem.Intrinsic
-	enumEntryMatchers map[*sem.EnumEntry]*sem.EnumMatcher
+	globals                   scope
+	builtins                  map[string]*sem.Intrinsic
+	unaryOperators            map[string]*sem.Intrinsic
+	binaryOperators           map[string]*sem.Intrinsic
+	constructorsAndConverters map[string]*sem.Intrinsic
+	enumEntryMatchers         map[*sem.EnumEntry]*sem.EnumMatcher
 }
 
 // Resolve processes the AST
 func Resolve(a *ast.AST) (*sem.Sem, error) {
 	r := resolver{
-		a:                 a,
-		s:                 sem.New(),
-		globals:           newScope(nil),
-		builtins:          map[string]*sem.Intrinsic{},
-		unaryOperators:    map[string]*sem.Intrinsic{},
-		binaryOperators:   map[string]*sem.Intrinsic{},
-		enumEntryMatchers: map[*sem.EnumEntry]*sem.EnumMatcher{},
+		a:                         a,
+		s:                         sem.New(),
+		globals:                   newScope(nil),
+		builtins:                  map[string]*sem.Intrinsic{},
+		unaryOperators:            map[string]*sem.Intrinsic{},
+		binaryOperators:           map[string]*sem.Intrinsic{},
+		constructorsAndConverters: map[string]*sem.Intrinsic{},
+		enumEntryMatchers:         map[*sem.EnumEntry]*sem.EnumMatcher{},
 	}
 	// Declare and resolve all the enumerators
 	for _, e := range a.Enums {
@@ -85,6 +87,21 @@
 		}
 	}
 
+	// Declare and resolve type constructors and converters
+	for _, c := range a.Constructors {
+		if err := r.intrinsic(c, r.constructorsAndConverters, &r.s.ConstructorsAndConverters); err != nil {
+			return nil, err
+		}
+	}
+	for _, c := range a.Converters {
+		if len(c.Parameters) != 1 {
+			return nil, fmt.Errorf("%v conversions must have a single parameter", c.Source)
+		}
+		if err := r.intrinsic(c, r.constructorsAndConverters, &r.s.ConstructorsAndConverters); err != nil {
+			return nil, err
+		}
+	}
+
 	// Calculate the unique parameter names
 	r.s.UniqueParameterNames = r.calculateUniqueParameterNames()
 
@@ -440,6 +457,8 @@
 			return &sem.TemplateEnumParam{Name: a.Name, Enum: r.Enum, Matcher: r}, nil
 		case *sem.TypeMatcher:
 			return &sem.TemplateTypeParam{Name: a.Name, Type: r}, nil
+		case *sem.Type:
+			return &sem.TemplateTypeParam{Name: a.Name, Type: r}, nil
 		default:
 			return nil, fmt.Errorf("%v invalid template parameter type '%v'", a.Source, a.Type.Name)
 		}
@@ -525,6 +544,7 @@
 		r.s.Builtins,
 		r.s.UnaryOperators,
 		r.s.BinaryOperators,
+		r.s.ConstructorsAndConverters,
 	} {
 		for _, i := range intrinsics {
 			for _, o := range i.Overloads {
diff --git a/tools/src/cmd/intrinsic-gen/resolver/resolver_test.go b/tools/src/cmd/intrinsic-gen/resolver/resolver_test.go
index 9f3aac0..7ce9236 100644
--- a/tools/src/cmd/intrinsic-gen/resolver/resolver_test.go
+++ b/tools/src/cmd/intrinsic-gen/resolver/resolver_test.go
@@ -142,6 +142,40 @@
 fn f(T< T<f32> >)`,
 			success,
 		}, {
+			`
+type f32
+op -(f32)`,
+			success,
+		}, {
+			`
+type f32
+type T<x>
+op +(T<f32>, T<f32>)`,
+			success,
+		}, {
+			`
+type f32
+ctor f32(f32)`,
+			success,
+		}, {
+			`
+type f32
+type T<x>
+ctor f32(T<f32>)`,
+			success,
+		}, {
+			`
+type f32
+type i32
+conv f32(i32)`,
+			success,
+		}, {
+			`
+type f32
+type T<x>
+conv f32(T<f32>)`,
+			success,
+		}, {
 			`enum E {A A}`,
 			`
 file.txt:1:6 'A' already declared
@@ -363,6 +397,125 @@
 			`file.txt:4:16 cannot use template enum 'E' as template number`,
 		}, {
 			`
+type i
+enum e { a }
+ctor F(i) -> e`,
+			`file.txt:3:14 cannot use 'e' as return type. Must be a type or template type`,
+		}, {
+			`
+type T<x>
+ctor F(T<u>)`,
+			`file.txt:2:10 cannot resolve 'u'`,
+		}, {
+			`
+type x
+ctor F<T>(T<x>)`,
+			`file.txt:2:11 'T' template parameters do not accept template arguments`,
+		}, {
+			`
+type A<N: num>
+type B
+ctor F(A<B>)`,
+			`file.txt:3:10 cannot use type 'B' as template number`,
+		}, {
+			`
+type A<N>
+enum E { b }
+ctor F(A<b>)`,
+			`file.txt:3:10 cannot use enum entry 'E.b' as template type`,
+		}, {
+			`
+type T
+type P<N: num>
+match m: T
+ctor F(P<m>)`,
+			`file.txt:4:10 cannot use type matcher 'm' as template number`,
+		}, {
+			`
+type P<N: num>
+enum E { b }
+ctor F(P<E>)`,
+			`file.txt:3:10 cannot use enum 'E' as template number`,
+		}, {
+			`
+type P<N: num>
+enum E { a b }
+match m: a | b
+ctor F(P<m>)`,
+			`file.txt:4:10 cannot use enum matcher 'm' as template number`,
+		}, {
+			`
+type P<N: num>
+enum E { a b }
+match m: a | b
+ctor F<M: m>(P<M>)`,
+			`file.txt:4:16 cannot use template enum 'E' as template number`,
+		}, {
+			`
+conv F()`,
+			`file.txt:1:6 conversions must have a single parameter`,
+		}, {
+			`
+type i
+conv F(i, i, i)`,
+			`file.txt:2:6 conversions must have a single parameter`,
+		}, {
+			`
+type i
+enum e { a }
+conv F(i) -> e`,
+			`file.txt:3:14 cannot use 'e' as return type. Must be a type or template type`,
+		}, {
+			`
+type T<x>
+conv F(T<u>)`,
+			`file.txt:2:10 cannot resolve 'u'`,
+		}, {
+			`
+type x
+conv F<T>(T<x>)`,
+			`file.txt:2:11 'T' template parameters do not accept template arguments`,
+		}, {
+			`
+type A<N: num>
+type B
+conv F(A<B>)`,
+			`file.txt:3:10 cannot use type 'B' as template number`,
+		}, {
+			`
+type A<N>
+enum E { b }
+conv F(A<b>)`,
+			`file.txt:3:10 cannot use enum entry 'E.b' as template type`,
+		}, {
+			`
+type T
+type P<N: num>
+match m: T
+conv F(P<m>)`,
+			`file.txt:4:10 cannot use type matcher 'm' as template number`,
+		}, {
+			`
+type P<N: num>
+enum E { b }
+conv F(P<E>)`,
+			`file.txt:3:10 cannot use enum 'E' as template number`,
+		}, {
+			`
+type P<N: num>
+enum E { a b }
+match m: a | b
+conv F(P<m>)`,
+			`file.txt:4:10 cannot use enum matcher 'm' as template number`,
+		}, {
+			`
+type P<N: num>
+enum E { a b }
+match m: a | b
+conv F<M: m>(P<M>)`,
+			`file.txt:4:16 cannot use template enum 'E' as template number`,
+		}, {
+			`
 enum E { a }
 type T<X: a>`,
 			`file.txt:2:8 invalid template parameter type 'a'`,
diff --git a/tools/src/cmd/intrinsic-gen/sem/sem.go b/tools/src/cmd/intrinsic-gen/sem/sem.go
index 33abb42..f61f218 100644
--- a/tools/src/cmd/intrinsic-gen/sem/sem.go
+++ b/tools/src/cmd/intrinsic-gen/sem/sem.go
@@ -22,13 +22,14 @@
 
 // Sem is the root of the semantic tree
 type Sem struct {
-	Enums           []*Enum
-	Types           []*Type
-	TypeMatchers    []*TypeMatcher
-	EnumMatchers    []*EnumMatcher
-	Builtins        []*Intrinsic
-	UnaryOperators  []*Intrinsic
-	BinaryOperators []*Intrinsic
+	Enums                     []*Enum
+	Types                     []*Type
+	TypeMatchers              []*TypeMatcher
+	EnumMatchers              []*EnumMatcher
+	Builtins                  []*Intrinsic
+	UnaryOperators            []*Intrinsic
+	BinaryOperators           []*Intrinsic
+	ConstructorsAndConverters []*Intrinsic
 	// Maximum number of open-types used across all builtins
 	MaxOpenTypes int
 	// Maximum number of open-numbers used across all builtins
diff --git a/tools/src/cmd/intrinsic-gen/tok/tok.go b/tools/src/cmd/intrinsic-gen/tok/tok.go
index f152527..c5def48 100644
--- a/tools/src/cmd/intrinsic-gen/tok/tok.go
+++ b/tools/src/cmd/intrinsic-gen/tok/tok.go
@@ -30,6 +30,8 @@
 	Match        Kind = "match"
 	Function     Kind = "fn"
 	Operator     Kind = "op"
+	Constructor  Kind = "ctor"
+	Converter    Kind = "conv"
 	Type         Kind = "type"
 	Enum         Kind = "enum"
 	And          Kind = "&"