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);