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>
This commit is contained in:
Ben Clayton 2022-05-16 09:48:41 +00:00 committed by Dawn LUCI CQ
parent 2b3dcf45b7
commit 5a69597698
10 changed files with 534 additions and 33 deletions

View File

@ -29,6 +29,8 @@ type AST struct {
Types []TypeDecl Types []TypeDecl
Matchers []MatcherDecl Matchers []MatcherDecl
Builtins []IntrinsicDecl Builtins []IntrinsicDecl
Constructors []IntrinsicDecl
Converters []IntrinsicDecl
Operators []IntrinsicDecl Operators []IntrinsicDecl
} }
@ -50,6 +52,14 @@ func (a AST) String() string {
fmt.Fprintf(&sb, "%v", b) fmt.Fprintf(&sb, "%v", b)
fmt.Fprintln(&sb) 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 { for _, o := range a.Operators {
fmt.Fprintf(&sb, "%v", o) fmt.Fprintf(&sb, "%v", o)
fmt.Fprintln(&sb) fmt.Fprintln(&sb)
@ -103,7 +113,7 @@ func (m MatcherDecl) Format(w fmt.State, verb rune) {
m.Options.Format(w, verb) m.Options.Format(w, verb)
} }
// IntrinsicKind is either a Builtin or Operator // IntrinsicKind is either a Builtin, Operator, Constructor or Converter
type IntrinsicKind string type IntrinsicKind string
const ( const (
@ -113,6 +123,12 @@ const (
// Operator is a unary or binary operator. // Operator is a unary or binary operator.
// Declared with 'op'. // Declared with 'op'.
Operator IntrinsicKind = "operator" 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 // IntrinsicDecl describes a builtin or operator declaration
@ -133,6 +149,10 @@ func (i IntrinsicDecl) Format(w fmt.State, verb rune) {
fmt.Fprintf(w, "fn ") fmt.Fprintf(w, "fn ")
case Operator: case Operator:
fmt.Fprintf(w, "op ") fmt.Fprintf(w, "op ")
case Constructor:
fmt.Fprintf(w, "ctor ")
case Converter:
fmt.Fprintf(w, "conv ")
} }
fmt.Fprintf(w, "%v", i.Name) fmt.Fprintf(w, "%v", i.Name)
i.TemplateParams.Format(w, verb) i.TemplateParams.Format(w, verb)

View File

@ -45,6 +45,7 @@ type IntrinsicTable struct {
Builtins []Intrinsic // kBuiltins table content Builtins []Intrinsic // kBuiltins table content
UnaryOperators []Intrinsic // kUnaryOperators table content UnaryOperators []Intrinsic // kUnaryOperators table content
BinaryOperators []Intrinsic // kBinaryOperators table content BinaryOperators []Intrinsic // kBinaryOperators table content
ConstructorsAndConverters []Intrinsic // kConstructorsAndConverters table content
} }
// OpenType is used to create the C++ OpenTypeInfo structure // OpenType is used to create the C++ OpenTypeInfo structure
@ -372,6 +373,7 @@ func buildIntrinsicTable(s *sem.Sem) (*IntrinsicTable, error) {
{s.Builtins, &b.Builtins}, {s.Builtins, &b.Builtins},
{s.UnaryOperators, &b.UnaryOperators}, {s.UnaryOperators, &b.UnaryOperators},
{s.BinaryOperators, &b.BinaryOperators}, {s.BinaryOperators, &b.BinaryOperators},
{s.ConstructorsAndConverters, &b.ConstructorsAndConverters},
} { } {
out := make([]Intrinsic, len(intrinsics.in)) out := make([]Intrinsic, len(intrinsics.in))
for i, f := range intrinsics.in { for i, f := range intrinsics.in {

View File

@ -97,6 +97,8 @@ func (l *lexer) lex() error {
case l.match("op", tok.Operator): case l.match("op", tok.Operator):
case l.match("enum", tok.Enum): case l.match("enum", tok.Enum):
case l.match("type", tok.Type): 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 l.match("match", tok.Match):
case unicode.IsLetter(l.peek(0)) || l.peek(0) == '_': case unicode.IsLetter(l.peek(0)) || l.peek(0) == '_':
l.tok(l.count(alphaNumericOrUnderscore), tok.Identifier) l.tok(l.count(alphaNumericOrUnderscore), tok.Identifier)

View File

@ -58,6 +58,12 @@ func TestLexTokens(t *testing.T) {
{"type", tok.Token{Kind: tok.Type, Runes: []rune("type"), Source: tok.Source{ {"type", tok.Token{Kind: tok.Type, Runes: []rune("type"), Source: tok.Source{
S: loc(1, 1, 0), E: loc(1, 5, 4), 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{ {"enum", tok.Token{Kind: tok.Enum, Runes: []rune("enum"), Source: tok.Source{
S: loc(1, 1, 0), E: loc(1, 5, 4), S: loc(1, 1, 0), E: loc(1, 5, 4),
}}}, }}},

View File

@ -71,6 +71,12 @@ func (p *parser) parse() (*ast.AST, error) {
case tok.Operator: case tok.Operator:
out.Operators = append(out.Operators, p.operatorDecl(decorations)) out.Operators = append(out.Operators, p.operatorDecl(decorations))
decorations = nil 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: default:
p.err = fmt.Errorf("%v unexpected token '%v'", t.Source, t.Kind) p.err = fmt.Errorf("%v unexpected token '%v'", t.Source, t.Kind)
} }
@ -195,6 +201,47 @@ func (p *parser) operatorDecl(decos ast.Decorations) ast.IntrinsicDecl {
} }
return f 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 { func (p *parser) parameters() ast.Parameters {
l := ast.Parameters{} l := ast.Parameters{}
p.expect(tok.Lparen, "function parameter list") p.expect(tok.Lparen, "function parameter list")

View File

@ -370,6 +370,254 @@ func TestParser(t *testing.T) {
}, },
Parameters: ast.Parameters{}, 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") got, err := parser.Parse(test.src, "file.txt")

View File

@ -31,6 +31,7 @@ type resolver struct {
builtins map[string]*sem.Intrinsic builtins map[string]*sem.Intrinsic
unaryOperators map[string]*sem.Intrinsic unaryOperators map[string]*sem.Intrinsic
binaryOperators map[string]*sem.Intrinsic binaryOperators map[string]*sem.Intrinsic
constructorsAndConverters map[string]*sem.Intrinsic
enumEntryMatchers map[*sem.EnumEntry]*sem.EnumMatcher enumEntryMatchers map[*sem.EnumEntry]*sem.EnumMatcher
} }
@ -43,6 +44,7 @@ func Resolve(a *ast.AST) (*sem.Sem, error) {
builtins: map[string]*sem.Intrinsic{}, builtins: map[string]*sem.Intrinsic{},
unaryOperators: map[string]*sem.Intrinsic{}, unaryOperators: map[string]*sem.Intrinsic{},
binaryOperators: map[string]*sem.Intrinsic{}, binaryOperators: map[string]*sem.Intrinsic{},
constructorsAndConverters: map[string]*sem.Intrinsic{},
enumEntryMatchers: map[*sem.EnumEntry]*sem.EnumMatcher{}, enumEntryMatchers: map[*sem.EnumEntry]*sem.EnumMatcher{},
} }
// Declare and resolve all the enumerators // Declare and resolve all the enumerators
@ -85,6 +87,21 @@ func Resolve(a *ast.AST) (*sem.Sem, error) {
} }
} }
// 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 // Calculate the unique parameter names
r.s.UniqueParameterNames = r.calculateUniqueParameterNames() r.s.UniqueParameterNames = r.calculateUniqueParameterNames()
@ -440,6 +457,8 @@ func (r *resolver) templateParam(a ast.TemplateParam) (sem.TemplateParam, error)
return &sem.TemplateEnumParam{Name: a.Name, Enum: r.Enum, Matcher: r}, nil return &sem.TemplateEnumParam{Name: a.Name, Enum: r.Enum, Matcher: r}, nil
case *sem.TypeMatcher: case *sem.TypeMatcher:
return &sem.TemplateTypeParam{Name: a.Name, Type: r}, nil return &sem.TemplateTypeParam{Name: a.Name, Type: r}, nil
case *sem.Type:
return &sem.TemplateTypeParam{Name: a.Name, Type: r}, nil
default: default:
return nil, fmt.Errorf("%v invalid template parameter type '%v'", a.Source, a.Type.Name) return nil, fmt.Errorf("%v invalid template parameter type '%v'", a.Source, a.Type.Name)
} }
@ -525,6 +544,7 @@ func (r *resolver) calculateUniqueParameterNames() []string {
r.s.Builtins, r.s.Builtins,
r.s.UnaryOperators, r.s.UnaryOperators,
r.s.BinaryOperators, r.s.BinaryOperators,
r.s.ConstructorsAndConverters,
} { } {
for _, i := range intrinsics { for _, i := range intrinsics {
for _, o := range i.Overloads { for _, o := range i.Overloads {

View File

@ -141,6 +141,40 @@ type f32
type T<x> type T<x>
fn f(T< T<f32> >)`, fn f(T< T<f32> >)`,
success, 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}`, `enum E {A A}`,
` `
@ -363,6 +397,125 @@ op << <M: m>(P<M>)`,
`file.txt:4:16 cannot use template enum 'E' as template number`, `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 } enum E { a }
type T<X: a>`, type T<X: a>`,
`file.txt:2:8 invalid template parameter type 'a'`, `file.txt:2:8 invalid template parameter type 'a'`,

View File

@ -29,6 +29,7 @@ type Sem struct {
Builtins []*Intrinsic Builtins []*Intrinsic
UnaryOperators []*Intrinsic UnaryOperators []*Intrinsic
BinaryOperators []*Intrinsic BinaryOperators []*Intrinsic
ConstructorsAndConverters []*Intrinsic
// Maximum number of open-types used across all builtins // Maximum number of open-types used across all builtins
MaxOpenTypes int MaxOpenTypes int
// Maximum number of open-numbers used across all builtins // Maximum number of open-numbers used across all builtins

View File

@ -30,6 +30,8 @@ const (
Match Kind = "match" Match Kind = "match"
Function Kind = "fn" Function Kind = "fn"
Operator Kind = "op" Operator Kind = "op"
Constructor Kind = "ctor"
Converter Kind = "conv"
Type Kind = "type" Type Kind = "type"
Enum Kind = "enum" Enum Kind = "enum"
And Kind = "&" And Kind = "&"