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
Matchers []MatcherDecl
Builtins []IntrinsicDecl
Constructors []IntrinsicDecl
Converters []IntrinsicDecl
Operators []IntrinsicDecl
}
@ -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

@ -45,6 +45,7 @@ type IntrinsicTable struct {
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

@ -31,6 +31,7 @@ type resolver struct {
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
}
@ -43,6 +44,7 @@ func Resolve(a *ast.AST) (*sem.Sem, error) {
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
@ -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

@ -29,6 +29,7 @@ type Sem struct {
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 = "&"