Resolver: Validate <storage> var types

https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
Variables in the storage storage class and variables with a storage
texture type must have an access attribute applied to the store type.

https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
A variable in the storage storage class is a storage buffer variable. Its
store type must be a host-shareable structure type with block attribute,
satisfying the storage class constraints.

Fixup tests, including those that were producing warnings about `var <in>`

The WGSL writer seems to want to put a newline after every decoration block, leading to some ugly output. I'll fix this as a separate change.

Fixes: tint:531
Fixes: tint:692
Change-Id: If09d987477247ab4a7c635f6ee6e616a06061515
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/47763
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/resolver/storage_class_validation_test.cc b/src/resolver/storage_class_validation_test.cc
new file mode 100644
index 0000000..0fde1a1
--- /dev/null
+++ b/src/resolver/storage_class_validation_test.cc
@@ -0,0 +1,123 @@
+// Copyright 2021 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/resolver/resolver.h"
+
+#include "gmock/gmock.h"
+#include "src/resolver/resolver_test_helper.h"
+#include "src/semantic/struct.h"
+#include "src/type/access_control_type.h"
+
+namespace tint {
+namespace resolver {
+namespace {
+
+using ResolverStorageClassValidationTest = ResolverTest;
+
+TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
+  // var g : f32;
+  Global(Source{{12, 34}}, "g", 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(ResolverStorageClassValidationTest, Bool) {
+  // var<storage> g : bool;
+  Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage);
+
+  ASSERT_FALSE(r()->Resolve());
+
+  EXPECT_EQ(
+      r()->error(),
+      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+}
+
+TEST_F(ResolverStorageClassValidationTest, Pointer) {
+  // var<storage> g : ptr<i32, input>;
+  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: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+}
+
+TEST_F(ResolverStorageClassValidationTest, Array) {
+  // var<storage> g : [[access(read)]] array<S, 3>;
+  auto* s = Structure("S", {Member("a", ty.f32())});
+  auto* a = ty.array(s, 3);
+  auto* ac = ty.access(ast::AccessControl::kReadOnly, a);
+  Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kStorage);
+
+  ASSERT_FALSE(r()->Resolve());
+
+  EXPECT_EQ(
+      r()->error(),
+      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+}
+
+TEST_F(ResolverStorageClassValidationTest, BoolAlias) {
+  // type a = bool;
+  // var<storage> g : [[access(read)]] a;
+  auto* a = ty.alias("a", ty.bool_());
+  Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
+
+  ASSERT_FALSE(r()->Resolve());
+
+  EXPECT_EQ(
+      r()->error(),
+      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+}
+
+TEST_F(ResolverStorageClassValidationTest, NoAccessControl) {
+  // var<storage> g : S;
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+  Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage);
+
+  ASSERT_FALSE(r()->Resolve());
+
+  EXPECT_EQ(
+      r()->error(),
+      R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
+}
+
+TEST_F(ResolverStorageClassValidationTest, NoError_Basic) {
+  // var<storage> g : [[access(read)]] S;
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+  auto* a = ty.access(ast::AccessControl::kReadOnly, s);
+  Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
+
+  ASSERT_TRUE(r()->Resolve());
+}
+
+TEST_F(ResolverStorageClassValidationTest, NoError_Aliases) {
+  // type a1 = S;
+  // type a2 = [[access(read)]] a1;
+  // var<storage> g : a2;
+  auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+  auto* a1 = ty.alias("a1", s);
+  auto* ac = ty.access(ast::AccessControl::kReadOnly, a1);
+  auto* a2 = ty.alias("a2", ac);
+  Global(Source{{56, 78}}, "g", a2, ast::StorageClass::kStorage);
+
+  ASSERT_TRUE(r()->Resolve());
+}
+
+}  // namespace
+}  // namespace resolver
+}  // namespace tint