tint/resolver: Fix failures with no error
Invalid values to @binding(), @group() and @location() would fail resolving without an error diagnostic. This later triggers and ICE.
Refactor duplicate @location resolving in 4 places to a single method.
Canonicalize the diagnostic messages for attributes.
Remove a bunch of TODOs
Bug: chromium:1380212
Bug: tint:1633
Change-Id: Id2cc6ba4b807f12f350a2a31ef87fa0f185b64c3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/108144
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 86d6825..762eea9 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -1606,12 +1606,22 @@
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
+TEST_F(GroupAndBindingTest, Binding_NonConstant) {
+ GlobalVar("val", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+ Binding(Construct(ty.u32(), Call(Source{{12, 34}}, "dpdx", 1_a))), Group(1_i));
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: @binding requires a const-expression, but expression is a runtime-expression)");
+}
+
TEST_F(GroupAndBindingTest, Binding_Negative) {
GlobalVar("val", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
Binding(Source{{12, 34}}, -2_i), Group(1_i));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'binding' value must be non-negative)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @binding value must be non-negative)");
}
TEST_F(GroupAndBindingTest, Binding_F32) {
@@ -1619,7 +1629,7 @@
Binding(Source{{12, 34}}, 2.0_f), Group(1_u));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'binding' must be an i32 or u32 value)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @binding must be an i32 or u32 value)");
}
TEST_F(GroupAndBindingTest, Binding_AFloat) {
@@ -1627,7 +1637,17 @@
Binding(Source{{12, 34}}, 2.0_a), Group(1_u));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'binding' must be an i32 or u32 value)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @binding must be an i32 or u32 value)");
+}
+
+TEST_F(GroupAndBindingTest, Group_NonConstant) {
+ GlobalVar("val", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), Binding(2_u),
+ Group(Construct(ty.u32(), Call(Source{{12, 34}}, "dpdx", 1_a))));
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: @group requires a const-expression, but expression is a runtime-expression)");
}
TEST_F(GroupAndBindingTest, Group_Negative) {
@@ -1635,7 +1655,7 @@
Group(Source{{12, 34}}, -1_i));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'group' value must be non-negative)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @group value must be non-negative)");
}
TEST_F(GroupAndBindingTest, Group_F32) {
@@ -1643,7 +1663,7 @@
Group(Source{{12, 34}}, 1.0_f));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'group' must be an i32 or u32 value)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @group must be an i32 or u32 value)");
}
TEST_F(GroupAndBindingTest, Group_AFloat) {
@@ -1651,7 +1671,7 @@
Group(Source{{12, 34}}, 1.0_a));
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'group' must be an i32 or u32 value)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @group must be an i32 or u32 value)");
}
using IdTest = ResolverTest;
@@ -1671,24 +1691,125 @@
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
+TEST_F(IdTest, NonConstant) {
+ Override("val", ty.f32(),
+ utils::Vector{Id(Construct(ty.u32(), Call(Source{{12, 34}}, "dpdx", 1_a)))});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: @id requires a const-expression, but expression is a runtime-expression)");
+}
+
TEST_F(IdTest, Negative) {
Override("val", ty.f32(), utils::Vector{Id(Source{{12, 34}}, -1_i)});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'id' value must be non-negative)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @id value must be non-negative)");
}
TEST_F(IdTest, F32) {
Override("val", ty.f32(), utils::Vector{Id(Source{{12, 34}}, 1_f)});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'id' must be an i32 or u32 value)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @id must be an i32 or u32 value)");
}
TEST_F(IdTest, AFloat) {
Override("val", ty.f32(), utils::Vector{Id(Source{{12, 34}}, 1.0_a)});
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(12:34 error: 'id' must be an i32 or u32 value)");
+ EXPECT_EQ(r()->error(), R"(12:34 error: @id must be an i32 or u32 value)");
}
+enum class LocationAttributeType {
+ kEntryPointParameter,
+ kEntryPointReturnType,
+ kStructureMember,
+};
+
+struct LocationTest : ResolverTestWithParam<LocationAttributeType> {
+ void Build(const ast::Expression* location_value) {
+ switch (GetParam()) {
+ case LocationAttributeType::kEntryPointParameter:
+ Func("main",
+ utils::Vector{Param(Source{{12, 34}}, "a", ty.i32(),
+ utils::Vector{
+ Location(Source{{12, 34}}, location_value),
+ Flat(),
+ })},
+ ty.void_(), utils::Empty,
+ utils::Vector{
+ Stage(ast::PipelineStage::kFragment),
+ });
+ return;
+ case LocationAttributeType::kEntryPointReturnType:
+ Func("main", utils::Empty, ty.f32(),
+ utils::Vector{
+ Return(1_a),
+ },
+ utils::Vector{
+ Stage(ast::PipelineStage::kFragment),
+ },
+ utils::Vector{
+ Location(Source{{12, 34}}, location_value),
+ });
+ return;
+ case LocationAttributeType::kStructureMember:
+ Structure("S", utils::Vector{
+ Member("m", ty.f32(),
+ utils::Vector{
+ Location(Source{{12, 34}}, location_value),
+ }),
+ });
+ return;
+ }
+ }
+};
+
+TEST_P(LocationTest, Const_I32) {
+ Build(Expr(0_i));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_P(LocationTest, Const_U32) {
+ Build(Expr(0_u));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_P(LocationTest, Const_AInt) {
+ Build(Expr(0_a));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_P(LocationTest, NonConstant) {
+ Build(Construct(ty.u32(), Call(Source{{12, 34}}, "dpdx", 1_a)));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: @location value requires a const-expression, but expression is a runtime-expression)");
+}
+
+TEST_P(LocationTest, Negative) {
+ Build(Expr(-1_a));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(12:34 error: @location value must be non-negative)");
+}
+
+TEST_P(LocationTest, F32) {
+ Build(Expr(1_f));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(12:34 error: @location must be an i32 or u32 value)");
+}
+
+TEST_P(LocationTest, AFloat) {
+ Build(Expr(1.0_a));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(12:34 error: @location must be an i32 or u32 value)");
+}
+
+INSTANTIATE_TEST_SUITE_P(LocationTest,
+ LocationTest,
+ testing::Values(LocationAttributeType::kEntryPointParameter,
+ LocationAttributeType::kEntryPointReturnType,
+ LocationAttributeType::kStructureMember));
+
} // namespace
} // namespace InterpolateTests
diff --git a/src/tint/resolver/override_test.cc b/src/tint/resolver/override_test.cc
index 4fdbcee..a12d3c1 100644
--- a/src/tint/resolver/override_test.cc
+++ b/src/tint/resolver/override_test.cc
@@ -91,7 +91,7 @@
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: override IDs must be unique
+ EXPECT_EQ(r()->error(), R"(56:78 error: @id values must be unique
12:34 note: a override with an ID of 7 was previously declared here:)");
}
@@ -100,7 +100,7 @@
EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: override IDs must be between 0 and 65535");
+ EXPECT_EQ(r()->error(), "12:34 error: @id value must be between 0 and 65535");
}
TEST_F(ResolverOverrideTest, F16_TemporallyBan) {
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 3252829..cb24955 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -461,18 +461,18 @@
return nullptr;
}
if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
- AddError("'id' must be an i32 or u32 value", id_attr->source);
+ AddError("@id must be an i32 or u32 value", id_attr->source);
return nullptr;
}
auto const_value = materialized->ConstantValue();
auto value = const_value->As<AInt>();
if (value < 0) {
- AddError("'id' value must be non-negative", id_attr->source);
+ AddError("@id value must be non-negative", id_attr->source);
return nullptr;
}
if (value > std::numeric_limits<decltype(OverrideId::value)>::max()) {
- AddError("override IDs must be between 0 and " +
+ AddError("@id value must be between 0 and " +
std::to_string(std::numeric_limits<decltype(OverrideId::value)>::max()),
id_attr->source);
return nullptr;
@@ -638,14 +638,14 @@
return nullptr;
}
if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
- AddError("'binding' must be an i32 or u32 value", attr->source);
+ AddError("@binding must be an i32 or u32 value", attr->source);
return nullptr;
}
auto const_value = materialized->ConstantValue();
auto value = const_value->As<AInt>();
if (value < 0) {
- AddError("'binding' value must be non-negative", attr->source);
+ AddError("@binding value must be non-negative", attr->source);
return nullptr;
}
binding = u32(value);
@@ -662,14 +662,14 @@
return nullptr;
}
if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
- AddError("'group' must be an i32 or u32 value", attr->source);
+ AddError("@group must be an i32 or u32 value", attr->source);
return nullptr;
}
auto const_value = materialized->ConstantValue();
auto value = const_value->As<AInt>();
if (value < 0) {
- AddError("'group' value must be non-negative", attr->source);
+ AddError("@group value must be non-negative", attr->source);
return nullptr;
}
group = u32(value);
@@ -679,22 +679,11 @@
std::optional<uint32_t> location;
if (auto* attr = ast::GetAttribute<ast::LocationAttribute>(var->attributes)) {
- auto* materialized = Materialize(Expression(attr->expr));
- if (!materialized) {
+ auto value = LocationAttribute(attr);
+ if (!value) {
return nullptr;
}
- if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
- AddError("'location' must be an i32 or u32 value", attr->source);
- return nullptr;
- }
-
- auto const_value = materialized->ConstantValue();
- auto value = const_value->As<AInt>();
- if (value < 0) {
- AddError("'location' value must be non-negative", attr->source);
- return nullptr;
- }
- location = u32(value);
+ location = value.Get();
}
sem = builder_->create<sem::GlobalVariable>(
@@ -748,48 +737,36 @@
sem::BindingPoint binding_point;
if (param->HasBindingPoint()) {
{
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@binding value"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
auto* attr = ast::GetAttribute<ast::BindingAttribute>(param->attributes);
- auto* materialize = Materialize(Expression(attr->expr));
- if (!materialize) {
+ auto* materialized = Materialize(Expression(attr->expr));
+ if (!materialized) {
return nullptr;
}
- auto* c = materialize->ConstantValue();
- if (!c) {
- // TODO(crbug.com/tint/1633): Add error message about invalid materialization when
- // binding can be an expression.
- return nullptr;
- }
- binding_point.binding = c->As<uint32_t>();
+ binding_point.binding = materialized->ConstantValue()->As<uint32_t>();
}
{
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@group value"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
auto* attr = ast::GetAttribute<ast::GroupAttribute>(param->attributes);
- auto* materialize = Materialize(Expression(attr->expr));
- if (!materialize) {
+ auto* materialized = Materialize(Expression(attr->expr));
+ if (!materialized) {
return nullptr;
}
- auto* c = materialize->ConstantValue();
- if (!c) {
- // TODO(crbug.com/tint/1633): Add error message about invalid materialization when
- // binding can be an expression.
- return nullptr;
- }
- binding_point.group = c->As<uint32_t>();
+ binding_point.group = materialized->ConstantValue()->As<uint32_t>();
}
}
std::optional<uint32_t> location;
- if (auto* l = ast::GetAttribute<ast::LocationAttribute>(param->attributes)) {
- auto* materialize = Materialize(Expression(l->expr));
- if (!materialize) {
+ if (auto* attr = ast::GetAttribute<ast::LocationAttribute>(param->attributes)) {
+ auto value = LocationAttribute(attr);
+ if (!value) {
return nullptr;
}
- auto* c = materialize->ConstantValue();
- if (!c) {
- // TODO(crbug.com/tint/1633): Add error message about invalid materialization when
- // location can be an expression.
- return nullptr;
- }
- location = c->As<uint32_t>();
+ location = value.Get();
}
auto* sem = builder_->create<sem::Parameter>(
@@ -799,6 +776,30 @@
return sem;
}
+utils::Result<uint32_t> Resolver::LocationAttribute(const ast::LocationAttribute* attr) {
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@location value"};
+ TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
+
+ auto* materialized = Materialize(Expression(attr->expr));
+ if (!materialized) {
+ return utils::Failure;
+ }
+
+ if (!materialized->Type()->IsAnyOf<sem::I32, sem::U32>()) {
+ AddError("@location must be an i32 or u32 value", attr->source);
+ return utils::Failure;
+ }
+
+ auto const_value = materialized->ConstantValue();
+ auto value = const_value->As<AInt>();
+ if (value < 0) {
+ AddError("@location value must be non-negative", attr->source);
+ return utils::Failure;
+ }
+
+ return static_cast<uint32_t>(value);
+}
+
ast::Access Resolver::DefaultAccessForAddressSpace(ast::AddressSpace address_space) {
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class
switch (address_space) {
@@ -984,18 +985,12 @@
for (auto* attr : decl->return_type_attributes) {
Mark(attr);
- if (auto* a = attr->As<ast::LocationAttribute>()) {
- auto* materialize = Materialize(Expression(a->expr));
- if (!materialize) {
+ if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
+ auto value = LocationAttribute(loc_attr);
+ if (!value) {
return nullptr;
}
- auto* c = materialize->ConstantValue();
- if (!c) {
- // TODO(crbug.com/tint/1633): Add error message about invalid materialization when
- // location can be an expression.
- return nullptr;
- }
- return_location = c->As<uint32_t>();
+ return_location = value.Get();
}
}
if (!validator_.NoDuplicateAttributes(decl->attributes)) {
@@ -2969,22 +2964,12 @@
has_size_attr = true;
return true;
},
- [&](const ast::LocationAttribute* l) {
- ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant,
- "@location"};
- TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
-
- auto* materialize = Materialize(Expression(l->expr));
- if (!materialize) {
+ [&](const ast::LocationAttribute* loc_attr) {
+ auto value = LocationAttribute(loc_attr);
+ if (!value) {
return false;
}
- auto* c = materialize->ConstantValue();
- if (!c) {
- // TODO(crbug.com/tint/1633): Add error message about invalid
- // materialization when location can be an expression.
- return false;
- }
- location = c->As<uint32_t>();
+ location = value.Get();
return true;
},
[&](Default) {
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index b657b46..89aab13 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -349,6 +349,10 @@
/// @param index the index of the parameter
sem::Parameter* Parameter(const ast::Parameter* param, uint32_t index);
+ /// @returns the location value for a `@location` attribute, validating the value's range and
+ /// type.
+ utils::Result<uint32_t> LocationAttribute(const ast::LocationAttribute* attr);
+
/// Records the address space usage for the given type, and any transient
/// dependencies of the type. Validates that the type can be used for the
/// given address space, erroring if it cannot.
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 7f1cecf..0d6bfe6 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -797,7 +797,7 @@
if (attr->Is<ast::IdAttribute>()) {
auto id = v->OverrideId();
if (auto it = override_ids.find(id); it != override_ids.end() && it->second != v) {
- AddError("override IDs must be unique", attr->source);
+ AddError("@id values must be unique", attr->source);
AddNote("a override with an ID of " + std::to_string(id.value) +
" was previously declared here:",
ast::GetAttribute<ast::IdAttribute>(it->second->Declaration()->attributes)