tint/reader: Enable 'const' parsing for unit-tests
This enables the WGSL parsing of 'const' for only tint_unittests,
allowing code to be split up into smaller chunks for review.
Once all the logic is submitted to handle 'const', the test-only
flag will be removed, and 'const' will be enabled for production.
Bug: tint:1580
Change-Id: I3189b6bd15445ecc3fa1cd6f568885e7ba3c91c0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94680
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/ast/const.cc b/src/tint/ast/const.cc
index 6ae8e8c..e13cc25 100644
--- a/src/tint/ast/const.cc
+++ b/src/tint/ast/const.cc
@@ -34,6 +34,10 @@
Const::~Const() = default;
+const char* Const::Kind() const {
+ return "const";
+}
+
const Const* Const::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/const.h b/src/tint/ast/const.h
index 39ac6c3..32e3ddd 100644
--- a/src/tint/ast/const.h
+++ b/src/tint/ast/const.h
@@ -52,6 +52,9 @@
/// Destructor
~Const() override;
+ /// @returns "const"
+ const char* Kind() const override;
+
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @param ctx the clone context
diff --git a/src/tint/ast/let.cc b/src/tint/ast/let.cc
index e771c4d..8db8ec1 100644
--- a/src/tint/ast/let.cc
+++ b/src/tint/ast/let.cc
@@ -34,6 +34,10 @@
Let::~Let() = default;
+const char* Let::Kind() const {
+ return "let";
+}
+
const Let* Let::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/let.h b/src/tint/ast/let.h
index 2b0a6aa..2c1ad7c 100644
--- a/src/tint/ast/let.h
+++ b/src/tint/ast/let.h
@@ -49,6 +49,9 @@
/// Destructor
~Let() override;
+ /// @returns "let"
+ const char* Kind() const override;
+
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @param ctx the clone context
diff --git a/src/tint/ast/override.cc b/src/tint/ast/override.cc
index f494bc8..efd048d 100644
--- a/src/tint/ast/override.cc
+++ b/src/tint/ast/override.cc
@@ -32,6 +32,10 @@
Override::~Override() = default;
+const char* Override::Kind() const {
+ return "override";
+}
+
const Override* Override::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/override.h b/src/tint/ast/override.h
index 168b9b2..98319e5 100644
--- a/src/tint/ast/override.h
+++ b/src/tint/ast/override.h
@@ -50,6 +50,9 @@
/// Destructor
~Override() override;
+ /// @returns "override"
+ const char* Kind() const override;
+
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @param ctx the clone context
diff --git a/src/tint/ast/parameter.cc b/src/tint/ast/parameter.cc
index b7ea3b1..ea3c51f 100644
--- a/src/tint/ast/parameter.cc
+++ b/src/tint/ast/parameter.cc
@@ -31,6 +31,10 @@
Parameter::~Parameter() = default;
+const char* Parameter::Kind() const {
+ return "parameter";
+}
+
const Parameter* Parameter::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/parameter.h b/src/tint/ast/parameter.h
index d3ecf8d..eb4b688 100644
--- a/src/tint/ast/parameter.h
+++ b/src/tint/ast/parameter.h
@@ -51,6 +51,9 @@
/// Destructor
~Parameter() override;
+ /// @returns "parameter"
+ const char* Kind() const override;
+
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @param ctx the clone context
diff --git a/src/tint/ast/var.cc b/src/tint/ast/var.cc
index 622aa03..854503e 100644
--- a/src/tint/ast/var.cc
+++ b/src/tint/ast/var.cc
@@ -36,6 +36,10 @@
Var::~Var() = default;
+const char* Var::Kind() const {
+ return "var";
+}
+
const Var* Var::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
auto sym = ctx->Clone(symbol);
diff --git a/src/tint/ast/var.h b/src/tint/ast/var.h
index fd03580..565ebbb 100644
--- a/src/tint/ast/var.h
+++ b/src/tint/ast/var.h
@@ -65,6 +65,9 @@
/// Destructor
~Var() override;
+ /// @returns "var"
+ const char* Kind() const override;
+
/// Clones this node and all transitive child nodes using the `CloneContext`
/// `ctx`.
/// @param ctx the clone context
diff --git a/src/tint/ast/variable.h b/src/tint/ast/variable.h
index bc25753..631fbe5 100644
--- a/src/tint/ast/variable.h
+++ b/src/tint/ast/variable.h
@@ -77,6 +77,10 @@
/// @note binding points should only be applied to Var and Parameter types.
VariableBindingPoint BindingPoint() const;
+ /// @returns the kind of the variable, which can be used in diagnostics
+ /// e.g. "var", "let", "const", etc
+ virtual const char* Kind() const = 0;
+
/// The variable symbol
const Symbol symbol;
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index 712474f..bf32a52 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -144,18 +144,18 @@
// Test that line comments are ended by blankspace characters other than
// space, horizontal tab, left-to-right mark, and right-to-left mark.
auto* c = GetParam();
- std::string src = "let// This is a comment";
+ std::string src = "const// This is a comment";
src += c;
src += "ident";
Source::File file("", src);
Lexer l(&file);
auto t = l.next();
- EXPECT_TRUE(t.Is(Token::Type::kLet));
+ EXPECT_TRUE(t.Is(Token::Type::kConst));
EXPECT_EQ(t.source().range.begin.line, 1u);
EXPECT_EQ(t.source().range.begin.column, 1u);
EXPECT_EQ(t.source().range.end.line, 1u);
- EXPECT_EQ(t.source().range.end.column, 4u);
+ EXPECT_EQ(t.source().range.end.column, 6u);
auto is_same_line = [](std::string_view v) {
return v == kSpace || v == kHTab || v == kL2R || v == kR2L;
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 3f1fad4..f6ab166 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -47,6 +47,9 @@
namespace tint::reader::wgsl {
namespace {
+// TODO(crbug.com/tint/1580): Remove when 'const' is fully implemented.
+bool const_enabled = false;
+
template <typename T>
using Expect = ParserImpl::Expect<T>;
@@ -245,6 +248,11 @@
ParserImpl::~ParserImpl() = default;
+// TODO(crbug.com/tint/1580): Remove when 'const' is fully implemented.
+void ParserImpl::EnableConst() {
+ const_enabled = true;
+}
+
ParserImpl::Failure::Errored ParserImpl::add_error(const Source& source,
std::string_view err,
std::string_view use) {
@@ -437,8 +445,12 @@
}
if (gc.matched) {
- if (!expect("'let' declaration", Token::Type::kSemicolon)) {
- return Failure::kErrored;
+ // Avoid the cost of the string allocation for the common no-error case
+ if (!peek().Is(Token::Type::kSemicolon)) {
+ std::string kind = gc->Kind();
+ if (!expect("'" + kind + "' declaration", Token::Type::kSemicolon)) {
+ return Failure::kErrored;
+ }
}
builder_.AST().AddGlobalVariable(gc.value);
@@ -561,10 +573,14 @@
// global_const_initializer
// : EQUAL const_expr
Maybe<const ast::Variable*> ParserImpl::global_constant_decl(ast::AttributeList& attrs) {
+ bool is_const = false;
bool is_overridable = false;
const char* use = nullptr;
if (match(Token::Type::kLet)) {
use = "'let' declaration";
+ } else if (const_enabled && match(Token::Type::kConst)) {
+ use = "'const' declaration";
+ is_const = true;
} else if (match(Token::Type::kOverride)) {
use = "'override' declaration";
is_overridable = true;
@@ -596,6 +612,13 @@
initializer = std::move(init.value);
}
+ if (is_const) {
+ return create<ast::Const>(decl->source, // source
+ builder_.Symbols().Register(decl->name), // symbol
+ decl->type, // type
+ initializer, // constructor
+ std::move(attrs)); // attributes
+ }
if (is_overridable) {
return create<ast::Override>(decl->source, // source
builder_.Symbols().Register(decl->name), // symbol
@@ -1769,6 +1792,34 @@
// | variable_decl EQUAL logical_or_expression
// | CONST variable_ident_decl EQUAL logical_or_expression
Maybe<const ast::VariableDeclStatement*> ParserImpl::variable_stmt() {
+ if (const_enabled && match(Token::Type::kConst)) {
+ auto decl = expect_variable_ident_decl("'const' declaration",
+ /*allow_inferred = */ true);
+ if (decl.errored) {
+ return Failure::kErrored;
+ }
+
+ if (!expect("'const' declaration", Token::Type::kEqual)) {
+ return Failure::kErrored;
+ }
+
+ auto constructor = logical_or_expression();
+ if (constructor.errored) {
+ return Failure::kErrored;
+ }
+ if (!constructor.matched) {
+ return add_error(peek(), "missing constructor for 'const' declaration");
+ }
+
+ auto* const_ = create<ast::Const>(decl->source, // source
+ builder_.Symbols().Register(decl->name), // symbol
+ decl->type, // type
+ constructor.value, // constructor
+ ast::AttributeList{}); // attributes
+
+ return create<ast::VariableDeclStatement>(decl->source, const_);
+ }
+
if (match(Token::Type::kLet)) {
auto decl = expect_variable_ident_decl("'let' declaration",
/*allow_inferred = */ true);
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index c6e696b..89d5163 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -72,6 +72,11 @@
};
public:
+ /// A temporary bodge to enable unit-testing of 'const' variable types while still under active
+ /// development.
+ // TODO(crbug.com/tint/1580): Remove when 'const' is fully implemented.
+ static void EnableConst();
+
/// Expect is the return type of the parser methods that are expected to
/// return a parsed value of type T, unless there was an parse error.
/// In the case of a parse error the called method will have called
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index fc141c0..5e99d34 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -471,6 +471,107 @@
}
TEST_F(ParserImplErrorTest, GlobalDeclConstInvalidIdentifier) {
+ EXPECT("const ^ : i32 = 1;",
+ R"(test.wgsl:1:7 error: expected identifier for 'const' declaration
+const ^ : i32 = 1;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstMissingSemicolon) {
+ EXPECT("const i : i32 = 1",
+ R"(test.wgsl:1:18 error: expected ';' for 'const' declaration
+const i : i32 = 1
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstMissingLParen) {
+ EXPECT("const i : vec2<i32> = vec2<i32>;",
+ R"(test.wgsl:1:32 error: expected '(' for type constructor
+const i : vec2<i32> = vec2<i32>;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstMissingRParen) {
+ EXPECT("const i : vec2<i32> = vec2<i32>(1., 2.;",
+ R"(test.wgsl:1:39 error: expected ')' for type constructor
+const i : vec2<i32> = vec2<i32>(1., 2.;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteral) {
+ EXPECT("const i : vec2<i32> = vec2<i32>(!);",
+ R"(test.wgsl:1:33 error: unable to parse const_expr
+const i : vec2<i32> = vec2<i32>(!);
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteralSpaceLessThan) {
+ EXPECT("const i = 1 < 2;",
+ R"(test.wgsl:1:13 error: expected ';' for 'const' declaration
+const i = 1 < 2;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstNotConstExpr) {
+ EXPECT(
+ "const a = 1;\n"
+ "const b = a;",
+ R"(test.wgsl:2:11 error: unable to parse const_expr
+const b = a;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstExprMaxDepth) {
+ uint32_t kMaxDepth = 128;
+
+ std::stringstream src;
+ std::stringstream mkr;
+ src << "const i : i32 = ";
+ mkr << " ";
+ for (size_t i = 0; i < kMaxDepth + 8; i++) {
+ src << "f32(";
+ if (i < kMaxDepth) {
+ mkr << " ";
+ } else if (i == kMaxDepth) {
+ mkr << "^^^";
+ }
+ }
+ src << "1.0";
+ for (size_t i = 0; i < 200; i++) {
+ src << ")";
+ }
+ src << ";";
+ std::stringstream err;
+ err << "test.wgsl:1:529 error: maximum parser recursive depth reached\n"
+ << src.str() << "\n"
+ << mkr.str() << "\n";
+ EXPECT(src.str().c_str(), err.str().c_str());
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstExprMissingLParen) {
+ EXPECT("const i : vec2<i32> = vec2<i32> 1, 2);",
+ R"(test.wgsl:1:33 error: expected '(' for type constructor
+const i : vec2<i32> = vec2<i32> 1, 2);
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstExprMissingRParen) {
+ EXPECT("const i : vec2<i32> = vec2<i32>(1, 2;",
+ R"(test.wgsl:1:37 error: expected ')' for type constructor
+const i : vec2<i32> = vec2<i32>(1, 2;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclLetInvalidIdentifier) {
EXPECT("let ^ : i32 = 1;",
R"(test.wgsl:1:5 error: expected identifier for 'let' declaration
let ^ : i32 = 1;
@@ -478,7 +579,7 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstMissingSemicolon) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetMissingSemicolon) {
EXPECT("let i : i32 = 1",
R"(test.wgsl:1:16 error: expected ';' for 'let' declaration
let i : i32 = 1
@@ -486,7 +587,7 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstMissingLParen) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetMissingLParen) {
EXPECT("let i : vec2<i32> = vec2<i32>;",
R"(test.wgsl:1:30 error: expected '(' for type constructor
let i : vec2<i32> = vec2<i32>;
@@ -494,7 +595,7 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstMissingRParen) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetMissingRParen) {
EXPECT("let i : vec2<i32> = vec2<i32>(1., 2.;",
R"(test.wgsl:1:37 error: expected ')' for type constructor
let i : vec2<i32> = vec2<i32>(1., 2.;
@@ -502,7 +603,7 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteral) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetBadConstLiteral) {
EXPECT("let i : vec2<i32> = vec2<i32>(!);",
R"(test.wgsl:1:31 error: unable to parse const_expr
let i : vec2<i32> = vec2<i32>(!);
@@ -510,7 +611,7 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteralSpaceLessThan) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetBadConstLiteralSpaceLessThan) {
EXPECT("let i = 1 < 2;",
R"(test.wgsl:1:11 error: expected ';' for 'let' declaration
let i = 1 < 2;
@@ -518,7 +619,7 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstNotConstExpr) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetNotConstExpr) {
EXPECT(
"let a = 1;\n"
"let b = a;",
@@ -528,7 +629,7 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstExprMaxDepth) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetExprMaxDepth) {
uint32_t kMaxDepth = 128;
std::stringstream src;
@@ -555,7 +656,7 @@
EXPECT(src.str().c_str(), err.str().c_str());
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstExprMissingLParen) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetExprMissingLParen) {
EXPECT("let i : vec2<i32> = vec2<i32> 1, 2);",
R"(test.wgsl:1:31 error: expected '(' for type constructor
let i : vec2<i32> = vec2<i32> 1, 2);
@@ -563,7 +664,7 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclConstExprMissingRParen) {
+TEST_F(ParserImplErrorTest, GlobalDeclLetExprMissingRParen) {
EXPECT("let i : vec2<i32> = vec2<i32>(1, 2;",
R"(test.wgsl:1:35 error: expected ')' for type constructor
let i : vec2<i32> = vec2<i32>(1, 2;
diff --git a/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
index f5428fc..70caa3f 100644
--- a/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_constant_decl_test.cc
@@ -18,7 +18,7 @@
namespace tint::reader::wgsl {
namespace {
-TEST_F(ParserImplTest, GlobalConstantDecl) {
+TEST_F(ParserImplTest, GlobalLetDecl) {
auto p = parser("let a : f32 = 1.");
auto attrs = p->attribute_list();
EXPECT_FALSE(attrs.errored);
@@ -43,7 +43,7 @@
EXPECT_TRUE(let->constructor->Is<ast::LiteralExpression>());
}
-TEST_F(ParserImplTest, GlobalConstantDecl_Inferred) {
+TEST_F(ParserImplTest, GlobalLetDecl_Inferred) {
auto p = parser("let a = 1.");
auto attrs = p->attribute_list();
EXPECT_FALSE(attrs.errored);
@@ -67,7 +67,7 @@
EXPECT_TRUE(let->constructor->Is<ast::LiteralExpression>());
}
-TEST_F(ParserImplTest, GlobalConstantDecl_InvalidExpression) {
+TEST_F(ParserImplTest, GlobalLetDecl_InvalidExpression) {
auto p = parser("let a : f32 = if (a) {}");
auto attrs = p->attribute_list();
EXPECT_FALSE(attrs.errored);
@@ -80,7 +80,7 @@
EXPECT_EQ(p->error(), "1:15: invalid type for const_expr");
}
-TEST_F(ParserImplTest, GlobalConstantDecl_MissingExpression) {
+TEST_F(ParserImplTest, GlobalLetDecl_MissingExpression) {
auto p = parser("let a : f32 =");
auto attrs = p->attribute_list();
EXPECT_FALSE(attrs.errored);
@@ -93,7 +93,82 @@
EXPECT_EQ(p->error(), "1:14: unable to parse const_expr");
}
-TEST_F(ParserImplTest, GlobalConstantDec_Override_WithId) {
+TEST_F(ParserImplTest, GlobalConstDecl) {
+ auto p = parser("const a : f32 = 1.");
+ auto attrs = p->attribute_list();
+ EXPECT_FALSE(attrs.errored);
+ EXPECT_FALSE(attrs.matched);
+ auto e = p->global_constant_decl(attrs.value);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ auto* c = e.value->As<ast::Const>();
+ ASSERT_NE(c, nullptr);
+
+ EXPECT_EQ(c->symbol, p->builder().Symbols().Get("a"));
+ ASSERT_NE(c->type, nullptr);
+ EXPECT_TRUE(c->type->Is<ast::F32>());
+
+ EXPECT_EQ(c->source.range.begin.line, 1u);
+ EXPECT_EQ(c->source.range.begin.column, 7u);
+ EXPECT_EQ(c->source.range.end.line, 1u);
+ EXPECT_EQ(c->source.range.end.column, 8u);
+
+ ASSERT_NE(c->constructor, nullptr);
+ EXPECT_TRUE(c->constructor->Is<ast::LiteralExpression>());
+}
+
+TEST_F(ParserImplTest, GlobalConstDecl_Inferred) {
+ auto p = parser("const a = 1.");
+ auto attrs = p->attribute_list();
+ EXPECT_FALSE(attrs.errored);
+ EXPECT_FALSE(attrs.matched);
+ auto e = p->global_constant_decl(attrs.value);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ auto* c = e.value->As<ast::Const>();
+ ASSERT_NE(c, nullptr);
+
+ EXPECT_EQ(c->symbol, p->builder().Symbols().Get("a"));
+ EXPECT_EQ(c->type, nullptr);
+
+ EXPECT_EQ(c->source.range.begin.line, 1u);
+ EXPECT_EQ(c->source.range.begin.column, 7u);
+ EXPECT_EQ(c->source.range.end.line, 1u);
+ EXPECT_EQ(c->source.range.end.column, 8u);
+
+ ASSERT_NE(c->constructor, nullptr);
+ EXPECT_TRUE(c->constructor->Is<ast::LiteralExpression>());
+}
+
+TEST_F(ParserImplTest, GlobalConstDecl_InvalidExpression) {
+ auto p = parser("const a : f32 = if (a) {}");
+ auto attrs = p->attribute_list();
+ EXPECT_FALSE(attrs.errored);
+ EXPECT_FALSE(attrs.matched);
+ auto e = p->global_constant_decl(attrs.value);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_TRUE(e.errored);
+ EXPECT_FALSE(e.matched);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_EQ(p->error(), "1:17: invalid type for const_expr");
+}
+
+TEST_F(ParserImplTest, GlobalConstDecl_MissingExpression) {
+ auto p = parser("const a : f32 =");
+ auto attrs = p->attribute_list();
+ EXPECT_FALSE(attrs.errored);
+ EXPECT_FALSE(attrs.matched);
+ auto e = p->global_constant_decl(attrs.value);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_TRUE(e.errored);
+ EXPECT_FALSE(e.matched);
+ EXPECT_EQ(e.value, nullptr);
+ EXPECT_EQ(p->error(), "1:16: unable to parse const_expr");
+}
+
+TEST_F(ParserImplTest, GlobalOverrideDecl_WithId) {
auto p = parser("@id(7) override a : f32 = 1.");
auto attrs = p->attribute_list();
EXPECT_FALSE(attrs.errored);
@@ -123,7 +198,7 @@
EXPECT_EQ(override_attr->value, 7u);
}
-TEST_F(ParserImplTest, GlobalConstantDec_Override_WithoutId) {
+TEST_F(ParserImplTest, GlobalOverrideDecl_WithoutId) {
auto p = parser("override a : f32 = 1.");
auto attrs = p->attribute_list();
EXPECT_FALSE(attrs.errored);
@@ -152,7 +227,7 @@
ASSERT_EQ(id_attr, nullptr);
}
-TEST_F(ParserImplTest, GlobalConstantDec_Override_MissingId) {
+TEST_F(ParserImplTest, GlobalOverrideDecl_MissingId) {
auto p = parser("@id() override a : f32 = 1.");
auto attrs = p->attribute_list();
EXPECT_TRUE(attrs.errored);
@@ -168,7 +243,7 @@
EXPECT_EQ(p->error(), "1:5: expected signed integer literal for id attribute");
}
-TEST_F(ParserImplTest, GlobalConstantDec_Override_InvalidId) {
+TEST_F(ParserImplTest, GlobalOverrideDecl_InvalidId) {
auto p = parser("@id(-7) override a : f32 = 1.");
auto attrs = p->attribute_list();
EXPECT_TRUE(attrs.errored);
diff --git a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
index 59012a3..a09486d 100644
--- a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
@@ -49,7 +49,7 @@
EXPECT_EQ(p->error(), "1:27: expected ';' for variable declaration");
}
-TEST_F(ParserImplTest, GlobalDecl_GlobalConstant) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalLet) {
auto p = parser("let a : i32 = 2;");
p->global_decl();
ASSERT_FALSE(p->has_error()) << p->error();
@@ -61,27 +61,60 @@
EXPECT_EQ(v->symbol, program.Symbols().Get("a"));
}
-TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingInitializer) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalLet_MissingInitializer) {
auto p = parser("let a : vec2<i32>;");
p->global_decl();
ASSERT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:18: expected '=' for 'let' declaration");
}
-TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_Invalid) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalLet_Invalid) {
auto p = parser("let a : vec2<i32> 1.0;");
p->global_decl();
ASSERT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:19: expected '=' for 'let' declaration");
}
-TEST_F(ParserImplTest, GlobalDecl_GlobalConstant_MissingSemicolon) {
+TEST_F(ParserImplTest, GlobalDecl_GlobalLet_MissingSemicolon) {
auto p = parser("let a : vec2<i32> = vec2<i32>(1, 2)");
p->global_decl();
ASSERT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:36: expected ';' for 'let' declaration");
}
+TEST_F(ParserImplTest, GlobalDecl_GlobalConst) {
+ auto p = parser("const a : i32 = 2;");
+ p->global_decl();
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ auto program = p->program();
+ ASSERT_EQ(program.AST().GlobalVariables().size(), 1u);
+
+ auto* v = program.AST().GlobalVariables()[0];
+ EXPECT_EQ(v->symbol, program.Symbols().Get("a"));
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConst_MissingInitializer) {
+ auto p = parser("const a : vec2<i32>;");
+ p->global_decl();
+ ASSERT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:20: expected '=' for 'const' declaration");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConst_Invalid) {
+ auto p = parser("const a : vec2<i32> 1.0;");
+ p->global_decl();
+ ASSERT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:21: expected '=' for 'const' declaration");
+}
+
+TEST_F(ParserImplTest, GlobalDecl_GlobalConst_MissingSemicolon) {
+ auto p = parser("const a : vec2<i32> = vec2<i32>(1, 2)");
+ p->global_decl();
+ ASSERT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:38: expected ';' for 'const' declaration");
+}
+
TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
auto p = parser("type A = i32;");
p->global_decl();
diff --git a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
index 8f2d470..65fae3c 100644
--- a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
@@ -32,6 +32,13 @@
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:5: '" + name + "' is a reserved keyword");
}
+TEST_P(ParserImplReservedKeywordTest, ModuleConst) {
+ auto name = GetParam();
+ auto p = parser("const " + name + " : i32 = 1;");
+ EXPECT_FALSE(p->Parse());
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:7: '" + name + "' is a reserved keyword");
+}
TEST_P(ParserImplReservedKeywordTest, ModuleVar) {
auto name = GetParam();
auto p = parser("var " + name + " : i32 = 1;");
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index ec450ec..be4511b 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -487,14 +487,11 @@
/// declaration
std::string KindOf(const ast::Node* node) {
return Switch(
- node, //
- [&](const ast::Struct*) { return "struct"; }, //
- [&](const ast::Alias*) { return "alias"; }, //
- [&](const ast::Function*) { return "function"; }, //
- [&](const ast::Var*) { return "var"; }, //
- [&](const ast::Let*) { return "let"; }, //
- [&](const ast::Override*) { return "override"; }, //
- [&](const ast::Const*) { return "const"; }, //
+ node, //
+ [&](const ast::Struct*) { return "struct"; }, //
+ [&](const ast::Alias*) { return "alias"; }, //
+ [&](const ast::Function*) { return "function"; }, //
+ [&](const ast::Variable* v) { return v->Kind(); }, //
[&](Default) {
UnhandledNode(diagnostics_, node);
return "<error>";
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 5676401..6ffbd62 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -327,16 +327,10 @@
// Value type has to match storage type
if (storage_ty != value_type) {
- std::string decl = Switch(
- v, //
- [&](const ast::Var*) { return "var"; }, //
- [&](const ast::Let*) { return "let"; }, //
- [&](const ast::Const*) { return "const"; }, //
- [&](Default) { return "<unknown>"; });
-
- AddError("cannot initialize " + decl + " of type '" + sem_.TypeNameOf(storage_ty) +
- "' with value of type '" + sem_.TypeNameOf(rhs_ty) + "'",
- v->source);
+ std::stringstream s;
+ s << "cannot initialize " << v->Kind() << " of type '" << sem_.TypeNameOf(storage_ty)
+ << "' with value of type '" << sem_.TypeNameOf(rhs_ty) << "'";
+ AddError(s.str(), v->source);
return false;
}
diff --git a/src/tint/test_main.cc b/src/tint/test_main.cc
index ca68271..0114d4b 100644
--- a/src/tint/test_main.cc
+++ b/src/tint/test_main.cc
@@ -15,6 +15,11 @@
#include "gmock/gmock.h"
#include "src/tint/program.h"
+// TODO(crbug.com/tint/1580): Remove when 'const' is fully implemented.
+#if TINT_BUILD_WGSL_READER
+#include "src/tint/reader/wgsl/parser_impl.h"
+#endif
+
#if TINT_BUILD_SPV_READER
#include "src/tint/reader/spirv/parser_impl_test_helper.h"
#endif
@@ -54,6 +59,11 @@
int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
+// TODO(crbug.com/tint/1580): Remove when 'const' is fully implemented.
+#if TINT_BUILD_WGSL_READER
+ tint::reader::wgsl::ParserImpl::EnableConst();
+#endif
+
#if TINT_BUILD_WGSL_WRITER
tint::Program::printer = [](const tint::Program* program) {
auto result = tint::writer::wgsl::Generate(program, {});