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

@ -25,11 +25,13 @@ import (
// 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 @@ func (a AST) String() string {
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 @@ func (m MatcherDecl) Format(w fmt.State, verb rune) {
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 @@ const (
// 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 @@ func (i IntrinsicDecl) Format(w fmt.State, verb rune) {
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)

View File

@ -37,14 +37,15 @@ type IntrinsicTable struct {
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 @@ func buildIntrinsicTable(s *sem.Sem) (*IntrinsicTable, error) {
{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 {

View File

@ -97,6 +97,8 @@ func (l *lexer) lex() error {
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)

View File

@ -58,6 +58,12 @@ func TestLexTokens(t *testing.T) {
{"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),
}}},

View File

@ -71,6 +71,12 @@ func (p *parser) parse() (*ast.AST, error) {
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 @@ func (p *parser) operatorDecl(decos ast.Decorations) ast.IntrinsicDecl {
}
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")

View File

@ -370,6 +370,254 @@ func TestParser(t *testing.T) {
},
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")

View File

@ -27,23 +27,25 @@ type resolver struct {
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 @@ 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
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
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 @@ func (r *resolver) calculateUniqueParameterNames() []string {
r.s.Builtins,
r.s.UnaryOperators,
r.s.BinaryOperators,
r.s.ConstructorsAndConverters,
} {
for _, i := range intrinsics {
for _, o := range i.Overloads {

View File

@ -141,6 +141,40 @@ type f32
type T<x>
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}`,
`
@ -363,6 +397,125 @@ op << <M: m>(P<M>)`,
`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'`,

View File

@ -22,13 +22,14 @@ import (
// 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

View File

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