tint/reader/wgsl: Drop const_expr parsing
The WGSL specification changed so that the enforcing of
initializers being only const-expr is no longer done by the
grammar. This is change is required to allow for 'const' expressions to
reference each other.
Bug: tint:1580
Change-Id: Ia95b6a0bc86ce391a38f4248f20898dd9a6cf27f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94683
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 8108e54..461556d 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -1333,7 +1333,6 @@
"reader/wgsl/parser_impl_bug_cases_test.cc",
"reader/wgsl/parser_impl_call_stmt_test.cc",
"reader/wgsl/parser_impl_case_body_test.cc",
- "reader/wgsl/parser_impl_const_expr_test.cc",
"reader/wgsl/parser_impl_const_literal_test.cc",
"reader/wgsl/parser_impl_continue_stmt_test.cc",
"reader/wgsl/parser_impl_continuing_stmt_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 3475b1b..32359b5 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -944,7 +944,6 @@
reader/wgsl/parser_impl_bug_cases_test.cc
reader/wgsl/parser_impl_call_stmt_test.cc
reader/wgsl/parser_impl_case_body_test.cc
- reader/wgsl/parser_impl_const_expr_test.cc
reader/wgsl/parser_impl_const_literal_test.cc
reader/wgsl/parser_impl_continue_stmt_test.cc
reader/wgsl/parser_impl_continuing_stmt_test.cc
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index f6ab166..c39df85 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -549,13 +549,16 @@
return Failure::kNoMatch;
}
- const ast::Expression* constructor = nullptr;
+ const ast::Expression* initalizer = nullptr;
if (match(Token::Type::kEqual)) {
- auto expr = expect_const_expr();
+ auto expr = logical_or_expression();
if (expr.errored) {
return Failure::kErrored;
}
- constructor = expr.value;
+ if (!expr.matched) {
+ return add_error(peek(), "missing initalizer for 'var' declaration");
+ }
+ initalizer = expr.value;
}
return create<ast::Var>(decl->source, // source
@@ -563,7 +566,7 @@
decl->type, // type
decl->storage_class, // storage class
decl->access, // access control
- constructor, // constructor
+ initalizer, // constructor
std::move(attrs)); // attributes
}
@@ -605,11 +608,14 @@
const ast::Expression* initializer = nullptr;
if (has_initializer) {
- auto init = expect_const_expr();
- if (init.errored) {
+ auto expr = logical_or_expression();
+ if (expr.errored) {
return Failure::kErrored;
}
- initializer = std::move(init.value);
+ if (!expr.matched) {
+ return add_error(peek(), "missing initializer for " + std::string(use));
+ }
+ initializer = std::move(expr.value);
}
if (is_const) {
@@ -3116,58 +3122,6 @@
return Failure::kNoMatch;
}
-// const_expr
-// : type_decl PAREN_LEFT ((const_expr COMMA)? const_expr COMMA?)? PAREN_RIGHT
-// | const_literal
-Expect<const ast::Expression*> ParserImpl::expect_const_expr() {
- auto t = peek();
- auto source = t.source();
- if (t.IsLiteral()) {
- auto lit = const_literal();
- if (lit.errored) {
- return Failure::kErrored;
- }
- if (!lit.matched) {
- return add_error(peek(), "unable to parse constant literal");
- }
- return lit.value;
- }
-
- if (peek_is(Token::Type::kParenLeft, 1) || peek_is(Token::Type::kLessThan, 1)) {
- auto type = expect_type("const_expr");
- if (type.errored) {
- return Failure::kErrored;
- }
-
- auto params = expect_paren_block("type constructor", [&]() -> Expect<ast::ExpressionList> {
- ast::ExpressionList list;
- while (continue_parsing()) {
- if (peek_is(Token::Type::kParenRight)) {
- break;
- }
-
- auto arg = expect_const_expr();
- if (arg.errored) {
- return Failure::kErrored;
- }
- list.emplace_back(arg.value);
-
- if (!match(Token::Type::kComma)) {
- break;
- }
- }
- return list;
- });
-
- if (params.errored) {
- return Failure::kErrored;
- }
-
- return builder_.Construct(source, type.value, params.value);
- }
- return add_error(peek(), "unable to parse const_expr");
-}
-
Maybe<ast::AttributeList> ParserImpl::attribute_list() {
bool errored = false;
ast::AttributeList attrs;
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index 89d5163..73c5829 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -541,9 +541,6 @@
/// Parses a `const_literal` grammar element
/// @returns the const literal parsed or nullptr if none found
Maybe<const ast::LiteralExpression*> const_literal();
- /// Parses a `const_expr` grammar element, erroring on parse failure.
- /// @returns the parsed constructor expression or nullptr on error
- Expect<const ast::Expression*> expect_const_expr();
/// Parses a `primary_expression` grammar element
/// @returns the parsed expression or nullptr
Maybe<const ast::Expression*> primary_expression();
diff --git a/src/tint/reader/wgsl/parser_impl_const_expr_test.cc b/src/tint/reader/wgsl/parser_impl_const_expr_test.cc
deleted file mode 100644
index 80536bd..0000000
--- a/src/tint/reader/wgsl/parser_impl_const_expr_test.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
-
-namespace tint::reader::wgsl {
-namespace {
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl) {
- auto p = parser("vec2<f32>(1., 2.)");
- auto e = p->expect_const_expr();
- ASSERT_FALSE(p->has_error()) << p->error();
- ASSERT_FALSE(e.errored);
- ASSERT_TRUE(e->Is<ast::CallExpression>());
-
- auto* t = e->As<ast::CallExpression>();
- ASSERT_TRUE(t->target.type->Is<ast::Vector>());
- EXPECT_EQ(t->target.type->As<ast::Vector>()->width, 2u);
-
- ASSERT_EQ(t->args.size(), 2u);
-
- ASSERT_TRUE(t->args[0]->Is<ast::FloatLiteralExpression>());
- EXPECT_DOUBLE_EQ(t->args[0]->As<ast::FloatLiteralExpression>()->value, 1.);
- EXPECT_EQ(t->args[0]->As<ast::FloatLiteralExpression>()->suffix,
- ast::FloatLiteralExpression::Suffix::kNone);
-
- ASSERT_TRUE(t->args[1]->Is<ast::FloatLiteralExpression>());
- EXPECT_DOUBLE_EQ(t->args[1]->As<ast::FloatLiteralExpression>()->value, 2.);
- EXPECT_EQ(t->args[1]->As<ast::FloatLiteralExpression>()->suffix,
- ast::FloatLiteralExpression::Suffix::kNone);
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_Empty) {
- auto p = parser("vec2<f32>()");
- auto e = p->expect_const_expr();
- ASSERT_FALSE(p->has_error()) << p->error();
- ASSERT_FALSE(e.errored);
- ASSERT_TRUE(e->Is<ast::CallExpression>());
-
- auto* t = e->As<ast::CallExpression>();
- ASSERT_TRUE(t->target.type->Is<ast::Vector>());
- EXPECT_EQ(t->target.type->As<ast::Vector>()->width, 2u);
-
- ASSERT_EQ(t->args.size(), 0u);
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_TrailingComma) {
- auto p = parser("vec2<f32>(1., 2.,)");
- auto e = p->expect_const_expr();
- ASSERT_FALSE(p->has_error()) << p->error();
- ASSERT_FALSE(e.errored);
- ASSERT_TRUE(e->Is<ast::CallExpression>());
-
- auto* t = e->As<ast::CallExpression>();
- ASSERT_TRUE(t->target.type->Is<ast::Vector>());
- EXPECT_EQ(t->target.type->As<ast::Vector>()->width, 2u);
-
- ASSERT_EQ(t->args.size(), 2u);
- ASSERT_TRUE(t->args[0]->Is<ast::LiteralExpression>());
- ASSERT_TRUE(t->args[1]->Is<ast::LiteralExpression>());
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingRightParen) {
- auto p = parser("vec2<f32>(1., 2.");
- auto e = p->expect_const_expr();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:17: expected ')' for type constructor");
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingLeftParen) {
- auto p = parser("vec2<f32> 1., 2.)");
- auto e = p->expect_const_expr();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:11: expected '(' for type constructor");
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeDecl_MissingComma) {
- auto p = parser("vec2<f32>(1. 2.");
- auto e = p->expect_const_expr();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:14: expected ')' for type constructor");
-}
-
-TEST_F(ParserImplTest, ConstExpr_InvalidExpr) {
- auto p = parser("vec2<f32>(1., if(a) {})");
- auto e = p->expect_const_expr();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:15: invalid type for const_expr");
-}
-
-TEST_F(ParserImplTest, ConstExpr_ConstLiteral) {
- auto p = parser("true");
- auto e = p->expect_const_expr();
- ASSERT_FALSE(p->has_error()) << p->error();
- ASSERT_FALSE(e.errored);
- ASSERT_NE(e.value, nullptr);
- ASSERT_TRUE(e.value->Is<ast::BoolLiteralExpression>());
- EXPECT_TRUE(e.value->As<ast::BoolLiteralExpression>()->value);
-}
-
-TEST_F(ParserImplTest, ConstExpr_ConstLiteral_Invalid) {
- auto p = parser("invalid");
- auto e = p->expect_const_expr();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:1: unable to parse const_expr");
-}
-
-TEST_F(ParserImplTest, ConstExpr_TypeConstructor) {
- auto p = parser("S(0)");
-
- auto e = p->expect_const_expr();
- ASSERT_FALSE(e.errored);
- ASSERT_TRUE(e->Is<ast::CallExpression>());
- ASSERT_NE(e->As<ast::CallExpression>()->target.type, nullptr);
- ASSERT_TRUE(e->As<ast::CallExpression>()->target.type->Is<ast::TypeName>());
- EXPECT_EQ(e->As<ast::CallExpression>()->target.type->As<ast::TypeName>()->name,
- p->builder().Symbols().Get("S"));
-}
-
-TEST_F(ParserImplTest, ConstExpr_Recursion) {
- std::stringstream out;
- for (size_t i = 0; i < 200; i++) {
- out << "f32(";
- }
- out << "1.0";
- for (size_t i = 0; i < 200; i++) {
- out << ")";
- }
- auto p = parser(out.str());
- auto e = p->expect_const_expr();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:517: maximum parser recursive depth reached");
-}
-
-TEST_F(ParserImplTest, UnaryOp_Recursion) {
- std::stringstream out;
- for (size_t i = 0; i < 200; i++) {
- out << "!";
- }
- out << "1.0";
- auto p = parser(out.str());
- auto e = p->unary_expression();
- ASSERT_TRUE(p->has_error());
- ASSERT_TRUE(e.errored);
- ASSERT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:130: maximum parser recursive depth reached");
-}
-
-} // namespace
-} // namespace tint::reader::wgsl
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 5e99d34..3273413 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -460,11 +460,7 @@
var a : f32 = bar[0];
return;
})",
- R"(test.wgsl:2:17 error: unable to parse const_expr
- var a : f32 = bar[0];
- ^^^
-
-test.wgsl:3:3 error: statement found outside of function body
+ R"(test.wgsl:3:3 error: statement found outside of function body
return;
^^^^^^
)");
@@ -504,27 +500,9 @@
TEST_F(ParserImplErrorTest, GlobalDeclConstBadConstLiteral) {
EXPECT("const i : vec2<i32> = vec2<i32>(!);",
- R"(test.wgsl:1:33 error: unable to parse const_expr
+ R"(test.wgsl:1:34 error: unable to parse right side of ! expression
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;
- ^
+ ^
)");
}
@@ -605,27 +583,9 @@
TEST_F(ParserImplErrorTest, GlobalDeclLetBadConstLiteral) {
EXPECT("let i : vec2<i32> = vec2<i32>(!);",
- R"(test.wgsl:1:31 error: unable to parse const_expr
+ R"(test.wgsl:1:32 error: unable to parse right side of ! expression
let i : vec2<i32> = vec2<i32>(!);
- ^
-)");
-}
-
-TEST_F(ParserImplErrorTest, GlobalDeclLetBadConstLiteralSpaceLessThan) {
- EXPECT("let i = 1 < 2;",
- R"(test.wgsl:1:11 error: expected ';' for 'let' declaration
-let i = 1 < 2;
- ^
-)");
-}
-
-TEST_F(ParserImplErrorTest, GlobalDeclLetNotConstExpr) {
- EXPECT(
- "let a = 1;\n"
- "let b = a;",
- R"(test.wgsl:2:9 error: unable to parse const_expr
-let b = a;
- ^
+ ^
)");
}
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 70caa3f..21c0078 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
@@ -77,7 +77,7 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:15: invalid type for const_expr");
+ EXPECT_EQ(p->error(), "1:15: missing initializer for 'let' declaration");
}
TEST_F(ParserImplTest, GlobalLetDecl_MissingExpression) {
@@ -90,7 +90,7 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:14: unable to parse const_expr");
+ EXPECT_EQ(p->error(), "1:14: missing initializer for 'let' declaration");
}
TEST_F(ParserImplTest, GlobalConstDecl) {
@@ -152,7 +152,7 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:17: invalid type for const_expr");
+ EXPECT_EQ(p->error(), "1:17: missing initializer for 'const' declaration");
}
TEST_F(ParserImplTest, GlobalConstDecl_MissingExpression) {
@@ -165,7 +165,7 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:16: unable to parse const_expr");
+ EXPECT_EQ(p->error(), "1:16: missing initializer for 'const' declaration");
}
TEST_F(ParserImplTest, GlobalOverrideDecl_WithId) {
diff --git a/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
index 11371e6..f671042 100644
--- a/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
@@ -152,7 +152,7 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:24: invalid type for const_expr");
+ EXPECT_EQ(p->error(), "1:24: missing initalizer for 'var' declaration");
}
TEST_F(ParserImplTest, GlobalVariableDecl_InvalidVariableDecl) {
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 76aaea2..235c575 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -353,8 +353,7 @@
ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
}
- if (rhs &&
- !validator_.VariableConstructorOrCast(v, ast::StorageClass::kNone, ty, rhs->Type())) {
+ if (rhs && !validator_.VariableInitializer(v, ast::StorageClass::kNone, ty, rhs)) {
return nullptr;
}
@@ -405,12 +404,11 @@
ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
}
} else if (!ty) {
- AddError("'override' declaration requires a type or initializer", v->source);
+ AddError("override declaration requires a type or initializer", v->source);
return nullptr;
}
- if (rhs &&
- !validator_.VariableConstructorOrCast(v, ast::StorageClass::kNone, ty, rhs->Type())) {
+ if (rhs && !validator_.VariableInitializer(v, ast::StorageClass::kNone, ty, rhs)) {
return nullptr;
}
@@ -479,8 +477,7 @@
return nullptr;
}
- if (rhs &&
- !validator_.VariableConstructorOrCast(c, ast::StorageClass::kNone, ty, rhs->Type())) {
+ if (!validator_.VariableInitializer(c, ast::StorageClass::kNone, ty, rhs)) {
return nullptr;
}
@@ -528,7 +525,7 @@
}
if (!storage_ty) {
- AddError("'var' declaration requires a type or initializer", var->source);
+ AddError("var declaration requires a type or initializer", var->source);
return nullptr;
}
@@ -558,7 +555,7 @@
access = DefaultAccessForStorageClass(storage_class);
}
- if (rhs && !validator_.VariableConstructorOrCast(var, storage_class, storage_ty, rhs->Type())) {
+ if (rhs && !validator_.VariableInitializer(var, storage_class, storage_ty, rhs)) {
return nullptr;
}
@@ -1864,8 +1861,8 @@
sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
auto symbol = expr->symbol;
auto* resolved = sem_.ResolvedSymbol(expr);
- if (auto* var = As<sem::Variable>(resolved)) {
- auto* user = builder_->create<sem::VariableUser>(expr, current_statement_, var);
+ if (auto* variable = As<sem::Variable>(resolved)) {
+ auto* user = builder_->create<sem::VariableUser>(expr, current_statement_, variable);
if (current_statement_) {
// If identifier is part of a loop continuing block, make sure it
@@ -1901,12 +1898,20 @@
}
if (current_function_) {
- if (auto* global = var->As<sem::GlobalVariable>()) {
+ if (auto* global = variable->As<sem::GlobalVariable>()) {
current_function_->AddDirectlyReferencedGlobal(global);
}
+ } else if (variable->Declaration()->Is<ast::Var>()) {
+ // Use of a module-scope 'var' outside of a function.
+ // Note: The spec is currently vague around the rules here. See
+ // https://github.com/gpuweb/gpuweb/issues/3081. Remove this comment when resolved.
+ std::string desc = "var '" + builder_->Symbols().NameFor(symbol) + "' ";
+ AddError(desc + "cannot not be referenced at module-scope", expr->source);
+ AddNote(desc + "declared here", variable->Declaration()->source);
+ return nullptr;
}
- var->AddUser(user);
+ variable->AddUser(user);
return user;
}
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index aaf3b31..451659e 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -421,7 +421,8 @@
GlobalVar("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: array size must evaluate to a constant integer expression");
+ R"(12:34 error: var 'size' cannot not be referenced at module-scope
+note: var 'size' declared here)");
}
TEST_F(ResolverTypeValidationTest, ArraySize_FunctionConst) {
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 6ffbd62..e8f28bb 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -319,17 +319,18 @@
return true;
}
-bool Validator::VariableConstructorOrCast(const ast::Variable* v,
- ast::StorageClass storage_class,
- const sem::Type* storage_ty,
- const sem::Type* rhs_ty) const {
- auto* value_type = rhs_ty->UnwrapRef(); // Implicit load of RHS
+bool Validator::VariableInitializer(const ast::Variable* v,
+ ast::StorageClass storage_class,
+ const sem::Type* storage_ty,
+ const sem::Expression* initializer) const {
+ auto* initializer_ty = initializer->Type();
+ auto* value_type = initializer_ty->UnwrapRef(); // Implicit load of RHS
// Value type has to match storage type
if (storage_ty != value_type) {
std::stringstream s;
s << "cannot initialize " << v->Kind() << " of type '" << sem_.TypeNameOf(storage_ty)
- << "' with value of type '" << sem_.TypeNameOf(rhs_ty) << "'";
+ << "' with value of type '" << sem_.TypeNameOf(initializer_ty) << "'";
AddError(s.str(), v->source);
return false;
}
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 9610e33..4fdac53 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -378,16 +378,16 @@
/// @returns true on success, false otherwise.
bool Const(const sem::Variable* v) const;
- /// Validates a variable constructor or cast
+ /// Validates a variable initializer
/// @param v the variable to validate
/// @param storage_class the storage class of the variable
/// @param storage_type the type of the storage
- /// @param rhs_type the right hand side of the expression
+ /// @param initializer the RHS initializer expression
/// @returns true on succes, false otherwise
- bool VariableConstructorOrCast(const ast::Variable* v,
- ast::StorageClass storage_class,
- const sem::Type* storage_type,
- const sem::Type* rhs_type) const;
+ bool VariableInitializer(const ast::Variable* v,
+ ast::StorageClass storage_class,
+ const sem::Type* storage_type,
+ const sem::Expression* initializer) const;
/// Validates a vector
/// @param ty the vector to validate
diff --git a/src/tint/resolver/variable_validation_test.cc b/src/tint/resolver/variable_validation_test.cc
index 435111b..51b430f 100644
--- a/src/tint/resolver/variable_validation_test.cc
+++ b/src/tint/resolver/variable_validation_test.cc
@@ -29,7 +29,7 @@
WrapInFunction(Var(Source{{12, 34}}, "a", nullptr));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: 'var' declaration requires a type or initializer");
+ EXPECT_EQ(r()->error(), "12:34 error: var declaration requires a type or initializer");
}
TEST_F(ResolverVariableValidationTest, GlobalVarNoInitializerNoType) {
@@ -37,7 +37,18 @@
GlobalVar(Source{{12, 34}}, "a", nullptr);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: 'var' declaration requires a type or initializer");
+ EXPECT_EQ(r()->error(), "12:34 error: var declaration requires a type or initializer");
+}
+
+TEST_F(ResolverVariableValidationTest, GlobalVarUsedAtModuleScope) {
+ // var<private> a : i32;
+ // var<private> b : i32 = a;
+ GlobalVar(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kPrivate, nullptr);
+ GlobalVar("b", ty.i32(), ast::StorageClass::kPrivate, Expr(Source{{56, 78}}, "a"));
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(56:78 error: var 'a' cannot not be referenced at module-scope
+12:34 note: var 'a' declared here)");
}
TEST_F(ResolverVariableValidationTest, OverrideNoInitializerNoType) {
@@ -45,7 +56,7 @@
Override(Source{{12, 34}}, "a", nullptr, nullptr);
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: 'override' declaration requires a type or initializer");
+ EXPECT_EQ(r()->error(), "12:34 error: override declaration requires a type or initializer");
}
TEST_F(ResolverVariableValidationTest, VarTypeNotStorable) {
diff --git a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
index f8983cb..c3a02b4 100644
--- a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
@@ -76,19 +76,6 @@
EXPECT_THAT(gen.result(), HasSubstr(" float a = 0.0f;\n"));
}
-TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_Private) {
- GlobalVar("initializer", ty.f32(), ast::StorageClass::kPrivate);
- GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
-
- WrapInFunction(Expr("a"));
-
- GeneratorImpl& gen = Build();
-
- ASSERT_TRUE(gen.Generate()) << gen.error();
- EXPECT_THAT(gen.result(), HasSubstr(R"(float a = initializer;
-)"));
-}
-
TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec) {
auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, vec3<f32>());
diff --git a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
index 3a857b7..1109014 100644
--- a/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_variable_decl_statement_test.cc
@@ -75,19 +75,6 @@
EXPECT_THAT(gen.result(), HasSubstr(" static float a = 0.0f;\n"));
}
-TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_Private) {
- GlobalVar("initializer", ty.f32(), ast::StorageClass::kPrivate);
- GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
-
- WrapInFunction(Expr("a"));
-
- GeneratorImpl& gen = Build();
-
- ASSERT_TRUE(gen.Generate()) << gen.error();
- EXPECT_THAT(gen.result(), HasSubstr(R"(float a = initializer;
-)"));
-}
-
TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec) {
auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, vec3<f32>());
diff --git a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
index 311d33c..7a0a379 100644
--- a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -123,22 +123,6 @@
EXPECT_THAT(gen.result(), HasSubstr("thread float tint_symbol_1 = 0.0f;\n"));
}
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_Private) {
- GlobalLet("initializer", ty.f32(), Expr(0_f));
- GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
-
- WrapInFunction(Expr("a"));
-
- GeneratorImpl& gen = SanitizeAndBuild();
-
- ASSERT_TRUE(gen.Generate()) << gen.error();
- EXPECT_THAT(gen.result(), HasSubstr(R"(
- thread float tint_symbol_1 = initializer;
- float const tint_symbol = tint_symbol_1;
- return;
-)"));
-}
-
TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Workgroup) {
GlobalVar("a", ty.f32(), ast::StorageClass::kWorkgroup);