Move global var validation from Validator to Resolver

* Moved global variable resolving logic to new function Resolver::GlobalVariable, and moved validation logic there.
* Moved global variable-related tests to resolver tests.
* Fixed many tests that started failing after this change, mainly because many globals were declared with no storage class. I set most of these to "Input".

Bug: tint:642
Change-Id: I0f8ea2091ed2bb3faa358f9497cd884b2994a40f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/46940
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/ast/module_clone_test.cc b/src/ast/module_clone_test.cc
index 4decb92..1fc36c1 100644
--- a/src/ast/module_clone_test.cc
+++ b/src/ast/module_clone_test.cc
@@ -48,10 +48,10 @@
 var g6 : [[access(write)]] texture_storage_2d<rg32float>;
 
 [[builtin(position)]] var<uniform> g7 : vec3<f32>;
-[[group(10), binding(20)]] var<storage> g7 : S;
-[[group(10), binding(20)]] var<storage> g8 : [[access(read)]]
+[[group(10), binding(20)]] var<storage> g8 : S;
+[[group(10), binding(20)]] var<storage> g9 : [[access(read)]]
 S;
-[[group(10), binding(20)]] var<storage> g9 : [[access(read_write)]]
+[[group(10), binding(20)]] var<storage> g10 : [[access(read_write)]]
 S;
 
 fn f0(p0 : bool) -> f32 {
diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc
index 39f8f8d..1669a50 100644
--- a/src/resolver/decoration_validation_test.cc
+++ b/src/resolver/decoration_validation_test.cc
@@ -237,5 +237,40 @@
                     TestParams{DecorationKind::kStructBlock, false},
                     TestParams{DecorationKind::kWorkgroup, false}));
 
+using VariableDecorationTest = TestWithParams;
+TEST_P(VariableDecorationTest, IsValid) {
+  auto params = GetParam();
+
+  Global("a", ty.f32(), ast::StorageClass::kInput, nullptr,
+         ast::DecorationList{
+             createDecoration(Source{{12, 34}}, *this, params.kind)});
+
+  WrapInFunction();
+
+  if (params.should_pass) {
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+  } else {
+    EXPECT_FALSE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(),
+              "12:34 error: decoration is not valid for variables");
+  }
+}
+INSTANTIATE_TEST_SUITE_P(
+    ValidatorTest,
+    VariableDecorationTest,
+    testing::Values(TestParams{DecorationKind::kAccess, false},
+                    TestParams{DecorationKind::kAlign, false},
+                    TestParams{DecorationKind::kBinding, true},
+                    TestParams{DecorationKind::kBuiltin, true},
+                    TestParams{DecorationKind::kConstantId, true},
+                    TestParams{DecorationKind::kGroup, true},
+                    TestParams{DecorationKind::kLocation, true},
+                    TestParams{DecorationKind::kOffset, false},
+                    TestParams{DecorationKind::kSize, false},
+                    TestParams{DecorationKind::kStage, false},
+                    TestParams{DecorationKind::kStride, false},
+                    TestParams{DecorationKind::kStructBlock, false},
+                    TestParams{DecorationKind::kWorkgroup, false}));
+
 }  // namespace
 }  // namespace tint
diff --git a/src/resolver/host_shareable_validation_test.cc b/src/resolver/host_shareable_validation_test.cc
index aaac6a3..82fecda 100644
--- a/src/resolver/host_shareable_validation_test.cc
+++ b/src/resolver/host_shareable_validation_test.cc
@@ -37,14 +37,14 @@
 }
 
 TEST_F(ResolverHostShareableValidationTest, Pointer) {
-  Global(Source{{56, 78}}, "g", ty.pointer<i32>(ast::StorageClass::kNone),
+  Global(Source{{56, 78}}, "g", ty.pointer<i32>(ast::StorageClass::kInput),
          ast::StorageClass::kStorage);
 
   ASSERT_FALSE(r()->Resolve());
 
   EXPECT_EQ(
       r()->error(),
-      R"(56:78 error: Type 'ptr<i32>' cannot be used in storage class 'storage' as it is non-host-shareable
+      R"(56:78 error: Type 'ptr<in, i32>' cannot be used in storage class 'storage' as it is non-host-shareable
 56:78 note: while instantiating variable g)");
 }
 
diff --git a/src/resolver/intrinsic_test.cc b/src/resolver/intrinsic_test.cc
index 22bb0ee..1ec20a9 100644
--- a/src/resolver/intrinsic_test.cc
+++ b/src/resolver/intrinsic_test.cc
@@ -52,7 +52,7 @@
 TEST_P(ResolverIntrinsicDerivativeTest, Scalar) {
   auto name = GetParam();
 
-  Global("ident", ty.f32(), ast::StorageClass::kNone);
+  Global("ident", ty.f32(), ast::StorageClass::kInput);
 
   auto* expr = Call(name, "ident");
   WrapInFunction(expr);
@@ -65,7 +65,7 @@
 
 TEST_P(ResolverIntrinsicDerivativeTest, Vector) {
   auto name = GetParam();
-  Global("ident", ty.vec4<f32>(), ast::StorageClass::kNone);
+  Global("ident", ty.vec4<f32>(), ast::StorageClass::kInput);
 
   auto* expr = Call(name, "ident");
   WrapInFunction(expr);
@@ -109,7 +109,7 @@
 TEST_P(ResolverIntrinsic, Test) {
   auto name = GetParam();
 
-  Global("my_var", ty.vec3<bool>(), ast::StorageClass::kNone);
+  Global("my_var", ty.vec3<bool>(), ast::StorageClass::kInput);
 
   auto* expr = Call(name, "my_var");
   WrapInFunction(expr);
@@ -127,7 +127,7 @@
 TEST_P(ResolverIntrinsicTest_FloatMethod, Vector) {
   auto name = GetParam();
 
-  Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.vec3<f32>(), ast::StorageClass::kInput);
 
   auto* expr = Call(name, "my_var");
   WrapInFunction(expr);
@@ -143,7 +143,7 @@
 TEST_P(ResolverIntrinsicTest_FloatMethod, Scalar) {
   auto name = GetParam();
 
-  Global("my_var", ty.f32(), ast::StorageClass::kNone);
+  Global("my_var", ty.f32(), ast::StorageClass::kInput);
 
   auto* expr = Call(name, "my_var");
   WrapInFunction(expr);
@@ -157,7 +157,7 @@
 TEST_P(ResolverIntrinsicTest_FloatMethod, MissingParam) {
   auto name = GetParam();
 
-  Global("my_var", ty.f32(), ast::StorageClass::kNone);
+  Global("my_var", ty.f32(), ast::StorageClass::kInput);
 
   auto* expr = Call(name);
   WrapInFunction(expr);
@@ -174,7 +174,7 @@
 TEST_P(ResolverIntrinsicTest_FloatMethod, TooManyParams) {
   auto name = GetParam();
 
-  Global("my_var", ty.f32(), ast::StorageClass::kNone);
+  Global("my_var", ty.f32(), ast::StorageClass::kInput);
 
   auto* expr = Call(name, "my_var", 1.23f);
   WrapInFunction(expr);
@@ -182,7 +182,7 @@
   EXPECT_FALSE(r()->Resolve());
 
   EXPECT_EQ(r()->error(), "error: no matching call to " + name +
-                              "(ptr<f32>, f32)\n\n"
+                              "(ptr<in, f32>, f32)\n\n"
                               "2 candidate functions:\n  " +
                               name + "(f32) -> bool\n  " + name +
                               "(vecN<f32>) -> vecN<bool>\n");
@@ -242,7 +242,7 @@
   void add_call_param(std::string name,
                       type::Type* type,
                       ast::ExpressionList* call_params) {
-    Global(name, type, ast::StorageClass::kNone);
+    Global(name, type, ast::StorageClass::kInput);
     call_params->push_back(Expr(name));
   }
   type::Type* subtype(Texture type) {
@@ -370,7 +370,7 @@
                     TextureTestParams{type::TextureDimension::k3d}));
 
 TEST_F(ResolverIntrinsicTest, Dot_Vec2) {
-  Global("my_var", ty.vec2<f32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.vec2<f32>(), ast::StorageClass::kInput);
 
   auto* expr = Call("dot", "my_var", "my_var");
   WrapInFunction(expr);
@@ -382,7 +382,7 @@
 }
 
 TEST_F(ResolverIntrinsicTest, Dot_Vec3) {
-  Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.vec3<f32>(), ast::StorageClass::kInput);
 
   auto* expr = Call("dot", "my_var", "my_var");
   WrapInFunction(expr);
@@ -394,7 +394,7 @@
 }
 
 TEST_F(ResolverIntrinsicTest, Dot_Vec4) {
-  Global("my_var", ty.vec4<f32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.vec4<f32>(), ast::StorageClass::kInput);
 
   auto* expr = Call("dot", "my_var", "my_var");
   WrapInFunction(expr);
@@ -420,15 +420,16 @@
 }
 
 TEST_F(ResolverIntrinsicTest, Dot_Error_VectorInt) {
-  Global("my_var", ty.vec4<i32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.vec4<i32>(), ast::StorageClass::kInput);
 
   auto* expr = Call("dot", "my_var", "my_var");
   WrapInFunction(expr);
 
   EXPECT_FALSE(r()->Resolve());
 
-  EXPECT_EQ(r()->error(),
-            R"(error: no matching call to dot(ptr<vec4<i32>>, ptr<vec4<i32>>)
+  EXPECT_EQ(
+      r()->error(),
+      R"(error: no matching call to dot(ptr<in, vec4<i32>>, ptr<in, vec4<i32>>)
 
 1 candidate function:
   dot(vecN<f32>, vecN<f32>) -> f32
@@ -436,9 +437,9 @@
 }
 
 TEST_F(ResolverIntrinsicTest, Select) {
-  Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.vec3<f32>(), ast::StorageClass::kInput);
 
-  Global("bool_var", ty.vec3<bool>(), ast::StorageClass::kNone);
+  Global("bool_var", ty.vec3<bool>(), ast::StorageClass::kInput);
 
   auto* expr = Call("select", "my_var", "my_var", "bool_var");
   WrapInFunction(expr);
@@ -756,7 +757,7 @@
 using ResolverIntrinsicDataTest = ResolverTest;
 
 TEST_F(ResolverIntrinsicDataTest, ArrayLength_Vector) {
-  Global("arr", ty.array<int>(), ast::StorageClass::kNone);
+  Global("arr", ty.array<int>(), ast::StorageClass::kInput);
   auto* call = Call("arrayLength", "arr");
   WrapInFunction(call);
 
@@ -767,14 +768,14 @@
 }
 
 TEST_F(ResolverIntrinsicDataTest, ArrayLength_Error_ArraySized) {
-  Global("arr", ty.array<int, 4>(), ast::StorageClass::kNone);
+  Global("arr", ty.array<int, 4>(), ast::StorageClass::kInput);
   auto* call = Call("arrayLength", "arr");
   WrapInFunction(call);
 
   EXPECT_FALSE(r()->Resolve());
 
   EXPECT_EQ(r()->error(),
-            "error: no matching call to arrayLength(ptr<array<i32, 4>>)\n\n"
+            "error: no matching call to arrayLength(ptr<in, array<i32, 4>>)\n\n"
             "1 candidate function:\n"
             "  arrayLength(array<T>) -> u32\n");
 }
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 5c54e48..d09ea62 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -21,6 +21,7 @@
 #include "src/ast/bitcast_expression.h"
 #include "src/ast/break_statement.h"
 #include "src/ast/call_statement.h"
+#include "src/ast/constant_id_decoration.h"
 #include "src/ast/continue_statement.h"
 #include "src/ast/discard_statement.h"
 #include "src/ast/fallthrough_statement.h"
@@ -202,20 +203,7 @@
         return false;
       }
     } else if (auto* var = decl->As<ast::Variable>()) {
-      auto* info = CreateVariableInfo(var);
-      variable_stack_.set_global(var->symbol(), info);
-
-      if (var->has_constructor()) {
-        if (!Expression(var->constructor())) {
-          return false;
-        }
-      }
-
-      if (!ApplyStorageClassUsageToType(var->declared_storage_class(),
-                                        info->type, var->source())) {
-        diagnostics_.add_note("while instantiating variable " +
-                                  builder_->Symbols().NameFor(var->symbol()),
-                              var->source());
+      if (!GlobalVariable(var)) {
         return false;
       }
     }
@@ -224,6 +212,59 @@
   return true;
 }
 
+bool Resolver::GlobalVariable(ast::Variable* var) {
+  if (variable_stack_.has(var->symbol())) {
+    diagnostics_.add_error("v-0011",
+                           "redeclared global identifier '" +
+                               builder_->Symbols().NameFor(var->symbol()) + "'",
+                           var->source());
+    return false;
+  }
+
+  auto* info = CreateVariableInfo(var);
+  variable_stack_.set_global(var->symbol(), info);
+
+  if (!var->is_const() && info->storage_class == ast::StorageClass::kNone) {
+    diagnostics_.add_error(
+        "v-0022", "global variables must have a storage class", var->source());
+    return false;
+  }
+  if (var->is_const() && !(info->storage_class == ast::StorageClass::kNone)) {
+    diagnostics_.add_error("v-global01",
+                           "global constants shouldn't have a storage class",
+                           var->source());
+    return false;
+  }
+
+  for (auto* deco : var->decorations()) {
+    if (!(deco->Is<ast::BindingDecoration>() ||
+          deco->Is<ast::BuiltinDecoration>() ||
+          deco->Is<ast::ConstantIdDecoration>() ||
+          deco->Is<ast::GroupDecoration>() ||
+          deco->Is<ast::LocationDecoration>())) {
+      diagnostics_.add_error("decoration is not valid for variables",
+                             deco->source());
+      return false;
+    }
+  }
+
+  if (var->has_constructor()) {
+    if (!Expression(var->constructor())) {
+      return false;
+    }
+  }
+
+  if (!ApplyStorageClassUsageToType(var->declared_storage_class(), info->type,
+                                    var->source())) {
+    diagnostics_.add_note("while instantiating variable " +
+                              builder_->Symbols().NameFor(var->symbol()),
+                          var->source());
+    return false;
+  }
+
+  return true;
+}
+
 bool Resolver::ValidateVariable(const ast::Variable* var) {
   auto* type = variable_to_info_[var]->type;
   if (auto* r = type->UnwrapAll()->As<type::Array>()) {
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index d74e826..e3d7edf 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -227,6 +227,7 @@
   bool Return(ast::ReturnStatement* ret);
   bool Switch(ast::SwitchStatement* s);
   bool Assignment(ast::AssignmentStatement* a);
+  bool GlobalVariable(ast::Variable* var);
 
   // AST and Type validation methods
   // Each return true on success, false on failure.
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc
index 7ee58c6..fd2c151 100644
--- a/src/resolver/resolver_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -275,7 +275,7 @@
 
 TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScope) {
   auto* init = Expr(2);
-  Global("my_var", ty.i32(), ast::StorageClass::kNone, init);
+  Global("my_var", ty.i32(), ast::StorageClass::kInput, init);
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 
@@ -352,19 +352,20 @@
   ast::VariableList params;
 
   // Declare i32 "foo" inside a function
-  auto* fn_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2));
+  auto* fn_i32 = Var("foo", ty.i32(), ast::StorageClass::kFunction, Expr(2));
   auto* fn_i32_init = fn_i32->constructor();
   auto* fn_i32_decl = create<ast::VariableDeclStatement>(fn_i32);
   Func("func_i32", params, ty.void_(), ast::StatementList{fn_i32_decl},
        ast::DecorationList{});
 
   // Declare f32 "foo" at module scope
-  auto* mod_f32 = Var("foo", ty.f32(), ast::StorageClass::kNone, Expr(2.f));
+  auto* mod_f32 = Var("foo", ty.f32(), ast::StorageClass::kInput, Expr(2.f));
   auto* mod_init = mod_f32->constructor();
   AST().AddGlobalVariable(mod_f32);
 
   // Reference "foo" in another function
-  auto* fn_f32 = Var("bar", ty.f32(), ast::StorageClass::kNone, Expr("foo"));
+  auto* fn_f32 =
+      Var("bar", ty.f32(), ast::StorageClass::kFunction, Expr("foo"));
   auto* fn_f32_init = fn_f32->constructor();
   auto* fn_f32_decl = create<ast::VariableDeclStatement>(fn_f32);
   Func("func_f32", params, ty.void_(), ast::StatementList{fn_f32_decl},
@@ -430,7 +431,7 @@
 }
 
 TEST_F(ResolverTest, Expr_ArrayAccessor_Matrix) {
-  Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kInput);
 
   auto* acc = IndexAccessor("my_var", 2);
   WrapInFunction(acc);
@@ -446,7 +447,7 @@
 }
 
 TEST_F(ResolverTest, Expr_ArrayAccessor_Matrix_BothDimensions) {
-  Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kInput);
 
   auto* acc = IndexAccessor(IndexAccessor("my_var", 2), 1);
   WrapInFunction(acc);
@@ -461,7 +462,7 @@
 }
 
 TEST_F(ResolverTest, Expr_ArrayAccessor_Vector) {
-  Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_var", ty.vec3<f32>(), ast::StorageClass::kInput);
 
   auto* acc = IndexAccessor("my_var", 2);
   WrapInFunction(acc);
@@ -600,7 +601,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Identifier_GlobalVariable) {
-  auto* my_var = Global("my_var", ty.f32(), ast::StorageClass::kNone);
+  auto* my_var = Global("my_var", ty.f32(), ast::StorageClass::kInput);
 
   auto* ident = Expr("my_var");
   WrapInFunction(ident);
@@ -838,7 +839,7 @@
       ast::DecorationList{});
 
   auto* st = ty.struct_("S", strct);
-  Global("my_struct", st, ast::StorageClass::kNone);
+  Global("my_struct", st, ast::StorageClass::kInput);
 
   auto* mem = MemberAccessor("my_struct", "second_member");
   WrapInFunction(mem);
@@ -860,7 +861,7 @@
 
   auto* st = ty.struct_("alias", strct);
   auto* alias = ty.alias("alias", st);
-  Global("my_struct", alias, ast::StorageClass::kNone);
+  Global("my_struct", alias, ast::StorageClass::kInput);
 
   auto* mem = MemberAccessor("my_struct", "second_member");
   WrapInFunction(mem);
@@ -875,7 +876,7 @@
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle) {
-  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kInput);
 
   auto* mem = MemberAccessor("my_vec", "xzyw");
   WrapInFunction(mem);
@@ -890,7 +891,7 @@
 }
 
 TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
-  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kInput);
 
   auto* mem = MemberAccessor("my_vec", "b");
   WrapInFunction(mem);
@@ -941,7 +942,7 @@
       ast::StructMemberList{Member("mem", &vecB)}, ast::DecorationList{});
 
   auto* stA = ty.struct_("A", strctA);
-  Global("c", stA, ast::StorageClass::kNone);
+  Global("c", stA, ast::StorageClass::kInput);
 
   auto* mem = MemberAccessor(
       MemberAccessor(IndexAccessor(MemberAccessor("c", "mem"), 0), "foo"),
@@ -963,7 +964,7 @@
       ast::DecorationList{});
 
   auto* st = ty.struct_("S", strct);
-  Global("my_struct", st, ast::StorageClass::kNone);
+  Global("my_struct", st, ast::StorageClass::kInput);
 
   auto* expr = Add(MemberAccessor("my_struct", "first_member"),
                    MemberAccessor("my_struct", "second_member"));
@@ -1148,8 +1149,8 @@
      << rhs_type->FriendlyName(Symbols());
   SCOPED_TRACE(ss.str());
 
-  Global("lhs", lhs_type, ast::StorageClass::kNone);
-  Global("rhs", rhs_type, ast::StorageClass::kNone);
+  Global("lhs", lhs_type, ast::StorageClass::kInput);
+  Global("rhs", rhs_type, ast::StorageClass::kInput);
 
   auto* expr =
       create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
@@ -1205,8 +1206,8 @@
      << params.op << " " << rhs_type->FriendlyName(Symbols());
   SCOPED_TRACE(ss.str());
 
-  Global("lhs", lhs_type, ast::StorageClass::kNone);
-  Global("rhs", rhs_type, ast::StorageClass::kNone);
+  Global("lhs", lhs_type, ast::StorageClass::kInput);
+  Global("rhs", rhs_type, ast::StorageClass::kInput);
 
   auto* expr =
       create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
@@ -1260,8 +1261,8 @@
      << rhs_type->FriendlyName(Symbols());
   SCOPED_TRACE(ss.str());
 
-  Global("lhs", lhs_type, ast::StorageClass::kNone);
-  Global("rhs", rhs_type, ast::StorageClass::kNone);
+  Global("lhs", lhs_type, ast::StorageClass::kInput);
+  Global("rhs", rhs_type, ast::StorageClass::kInput);
 
   auto* expr = create<ast::BinaryExpression>(Source{{12, 34}}, params.op,
                                              Expr("lhs"), Expr("rhs"));
@@ -1306,8 +1307,8 @@
     is_valid_expr = vec_size == mat_cols;
   }
 
-  Global("lhs", lhs_type, ast::StorageClass::kNone);
-  Global("rhs", rhs_type, ast::StorageClass::kNone);
+  Global("lhs", lhs_type, ast::StorageClass::kInput);
+  Global("rhs", rhs_type, ast::StorageClass::kInput);
 
   auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
   WrapInFunction(expr);
@@ -1346,8 +1347,8 @@
   auto* result_type =
       create<type::Matrix>(ty.f32(), lhs_mat_rows, rhs_mat_cols);
 
-  Global("lhs", lhs_type, ast::StorageClass::kNone);
-  Global("rhs", rhs_type, ast::StorageClass::kNone);
+  Global("lhs", lhs_type, ast::StorageClass::kInput);
+  Global("rhs", rhs_type, ast::StorageClass::kInput);
 
   auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
   WrapInFunction(expr);
@@ -1379,7 +1380,7 @@
 TEST_P(UnaryOpExpressionTest, Expr_UnaryOp) {
   auto op = GetParam();
 
-  Global("ident", ty.vec4<f32>(), ast::StorageClass::kNone);
+  Global("ident", ty.vec4<f32>(), ast::StorageClass::kInput);
   auto* der = create<ast::UnaryOpExpression>(op, Expr("ident"));
   WrapInFunction(der);
 
diff --git a/src/resolver/type_validation_test.cc b/src/resolver/type_validation_test.cc
index a7c2394..ee4144c 100644
--- a/src/resolver/type_validation_test.cc
+++ b/src/resolver/type_validation_test.cc
@@ -26,6 +26,85 @@
 class ResolverTypeValidationTest : public resolver::TestHelper,
                                    public testing::Test {};
 
+TEST_F(ResolverTypeValidationTest, GlobalVariableWithStorageClass_Pass) {
+  // var<in> global_var: f32;
+  Global(Source{{12, 34}}, "global_var", ty.f32(), ast::StorageClass::kInput);
+
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTypeValidationTest, GlobalVariableNoStorageClass_Fail) {
+  // var global_var: f32;
+  Global(Source{{12, 34}}, "global_var", ty.f32(), ast::StorageClass::kNone);
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error v-0022: global variables must have a storage class");
+}
+
+TEST_F(ResolverTypeValidationTest, GlobalConstantWithStorageClass_Fail) {
+  // const<in> global_var: f32;
+  AST().AddGlobalVariable(
+      create<ast::Variable>(Source{{12, 34}}, Symbols().Register("global_var"),
+                            ast::StorageClass::kInput, ty.f32(), true, nullptr,
+                            ast::DecorationList{}));
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error v-global01: global constants shouldn't have a storage "
+            "class");
+}
+
+TEST_F(ResolverTypeValidationTest, GlobalConstNoStorageClass_Pass) {
+  // const global_var: f32;
+  GlobalConst(Source{{12, 34}}, "global_var", ty.f32());
+
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTypeValidationTest, GlobalVariableUnique_Pass) {
+  // var global_var0 : f32 = 0.1;
+  // var global_var1 : i32 = 0;
+
+  Global("global_var0", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1f));
+
+  Global(Source{{12, 34}}, "global_var1", ty.f32(), ast::StorageClass::kPrivate,
+         Expr(0));
+
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTypeValidationTest, GlobalVariableNotUnique_Fail) {
+  // var global_var : f32 = 0.1;
+  // var global_var : i32 = 0;
+  Global("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1f));
+
+  Global(Source{{12, 34}}, "global_var", ty.i32(), ast::StorageClass::kPrivate,
+         Expr(0));
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error v-0011: redeclared global identifier 'global_var'");
+}
+
+TEST_F(ResolverTypeValidationTest,
+       GlobalVariableFunctionVariableNotUnique_Pass) {
+  // fn my_func -> void {
+  //   var a: f32 = 2.0;
+  // }
+  // var a: f32 = 2.1;
+
+  auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
+
+  Func("my_func", ast::VariableList{}, ty.void_(), {Decl(var)},
+       ast::DecorationList{
+           create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
+
+  Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
+
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverTypeValidationTest,
        GlobalVariableFunctionVariableNotUnique_Fail) {
   // var a: f32 = 2.1;
@@ -40,8 +119,7 @@
 
   Func("my_func", ast::VariableList{}, ty.void_(),
        ast::StatementList{
-           create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
-                                              var),
+           create<ast::VariableDeclStatement>(Source{{12, 34}}, var),
        },
        ast::DecorationList{});
 
@@ -61,8 +139,7 @@
   Func("my_func", ast::VariableList{}, ty.void_(),
        ast::StatementList{
            create<ast::VariableDeclStatement>(var),
-           create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
-                                              var_a_float),
+           create<ast::VariableDeclStatement>(Source{{12, 34}}, var_a_float),
        },
        ast::DecorationList{});
 
@@ -86,8 +163,7 @@
 
   auto* outer_body = create<ast::BlockStatement>(ast::StatementList{
       create<ast::IfStatement>(cond, body, ast::ElseStatementList{}),
-      create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
-                                         var_a_float),
+      create<ast::VariableDeclStatement>(Source{{12, 34}}, var_a_float),
   });
 
   WrapInFunction(outer_body);
@@ -109,7 +185,7 @@
 
   auto* cond = Expr(true);
   auto* body = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}}, var),
+      create<ast::VariableDeclStatement>(Source{{12, 34}}, var),
   });
 
   auto* outer_body = create<ast::BlockStatement>(ast::StatementList{
@@ -130,8 +206,7 @@
   // }
   auto* var_inner = Var("a", ty.f32(), ast::StorageClass::kNone);
   auto* inner = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
-                                         var_inner),
+      create<ast::VariableDeclStatement>(Source{{12, 34}}, var_inner),
   });
 
   auto* var_outer = Var("a", ty.f32(), ast::StorageClass::kNone);
@@ -152,8 +227,7 @@
   // }
   auto* var_inner = Var("a", ty.f32(), ast::StorageClass::kNone);
   auto* inner = create<ast::BlockStatement>(ast::StatementList{
-      create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
-                                         var_inner),
+      create<ast::VariableDeclStatement>(Source{{12, 34}}, var_inner),
   });
 
   auto* var_outer = Var("a", ty.f32(), ast::StorageClass::kNone);
@@ -178,16 +252,14 @@
 
   Func("func0", ast::VariableList{}, ty.void_(),
        ast::StatementList{
-           create<ast::VariableDeclStatement>(Source{Source::Location{12, 34}},
-                                              var0),
+           create<ast::VariableDeclStatement>(Source{{12, 34}}, var0),
            create<ast::ReturnStatement>(),
        },
        ast::DecorationList{});
 
   Func("func1", ast::VariableList{}, ty.void_(),
        ast::StatementList{
-           create<ast::VariableDeclStatement>(Source{Source::Location{13, 34}},
-                                              var1),
+           create<ast::VariableDeclStatement>(Source{{13, 34}}, var1),
            create<ast::ReturnStatement>(),
        },
        ast::DecorationList{
@@ -294,8 +366,8 @@
   // fn func(a : array<u32>) {}
   // [[stage(vertex)]] fn main() {}
 
-  auto* param = Var(Source{Source::Location{12, 34}}, "a", ty.array<i32>(),
-                    ast::StorageClass::kNone);
+  auto* param =
+      Var(Source{{12, 34}}, "a", ty.array<i32>(), ast::StorageClass::kNone);
 
   Func("func", ast::VariableList{param}, ty.void_(),
        ast::StatementList{
diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc
index 11266c4..408f0b2 100644
--- a/src/resolver/validation_test.cc
+++ b/src/resolver/validation_test.cc
@@ -381,7 +381,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
-  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kInput);
 
   auto* ident = create<ast::IdentifierExpression>(
       Source{{Source::Location{3, 3}, Source::Location{3, 7}}},
@@ -395,7 +395,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
-  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kInput);
 
   auto* ident = create<ast::IdentifierExpression>(
       Source{{Source::Location{3, 3}, Source::Location{3, 7}}},
@@ -411,7 +411,7 @@
 }
 
 TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
-  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
+  Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kInput);
 
   auto* ident = create<ast::IdentifierExpression>(
       Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
@@ -1705,7 +1705,7 @@
 
 TEST_F(ResolverValidationTest, Expr_Constructor_Vector_Alias_Argument_Error) {
   auto* alias = ty.alias("UnsignedInt", ty.u32());
-  Global("uint_var", alias, ast::StorageClass::kNone);
+  Global("uint_var", alias, ast::StorageClass::kInput);
 
   auto* tc = vec2<f32>(Expr(Source{{12, 34}}, "uint_var"));
   WrapInFunction(tc);
@@ -1719,8 +1719,8 @@
 TEST_F(ResolverValidationTest, Expr_Constructor_Vector_Alias_Argument_Success) {
   auto* f32_alias = ty.alias("Float32", ty.f32());
   auto* vec2_alias = ty.alias("VectorFloat2", ty.vec2<f32>());
-  Global("my_f32", f32_alias, ast::StorageClass::kNone);
-  Global("my_vec2", vec2_alias, ast::StorageClass::kNone);
+  Global("my_f32", f32_alias, ast::StorageClass::kInput);
+  Global("my_vec2", vec2_alias, ast::StorageClass::kInput);
 
   auto* tc = vec3<f32>("my_vec2", "my_f32");
   WrapInFunction(tc);
diff --git a/src/transform/bound_array_accessors_test.cc b/src/transform/bound_array_accessors_test.cc
index 4f82af4..fc9b87e 100644
--- a/src/transform/bound_array_accessors_test.cc
+++ b/src/transform/bound_array_accessors_test.cc
@@ -50,11 +50,11 @@
 
 TEST_F(BoundArrayAccessorsTest, Array_Idx_Nested_Scalar) {
   auto* src = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
-var b : array<f32, 5>;
+var<in> b : array<f32, 5>;
 
-var i : u32;
+var<in> i : u32;
 
 fn f() -> void {
   var c : f32 = a[ b[i] ];
@@ -62,11 +62,11 @@
 )";
 
   auto* expect = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
-var b : array<f32, 5>;
+var<in> b : array<f32, 5>;
 
-var i : u32;
+var<in> i : u32;
 
 fn f() -> void {
   var c : f32 = a[min(u32(b[min(u32(i), 4u)]), 2u)];
@@ -80,7 +80,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Array_Idx_Scalar) {
   auto* src = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
 fn f() -> void {
   var b : f32 = a[1];
@@ -88,7 +88,7 @@
 )";
 
   auto* expect = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
 fn f() -> void {
   var b : f32 = a[1];
@@ -102,9 +102,9 @@
 
 TEST_F(BoundArrayAccessorsTest, Array_Idx_Expr) {
   auto* src = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a[c + 2 - 3];
@@ -112,9 +112,9 @@
 )";
 
   auto* expect = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
@@ -128,7 +128,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Array_Idx_Negative) {
   auto* src = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
 fn f() -> void {
   var b : f32 = a[-1];
@@ -136,7 +136,7 @@
 )";
 
   auto* expect = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
 fn f() -> void {
   var b : f32 = a[0];
@@ -150,7 +150,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Array_Idx_OutOfBounds) {
   auto* src = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
 fn f() -> void {
   var b : f32 = a[3];
@@ -158,7 +158,7 @@
 )";
 
   auto* expect = R"(
-var a : array<f32, 3>;
+var<in> a : array<f32, 3>;
 
 fn f() -> void {
   var b : f32 = a[2];
@@ -172,7 +172,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Vector_Idx_Scalar) {
   auto* src = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
 fn f() -> void {
   var b : f32 = a[1];
@@ -180,7 +180,7 @@
 )";
 
   auto* expect = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
 fn f() -> void {
   var b : f32 = a[1];
@@ -194,9 +194,9 @@
 
 TEST_F(BoundArrayAccessorsTest, Vector_Idx_Expr) {
   auto* src = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a[c + 2 - 3];
@@ -204,9 +204,9 @@
 )";
 
   auto* expect = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
@@ -220,7 +220,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Vector_Swizzle_Idx_Scalar) {
   auto* src = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
 fn f() -> void {
   var b : f32 = a.xy[2];
@@ -228,7 +228,7 @@
 )";
 
   auto* expect = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
 fn f() -> void {
   var b : f32 = a.xy[1];
@@ -242,9 +242,9 @@
 
 TEST_F(BoundArrayAccessorsTest, Vector_Swizzle_Idx_Var) {
   auto* src = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a.xy[c];
@@ -252,9 +252,9 @@
 )";
 
   auto* expect = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a.xy[min(u32(c), 1u)];
@@ -267,9 +267,9 @@
 }
 TEST_F(BoundArrayAccessorsTest, Vector_Swizzle_Idx_Expr) {
   auto* src = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a.xy[c + 2 - 3];
@@ -277,9 +277,9 @@
 )";
 
   auto* expect = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)];
@@ -293,7 +293,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Vector_Idx_Negative) {
   auto* src = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
 fn f() -> void {
   var b : f32 = a[-1];
@@ -301,7 +301,7 @@
 )";
 
   auto* expect = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
 fn f() -> void {
   var b : f32 = a[0];
@@ -315,7 +315,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Vector_Idx_OutOfBounds) {
   auto* src = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
 fn f() -> void {
   var b : f32 = a[3];
@@ -323,7 +323,7 @@
 )";
 
   auto* expect = R"(
-var a : vec3<f32>;
+var<in> a : vec3<f32>;
 
 fn f() -> void {
   var b : f32 = a[2];
@@ -337,7 +337,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Scalar) {
   auto* src = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[2][1];
@@ -345,7 +345,7 @@
 )";
 
   auto* expect = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[2][1];
@@ -359,9 +359,9 @@
 
 TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Column) {
   auto* src = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a[c + 2 - 3][1];
@@ -369,9 +369,9 @@
 )";
 
   auto* expect = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1];
@@ -385,9 +385,9 @@
 
 TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Row) {
   auto* src = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a[1][c + 2 - 3];
@@ -395,9 +395,9 @@
 )";
 
   auto* expect = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
-var c : i32;
+var<in> c : i32;
 
 fn f() -> void {
   var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)];
@@ -411,7 +411,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Negative_Column) {
   auto* src = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[-1][1];
@@ -419,7 +419,7 @@
 )";
 
   auto* expect = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[0][1];
@@ -433,7 +433,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Negative_Row) {
   auto* src = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[2][-1];
@@ -441,7 +441,7 @@
 )";
 
   auto* expect = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[2][0];
@@ -455,7 +455,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Matrix_Idx_OutOfBounds_Column) {
   auto* src = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[5][1];
@@ -463,7 +463,7 @@
 )";
 
   auto* expect = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[2][1];
@@ -477,7 +477,7 @@
 
 TEST_F(BoundArrayAccessorsTest, Matrix_Idx_OutOfBounds_Row) {
   auto* src = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[2][5];
@@ -485,7 +485,7 @@
 )";
 
   auto* expect = R"(
-var a : mat3x2<f32>;
+var<in> a : mat3x2<f32>;
 
 fn f() -> void {
   var b : f32 = a[2][1];
@@ -540,7 +540,7 @@
   a : f32;
   b : array<f32>;
 };
-var s : S;
+var<in> s : S;
 
 fn f() -> void {
   var d : f32 = s.b[25];
@@ -554,7 +554,7 @@
   b : array<f32>;
 };
 
-var s : S;
+var<in> s : S;
 
 fn f() -> void {
   var d : f32 = s.b[min(u32(25), (arrayLength(s.b) - 1u))];
diff --git a/src/validator/validator_decoration_test.cc b/src/validator/validator_decoration_test.cc
index a48ff9f..069eb8a 100644
--- a/src/validator/validator_decoration_test.cc
+++ b/src/validator/validator_decoration_test.cc
@@ -121,38 +121,5 @@
                     DecorationTestParams{DecorationKind::kStructBlock, false},
                     DecorationTestParams{DecorationKind::kWorkgroup, true}));
 
-using VariableDecorationTest = ValidatorDecorationsTestWithParams;
-TEST_P(VariableDecorationTest, Decoration_IsValid) {
-  auto params = GetParam();
-
-  auto* var = Global("a", ty.f32(), ast::StorageClass::kInput, nullptr,
-                     ast::DecorationList{createDecoration(*this, params.kind)});
-
-  ValidatorImpl& v = Build();
-
-  if (params.should_pass) {
-    EXPECT_TRUE(v.ValidateGlobalVariable(var));
-  } else {
-    EXPECT_FALSE(v.ValidateGlobalVariable(var));
-    EXPECT_EQ(v.error(), "decoration is not valid for variables");
-  }
-}
-INSTANTIATE_TEST_SUITE_P(
-    ValidatorTest,
-    VariableDecorationTest,
-    testing::Values(DecorationTestParams{DecorationKind::kAccess, false},
-                    DecorationTestParams{DecorationKind::kAlign, false},
-                    DecorationTestParams{DecorationKind::kBinding, true},
-                    DecorationTestParams{DecorationKind::kBuiltin, true},
-                    DecorationTestParams{DecorationKind::kConstantId, true},
-                    DecorationTestParams{DecorationKind::kGroup, true},
-                    DecorationTestParams{DecorationKind::kLocation, true},
-                    DecorationTestParams{DecorationKind::kOffset, false},
-                    DecorationTestParams{DecorationKind::kSize, false},
-                    DecorationTestParams{DecorationKind::kStage, false},
-                    DecorationTestParams{DecorationKind::kStride, false},
-                    DecorationTestParams{DecorationKind::kStructBlock, false},
-                    DecorationTestParams{DecorationKind::kWorkgroup, false}));
-
 }  // namespace
 }  // namespace tint
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index 4952995..1e4558a 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -97,42 +97,6 @@
 }
 
 bool ValidatorImpl::ValidateGlobalVariable(const ast::Variable* var) {
-  auto* sem = program_->Sem().Get(var);
-  if (!sem) {
-    add_error(var->source(), "no semantic information for variable '" +
-                                 program_->Symbols().NameFor(var->symbol()) +
-                                 "'");
-    return false;
-  }
-
-  if (variable_stack_.has(var->symbol())) {
-    add_error(var->source(), "v-0011",
-              "redeclared global identifier '" +
-                  program_->Symbols().NameFor(var->symbol()) + "'");
-    return false;
-  }
-  if (!var->is_const() && sem->StorageClass() == ast::StorageClass::kNone) {
-    add_error(var->source(), "v-0022",
-              "global variables must have a storage class");
-    return false;
-  }
-  if (var->is_const() && !(sem->StorageClass() == ast::StorageClass::kNone)) {
-    add_error(var->source(), "v-global01",
-              "global constants shouldn't have a storage class");
-    return false;
-  }
-
-  for (auto* deco : var->decorations()) {
-    if (!(deco->Is<ast::BindingDecoration>() ||
-          deco->Is<ast::BuiltinDecoration>() ||
-          deco->Is<ast::ConstantIdDecoration>() ||
-          deco->Is<ast::GroupDecoration>() ||
-          deco->Is<ast::LocationDecoration>())) {
-      add_error(deco->source(), "decoration is not valid for variables");
-      return false;
-    }
-  }
-
   variable_stack_.set_global(var->symbol(), var);
   return true;
 }
diff --git a/src/validator/validator_test.cc b/src/validator/validator_test.cc
index d496233..c13a8f7 100644
--- a/src/validator/validator_test.cc
+++ b/src/validator/validator_test.cc
@@ -21,105 +21,6 @@
 
 class ValidatorTest : public ValidatorTestHelper, public testing::Test {};
 
-
-TEST_F(ValidatorTest, GlobalVariableWithStorageClass_Pass) {
-  // var<in> global_var: f32;
-  auto* var = Global(Source{Source::Location{12, 34}}, "global_var", ty.f32(),
-                     ast::StorageClass::kInput);
-
-  ValidatorImpl& v = Build();
-
-  EXPECT_TRUE(v.ValidateGlobalVariable(var)) << v.error();
-}
-
-TEST_F(ValidatorTest, GlobalVariableNoStorageClass_Fail) {
-  // var global_var: f32;
-  Global(Source{Source::Location{12, 34}}, "global_var", ty.f32(),
-         ast::StorageClass::kNone);
-
-  ValidatorImpl& v = Build();
-
-  EXPECT_FALSE(v.Validate());
-  EXPECT_EQ(v.error(),
-            "12:34 v-0022: global variables must have a storage class");
-}
-
-TEST_F(ValidatorTest, GlobalConstantWithStorageClass_Fail) {
-  // const<in> global_var: f32;
-  AST().AddGlobalVariable(create<ast::Variable>(
-      Source{Source::Location{12, 34}}, Symbols().Register("global_var"),
-      ast::StorageClass::kInput, ty.f32(), true, nullptr,
-      ast::DecorationList{}));
-
-  ValidatorImpl& v = Build();
-
-  EXPECT_FALSE(v.Validate());
-  EXPECT_EQ(
-      v.error(),
-      "12:34 v-global01: global constants shouldn't have a storage class");
-}
-
-TEST_F(ValidatorTest, GlobalConstNoStorageClass_Pass) {
-  // const global_var: f32;
-  GlobalConst(Source{Source::Location{12, 34}}, "global_var", ty.f32());
-
-  ValidatorImpl& v = Build();
-
-  EXPECT_TRUE(v.Validate()) << v.error();
-}
-
-TEST_F(ValidatorTest, GlobalVariableUnique_Pass) {
-  // var global_var0 : f32 = 0.1;
-  // var global_var1 : i32 = 0;
-  auto* var0 =
-      Global("global_var0", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1f));
-
-  auto* var1 = Global(Source{Source::Location{12, 34}}, "global_var1", ty.f32(),
-                      ast::StorageClass::kPrivate, Expr(0));
-
-  ValidatorImpl& v = Build();
-
-  EXPECT_TRUE(v.ValidateGlobalVariable(var0)) << v.error();
-  EXPECT_TRUE(v.ValidateGlobalVariable(var1)) << v.error();
-}
-
-TEST_F(ValidatorTest, GlobalVariableNotUnique_Fail) {
-  // var global_var : f32 = 0.1;
-  // var global_var : i32 = 0;
-  Global("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1f));
-
-  Global(Source{Source::Location{12, 34}}, "global_var", ty.i32(),
-         ast::StorageClass::kPrivate, Expr(0));
-
-  ValidatorImpl& v = Build();
-
-  EXPECT_FALSE(v.Validate());
-  EXPECT_EQ(v.error(),
-            "12:34 v-0011: redeclared global identifier 'global_var'");
-}
-
-TEST_F(ValidatorTest, GlobalVariableFunctionVariableNotUnique_Pass) {
-  // fn my_func -> void {
-  //   var a: f32 = 2.0;
-  // }
-  // var a: f32 = 2.1;
-
-  auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
-
-  Func("my_func", ast::VariableList{}, ty.void_(),
-       ast::StatementList{
-           create<ast::VariableDeclStatement>(var),
-       },
-       ast::DecorationList{
-           create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
-
-  Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
-
-  ValidatorImpl& v = Build();
-
-  EXPECT_TRUE(v.Validate()) << v.error();
-}
-
 TEST_F(ValidatorTest, VariableDeclNoConstructor_Pass) {
   // {
   // var a :i32;
diff --git a/src/writer/hlsl/generator_impl_binary_test.cc b/src/writer/hlsl/generator_impl_binary_test.cc
index f5941f0..deb76a0 100644
--- a/src/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/writer/hlsl/generator_impl_binary_test.cc
@@ -477,10 +477,10 @@
 
   Func("foo", ast::VariableList{}, ty.void_(), ast::StatementList{},
        ast::DecorationList{});
-  Global("a", ty.bool_(), ast::StorageClass::kNone);
-  Global("b", ty.bool_(), ast::StorageClass::kNone);
-  Global("c", ty.bool_(), ast::StorageClass::kNone);
-  Global("d", ty.bool_(), ast::StorageClass::kNone);
+  Global("a", ty.bool_(), ast::StorageClass::kInput);
+  Global("b", ty.bool_(), ast::StorageClass::kInput);
+  Global("c", ty.bool_(), ast::StorageClass::kInput);
+  Global("d", ty.bool_(), ast::StorageClass::kInput);
 
   ast::ExpressionList params;
   params.push_back(create<ast::BinaryExpression>(ast::BinaryOp::kLogicalAnd,
diff --git a/src/writer/hlsl/generator_impl_call_test.cc b/src/writer/hlsl/generator_impl_call_test.cc
index ea505ee..189e44e 100644
--- a/src/writer/hlsl/generator_impl_call_test.cc
+++ b/src/writer/hlsl/generator_impl_call_test.cc
@@ -38,8 +38,8 @@
 TEST_F(HlslGeneratorImplTest_Call, EmitExpression_Call_WithParams) {
   Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{},
        ast::DecorationList{});
-  Global("param1", ty.f32(), ast::StorageClass::kNone);
-  Global("param2", ty.f32(), ast::StorageClass::kNone);
+  Global("param1", ty.f32(), ast::StorageClass::kInput);
+  Global("param2", ty.f32(), ast::StorageClass::kInput);
 
   auto* call = Call("my_func", "param1", "param2");
   WrapInFunction(call);
@@ -53,8 +53,8 @@
 TEST_F(HlslGeneratorImplTest_Call, EmitStatement_Call) {
   Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{},
        ast::DecorationList{});
-  Global("param1", ty.f32(), ast::StorageClass::kNone);
-  Global("param2", ty.f32(), ast::StorageClass::kNone);
+  Global("param1", ty.f32(), ast::StorageClass::kInput);
+  Global("param2", ty.f32(), ast::StorageClass::kInput);
 
   auto* call = create<ast::CallStatement>(Call("my_func", "param1", "param2"));
   WrapInFunction(call);
diff --git a/src/writer/hlsl/generator_impl_loop_test.cc b/src/writer/hlsl/generator_impl_loop_test.cc
index ff38720..62b5278 100644
--- a/src/writer/hlsl/generator_impl_loop_test.cc
+++ b/src/writer/hlsl/generator_impl_loop_test.cc
@@ -73,8 +73,8 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Loop, Emit_LoopNestedWithContinuing) {
-  Global("lhs", ty.f32(), ast::StorageClass::kNone);
-  Global("rhs", ty.f32(), ast::StorageClass::kNone);
+  Global("lhs", ty.f32(), ast::StorageClass::kInput);
+  Global("rhs", ty.f32(), ast::StorageClass::kInput);
 
   auto* body = create<ast::BlockStatement>(ast::StatementList{
       create<ast::DiscardStatement>(),
@@ -149,7 +149,7 @@
   //   }
   // }
 
-  Global("rhs", ty.f32(), ast::StorageClass::kNone);
+  Global("rhs", ty.f32(), ast::StorageClass::kInput);
 
   auto* var = Var("lhs", ty.f32(), ast::StorageClass::kFunction, Expr(2.4f));
 
diff --git a/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc b/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
index 495763d..b5aacb3 100644
--- a/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
@@ -23,7 +23,7 @@
 using HlslGeneratorImplTest_VariableDecl = TestHelper;
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement) {
-  auto* var = Global("a", ty.f32(), ast::StorageClass::kNone);
+  auto* var = Global("a", ty.f32(), ast::StorageClass::kInput);
 
   auto* stmt = create<ast::VariableDeclStatement>(var);
 
@@ -50,7 +50,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Array) {
-  auto* var = Global("a", ty.array<f32, 5>(), ast::StorageClass::kNone);
+  auto* var = Global("a", ty.array<f32, 5>(), ast::StorageClass::kInput);
 
   auto* stmt = create<ast::VariableDeclStatement>(var);
 
@@ -91,7 +91,7 @@
 
 TEST_F(HlslGeneratorImplTest_VariableDecl,
        Emit_VariableDeclStatement_Initializer_Private) {
-  Global("initializer", ty.f32(), ast::StorageClass::kNone);
+  Global("initializer", ty.f32(), ast::StorageClass::kInput);
   auto* var =
       Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
 
diff --git a/src/writer/msl/generator_impl_call_test.cc b/src/writer/msl/generator_impl_call_test.cc
index 03780de..bd015b6 100644
--- a/src/writer/msl/generator_impl_call_test.cc
+++ b/src/writer/msl/generator_impl_call_test.cc
@@ -38,8 +38,8 @@
 TEST_F(MslGeneratorImplTest, EmitExpression_Call_WithParams) {
   Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{},
        ast::DecorationList{});
-  Global("param1", ty.f32(), ast::StorageClass::kNone);
-  Global("param2", ty.f32(), ast::StorageClass::kNone);
+  Global("param1", ty.f32(), ast::StorageClass::kInput);
+  Global("param2", ty.f32(), ast::StorageClass::kInput);
 
   auto* call = Call("my_func", "param1", "param2");
   WrapInFunction(call);
@@ -53,8 +53,8 @@
 TEST_F(MslGeneratorImplTest, EmitStatement_Call) {
   Func("my_func", ast::VariableList{}, ty.void_(), ast::StatementList{},
        ast::DecorationList{});
-  Global("param1", ty.f32(), ast::StorageClass::kNone);
-  Global("param2", ty.f32(), ast::StorageClass::kNone);
+  Global("param1", ty.f32(), ast::StorageClass::kInput);
+  Global("param2", ty.f32(), ast::StorageClass::kInput);
 
   auto* call = Call("my_func", "param1", "param2");
   auto* stmt = create<ast::CallStatement>(call);
diff --git a/src/writer/msl/generator_impl_loop_test.cc b/src/writer/msl/generator_impl_loop_test.cc
index da4e36d..a7c638b 100644
--- a/src/writer/msl/generator_impl_loop_test.cc
+++ b/src/writer/msl/generator_impl_loop_test.cc
@@ -71,8 +71,8 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_LoopNestedWithContinuing) {
-  Global("lhs", ty.f32(), ast::StorageClass::kNone);
-  Global("rhs", ty.f32(), ast::StorageClass::kNone);
+  Global("lhs", ty.f32(), ast::StorageClass::kInput);
+  Global("rhs", ty.f32(), ast::StorageClass::kInput);
 
   auto* body = create<ast::BlockStatement>(ast::StatementList{
       create<ast::DiscardStatement>(),
@@ -144,7 +144,7 @@
   //   }
   // }
 
-  Global("rhs", ty.f32(), ast::StorageClass::kNone);
+  Global("rhs", ty.f32(), ast::StorageClass::kInput);
 
   auto* var = Var("lhs", ty.f32(), ast::StorageClass::kFunction, Expr(2.4f));
 
diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc
index 2540a8f..51b0325 100644
--- a/src/writer/msl/generator_impl_type_test.cc
+++ b/src/writer/msl/generator_impl_type_test.cc
@@ -654,7 +654,7 @@
       create<type::AccessControl>(params.ro ? ast::AccessControl::kReadOnly
                                             : ast::AccessControl::kWriteOnly,
                                   s);
-  Global("test_var", ac, ast::StorageClass::kNone);
+  Global("test_var", ac, ast::StorageClass::kInput);
 
   GeneratorImpl& gen = Build();
 
diff --git a/src/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/writer/msl/generator_impl_variable_decl_statement_test.cc
index e67fcdb..799555a 100644
--- a/src/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -122,7 +122,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_Private) {
-  Global("initializer", ty.f32(), ast::StorageClass::kNone);
+  Global("initializer", ty.f32(), ast::StorageClass::kInput);
   auto* var =
       Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
   auto* stmt = create<ast::VariableDeclStatement>(var);
diff --git a/src/writer/spirv/builder_function_variable_test.cc b/src/writer/spirv/builder_function_variable_test.cc
index 18c1227..8d87df3 100644
--- a/src/writer/spirv/builder_function_variable_test.cc
+++ b/src/writer/spirv/builder_function_variable_test.cc
@@ -23,7 +23,7 @@
 using BuilderTest = TestHelper;
 
 TEST_F(BuilderTest, FunctionVar_NoStorageClass) {
-  auto* v = Global("var", ty.f32(), ast::StorageClass::kNone);
+  auto* v = Global("var", ty.f32(), ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc
index bc782e1..f3f3217 100644
--- a/src/writer/spirv/builder_global_variable_test.cc
+++ b/src/writer/spirv/builder_global_variable_test.cc
@@ -24,21 +24,6 @@
 
 using BuilderTest = TestHelper;
 
-TEST_F(BuilderTest, GlobalVar_NoStorageClass) {
-  auto* v = Global("var", ty.f32(), ast::StorageClass::kNone);
-
-  spirv::Builder& b = Build();
-
-  EXPECT_TRUE(b.GenerateGlobalVariable(v)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "var"
-)");
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
-%2 = OpTypePointer Private %3
-%4 = OpConstantNull %3
-%1 = OpVariable %2 Private %4
-)");
-}
-
 TEST_F(BuilderTest, GlobalVar_WithStorageClass) {
   auto* v = Global("var", ty.f32(), ast::StorageClass::kOutput);
 
@@ -218,7 +203,7 @@
 }
 
 TEST_F(BuilderTest, GlobalVar_ConstantId_Bool) {
-  auto* v = Global("var", ty.bool_(), ast::StorageClass::kNone, Expr(true),
+  auto* v = Global("var", ty.bool_(), ast::StorageClass::kInput, Expr(true),
                    ast::DecorationList{
                        create<ast::ConstantIdDecoration>(1200),
                    });
@@ -232,13 +217,13 @@
 )");
   EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
 %2 = OpSpecConstantTrue %1
-%4 = OpTypePointer Private %1
-%3 = OpVariable %4 Private %2
+%4 = OpTypePointer Input %1
+%3 = OpVariable %4 Input %2
 )");
 }
 
 TEST_F(BuilderTest, GlobalVar_ConstantId_Bool_NoConstructor) {
-  auto* v = Global("var", ty.bool_(), ast::StorageClass::kNone, nullptr,
+  auto* v = Global("var", ty.bool_(), ast::StorageClass::kInput, nullptr,
                    ast::DecorationList{
                        create<ast::ConstantIdDecoration>(1200),
                    });
@@ -251,14 +236,14 @@
   EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %4 SpecId 1200
 )");
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeBool
-%2 = OpTypePointer Private %3
+%2 = OpTypePointer Input %3
 %4 = OpSpecConstantFalse %3
-%1 = OpVariable %2 Private %4
+%1 = OpVariable %2 Input %4
 )");
 }
 
 TEST_F(BuilderTest, GlobalVar_ConstantId_Scalar) {
-  auto* v = Global("var", ty.f32(), ast::StorageClass::kNone, Expr(2.f),
+  auto* v = Global("var", ty.f32(), ast::StorageClass::kInput, Expr(2.f),
                    ast::DecorationList{
                        create<ast::ConstantIdDecoration>(0),
                    });
@@ -272,13 +257,13 @@
 )");
   EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
 %2 = OpSpecConstant %1 2
-%4 = OpTypePointer Private %1
-%3 = OpVariable %4 Private %2
+%4 = OpTypePointer Input %1
+%3 = OpVariable %4 Input %2
 )");
 }
 
 TEST_F(BuilderTest, GlobalVar_ConstantId_Scalar_F32_NoConstructor) {
-  auto* v = Global("var", ty.f32(), ast::StorageClass::kNone, nullptr,
+  auto* v = Global("var", ty.f32(), ast::StorageClass::kInput, nullptr,
                    ast::DecorationList{
                        create<ast::ConstantIdDecoration>(0),
                    });
@@ -291,14 +276,14 @@
   EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %4 SpecId 0
 )");
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
-%2 = OpTypePointer Private %3
+%2 = OpTypePointer Input %3
 %4 = OpSpecConstant %3 0
-%1 = OpVariable %2 Private %4
+%1 = OpVariable %2 Input %4
 )");
 }
 
 TEST_F(BuilderTest, GlobalVar_ConstantId_Scalar_I32_NoConstructor) {
-  auto* v = Global("var", ty.i32(), ast::StorageClass::kNone, nullptr,
+  auto* v = Global("var", ty.i32(), ast::StorageClass::kInput, nullptr,
                    ast::DecorationList{
                        create<ast::ConstantIdDecoration>(0),
                    });
@@ -311,14 +296,14 @@
   EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %4 SpecId 0
 )");
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
-%2 = OpTypePointer Private %3
+%2 = OpTypePointer Input %3
 %4 = OpSpecConstant %3 0
-%1 = OpVariable %2 Private %4
+%1 = OpVariable %2 Input %4
 )");
 }
 
 TEST_F(BuilderTest, GlobalVar_ConstantId_Scalar_U32_NoConstructor) {
-  auto* v = Global("var", ty.u32(), ast::StorageClass::kNone, nullptr,
+  auto* v = Global("var", ty.u32(), ast::StorageClass::kInput, nullptr,
                    ast::DecorationList{
                        create<ast::ConstantIdDecoration>(0),
                    });
@@ -331,9 +316,9 @@
   EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %4 SpecId 0
 )");
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 0
-%2 = OpTypePointer Private %3
+%2 = OpTypePointer Input %3
 %4 = OpSpecConstant %3 0
-%1 = OpVariable %2 Private %4
+%1 = OpVariable %2 Input %4
 )");
 }
 
@@ -545,7 +530,7 @@
       type::StorageTexture::SubtypeFor(type::ImageFormat::kR32Uint, Types());
   auto* type = create<type::StorageTexture>(
       type::TextureDimension::k2d, type::ImageFormat::kR32Uint, subtype);
-  Global("test_var", type, ast::StorageClass::kNone);
+  Global("test_var", type, ast::StorageClass::kInput);
 
   auto* ac = create<type::AccessControl>(ast::AccessControl::kWriteOnly, type);
 
@@ -575,7 +560,7 @@
   auto* st = create<type::StorageTexture>(type::TextureDimension::k2d,
                                           type::ImageFormat::kR32Uint, subtype);
 
-  Global("test_var", st, ast::StorageClass::kNone);
+  Global("test_var", st, ast::StorageClass::kInput);
 
   auto* type_a = create<type::AccessControl>(ast::AccessControl::kReadOnly, st);
   auto* var_a = Global("a", type_a, ast::StorageClass::kUniformConstant);
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index ba07575..47fd65b 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -373,9 +373,9 @@
   type::Sampler s(type::SamplerKind::kComparisonSampler);
   type::DepthTexture t(type::TextureDimension::k2d);
 
-  auto* tex = Global("texture", &t, ast::StorageClass::kNone);
+  auto* tex = Global("texture", &t, ast::StorageClass::kInput);
 
-  auto* sampler = Global("sampler", &s, ast::StorageClass::kNone);
+  auto* sampler = Global("sampler", &s, ast::StorageClass::kInput);
 
   auto* expr1 = Call("textureSampleCompare", "texture", "sampler",
                      vec2<f32>(1.0f, 2.0f), 2.0f);
@@ -397,11 +397,11 @@
 
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
 %3 = OpTypeImage %4 2D 1 0 0 1 Unknown
-%2 = OpTypePointer Private %3
-%1 = OpVariable %2 Private
+%2 = OpTypePointer Input %3
+%1 = OpVariable %2 Input
 %7 = OpTypeSampler
-%6 = OpTypePointer Private %7
-%5 = OpVariable %6 Private
+%6 = OpTypePointer Input %7
+%5 = OpVariable %6 Input
 %11 = OpTypeSampledImage %3
 %13 = OpTypeVector %4 2
 %14 = OpConstant %4 1
diff --git a/src/writer/spirv/builder_intrinsic_texture_test.cc b/src/writer/spirv/builder_intrinsic_texture_test.cc
index 328de28..ea77938 100644
--- a/src/writer/spirv/builder_intrinsic_texture_test.cc
+++ b/src/writer/spirv/builder_intrinsic_texture_test.cc
@@ -3646,8 +3646,6 @@
 
   auto* texture = param.buildTextureVariable(this);
   auto* sampler = param.buildSamplerVariable(this);
-  AST().AddGlobalVariable(texture);
-  AST().AddGlobalVariable(sampler);
 
   auto* call =
       create<ast::CallExpression>(Expr(param.function), param.args(this));
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
index 6ebb64d..09541b5 100644
--- a/src/writer/spirv/builder_type_test.cc
+++ b/src/writer/spirv/builder_type_test.cc
@@ -832,7 +832,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k1d,
                                          type::ImageFormat::kR16Float, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -854,7 +854,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k1d,
                                          type::ImageFormat::kR8Snorm, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -876,7 +876,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k1d,
                                          type::ImageFormat::kR8Unorm, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -898,7 +898,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k1d,
                                          type::ImageFormat::kR8Uint, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -915,7 +915,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k1d,
                                          type::ImageFormat::kR8Sint, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -932,7 +932,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k2d,
                                          type::ImageFormat::kR16Float, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -949,7 +949,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k2dArray,
                                          type::ImageFormat::kR16Float, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -966,7 +966,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k3d,
                                          type::ImageFormat::kR16Float, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -984,7 +984,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k2d,
                                          type::ImageFormat::kR32Float, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -1002,7 +1002,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k2d,
                                          type::ImageFormat::kR32Sint, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
@@ -1020,7 +1020,7 @@
   auto* s = create<type::StorageTexture>(type::TextureDimension::k2d,
                                          type::ImageFormat::kR32Uint, subtype);
 
-  Global("test_var", s, ast::StorageClass::kNone);
+  Global("test_var", s, ast::StorageClass::kInput);
 
   spirv::Builder& b = Build();
 
diff --git a/src/writer/wgsl/generator_impl_test.cc b/src/writer/wgsl/generator_impl_test.cc
index 77c5a1a..5464514 100644
--- a/src/writer/wgsl/generator_impl_test.cc
+++ b/src/writer/wgsl/generator_impl_test.cc
@@ -46,7 +46,7 @@
 TEST_P(WgslBuiltinConversionTest, Emit) {
   auto params = GetParam();
 
-  auto* var = Global("a", ty.f32(), ast::StorageClass::kNone, nullptr,
+  auto* var = Global("a", ty.f32(), ast::StorageClass::kInput, nullptr,
                      ast::DecorationList{
                          create<ast::BuiltinDecoration>(params.builtin),
                      });
diff --git a/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc b/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
index a91c5e1..545de83 100644
--- a/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
@@ -25,7 +25,7 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement) {
-  auto* var = Global("a", ty.f32(), ast::StorageClass::kNone);
+  auto* var = Global("a", ty.f32(), ast::StorageClass::kInput);
 
   auto* stmt = create<ast::VariableDeclStatement>(var);
   WrapInFunction(stmt);
@@ -35,7 +35,7 @@
   gen.increment_indent();
 
   ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
-  EXPECT_EQ(gen.result(), "  var a : f32;\n");
+  EXPECT_EQ(gen.result(), "  var<in> a : f32;\n");
 }
 
 TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Function) {
diff --git a/src/writer/wgsl/generator_impl_variable_test.cc b/src/writer/wgsl/generator_impl_variable_test.cc
index 87660fd..d091291 100644
--- a/src/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/writer/wgsl/generator_impl_variable_test.cc
@@ -23,12 +23,12 @@
 using WgslGeneratorImplTest = TestHelper;
 
 TEST_F(WgslGeneratorImplTest, EmitVariable) {
-  auto* v = Global("a", ty.f32(), ast::StorageClass::kNone);
+  auto* v = Global("a", ty.f32(), ast::StorageClass::kInput);
 
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
-  EXPECT_EQ(gen.result(), R"(var a : f32;
+  EXPECT_EQ(gen.result(), R"(var<in> a : f32;
 )");
 }
 
@@ -43,7 +43,7 @@
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated) {
-  auto* v = Global("a", ty.f32(), ast::StorageClass::kNone, nullptr,
+  auto* v = Global("a", ty.f32(), ast::StorageClass::kInput, nullptr,
                    ast::DecorationList{
                        create<ast::LocationDecoration>(2),
                    });
@@ -51,12 +51,12 @@
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
-  EXPECT_EQ(gen.result(), R"([[location(2)]] var a : f32;
+  EXPECT_EQ(gen.result(), R"([[location(2)]] var<in> a : f32;
 )");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated_Multiple) {
-  auto* v = Global("a", ty.f32(), ast::StorageClass::kNone, nullptr,
+  auto* v = Global("a", ty.f32(), ast::StorageClass::kInput, nullptr,
                    ast::DecorationList{
                        create<ast::BuiltinDecoration>(ast::Builtin::kPosition),
                        create<ast::BindingDecoration>(0),
@@ -70,18 +70,18 @@
   ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
   EXPECT_EQ(
       gen.result(),
-      R"([[builtin(position), binding(0), group(1), location(2), constant_id(42)]] var a : f32;
+      R"([[builtin(position), binding(0), group(1), location(2), constant_id(42)]] var<in> a : f32;
 )");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitVariable_Constructor) {
-  auto* v = Global("a", ty.f32(), ast::StorageClass::kNone, Expr(1.0f));
+  auto* v = Global("a", ty.f32(), ast::StorageClass::kInput, Expr(1.0f));
   WrapInFunction(Decl(v));
 
   GeneratorImpl& gen = Build();
 
   ASSERT_TRUE(gen.EmitVariable(v)) << gen.error();
-  EXPECT_EQ(gen.result(), R"(var a : f32 = 1.0;
+  EXPECT_EQ(gen.result(), R"(var<in> a : f32 = 1.0;
 )");
 }