tint/intrinsics.def: Change language for enums

Previously enum members were added to the global namespace, so that
overload template parameters could be constrained to a single
enum-entry without the need to declare a matcher. While this was a minor
convenience feature, it means that you cannot declare an enum with
members that share the same name as a type. This will be very common for
extensions, like 'f16' where 'f16' is the name of an extension and a
type name.

Change scoping so that enum members need to be fully qualified. Also
change the intrinsic syntax so that enums always need to use a matcher
for enums.

Change-Id: Ided91130e9df537d38dc8ecb41325c0992dea14b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/97146
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2022-07-26 17:49:54 +00:00 committed by Dawn LUCI CQ
parent 1260a53018
commit c768e640f4
9 changed files with 1677 additions and 1607 deletions

View File

@ -153,17 +153,38 @@ match iu32: i32 | u32
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// https://gpuweb.github.io/gpuweb/wgsl/#texel-formats // https://gpuweb.github.io/gpuweb/wgsl/#texel-formats
match f32_texel_format: match f32_texel_format
rgba8unorm | rgba8snorm | rgba16float | r32float | rg32float | rgba32float : texel_format.rgba8unorm
match i32_texel_format: | texel_format.rgba8snorm
rgba8sint | rgba16sint | r32sint | rg32sint | rgba32sint | texel_format.rgba16float
match u32_texel_format: | texel_format.r32float
rgba8uint | rgba16uint | r32uint | rg32uint | rgba32uint | texel_format.rg32float
| texel_format.rgba32float
match i32_texel_format
: texel_format.rgba8sint
| texel_format.rgba16sint
| texel_format.r32sint
| texel_format.rg32sint
| texel_format.rgba32sint
match u32_texel_format
: texel_format.rgba8uint
| texel_format.rgba16uint
| texel_format.r32uint
| texel_format.rg32uint
| texel_format.rgba32uint
match write_only: write match write: access.write
match read_write: access.read_write
match function_private_workgroup: function | private | workgroup match function_private_workgroup
match workgroup_or_storage: workgroup | storage : storage_class.function
| storage_class.private
| storage_class.workgroup
match workgroup_or_storage
: storage_class.workgroup
| storage_class.storage
match storage
: storage_class.storage
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Builtin Functions // // Builtin Functions //
@ -502,10 +523,10 @@ fn textureDimensions(texture: texture_depth_cube, level: i32) -> vec2<i32>
fn textureDimensions(texture: texture_depth_cube_array) -> vec2<i32> fn textureDimensions(texture: texture_depth_cube_array) -> vec2<i32>
fn textureDimensions(texture: texture_depth_cube_array, level: i32) -> vec2<i32> fn textureDimensions(texture: texture_depth_cube_array, level: i32) -> vec2<i32>
fn textureDimensions(texture: texture_depth_multisampled_2d) -> vec2<i32> fn textureDimensions(texture: texture_depth_multisampled_2d) -> vec2<i32>
fn textureDimensions<F: texel_format, A: write_only>(texture: texture_storage_1d<F, A>) -> i32 fn textureDimensions<F: texel_format, A: write>(texture: texture_storage_1d<F, A>) -> i32
fn textureDimensions<F: texel_format, A: write_only>(texture: texture_storage_2d<F, A>) -> vec2<i32> fn textureDimensions<F: texel_format, A: write>(texture: texture_storage_2d<F, A>) -> vec2<i32>
fn textureDimensions<F: texel_format, A: write_only>(texture: texture_storage_2d_array<F, A>) -> vec2<i32> fn textureDimensions<F: texel_format, A: write>(texture: texture_storage_2d_array<F, A>) -> vec2<i32>
fn textureDimensions<F: texel_format, A: write_only>(texture: texture_storage_3d<F, A>) -> vec3<i32> fn textureDimensions<F: texel_format, A: write>(texture: texture_storage_3d<F, A>) -> vec3<i32>
fn textureDimensions(texture: texture_external) -> vec2<i32> fn textureDimensions(texture: texture_external) -> vec2<i32>
fn textureGather<T: fiu32>(@const component: i32, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>) -> vec4<T> fn textureGather<T: fiu32>(@const component: i32, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>) -> vec4<T>
fn textureGather<T: fiu32>(@const component: i32, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>, @const offset: vec2<i32>) -> vec4<T> fn textureGather<T: fiu32>(@const component: i32, texture: texture_2d<T>, sampler: sampler, coords: vec2<f32>, @const offset: vec2<i32>) -> vec4<T>
@ -529,7 +550,7 @@ fn textureNumLayers<T: fiu32>(texture: texture_2d_array<T>) -> i32
fn textureNumLayers<T: fiu32>(texture: texture_cube_array<T>) -> i32 fn textureNumLayers<T: fiu32>(texture: texture_cube_array<T>) -> i32
fn textureNumLayers(texture: texture_depth_2d_array) -> i32 fn textureNumLayers(texture: texture_depth_2d_array) -> i32
fn textureNumLayers(texture: texture_depth_cube_array) -> i32 fn textureNumLayers(texture: texture_depth_cube_array) -> i32
fn textureNumLayers<F: texel_format, A: write_only>(texture: texture_storage_2d_array<F, A>) -> i32 fn textureNumLayers<F: texel_format, A: write>(texture: texture_storage_2d_array<F, A>) -> i32
fn textureNumLevels<T: fiu32>(texture: texture_1d<T>) -> i32 fn textureNumLevels<T: fiu32>(texture: texture_1d<T>) -> i32
fn textureNumLevels<T: fiu32>(texture: texture_2d<T>) -> i32 fn textureNumLevels<T: fiu32>(texture: texture_2d<T>) -> i32
fn textureNumLevels<T: fiu32>(texture: texture_2d_array<T>) -> i32 fn textureNumLevels<T: fiu32>(texture: texture_2d_array<T>) -> i32

File diff suppressed because it is too large Load Diff

View File

@ -195,12 +195,21 @@ func (p Parameter) Format(w fmt.State, verb rune) {
p.Type.Format(w, verb) p.Type.Format(w, verb)
} }
// MatcherOptions is a list of TemplatedName // MatcherOptions is a list of TemplatedNames or MemberNames
type MatcherOptions TemplatedNames type MatcherOptions struct {
Types TemplatedNames
Enums MemberNames
}
// Format implements the fmt.Formatter interface // Format implements the fmt.Formatter interface
func (o MatcherOptions) Format(w fmt.State, verb rune) { func (o MatcherOptions) Format(w fmt.State, verb rune) {
for i, mo := range o { for i, mo := range o.Types {
if i > 0 {
fmt.Fprintf(w, " | ")
}
mo.Format(w, verb)
}
for i, mo := range o.Enums {
if i > 0 { if i > 0 {
fmt.Fprintf(w, " | ") fmt.Fprintf(w, " | ")
} }
@ -242,6 +251,33 @@ func (t TemplatedName) Format(w fmt.State, verb rune) {
} }
} }
// MemberNames is a list of MemberName
// Example:
// a.b, c.d
type MemberNames []MemberName
// Format implements the fmt.Formatter interface
func (l MemberNames) Format(w fmt.State, verb rune) {
for i, n := range l {
if i > 0 {
fmt.Fprintf(w, ", ")
}
n.Format(w, verb)
}
}
// MemberName is two identifiers separated by a dot (Owner.Member)
type MemberName struct {
Source tok.Source
Owner string
Member string
}
// Format implements the fmt.Formatter interface
func (t MemberName) Format(w fmt.State, verb rune) {
fmt.Fprintf(w, "%v.%v", t.Owner, t.Member)
}
// TypeDecl describes a type declaration // TypeDecl describes a type declaration
type TypeDecl struct { type TypeDecl struct {
Source tok.Source Source tok.Source

View File

@ -91,6 +91,7 @@ func (l *lexer) lex() error {
l.skip(l.count(toFirst('\n'))) l.skip(l.count(toFirst('\n')))
l.next() // Consume newline l.next() // Consume newline
case l.match("/", tok.Divide): case l.match("/", tok.Divide):
case l.match(".", tok.Dot):
case l.match("->", tok.Arrow): case l.match("->", tok.Arrow):
case l.match("-", tok.Minus): case l.match("-", tok.Minus):
case l.match("fn", tok.Function): case l.match("fn", tok.Function):

View File

@ -109,10 +109,19 @@ func (p *parser) matcherDecl() ast.MatcherDecl {
name := p.expect(tok.Identifier, "matcher name") name := p.expect(tok.Identifier, "matcher name")
m := ast.MatcherDecl{Source: name.Source, Name: string(name.Runes)} m := ast.MatcherDecl{Source: name.Source, Name: string(name.Runes)}
p.expect(tok.Colon, "matcher declaration") p.expect(tok.Colon, "matcher declaration")
for p.err == nil { if p.peekIs(1, tok.Dot) { // enum list
m.Options = append(m.Options, p.templatedName()) for p.err == nil {
if p.match(tok.Or) == nil { m.Options.Enums = append(m.Options.Enums, p.memberName())
break if p.match(tok.Or) == nil {
break
}
}
} else { // type list
for p.err == nil {
m.Options.Types = append(m.Options.Types, p.templatedName())
if p.match(tok.Or) == nil {
break
}
} }
} }
return m return m
@ -277,6 +286,17 @@ func (p *parser) string() string {
return string(s.Runes) return string(s.Runes)
} }
func (p *parser) memberName() ast.MemberName {
owner := p.expect(tok.Identifier, "member name")
p.expect(tok.Dot, "member name")
member := p.expect(tok.Identifier, "member name")
return ast.MemberName{
Source: member.Source,
Owner: string(owner.Runes),
Member: string(member.Runes),
}
}
func (p *parser) templatedName() ast.TemplatedName { func (p *parser) templatedName() ast.TemplatedName {
name := p.expect(tok.Identifier, "type name") name := p.expect(tok.Identifier, "type name")
m := ast.TemplatedName{Source: name.Source, Name: string(name.Runes)} m := ast.TemplatedName{Source: name.Source, Name: string(name.Runes)}

View File

@ -129,7 +129,9 @@ func TestParser(t *testing.T) {
Matchers: []ast.MatcherDecl{{ Matchers: []ast.MatcherDecl{{
Name: "M", Name: "M",
Options: ast.MatcherOptions{ Options: ast.MatcherOptions{
ast.TemplatedName{Name: "A"}, Types: ast.TemplatedNames{
{Name: "A"},
},
}, },
}}, }},
}, },
@ -140,8 +142,37 @@ func TestParser(t *testing.T) {
Matchers: []ast.MatcherDecl{{ Matchers: []ast.MatcherDecl{{
Name: "M", Name: "M",
Options: ast.MatcherOptions{ Options: ast.MatcherOptions{
ast.TemplatedName{Name: "A"}, Types: ast.TemplatedNames{
ast.TemplatedName{Name: "B"}, {Name: "A"},
{Name: "B"},
},
},
}},
},
}, { ///////////////////////////////////////////////////////////////////
utils.ThisLine(),
"match M : A.B",
ast.AST{
Matchers: []ast.MatcherDecl{{
Name: "M",
Options: ast.MatcherOptions{
Enums: ast.MemberNames{
{Owner: "A", Member: "B"},
},
},
}},
},
}, { ///////////////////////////////////////////////////////////////////
utils.ThisLine(),
"match M : A.B | B.C",
ast.AST{
Matchers: []ast.MatcherDecl{{
Name: "M",
Options: ast.MatcherOptions{
Enums: ast.MemberNames{
{Owner: "A", Member: "B"},
{Owner: "B", Member: "C"},
},
}, },
}}, }},
}, },

View File

@ -19,6 +19,7 @@ import (
"sort" "sort"
"strconv" "strconv"
"dawn.googlesource.com/dawn/tools/src/container"
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/ast" "dawn.googlesource.com/dawn/tools/src/tint/intrinsic/ast"
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/sem" "dawn.googlesource.com/dawn/tools/src/tint/intrinsic/sem"
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/tok" "dawn.googlesource.com/dawn/tools/src/tint/intrinsic/tok"
@ -125,6 +126,7 @@ func (r *resolver) enum(e ast.EnumDecl) error {
} }
// Register each of the enum entries // Register each of the enum entries
names := container.NewSet[string]()
for _, ast := range e.Entries { for _, ast := range e.Entries {
entry := &sem.EnumEntry{ entry := &sem.EnumEntry{
Name: ast.Name, Name: ast.Name,
@ -139,10 +141,11 @@ func (r *resolver) enum(e ast.EnumDecl) error {
if len(ast.Attributes) != 0 { if len(ast.Attributes) != 0 {
return fmt.Errorf("%v unknown attribute", ast.Attributes[0].Source) return fmt.Errorf("%v unknown attribute", ast.Attributes[0].Source)
} }
if err := r.globals.declare(entry, e.Source); err != nil {
return err
}
s.Entries = append(s.Entries, entry) s.Entries = append(s.Entries, entry)
if names.Contains(ast.Name) {
return fmt.Errorf("%v duplicate enum entry '%v'", ast.Source, ast.Name)
}
names.Add(ast.Name)
} }
return nil return nil
@ -203,16 +206,13 @@ func (r *resolver) ty(a ast.TypeDecl) error {
// The resulting matcher is appended to either Sem.TypeMatchers or // The resulting matcher is appended to either Sem.TypeMatchers or
// Sem.EnumMatchers, and is registered with the global scope. // Sem.EnumMatchers, and is registered with the global scope.
func (r *resolver) matcher(a ast.MatcherDecl) error { func (r *resolver) matcher(a ast.MatcherDecl) error {
// Determine whether this is a type matcher or enum matcher by resolving the isTypeMatcher := len(a.Options.Types) != 0
// first option isEnumMatcher := len(a.Options.Enums) != 0
firstOption, err := r.lookupNamed(&r.globals, a.Options[0]) if isTypeMatcher && isEnumMatcher {
if err != nil { return fmt.Errorf("%v matchers cannot mix enums and types", a.Source)
return err
} }
// Resolve to a sem.TypeMatcher or a sem.EnumMatcher if isTypeMatcher {
switch firstOption := firstOption.(type) {
case *sem.Type:
options := map[sem.Named]tok.Source{} options := map[sem.Named]tok.Source{}
m := &sem.TypeMatcher{ m := &sem.TypeMatcher{
Decl: a, Decl: a,
@ -226,7 +226,7 @@ func (r *resolver) matcher(a ast.MatcherDecl) error {
} }
// Resolve each of the types in the options list // Resolve each of the types in the options list
for _, ast := range m.Decl.Options { for _, ast := range m.Decl.Options.Types {
ty, err := r.lookupType(&r.globals, ast) ty, err := r.lookupType(&r.globals, ast)
if err != nil { if err != nil {
return err return err
@ -239,9 +239,25 @@ func (r *resolver) matcher(a ast.MatcherDecl) error {
} }
return nil return nil
}
if isEnumMatcher {
owners := container.NewSet[string]()
for _, enum := range a.Options.Enums {
owners.Add(enum.Owner)
}
if len(owners) > 1 {
return fmt.Errorf("%v cannot mix enums (%v) in type matcher", a.Source, owners)
}
enumName := owners.One()
lookup := r.globals.lookup(enumName)
if lookup == nil {
return fmt.Errorf("%v cannot resolve enum '%v'", a.Source, enumName)
}
enum, _ := lookup.object.(*sem.Enum)
if enum == nil {
return fmt.Errorf("%v cannot resolve enum '%v'", a.Source, enumName)
}
case *sem.EnumEntry:
enum := firstOption.Enum
m := &sem.EnumMatcher{ m := &sem.EnumMatcher{
Decl: a, Decl: a,
Name: a.Name, Name: a.Name,
@ -255,17 +271,18 @@ func (r *resolver) matcher(a ast.MatcherDecl) error {
} }
// Resolve each of the enums in the options list // Resolve each of the enums in the options list
for _, ast := range m.Decl.Options { for _, ast := range m.Decl.Options.Enums {
entry := enum.FindEntry(ast.Name) entry := enum.FindEntry(ast.Member)
if entry == nil { if entry == nil {
return fmt.Errorf("%v enum '%v' does not contain '%v'", ast.Source, enum.Name, ast.Name) return fmt.Errorf("%v enum '%v' does not contain '%v'", ast.Source, enum.Name, ast.Member)
} }
m.Options = append(m.Options, entry) m.Options = append(m.Options, entry)
} }
return nil return nil
} }
return fmt.Errorf("'%v' cannot be used for matcher", a.Name)
return fmt.Errorf("%v matcher cannot be empty", a.Source)
} }
// intrinsic() resolves a intrinsic overload declaration. // intrinsic() resolves a intrinsic overload declaration.
@ -419,33 +436,6 @@ func (r *resolver) fullyQualifiedName(s *scope, arg ast.TemplatedName) (sem.Full
return sem.FullyQualifiedName{}, err return sem.FullyQualifiedName{}, err
} }
if entry, ok := target.(*sem.EnumEntry); ok {
// The target resolved to an enum entry.
// Automagically transform this into a synthetic matcher with a single
// option. i.e.
// This:
// enum E{ a b c }
// fn F(b)
// Becomes:
// enum E{ a b c }
// matcher b
// fn F(b)
// We don't really care right now that we have a symbol collision
// between E.b and b, as the generators return different names for
// these.
matcher, ok := r.enumEntryMatchers[entry]
if !ok {
matcher = &sem.EnumMatcher{
Name: entry.Name,
Enum: entry.Enum,
Options: []*sem.EnumEntry{entry},
}
r.enumEntryMatchers[entry] = matcher
r.s.EnumMatchers = append(r.s.EnumMatchers, matcher)
}
target = matcher
}
fqn := sem.FullyQualifiedName{ fqn := sem.FullyQualifiedName{
Target: target, Target: target,
TemplateArguments: make([]interface{}, len(arg.TemplateArgs)), TemplateArguments: make([]interface{}, len(arg.TemplateArgs)),

View File

@ -54,7 +54,7 @@ match y: x`,
}, { }, {
` `
enum e {a b c} enum e {a b c}
match y: c | a | b`, match y: e.c | e.a | e.b`,
success, success,
}, { }, {
`fn f()`, `fn f()`,
@ -94,26 +94,35 @@ fn f(P<m>)`,
}, { }, {
` `
enum e { a } enum e { a }
fn f(a)`, match m: e.a
fn f(m)`,
success, success,
}, { }, {
` `
enum e { a b } enum e { a b }
type T<E: e> type T<E: e>
match m: a match m: e.a
fn f<E: m>(T<E>)`, fn f<E: m>(T<E>)`,
success, success,
}, { }, {
` `
enum e { a b } enum e { a b }
type T<E: e> type T<E: e>
match m: a match m: e.a
fn f(T<m>)`, fn f(T<m>)`,
success, success,
}, { }, {
` `
enum e { a } enum e { a }
type T<E: e> type T<E: e>
match m : e.a
fn f(T<m>)`,
success,
}, {
`
enum e { a }
type T<E: e>
match a : e.a
fn f(T<a>)`, fn f(T<a>)`,
success, success,
}, { }, {
@ -132,7 +141,7 @@ fn f<E: e>()`,
}, { }, {
` `
enum e { a b } enum e { a b }
match m: a | b match m: e.a | e.b
fn f<E: m>()`, fn f<E: m>()`,
success, success,
}, { }, {
@ -178,8 +187,7 @@ conv f32(T<f32>)`,
}, { }, {
`enum E {A A}`, `enum E {A A}`,
` `
file.txt:1:6 'A' already declared file.txt:1:11 duplicate enum entry 'A'
First declared here: file.txt:1:6
`, `,
}, },
{ {
@ -230,14 +238,6 @@ file.txt:2:14 cannot resolve 'b'
}, { }, {
` `
type a type a
enum e { b }
match x: a | b`,
`
file.txt:3:14 'b' resolves to enum entry 'e.b' but type is expected
`,
}, {
`
type a
type b type b
match x: a | b | a`, match x: a | b | a`,
` `
@ -247,15 +247,15 @@ First declared here: file.txt:3:10
}, { }, {
` `
enum e { a c } enum e { a c }
match x: a | b | c`, match x: e.a | e.b | e.c`,
` `
file.txt:2:14 enum 'e' does not contain 'b' file.txt:2:18 enum 'e' does not contain 'b'
`, `,
}, { }, {
` `
enum e { a } enum e { a }
match x: a match x: e.a
match x: a`, match x: e.a`,
` `
file.txt:3:7 'x' already declared file.txt:3:7 'x' already declared
First declared here: file.txt:2:7 First declared here: file.txt:2:7
@ -266,7 +266,7 @@ type t
match x: t match x: t
match y: x`, match y: x`,
` `
'y' cannot be used for matcher file.txt:3:10 'x' resolves to type matcher 'x' but type is expected
`, `,
}, { }, {
`fn f(u)`, `fn f(u)`,
@ -300,12 +300,6 @@ fn f(A<B>)`,
`file.txt:3:8 cannot use type 'B' as template number`, `file.txt:3:8 cannot use type 'B' as template number`,
}, { }, {
` `
type A<N>
enum E { b }
fn f(A<b>)`,
`file.txt:3:8 cannot use enum entry 'E.b' as template type`,
}, {
`
type T type T
type P<N: num> type P<N: num>
match m: T match m: T
@ -321,14 +315,14 @@ fn f(P<E>)`,
` `
type P<N: num> type P<N: num>
enum E { a b } enum E { a b }
match m: a | b match m: E.a | E.b
fn f(P<m>)`, fn f(P<m>)`,
`file.txt:4:8 cannot use enum matcher 'm' as template number`, `file.txt:4:8 cannot use enum matcher 'm' as template number`,
}, { }, {
` `
type P<N: num> type P<N: num>
enum E { a b } enum E { a b }
match m: a | b match m: E.a | E.b
fn f<M: m>(P<M>)`, fn f<M: m>(P<M>)`,
`file.txt:4:14 cannot use template enum 'E' as template number`, `file.txt:4:14 cannot use template enum 'E' as template number`,
}, { }, {
@ -366,8 +360,9 @@ op << (A<B>)`,
` `
type A<N> type A<N>
enum E { b } enum E { b }
op << (A<b>)`, match M: E.b
`file.txt:3:10 cannot use enum entry 'E.b' as template type`, op << (A<M>)`,
`file.txt:4:10 cannot use enum matcher 'M' as template type`,
}, { }, {
` `
type T type T
@ -385,14 +380,14 @@ op << (P<E>)`,
` `
type P<N: num> type P<N: num>
enum E { a b } enum E { a b }
match m: a | b match m: E.a | E.b
op << (P<m>)`, op << (P<m>)`,
`file.txt:4:10 cannot use enum matcher 'm' as template number`, `file.txt:4:10 cannot use enum matcher 'm' as template number`,
}, { }, {
` `
type P<N: num> type P<N: num>
enum E { a b } enum E { a b }
match m: a | b match m: E.a | E.b
op << <M: m>(P<M>)`, 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`,
}, { }, {
@ -421,8 +416,9 @@ ctor F(A<B>)`,
` `
type A<N> type A<N>
enum E { b } enum E { b }
ctor F(A<b>)`, match M: E.b
`file.txt:3:10 cannot use enum entry 'E.b' as template type`, ctor F(A<M>)`,
`file.txt:4:10 cannot use enum matcher 'M' as template type`,
}, { }, {
` `
type T type T
@ -440,14 +436,14 @@ ctor F(P<E>)`,
` `
type P<N: num> type P<N: num>
enum E { a b } enum E { a b }
match m: a | b match m: E.a | E.b
ctor F(P<m>)`, ctor F(P<m>)`,
`file.txt:4:10 cannot use enum matcher 'm' as template number`, `file.txt:4:10 cannot use enum matcher 'm' as template number`,
}, { }, {
` `
type P<N: num> type P<N: num>
enum E { a b } enum E { a b }
match m: a | b match m: E.a | E.b
ctor F<M: m>(P<M>)`, ctor F<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`,
}, { }, {
@ -485,8 +481,9 @@ conv F(A<B>)`,
` `
type A<N> type A<N>
enum E { b } enum E { b }
conv F(A<b>)`, match M: E.b
`file.txt:3:10 cannot use enum entry 'E.b' as template type`, conv F(A<M>)`,
`file.txt:4:10 cannot use enum matcher 'M' as template type`,
}, { }, {
` `
type T type T
@ -504,32 +501,22 @@ conv F(P<E>)`,
` `
type P<N: num> type P<N: num>
enum E { a b } enum E { a b }
match m: a | b match m: E.a | E.b
conv F(P<m>)`, conv F(P<m>)`,
`file.txt:4:10 cannot use enum matcher 'm' as template number`, `file.txt:4:10 cannot use enum matcher 'm' as template number`,
}, { }, {
` `
type P<N: num> type P<N: num>
enum E { a b } enum E { a b }
match m: a | b match m: E.a | E.b
conv F<M: m>(P<M>)`, conv F<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`,
}, {
`
enum E { a }
type T<X: a>`,
`file.txt:2:8 invalid template parameter type 'a'`,
}, {
`
enum E { a }
fn f<M: a>()`,
`file.txt:2:6 invalid template parameter type 'a'`,
}, },
} { } {
ast, err := parser.Parse(strings.TrimSpace(string(test.src)), "file.txt") ast, err := parser.Parse(strings.TrimSpace(string(test.src)), "file.txt")
if err != nil { if err != nil {
t.Errorf("Unexpected parser error: %v", err) t.Errorf("While parsing:\n%s\nUnexpected parser error: %v", test.src, err)
continue continue
} }

View File

@ -43,6 +43,7 @@ const (
Comma Kind = "," Comma Kind = ","
Complement Kind = "~" Complement Kind = "~"
Divide Kind = "/" Divide Kind = "/"
Dot Kind = "."
Equal Kind = "==" Equal Kind = "=="
Ge Kind = ">=" Ge Kind = ">="
Gt Kind = ">" Gt Kind = ">"