Allow array size to be a module-scope constant

Change ast::Array to use an ast::Expression for its `size` field. The
WGSL frontend now parses the array size as an `primary_expression`,
and the Resolver is responsible for validating the expression is a
signed or unsigned integer, and either a literal or a non-overridable
module-scope constant.

The Resolver evaluates the constant value of the size expression, and
so the resolved sem::Array type still has a constant size as before.

Fixed: tint:1068
Fixed: tint:1117

Change-Id: Icfa141482ea1e47ea8c21a25e9eb48221f176e9a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/63061
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price
2021-09-02 13:49:59 +00:00
committed by Tint LUCI CQ
parent 69ce5f74ed
commit 4cc4315d6c
41 changed files with 861 additions and 261 deletions

View File

@@ -187,7 +187,11 @@ Array::Array(const Type* t, uint32_t sz, uint32_t st)
Array::Array(const Array&) = default;
ast::Type* Array::Build(ProgramBuilder& b) const {
return b.ty.array(type->Build(b), size, stride);
if (size > 0) {
return b.ty.array(type->Build(b), size, stride);
} else {
return b.ty.array(type->Build(b), nullptr, stride);
}
}
Sampler::Sampler(ast::SamplerKind k) : kind(k) {}

View File

@@ -1187,7 +1187,7 @@ Expect<ast::Type*> ParserImpl::expect_type_decl_array(
ast::DecorationList decos) {
const char* use = "array declaration";
uint32_t size = 0;
ast::Expression* size = nullptr;
auto subtype = expect_lt_gt_block(use, [&]() -> Expect<ast::Type*> {
auto type = expect_type(use);
@@ -1195,10 +1195,14 @@ Expect<ast::Type*> ParserImpl::expect_type_decl_array(
return Failure::kErrored;
if (match(Token::Type::kComma)) {
auto val = expect_nonzero_positive_sint("array size");
if (val.errored)
auto expr = primary_expression();
if (expr.errored) {
return Failure::kErrored;
size = val.value;
} else if (!expr.matched) {
return add_error(peek(), "expected array size expression");
}
size = std::move(expr.value);
}
return type.value;

View File

@@ -862,19 +862,18 @@ TEST_F(ParserImplErrorTest, GlobalDeclVarArrayMissingType) {
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclVarArrayInvalidSize) {
EXPECT(
"var i : array<u32, x>;",
"test.wgsl:1:20 error: expected signed integer literal for array size\n"
"var i : array<u32, x>;\n"
" ^\n");
TEST_F(ParserImplErrorTest, GlobalDeclVarArrayMissingSize) {
EXPECT("var i : array<u32, >;",
"test.wgsl:1:20 error: expected array size expression\n"
"var i : array<u32, >;\n"
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclVarArrayNegativeSize) {
EXPECT("var i : array<u32, -3>;",
"test.wgsl:1:20 error: array size must be greater than 0\n"
"var i : array<u32, -3>;\n"
" ^^\n");
TEST_F(ParserImplErrorTest, GlobalDeclVarArrayInvalidSize) {
EXPECT("var i : array<u32, !>;",
"test.wgsl:1:20 error: expected array size expression\n"
"var i : array<u32, !>;\n"
" ^\n");
}
TEST_F(ParserImplErrorTest, GlobalDeclVarDecoListEmpty) {

View File

@@ -440,7 +440,7 @@ TEST_F(ParserImplTest, TypeDecl_Atomic_MissingType) {
ASSERT_EQ(p->error(), "1:8: invalid type for atomic declaration");
}
TEST_F(ParserImplTest, TypeDecl_Array) {
TEST_F(ParserImplTest, TypeDecl_Array_SintLiteralSize) {
auto p = parser("array<f32, 5>");
auto t = p->type_decl();
EXPECT_TRUE(t.matched);
@@ -451,10 +451,57 @@ TEST_F(ParserImplTest, TypeDecl_Array) {
auto* a = t.value->As<ast::Array>();
ASSERT_FALSE(a->IsRuntimeArray());
ASSERT_EQ(a->size(), 5u);
ASSERT_TRUE(a->type()->Is<ast::F32>());
EXPECT_EQ(a->decorations().size(), 0u);
EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 14u}}));
auto* size_expr = a->Size()->As<ast::ScalarConstructorExpression>();
ASSERT_NE(size_expr, nullptr);
auto* size = size_expr->literal()->As<ast::SintLiteral>();
ASSERT_NE(size, nullptr);
EXPECT_EQ(size->value_as_i32(), 5);
}
TEST_F(ParserImplTest, TypeDecl_Array_UintLiteralSize) {
auto p = parser("array<f32, 5u>");
auto t = p->type_decl();
EXPECT_TRUE(t.matched);
EXPECT_FALSE(t.errored);
ASSERT_NE(t.value, nullptr) << p->error();
ASSERT_FALSE(p->has_error());
ASSERT_TRUE(t.value->Is<ast::Array>());
auto* a = t.value->As<ast::Array>();
ASSERT_FALSE(a->IsRuntimeArray());
ASSERT_TRUE(a->type()->Is<ast::F32>());
EXPECT_EQ(a->decorations().size(), 0u);
EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 15u}}));
auto* size_expr = a->Size()->As<ast::ScalarConstructorExpression>();
ASSERT_NE(size_expr, nullptr);
auto* size = size_expr->literal()->As<ast::UintLiteral>();
ASSERT_NE(size, nullptr);
EXPECT_EQ(size->value_as_u32(), 5u);
}
TEST_F(ParserImplTest, TypeDecl_Array_ConstantSize) {
auto p = parser("array<f32, size>");
auto t = p->type_decl();
EXPECT_TRUE(t.matched);
EXPECT_FALSE(t.errored);
ASSERT_NE(t.value, nullptr) << p->error();
ASSERT_FALSE(p->has_error());
ASSERT_TRUE(t.value->Is<ast::Array>());
auto* a = t.value->As<ast::Array>();
ASSERT_FALSE(a->IsRuntimeArray());
ASSERT_TRUE(a->type()->Is<ast::F32>());
EXPECT_EQ(a->decorations().size(), 0u);
EXPECT_EQ(t.value->source().range, (Source::Range{{1u, 1u}, {1u, 17u}}));
auto* size_expr = a->Size()->As<ast::IdentifierExpression>();
ASSERT_NE(size_expr, nullptr);
EXPECT_EQ(p->builder().Symbols().NameFor(size_expr->symbol()), "size");
}
TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
@@ -468,9 +515,14 @@ TEST_F(ParserImplTest, TypeDecl_Array_Stride) {
auto* a = t.value->As<ast::Array>();
ASSERT_FALSE(a->IsRuntimeArray());
ASSERT_EQ(a->size(), 5u);
ASSERT_TRUE(a->type()->Is<ast::F32>());
auto* size_expr = a->Size()->As<ast::ScalarConstructorExpression>();
ASSERT_NE(size_expr, nullptr);
auto* size = size_expr->literal()->As<ast::SintLiteral>();
ASSERT_NE(size, nullptr);
EXPECT_EQ(size->value_as_i32(), 5);
ASSERT_EQ(a->decorations().size(), 1u);
auto* stride = a->decorations()[0];
ASSERT_TRUE(stride->Is<ast::StrideDecoration>());
@@ -664,34 +716,24 @@ TEST_F(ParserImplTest, TypeDecl_Array_BadType) {
ASSERT_EQ(p->error(), "1:7: unknown type 'unknown'");
}
TEST_F(ParserImplTest, TypeDecl_Array_ZeroSize) {
auto p = parser("array<f32, 0>");
auto t = p->type_decl();
EXPECT_TRUE(t.errored);
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:12: array size must be greater than 0");
}
TEST_F(ParserImplTest, TypeDecl_Array_NegativeSize) {
auto p = parser("array<f32, -1>");
auto t = p->type_decl();
EXPECT_TRUE(t.errored);
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:12: array size must be greater than 0");
}
TEST_F(ParserImplTest, TypeDecl_Array_BadSize) {
auto p = parser("array<f32, invalid>");
auto p = parser("array<f32, !>");
auto t = p->type_decl();
EXPECT_TRUE(t.errored);
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:12: expected signed integer literal for array size");
ASSERT_EQ(p->error(), "1:12: expected array size expression");
}
TEST_F(ParserImplTest, TypeDecl_Array_MissingSize) {
auto p = parser("array<f32,>");
auto t = p->type_decl();
EXPECT_TRUE(t.errored);
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(), "1:11: expected array size expression");
}
TEST_F(ParserImplTest, TypeDecl_Array_MissingLessThan) {