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>
This commit is contained in:
parent
511529f082
commit
e48ef8ef90
|
@ -34,6 +34,10 @@ Const::Const(Const&&) = default;
|
|||
|
||||
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);
|
||||
|
|
|
@ -52,6 +52,9 @@ class Const final : public Castable<Const, Variable> {
|
|||
/// 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
|
||||
|
|
|
@ -34,6 +34,10 @@ Let::Let(Let&&) = default;
|
|||
|
||||
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);
|
||||
|
|
|
@ -49,6 +49,9 @@ class Let final : public Castable<Let, Variable> {
|
|||
/// 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
|
||||
|
|
|
@ -32,6 +32,10 @@ Override::Override(Override&&) = default;
|
|||
|
||||
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);
|
||||
|
|
|
@ -50,6 +50,9 @@ class Override final : public Castable<Override, Variable> {
|
|||
/// 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
|
||||
|
|
|
@ -31,6 +31,10 @@ Parameter::Parameter(Parameter&&) = default;
|
|||
|
||||
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);
|
||||
|
|
|
@ -51,6 +51,9 @@ class Parameter final : public Castable<Parameter, Variable> {
|
|||
/// 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
|
||||
|
|
|
@ -36,6 +36,10 @@ Var::Var(Var&&) = default;
|
|||
|
||||
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);
|
||||
|
|
|
@ -65,6 +65,9 @@ class Var final : public Castable<Var, Variable> {
|
|||
/// 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
|
||||
|
|
|
@ -77,6 +77,10 @@ class Variable : public Castable<Variable, Node> {
|
|||
/// @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;
|
||||
|
||||
|
|
|
@ -144,18 +144,18 @@ TEST_P(LineCommentTerminatorTest, Terminators) {
|
|||
// 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;
|
||||
|
|
|
@ -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(Source::File const* file) : lexer_(std::make_unique<Lexer
|
|||
|
||||
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,9 +445,13 @@ Maybe<bool> ParserImpl::global_decl() {
|
|||
}
|
||||
|
||||
if (gc.matched) {
|
||||
if (!expect("'let' declaration", Token::Type::kSemicolon)) {
|
||||
// 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);
|
||||
return true;
|
||||
|
@ -561,10 +573,14 @@ Maybe<const ast::Variable*> ParserImpl::global_variable_decl(ast::AttributeList&
|
|||
// 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 @@ Maybe<const ast::Variable*> ParserImpl::global_constant_decl(ast::AttributeList&
|
|||
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 @@ Maybe<const ast::ReturnStatement*> ParserImpl::return_stmt() {
|
|||
// | 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);
|
||||
|
|
|
@ -72,6 +72,11 @@ class ParserImpl {
|
|||
};
|
||||
|
||||
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
|
||||
|
|
|
@ -471,6 +471,107 @@ test.wgsl:3:3 error: statement found outside of function body
|
|||
}
|
||||
|
||||
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 @@ let ^ : i32 = 1;
|
|||
)");
|
||||
}
|
||||
|
||||
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 @@ let i : i32 = 1
|
|||
)");
|
||||
}
|
||||
|
||||
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 @@ let i : vec2<i32> = vec2<i32>;
|
|||
)");
|
||||
}
|
||||
|
||||
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 @@ let i : vec2<i32> = vec2<i32>(1., 2.;
|
|||
)");
|
||||
}
|
||||
|
||||
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 @@ let i : vec2<i32> = vec2<i32>(!);
|
|||
)");
|
||||
}
|
||||
|
||||
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 @@ let i = 1 < 2;
|
|||
)");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclConstNotConstExpr) {
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclLetNotConstExpr) {
|
||||
EXPECT(
|
||||
"let a = 1;\n"
|
||||
"let b = a;",
|
||||
|
@ -528,7 +629,7 @@ let b = a;
|
|||
)");
|
||||
}
|
||||
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclConstExprMaxDepth) {
|
||||
TEST_F(ParserImplErrorTest, GlobalDeclLetExprMaxDepth) {
|
||||
uint32_t kMaxDepth = 128;
|
||||
|
||||
std::stringstream src;
|
||||
|
@ -555,7 +656,7 @@ TEST_F(ParserImplErrorTest, GlobalDeclConstExprMaxDepth) {
|
|||
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 @@ let i : vec2<i32> = vec2<i32> 1, 2);
|
|||
)");
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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 @@ TEST_F(ParserImplTest, GlobalConstantDecl) {
|
|||
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 @@ TEST_F(ParserImplTest, GlobalConstantDecl_Inferred) {
|
|||
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 @@ TEST_F(ParserImplTest, GlobalConstantDecl_InvalidExpression) {
|
|||
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 @@ TEST_F(ParserImplTest, GlobalConstantDecl_MissingExpression) {
|
|||
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 @@ TEST_F(ParserImplTest, GlobalConstantDec_Override_WithId) {
|
|||
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 @@ TEST_F(ParserImplTest, GlobalConstantDec_Override_WithoutId) {
|
|||
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 @@ TEST_F(ParserImplTest, GlobalConstantDec_Override_MissingId) {
|
|||
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);
|
||||
|
|
|
@ -49,7 +49,7 @@ TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_MissingSemicolon) {
|
|||
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 @@ TEST_F(ParserImplTest, GlobalDecl_GlobalConstant) {
|
|||
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();
|
||||
|
|
|
@ -32,6 +32,13 @@ TEST_P(ParserImplReservedKeywordTest, ModuleLet) {
|
|||
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;");
|
||||
|
|
|
@ -491,10 +491,7 @@ struct DependencyAnalysis {
|
|||
[&](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"; }, //
|
||||
[&](const ast::Variable* v) { return v->Kind(); }, //
|
||||
[&](Default) {
|
||||
UnhandledNode(diagnostics_, node);
|
||||
return "<error>";
|
||||
|
|
|
@ -327,16 +327,10 @@ bool Validator::VariableConstructorOrCast(const ast::Variable* v,
|
|||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ struct Flags {
|
|||
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, {});
|
||||
|
|
Loading…
Reference in New Issue