tint/resolver: Allow array sizes to be unnamed override-expressions
I got the rules around this wrong. This should be allowed, but the array types cannot compare equal if they are unnamed override-expressions.
Fixed tint:1737
Change-Id: I83dc49703eed015e9c183e804474886da5dad7b9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107685
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index ba6cfaa..3252829 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -2710,14 +2710,15 @@
return utils::Failure;
}
- // Note: If the array count is an 'override', but not a identifier expression, we do not return
- // here, but instead continue to the ConstantValue() check below.
- if (auto* user = count_sem->UnwrapMaterialize()->As<sem::VariableUser>()) {
- if (auto* global = user->Variable()->As<sem::GlobalVariable>()) {
- if (global->Declaration()->Is<ast::Override>()) {
- return sem::ArrayCount{sem::OverrideArrayCount{global}};
+ if (count_sem->Stage() == sem::EvaluationStage::kOverride) {
+ // array count is an override expression.
+ // Is the count a named 'override'?
+ if (auto* user = count_sem->UnwrapMaterialize()->As<sem::VariableUser>()) {
+ if (auto* global = user->Variable()->As<sem::GlobalVariable>()) {
+ return sem::ArrayCount{sem::NamedOverrideArrayCount{global}};
}
}
+ return sem::ArrayCount{sem::UnnamedOverrideArrayCount{count_sem}};
}
auto* count_val = count_sem->ConstantValue();
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index 935833a..13b1da1 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -486,8 +486,8 @@
EXPECT_EQ(ary->Count(), sem::ConstantArrayCount{10u});
}
-TEST_F(ResolverTest, ArraySize_Override) {
- // override size = 0;
+TEST_F(ResolverTest, ArraySize_NamedOverride) {
+ // override size = 10i;
// var<workgroup> a : array<f32, size>;
auto* override = Override("size", Expr(10_i));
auto* a = GlobalVar("a", ty.array(ty.f32(), Expr("size")), ast::AddressSpace::kWorkgroup);
@@ -500,11 +500,11 @@
auto* ary = ref->StoreType()->As<sem::Array>();
auto* sem_override = Sem().Get<sem::GlobalVariable>(override);
ASSERT_NE(sem_override, nullptr);
- EXPECT_EQ(ary->Count(), sem::OverrideArrayCount{sem_override});
+ EXPECT_EQ(ary->Count(), sem::NamedOverrideArrayCount{sem_override});
}
-TEST_F(ResolverTest, ArraySize_Override_Equivalence) {
- // override size = 0;
+TEST_F(ResolverTest, ArraySize_NamedOverride_Equivalence) {
+ // override size = 10i;
// var<workgroup> a : array<f32, size>;
// var<workgroup> b : array<f32, size>;
auto* override = Override("size", Expr(10_i));
@@ -525,11 +525,58 @@
auto* sem_override = Sem().Get<sem::GlobalVariable>(override);
ASSERT_NE(sem_override, nullptr);
- EXPECT_EQ(ary_a->Count(), sem::OverrideArrayCount{sem_override});
- EXPECT_EQ(ary_b->Count(), sem::OverrideArrayCount{sem_override});
+ EXPECT_EQ(ary_a->Count(), sem::NamedOverrideArrayCount{sem_override});
+ EXPECT_EQ(ary_b->Count(), sem::NamedOverrideArrayCount{sem_override});
EXPECT_EQ(ary_a, ary_b);
}
+TEST_F(ResolverTest, ArraySize_UnnamedOverride) {
+ // override size = 10i;
+ // var<workgroup> a : array<f32, size*2>;
+ auto* override = Override("size", Expr(10_i));
+ auto* cnt = Mul("size", 2_a);
+ auto* a = GlobalVar("a", ty.array(ty.f32(), cnt), ast::AddressSpace::kWorkgroup);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(a), nullptr);
+ auto* ref = TypeOf(a)->As<sem::Reference>();
+ ASSERT_NE(ref, nullptr);
+ auto* ary = ref->StoreType()->As<sem::Array>();
+ auto* sem_override = Sem().Get<sem::GlobalVariable>(override);
+ ASSERT_NE(sem_override, nullptr);
+ EXPECT_EQ(ary->Count(), sem::UnnamedOverrideArrayCount{Sem().Get(cnt)});
+}
+
+TEST_F(ResolverTest, ArraySize_UnamedOverride_Equivalence) {
+ // override size = 10i;
+ // var<workgroup> a : array<f32, size>;
+ // var<workgroup> b : array<f32, size>;
+ auto* override = Override("size", Expr(10_i));
+ auto* a_cnt = Mul("size", 2_a);
+ auto* b_cnt = Mul("size", 2_a);
+ auto* a = GlobalVar("a", ty.array(ty.f32(), a_cnt), ast::AddressSpace::kWorkgroup);
+ auto* b = GlobalVar("b", ty.array(ty.f32(), b_cnt), ast::AddressSpace::kWorkgroup);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(a), nullptr);
+ auto* ref_a = TypeOf(a)->As<sem::Reference>();
+ ASSERT_NE(ref_a, nullptr);
+ auto* ary_a = ref_a->StoreType()->As<sem::Array>();
+
+ ASSERT_NE(TypeOf(b), nullptr);
+ auto* ref_b = TypeOf(b)->As<sem::Reference>();
+ ASSERT_NE(ref_b, nullptr);
+ auto* ary_b = ref_b->StoreType()->As<sem::Array>();
+
+ auto* sem_override = Sem().Get<sem::GlobalVariable>(override);
+ ASSERT_NE(sem_override, nullptr);
+ EXPECT_EQ(ary_a->Count(), sem::UnnamedOverrideArrayCount{Sem().Get(a_cnt)});
+ EXPECT_EQ(ary_b->Count(), sem::UnnamedOverrideArrayCount{Sem().Get(b_cnt)});
+ EXPECT_NE(ary_a, ary_b);
+}
+
TEST_F(ResolverTest, Expr_Bitcast) {
GlobalVar("name", ty.f32(), ast::AddressSpace::kPrivate);
@@ -2331,8 +2378,8 @@
EXPECT_TRUE(r()->Resolve());
}
-// Windows debug builds have significantly smaller stack than other builds, and these tests will stack
-// overflow.
+// Windows debug builds have significantly smaller stack than other builds, and these tests will
+// stack overflow.
#if !defined(NDEBUG)
TEST_F(ResolverTest, ScopeDepth_NestedBlocks) {
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index d2587c5..b539e49 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -371,7 +371,7 @@
"12:34 error: array byte size (0x7a1185ee00) must not exceed 0xffffffff bytes");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_PrivateVar) {
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_PrivateVar) {
// override size = 10i;
// var<private> a : array<f32, size>;
Override("size", Expr(10_i));
@@ -382,19 +382,7 @@
"type of a 'var<workgroup>'");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_ComplexExpr) {
- // override size = 10i;
- // var<workgroup> a : array<f32, size + 1>;
- Override("size", Expr(10_i));
- GlobalVar("a", ty.array(ty.f32(), Add(Source{{12, 34}}, "size", 1_i)),
- ast::AddressSpace::kWorkgroup);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: array count must evaluate to a constant integer expression or override "
- "variable");
-}
-
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_InArray) {
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_InArray) {
// override size = 10i;
// var<workgroup> a : array<array<f32, size>, 4>;
Override("size", Expr(10_i));
@@ -406,7 +394,7 @@
"type of a 'var<workgroup>'");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_InStruct) {
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_InStruct) {
// override size = 10i;
// struct S {
// a : array<f32, size>
@@ -419,7 +407,7 @@
"type of a 'var<workgroup>'");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_FunctionVar_Explicit) {
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_FunctionVar_Explicit) {
// override size = 10i;
// fn f() {
// var a : array<f32, size>;
@@ -435,7 +423,7 @@
"type of a 'var<workgroup>'");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_FunctionLet_Explicit) {
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_FunctionLet_Explicit) {
// override size = 10i;
// fn f() {
// var a : array<f32, size>;
@@ -451,7 +439,7 @@
"type of a 'var<workgroup>'");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_FunctionVar_Implicit) {
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_FunctionVar_Implicit) {
// override size = 10i;
// var<workgroup> w : array<f32, size>;
// fn f() {
@@ -469,7 +457,7 @@
"type of a 'var<workgroup>'");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_FunctionLet_Implicit) {
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_FunctionLet_Implicit) {
// override size = 10i;
// var<workgroup> w : array<f32, size>;
// fn f() {
@@ -487,7 +475,24 @@
"type of a 'var<workgroup>'");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_Param) {
+TEST_F(ResolverTypeValidationTest, ArraySize_UnnamedOverride_Equivalence) {
+ // override size = 10i;
+ // var<workgroup> a : array<f32, size + 1>;
+ // var<workgroup> b : array<f32, size + 1>;
+ // fn f() {
+ // a = b;
+ // }
+ Override("size", Expr(10_i));
+ GlobalVar("a", ty.array(ty.f32(), Add("size", 1_i)), ast::AddressSpace::kWorkgroup);
+ GlobalVar("b", ty.array(ty.f32(), Add("size", 1_i)), ast::AddressSpace::kWorkgroup);
+ WrapInFunction(Assign(Source{{12, 34}}, "a", "b"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot assign 'array<f32, [unnamed override-expression]>' to "
+ "'array<f32, [unnamed override-expression]>'");
+}
+
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_Param) {
// override size = 10i;
// fn f(a : array<f32, size>) {
// }
@@ -498,7 +503,7 @@
EXPECT_EQ(r()->error(), "12:34 error: type of function parameter must be constructible");
}
-TEST_F(ResolverTypeValidationTest, ArraySize_Override_ReturnType) {
+TEST_F(ResolverTypeValidationTest, ArraySize_NamedOverride_ReturnType) {
// override size = 10i;
// fn f() -> array<f32, size> {
// }
diff --git a/src/tint/sem/array.cc b/src/tint/sem/array.cc
index 1cd6515..d61d430 100644
--- a/src/tint/sem/array.cc
+++ b/src/tint/sem/array.cc
@@ -40,7 +40,8 @@
}
}
if (std::holds_alternative<ConstantArrayCount>(count) ||
- std::holds_alternative<OverrideArrayCount>(count)) {
+ std::holds_alternative<NamedOverrideArrayCount>(count) ||
+ std::holds_alternative<UnnamedOverrideArrayCount>(count)) {
if (element->HasFixedFootprint()) {
flags.Add(TypeFlag::kFixedFootprint);
}
@@ -92,8 +93,10 @@
out << "array<" << element_->FriendlyName(symbols);
if (auto* const_count = std::get_if<ConstantArrayCount>(&count_)) {
out << ", " << const_count->value;
- } else if (auto* override_count = std::get_if<OverrideArrayCount>(&count_)) {
- out << ", " << symbols.NameFor(override_count->variable->Declaration()->symbol);
+ } else if (auto* named_override_count = std::get_if<NamedOverrideArrayCount>(&count_)) {
+ out << ", " << symbols.NameFor(named_override_count->variable->Declaration()->symbol);
+ } else if (std::holds_alternative<UnnamedOverrideArrayCount>(count_)) {
+ out << ", [unnamed override-expression]";
}
out << ">";
return out.str();
diff --git a/src/tint/sem/array.h b/src/tint/sem/array.h
index 24b2ee8..88e6373 100644
--- a/src/tint/sem/array.h
+++ b/src/tint/sem/array.h
@@ -26,6 +26,7 @@
// Forward declarations
namespace tint::sem {
+class Expression;
class GlobalVariable;
} // namespace tint::sem
@@ -48,11 +49,33 @@
/// override N : i32;
/// type arr = array<i32, N>
/// ```
-struct OverrideArrayCount {
+struct NamedOverrideArrayCount {
/// The `override` variable.
const GlobalVariable* variable;
};
+/// The variant of an ArrayCount when the count is an unnamed override variable.
+/// Example:
+/// ```
+/// override N : i32;
+/// type arr = array<i32, N*2>
+/// ```
+struct UnnamedOverrideArrayCount {
+ /// The unnamed override expression.
+ /// Note: Each AST expression gets a unique semantic expression node, so two equivalent AST
+ /// expressions will not result in the same `expr` pointer. This property is important to ensure
+ /// that two array declarations with equivalent AST expressions do not compare equal.
+ /// For example, consider:
+ /// ```
+ /// override size : u32;
+ /// var<workgroup> a : array<f32, size * 2>;
+ /// var<workgroup> b : array<f32, size * 2>;
+ /// ```
+ // The array count for `a` and `b` have equivalent AST expressions, but the types for `a` and
+ // `b` must not compare equal.
+ const Expression* expr;
+};
+
/// The variant of an ArrayCount when the array is is runtime-sized.
/// Example:
/// ```
@@ -60,8 +83,12 @@
/// ```
struct RuntimeArrayCount {};
-/// An array count is either a constant-expression value, an override identifier, or runtime-sized.
-using ArrayCount = std::variant<ConstantArrayCount, OverrideArrayCount, RuntimeArrayCount>;
+/// An array count is either a constant-expression value, a named override identifier, an unnamed
+/// override identifier, or runtime-sized.
+using ArrayCount = std::variant<ConstantArrayCount,
+ NamedOverrideArrayCount,
+ UnnamedOverrideArrayCount,
+ RuntimeArrayCount>;
/// Equality operator
/// @param a the LHS ConstantArrayCount
@@ -75,11 +102,19 @@
/// @param a the LHS OverrideArrayCount
/// @param b the RHS OverrideArrayCount
/// @returns true if @p a is equal to @p b
-inline bool operator==(const OverrideArrayCount& a, const OverrideArrayCount& b) {
+inline bool operator==(const NamedOverrideArrayCount& a, const NamedOverrideArrayCount& b) {
return a.variable == b.variable;
}
/// Equality operator
+/// @param a the LHS OverrideArrayCount
+/// @param b the RHS OverrideArrayCount
+/// @returns true if @p a is equal to @p b
+inline bool operator==(const UnnamedOverrideArrayCount& a, const UnnamedOverrideArrayCount& b) {
+ return a.expr == b.expr;
+}
+
+/// Equality operator
/// @returns true
inline bool operator==(const RuntimeArrayCount&, const RuntimeArrayCount&) {
return true;
@@ -90,9 +125,9 @@
/// @param b the RHS count
/// @returns true if @p a is equal to @p b
template <typename T,
- typename = std::enable_if_t<std::is_same_v<T, ConstantArrayCount> ||
- std::is_same_v<T, OverrideArrayCount> ||
- std::is_same_v<T, RuntimeArrayCount>>>
+ typename = std::enable_if_t<
+ std::is_same_v<T, ConstantArrayCount> || std::is_same_v<T, NamedOverrideArrayCount> ||
+ std::is_same_v<T, UnnamedOverrideArrayCount> || std::is_same_v<T, RuntimeArrayCount>>>
inline bool operator==(const ArrayCount& a, const T& b) {
TINT_BEGIN_DISABLE_WARNING(UNREACHABLE_CODE);
return std::visit(
@@ -178,8 +213,18 @@
/// @returns true if this array is sized using an const-expression
bool IsConstantSized() const { return std::holds_alternative<ConstantArrayCount>(count_); }
- /// @returns true if this array is sized using an override variable
- bool IsOverrideSized() const { return std::holds_alternative<OverrideArrayCount>(count_); }
+ /// @returns true if this array is sized using a named override variable
+ bool IsNamedOverrideSized() const {
+ return std::holds_alternative<NamedOverrideArrayCount>(count_);
+ }
+
+ /// @returns true if this array is sized using an unnamed override variable
+ bool IsUnnamedOverrideSized() const {
+ return std::holds_alternative<UnnamedOverrideArrayCount>(count_);
+ }
+
+ /// @returns true if this array is sized using a named or unnamed override variable
+ bool IsOverrideSized() const { return IsNamedOverrideSized() || IsUnnamedOverrideSized(); }
/// @returns true if this array is runtime sized
bool IsRuntimeSized() const { return std::holds_alternative<RuntimeArrayCount>(count_); }
@@ -213,17 +258,28 @@
}
};
-/// Custom std::hash specialization for tint::sem::OverrideArrayCount.
+/// Custom std::hash specialization for tint::sem::NamedOverrideArrayCount.
template <>
-class hash<tint::sem::OverrideArrayCount> {
+class hash<tint::sem::NamedOverrideArrayCount> {
public:
/// @param count the count to hash
/// @return the hash value
- inline std::size_t operator()(const tint::sem::OverrideArrayCount& count) const {
+ inline std::size_t operator()(const tint::sem::NamedOverrideArrayCount& count) const {
return std::hash<decltype(count.variable)>()(count.variable);
}
};
+/// Custom std::hash specialization for tint::sem::UnnamedOverrideArrayCount.
+template <>
+class hash<tint::sem::UnnamedOverrideArrayCount> {
+ public:
+ /// @param count the count to hash
+ /// @return the hash value
+ inline std::size_t operator()(const tint::sem::UnnamedOverrideArrayCount& count) const {
+ return std::hash<decltype(count.expr)>()(count.expr);
+ }
+};
+
/// Custom std::hash specialization for tint::sem::RuntimeArrayCount.
template <>
class hash<tint::sem::RuntimeArrayCount> {
diff --git a/src/tint/sem/array_test.cc b/src/tint/sem/array_test.cc
index 51c1894..a51b492 100644
--- a/src/tint/sem/array_test.cc
+++ b/src/tint/sem/array_test.cc
@@ -127,31 +127,43 @@
TEST_F(ArrayTest, IsConstructable) {
auto* fixed_sized = create<Array>(create<U32>(), ConstantArrayCount{2u}, 4u, 8u, 32u, 16u);
- auto* override_sized = create<Array>(create<U32>(), OverrideArrayCount{}, 4u, 8u, 32u, 16u);
+ auto* named_override_sized =
+ create<Array>(create<U32>(), NamedOverrideArrayCount{}, 4u, 8u, 32u, 16u);
+ auto* unnamed_override_sized =
+ create<Array>(create<U32>(), UnnamedOverrideArrayCount{}, 4u, 8u, 32u, 16u);
auto* runtime_sized = create<Array>(create<U32>(), RuntimeArrayCount{}, 4u, 8u, 32u, 16u);
EXPECT_TRUE(fixed_sized->IsConstructible());
- EXPECT_FALSE(override_sized->IsConstructible());
+ EXPECT_FALSE(named_override_sized->IsConstructible());
+ EXPECT_FALSE(unnamed_override_sized->IsConstructible());
EXPECT_FALSE(runtime_sized->IsConstructible());
}
TEST_F(ArrayTest, HasCreationFixedFootprint) {
auto* fixed_sized = create<Array>(create<U32>(), ConstantArrayCount{2u}, 4u, 8u, 32u, 16u);
- auto* override_sized = create<Array>(create<U32>(), OverrideArrayCount{}, 4u, 8u, 32u, 16u);
+ auto* named_override_sized =
+ create<Array>(create<U32>(), NamedOverrideArrayCount{}, 4u, 8u, 32u, 16u);
+ auto* unnamed_override_sized =
+ create<Array>(create<U32>(), UnnamedOverrideArrayCount{}, 4u, 8u, 32u, 16u);
auto* runtime_sized = create<Array>(create<U32>(), RuntimeArrayCount{}, 4u, 8u, 32u, 16u);
EXPECT_TRUE(fixed_sized->HasCreationFixedFootprint());
- EXPECT_FALSE(override_sized->HasCreationFixedFootprint());
+ EXPECT_FALSE(named_override_sized->HasCreationFixedFootprint());
+ EXPECT_FALSE(unnamed_override_sized->HasCreationFixedFootprint());
EXPECT_FALSE(runtime_sized->HasCreationFixedFootprint());
}
TEST_F(ArrayTest, HasFixedFootprint) {
auto* fixed_sized = create<Array>(create<U32>(), ConstantArrayCount{2u}, 4u, 8u, 32u, 16u);
- auto* override_sized = create<Array>(create<U32>(), OverrideArrayCount{}, 4u, 8u, 32u, 16u);
+ auto* named_override_sized =
+ create<Array>(create<U32>(), NamedOverrideArrayCount{}, 4u, 8u, 32u, 16u);
+ auto* unnamed_override_sized =
+ create<Array>(create<U32>(), UnnamedOverrideArrayCount{}, 4u, 8u, 32u, 16u);
auto* runtime_sized = create<Array>(create<U32>(), RuntimeArrayCount{}, 4u, 8u, 32u, 16u);
EXPECT_TRUE(fixed_sized->HasFixedFootprint());
- EXPECT_TRUE(override_sized->HasFixedFootprint());
+ EXPECT_TRUE(named_override_sized->HasFixedFootprint());
+ EXPECT_TRUE(unnamed_override_sized->HasFixedFootprint());
EXPECT_FALSE(runtime_sized->HasFixedFootprint());
}
diff --git a/src/tint/transform/transform.cc b/src/tint/transform/transform.cc
index 3bcac6b..3e03411 100644
--- a/src/tint/transform/transform.cc
+++ b/src/tint/transform/transform.cc
@@ -114,10 +114,14 @@
if (a->IsRuntimeSized()) {
return ctx.dst->ty.array(el, nullptr, std::move(attrs));
}
- if (auto* override = std::get_if<sem::OverrideArrayCount>(&a->Count())) {
+ if (auto* override = std::get_if<sem::NamedOverrideArrayCount>(&a->Count())) {
auto* count = ctx.Clone(override->variable->Declaration());
return ctx.dst->ty.array(el, count, std::move(attrs));
}
+ if (auto* override = std::get_if<sem::UnnamedOverrideArrayCount>(&a->Count())) {
+ auto* count = ctx.Clone(override->expr->Declaration());
+ return ctx.dst->ty.array(el, count, std::move(attrs));
+ }
if (auto count = a->ConstantCount()) {
return ctx.dst->ty.array(el, u32(count.value()), std::move(attrs));
}
diff --git a/test/tint/bug/tint/1737.wgsl b/test/tint/bug/tint/1737.wgsl
new file mode 100644
index 0000000..ea0899d
--- /dev/null
+++ b/test/tint/bug/tint/1737.wgsl
@@ -0,0 +1,10 @@
+// flags: --overrides wgsize=10
+
+override wgsize : u32;
+var<workgroup> a : array<f32, wgsize>; // Accepted
+var<workgroup> b : array<f32, wgsize * 2>; // Rejected
+
+fn f() {
+ let x = a[0];
+ let y = b[0];
+}
diff --git a/test/tint/bug/tint/1737.wgsl.expected.dxc.hlsl b/test/tint/bug/tint/1737.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..436f4ac
--- /dev/null
+++ b/test/tint/bug/tint/1737.wgsl.expected.dxc.hlsl
@@ -0,0 +1,12 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+ return;
+}
+
+groupshared float a[10];
+groupshared float b[20];
+
+void f() {
+ const float x = a[0];
+ const float y = b[0];
+}
diff --git a/test/tint/bug/tint/1737.wgsl.expected.fxc.hlsl b/test/tint/bug/tint/1737.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..436f4ac
--- /dev/null
+++ b/test/tint/bug/tint/1737.wgsl.expected.fxc.hlsl
@@ -0,0 +1,12 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+ return;
+}
+
+groupshared float a[10];
+groupshared float b[20];
+
+void f() {
+ const float x = a[0];
+ const float y = b[0];
+}
diff --git a/test/tint/bug/tint/1737.wgsl.expected.glsl b/test/tint/bug/tint/1737.wgsl.expected.glsl
new file mode 100644
index 0000000..43f1f25
--- /dev/null
+++ b/test/tint/bug/tint/1737.wgsl.expected.glsl
@@ -0,0 +1,13 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+ return;
+}
+shared float a[10];
+shared float b[20];
+void f() {
+ float x = a[0];
+ float y = b[0];
+}
+
diff --git a/test/tint/bug/tint/1737.wgsl.expected.msl b/test/tint/bug/tint/1737.wgsl.expected.msl
new file mode 100644
index 0000000..d8f8c1e
--- /dev/null
+++ b/test/tint/bug/tint/1737.wgsl.expected.msl
@@ -0,0 +1,21 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+template<typename T, size_t N>
+struct tint_array {
+ const constant T& operator[](size_t i) const constant { return elements[i]; }
+ device T& operator[](size_t i) device { return elements[i]; }
+ const device T& operator[](size_t i) const device { return elements[i]; }
+ thread T& operator[](size_t i) thread { return elements[i]; }
+ const thread T& operator[](size_t i) const thread { return elements[i]; }
+ threadgroup T& operator[](size_t i) threadgroup { return elements[i]; }
+ const threadgroup T& operator[](size_t i) const threadgroup { return elements[i]; }
+ T elements[N];
+};
+
+void f(threadgroup tint_array<float, 10>* const tint_symbol, threadgroup tint_array<float, 20>* const tint_symbol_1) {
+ float const x = (*(tint_symbol))[0];
+ float const y = (*(tint_symbol_1))[0];
+}
+
diff --git a/test/tint/bug/tint/1737.wgsl.expected.spvasm b/test/tint/bug/tint/1737.wgsl.expected.spvasm
new file mode 100644
index 0000000..1933b27
--- /dev/null
+++ b/test/tint/bug/tint/1737.wgsl.expected.spvasm
@@ -0,0 +1,42 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+ OpExecutionMode %unused_entry_point LocalSize 1 1 1
+ OpName %a "a"
+ OpName %b "b"
+ OpName %unused_entry_point "unused_entry_point"
+ OpName %f "f"
+ OpDecorate %_arr_float_uint_10 ArrayStride 4
+ OpDecorate %_arr_float_uint_20 ArrayStride 4
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+ %uint_10 = OpConstant %uint 10
+%_arr_float_uint_10 = OpTypeArray %float %uint_10
+%_ptr_Workgroup__arr_float_uint_10 = OpTypePointer Workgroup %_arr_float_uint_10
+ %a = OpVariable %_ptr_Workgroup__arr_float_uint_10 Workgroup
+ %uint_20 = OpConstant %uint 20
+%_arr_float_uint_20 = OpTypeArray %float %uint_20
+%_ptr_Workgroup__arr_float_uint_20 = OpTypePointer Workgroup %_arr_float_uint_20
+ %b = OpVariable %_ptr_Workgroup__arr_float_uint_20 Workgroup
+ %void = OpTypeVoid
+ %11 = OpTypeFunction %void
+ %int = OpTypeInt 32 1
+ %18 = OpConstantNull %int
+%_ptr_Workgroup_float = OpTypePointer Workgroup %float
+%unused_entry_point = OpFunction %void None %11
+ %14 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %f = OpFunction %void None %11
+ %16 = OpLabel
+ %20 = OpAccessChain %_ptr_Workgroup_float %a %18
+ %21 = OpLoad %float %20
+ %22 = OpAccessChain %_ptr_Workgroup_float %b %18
+ %23 = OpLoad %float %22
+ OpReturn
+ OpFunctionEnd
diff --git a/test/tint/bug/tint/1737.wgsl.expected.wgsl b/test/tint/bug/tint/1737.wgsl.expected.wgsl
new file mode 100644
index 0000000..ff19d2a
--- /dev/null
+++ b/test/tint/bug/tint/1737.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+const wgsize : u32 = 10u;
+
+var<workgroup> a : array<f32, wgsize>;
+
+var<workgroup> b : array<f32, (wgsize * 2)>;
+
+fn f() {
+ let x = a[0];
+ let y = b[0];
+}