mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-15 20:01:22 +00:00
tint/resolver: Improve errors for expr eval-stages
Raise the error on the inner-most expression that violates the required evaluation stage. Fixed: tint:1655 Change-Id: I82186e72ed6efa1cd6d4456c04446da18e9f1850 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/105640 Commit-Queue: Ben Clayton <bclayton@google.com> Auto-Submit: Ben Clayton <bclayton@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
559a248233
commit
c84d06e860
@ -239,7 +239,8 @@ TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidAccess) {
|
|||||||
EXPECT_EQ(t.value, nullptr);
|
EXPECT_EQ(t.value, nullptr);
|
||||||
EXPECT_FALSE(t.matched);
|
EXPECT_FALSE(t.matched);
|
||||||
EXPECT_TRUE(t.errored);
|
EXPECT_TRUE(t.errored);
|
||||||
EXPECT_EQ(p->error(), R"(1:30: expected access control for storage texture type. Did you mean 'read'?
|
EXPECT_EQ(p->error(),
|
||||||
|
R"(1:30: expected access control for storage texture type. Did you mean 'read'?
|
||||||
Possible values: 'read', 'read_write', 'write')");
|
Possible values: 'read', 'read_write', 'write')");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,7 +738,9 @@ TEST_F(StructMemberAttributeTest, Align_Attribute_Override) {
|
|||||||
Structure("mystruct", utils::Vector{Member(
|
Structure("mystruct", utils::Vector{Member(
|
||||||
"a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
|
"a", ty.f32(), utils::Vector{MemberAlign(Source{{12, 34}}, "val")})});
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(12:34 error: 'align' must be an i32 or u32 value)");
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(error: @align requires a const-expression, but expression is an override-expression)");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace StructAndStructMemberTests
|
} // namespace StructAndStructMemberTests
|
||||||
|
@ -67,7 +67,9 @@ TEST_F(ResolverBuiltinTest, ModuleScopeUsage) {
|
|||||||
|
|
||||||
// TODO(crbug.com/tint/1581): Once 'abs' is implemented as @const, this will no longer be an
|
// TODO(crbug.com/tint/1581): Once 'abs' is implemented as @const, this will no longer be an
|
||||||
// error.
|
// error.
|
||||||
EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be const-expression)");
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests for Logical builtins
|
// Tests for Logical builtins
|
||||||
|
@ -416,6 +416,8 @@ sem::Variable* Resolver::Override(const ast::Override* v) {
|
|||||||
|
|
||||||
// Does the variable have a constructor?
|
// Does the variable have a constructor?
|
||||||
if (v->constructor) {
|
if (v->constructor) {
|
||||||
|
ExprEvalStageConstraint constraint{sem::EvaluationStage::kOverride, "override initializer"};
|
||||||
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
rhs = Materialize(Expression(v->constructor), ty);
|
rhs = Materialize(Expression(v->constructor), ty);
|
||||||
if (!rhs) {
|
if (!rhs) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -453,8 +455,8 @@ sem::Variable* Resolver::Override(const ast::Override* v) {
|
|||||||
}
|
}
|
||||||
auto* c = materialize->ConstantValue();
|
auto* c = materialize->ConstantValue();
|
||||||
if (!c) {
|
if (!c) {
|
||||||
// TODO(crbug.com/tint/1633): Handle invalid materialization when expressions
|
// TODO(crbug.com/tint/1633): Handle invalid materialization when expressions are
|
||||||
// are supported.
|
// supported.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,10 +495,15 @@ sem::Variable* Resolver::Const(const ast::Const* c, bool is_global) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* rhs = Expression(c->constructor);
|
const sem::Expression* rhs = nullptr;
|
||||||
|
{
|
||||||
|
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "const initializer"};
|
||||||
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
|
rhs = Expression(c->constructor);
|
||||||
if (!rhs) {
|
if (!rhs) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ty) {
|
if (ty) {
|
||||||
// If an explicit type was specified, materialize to that type
|
// If an explicit type was specified, materialize to that type
|
||||||
@ -509,12 +516,6 @@ sem::Variable* Resolver::Const(const ast::Const* c, bool is_global) {
|
|||||||
ty = rhs->Type();
|
ty = rhs->Type();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto value = rhs->ConstantValue();
|
|
||||||
if (!value) {
|
|
||||||
AddError("'const' initializer must be const-expression", c->constructor->source);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!validator_.VariableInitializer(c, ast::AddressSpace::kNone, ty, rhs)) {
|
if (!validator_.VariableInitializer(c, ast::AddressSpace::kNone, ty, rhs)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -525,6 +526,7 @@ sem::Variable* Resolver::Const(const ast::Const* c, bool is_global) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto value = rhs->ConstantValue();
|
||||||
auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
|
auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
|
||||||
c, ty, sem::EvaluationStage::kConstant, ast::AddressSpace::kNone,
|
c, ty, sem::EvaluationStage::kConstant, ast::AddressSpace::kNone,
|
||||||
ast::Access::kUndefined, value, sem::BindingPoint{}, std::nullopt))
|
ast::Access::kUndefined, value, sem::BindingPoint{}, std::nullopt))
|
||||||
@ -552,6 +554,12 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
|
|||||||
|
|
||||||
// Does the variable have a constructor?
|
// Does the variable have a constructor?
|
||||||
if (var->constructor) {
|
if (var->constructor) {
|
||||||
|
ExprEvalStageConstraint constraint{
|
||||||
|
is_global ? sem::EvaluationStage::kOverride : sem::EvaluationStage::kRuntime,
|
||||||
|
"var initializer",
|
||||||
|
};
|
||||||
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
|
|
||||||
rhs = Materialize(Expression(var->constructor), storage_ty);
|
rhs = Materialize(Expression(var->constructor), storage_ty);
|
||||||
if (!rhs) {
|
if (!rhs) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -858,16 +866,13 @@ sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sem::Statement* Resolver::StaticAssert(const ast::StaticAssert* assertion) {
|
sem::Statement* Resolver::StaticAssert(const ast::StaticAssert* assertion) {
|
||||||
|
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "static assertion"};
|
||||||
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
auto* expr = Expression(assertion->condition);
|
auto* expr = Expression(assertion->condition);
|
||||||
if (!expr) {
|
if (!expr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto* cond = expr->ConstantValue();
|
auto* cond = expr->ConstantValue();
|
||||||
if (!cond) {
|
|
||||||
AddError("static assertion condition must be a const-expression",
|
|
||||||
assertion->condition->source);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (auto* ty = cond->Type(); !ty->Is<sem::Bool>()) {
|
if (auto* ty = cond->Type(); !ty->Is<sem::Bool>()) {
|
||||||
AddError(
|
AddError(
|
||||||
"static assertion condition must be a bool, got '" + builder_->FriendlyName(ty) + "'",
|
"static assertion condition must be a bool, got '" + builder_->FriendlyName(ty) + "'",
|
||||||
@ -1458,6 +1463,13 @@ sem::Expression* Resolver::Expression(const ast::Expression* root) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto* constraint = expr_eval_stage_constraint_.constraint) {
|
||||||
|
if (!validator_.EvaluationStage(sem_expr, expr_eval_stage_constraint_.stage,
|
||||||
|
constraint)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
builder_->Sem().Add(expr, sem_expr);
|
builder_->Sem().Add(expr, sem_expr);
|
||||||
if (expr == root) {
|
if (expr == root) {
|
||||||
return sem_expr;
|
return sem_expr;
|
||||||
@ -2818,13 +2830,17 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||||||
// Offset attributes are not part of the WGSL spec, but are emitted
|
// Offset attributes are not part of the WGSL spec, but are emitted
|
||||||
// by the SPIR-V reader.
|
// by the SPIR-V reader.
|
||||||
|
|
||||||
|
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant,
|
||||||
|
"@offset value"};
|
||||||
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
|
|
||||||
auto* materialized = Materialize(Expression(o->expr));
|
auto* materialized = Materialize(Expression(o->expr));
|
||||||
if (!materialized) {
|
if (!materialized) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto const_value = materialized->ConstantValue();
|
auto const_value = materialized->ConstantValue();
|
||||||
if (!const_value) {
|
if (!const_value) {
|
||||||
AddError("'offset' must be const-expression", o->expr->source);
|
AddError("'offset' must be constant expression", o->expr->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
offset = const_value->As<uint64_t>();
|
offset = const_value->As<uint64_t>();
|
||||||
@ -2836,6 +2852,9 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||||||
align = 1;
|
align = 1;
|
||||||
has_offset_attr = true;
|
has_offset_attr = true;
|
||||||
} else if (auto* a = attr->As<ast::StructMemberAlignAttribute>()) {
|
} else if (auto* a = attr->As<ast::StructMemberAlignAttribute>()) {
|
||||||
|
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@align"};
|
||||||
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
|
|
||||||
auto* materialized = Materialize(Expression(a->expr));
|
auto* materialized = Materialize(Expression(a->expr));
|
||||||
if (!materialized) {
|
if (!materialized) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -2847,7 +2866,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||||||
|
|
||||||
auto const_value = materialized->ConstantValue();
|
auto const_value = materialized->ConstantValue();
|
||||||
if (!const_value) {
|
if (!const_value) {
|
||||||
AddError("'align' must be const-expression", a->source);
|
AddError("'align' must be constant expression", a->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto value = const_value->As<AInt>();
|
auto value = const_value->As<AInt>();
|
||||||
@ -2856,16 +2875,19 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||||||
AddError("'align' value must be a positive, power-of-two integer", a->source);
|
AddError("'align' value must be a positive, power-of-two integer", a->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
align = const_value->As<u32>();
|
align = u32(value);
|
||||||
has_align_attr = true;
|
has_align_attr = true;
|
||||||
} else if (auto* s = attr->As<ast::StructMemberSizeAttribute>()) {
|
} else if (auto* s = attr->As<ast::StructMemberSizeAttribute>()) {
|
||||||
|
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@size"};
|
||||||
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
|
|
||||||
auto* materialized = Materialize(Expression(s->expr));
|
auto* materialized = Materialize(Expression(s->expr));
|
||||||
if (!materialized) {
|
if (!materialized) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto const_value = materialized->ConstantValue();
|
auto const_value = materialized->ConstantValue();
|
||||||
if (!const_value) {
|
if (!const_value) {
|
||||||
AddError("'size' must be const-expression", s->expr->source);
|
AddError("'size' must be constant expression", s->expr->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto value = const_value->As<uint64_t>();
|
auto value = const_value->As<uint64_t>();
|
||||||
@ -2876,9 +2898,12 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||||||
s->source);
|
s->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
size = const_value->As<u32>();
|
size = u32(value);
|
||||||
has_size_attr = true;
|
has_size_attr = true;
|
||||||
} else if (auto* l = attr->As<ast::LocationAttribute>()) {
|
} else if (auto* l = attr->As<ast::LocationAttribute>()) {
|
||||||
|
ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "@location"};
|
||||||
|
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
|
||||||
|
|
||||||
auto* materialize = Materialize(Expression(l->expr));
|
auto* materialize = Materialize(Expression(l->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -414,6 +414,15 @@ class Resolver {
|
|||||||
using StructConstructorSig =
|
using StructConstructorSig =
|
||||||
utils::UnorderedKeyWrapper<std::tuple<const sem::Struct*, size_t, sem::EvaluationStage>>;
|
utils::UnorderedKeyWrapper<std::tuple<const sem::Struct*, size_t, sem::EvaluationStage>>;
|
||||||
|
|
||||||
|
/// ExprEvalStageConstraint describes a constraint on when expressions can be evaluated.
|
||||||
|
struct ExprEvalStageConstraint {
|
||||||
|
/// The latest stage that the expression can be evaluated
|
||||||
|
sem::EvaluationStage stage = sem::EvaluationStage::kRuntime;
|
||||||
|
/// The 'thing' that is imposing the contraint. e.g. "var declaration"
|
||||||
|
/// If nullptr, then there is no constraint
|
||||||
|
const char* constraint = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
ProgramBuilder* const builder_;
|
ProgramBuilder* const builder_;
|
||||||
diag::List& diagnostics_;
|
diag::List& diagnostics_;
|
||||||
ConstEval const_eval_;
|
ConstEval const_eval_;
|
||||||
@ -425,10 +434,10 @@ class Resolver {
|
|||||||
std::vector<sem::Function*> entry_points_;
|
std::vector<sem::Function*> entry_points_;
|
||||||
std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
|
std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
|
||||||
utils::Bitset<0> marked_;
|
utils::Bitset<0> marked_;
|
||||||
|
ExprEvalStageConstraint expr_eval_stage_constraint_;
|
||||||
std::unordered_map<OverrideId, const sem::Variable*> override_ids_;
|
std::unordered_map<OverrideId, const sem::Variable*> override_ids_;
|
||||||
std::unordered_map<ArrayConstructorSig, sem::CallTarget*> array_ctors_;
|
std::unordered_map<ArrayConstructorSig, sem::CallTarget*> array_ctors_;
|
||||||
std::unordered_map<StructConstructorSig, sem::CallTarget*> struct_ctors_;
|
std::unordered_map<StructConstructorSig, sem::CallTarget*> struct_ctors_;
|
||||||
|
|
||||||
sem::Function* current_function_ = nullptr;
|
sem::Function* current_function_ = nullptr;
|
||||||
sem::Statement* current_statement_ = nullptr;
|
sem::Statement* current_statement_ = nullptr;
|
||||||
sem::CompoundStatement* current_compound_statement_ = nullptr;
|
sem::CompoundStatement* current_compound_statement_ = nullptr;
|
||||||
|
@ -88,7 +88,8 @@ TEST_F(ResolverStaticAssertTest, Local_NonConst) {
|
|||||||
WrapInFunction(StaticAssert(Expr(Source{{12, 34}}, "V")));
|
WrapInFunction(StaticAssert(Expr(Source{{12, 34}}, "V")));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: static assertion condition must be a const-expression");
|
"12:34 error: static assertion requires a const-expression, but expression is a "
|
||||||
|
"runtime-expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverStaticAssertTest, Local_LessThan_Pass) {
|
TEST_F(ResolverStaticAssertTest, Local_LessThan_Pass) {
|
||||||
|
@ -1391,6 +1391,30 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Validator::EvaluationStage(const sem::Expression* expr,
|
||||||
|
sem::EvaluationStage latest_stage,
|
||||||
|
std::string_view constraint) const {
|
||||||
|
if (expr->Stage() > latest_stage) {
|
||||||
|
auto stage_name = [](sem::EvaluationStage stage) -> std::string {
|
||||||
|
switch (stage) {
|
||||||
|
case sem::EvaluationStage::kRuntime:
|
||||||
|
return "a runtime-expression";
|
||||||
|
case sem::EvaluationStage::kOverride:
|
||||||
|
return "an override-expression";
|
||||||
|
case sem::EvaluationStage::kConstant:
|
||||||
|
return "a const-expression";
|
||||||
|
}
|
||||||
|
return "<unknown>";
|
||||||
|
};
|
||||||
|
|
||||||
|
AddError(std::string(constraint) + " requires " + stage_name(latest_stage) +
|
||||||
|
", but expression is " + stage_name(expr->Stage()),
|
||||||
|
expr->Declaration()->source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Validator::Statements(utils::VectorRef<const ast::Statement*> stmts) const {
|
bool Validator::Statements(utils::VectorRef<const ast::Statement*> stmts) const {
|
||||||
for (auto* stmt : stmts) {
|
for (auto* stmt : stmts) {
|
||||||
if (!sem_.Get(stmt)->IsReachable()) {
|
if (!sem_.Get(stmt)->IsReachable()) {
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "src/tint/ast/pipeline_stage.h"
|
#include "src/tint/ast/pipeline_stage.h"
|
||||||
#include "src/tint/program_builder.h"
|
#include "src/tint/program_builder.h"
|
||||||
#include "src/tint/resolver/sem_helper.h"
|
#include "src/tint/resolver/sem_helper.h"
|
||||||
|
#include "src/tint/sem/evaluation_stage.h"
|
||||||
#include "src/tint/source.h"
|
#include "src/tint/source.h"
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
@ -209,6 +210,15 @@ class Validator {
|
|||||||
/// @returns true on success, false otherwise
|
/// @returns true on success, false otherwise
|
||||||
bool EntryPoint(const sem::Function* func, ast::PipelineStage stage) const;
|
bool EntryPoint(const sem::Function* func, ast::PipelineStage stage) const;
|
||||||
|
|
||||||
|
/// Validates that the expression must not be evaluated any later than @p latest_stage
|
||||||
|
/// @param expr the expression to check
|
||||||
|
/// @param latest_stage the latest evaluation stage that the expression can be evaluated
|
||||||
|
/// @param constraint the 'thing' that is imposing the contraint. e.g. "var declaration"
|
||||||
|
/// @returns true if @p expr is evaluated in or before @p latest_stage, false otherwise
|
||||||
|
bool EvaluationStage(const sem::Expression* expr,
|
||||||
|
sem::EvaluationStage latest_stage,
|
||||||
|
std::string_view constraint) const;
|
||||||
|
|
||||||
/// Validates a for loop
|
/// Validates a for loop
|
||||||
/// @param stmt the for loop statement to validate
|
/// @param stmt the for loop statement to validate
|
||||||
/// @returns true on success, false otherwise
|
/// @returns true on success, false otherwise
|
||||||
|
@ -423,7 +423,9 @@ TEST_F(ResolverVariableValidationTest, ConstInitWithVar) {
|
|||||||
WrapInFunction(v, c);
|
WrapInFunction(v, c);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be const-expression)");
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverVariableValidationTest, ConstInitWithOverride) {
|
TEST_F(ResolverVariableValidationTest, ConstInitWithOverride) {
|
||||||
@ -432,7 +434,9 @@ TEST_F(ResolverVariableValidationTest, ConstInitWithOverride) {
|
|||||||
WrapInFunction(c);
|
WrapInFunction(c);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be const-expression)");
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: const initializer requires a const-expression, but expression is an override-expression)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverVariableValidationTest, ConstInitWithLet) {
|
TEST_F(ResolverVariableValidationTest, ConstInitWithLet) {
|
||||||
@ -441,7 +445,30 @@ TEST_F(ResolverVariableValidationTest, ConstInitWithLet) {
|
|||||||
WrapInFunction(l, c);
|
WrapInFunction(l, c);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be const-expression)");
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverVariableValidationTest, ConstInitWithRuntimeExpr) {
|
||||||
|
// const c = clamp(2, dpdx(0.5), 3);
|
||||||
|
WrapInFunction(Const("c", Call("clamp", 2_a, Call(Source{{12, 34}}, "dpdx", 0.5_a), 3_a)));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: const initializer requires a const-expression, but expression is a runtime-expression)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverVariableValidationTest, ConstInitWithOverrideExpr) {
|
||||||
|
auto* o = Override("v", Expr(1_i));
|
||||||
|
auto* c = Const("c", Add(10_a, Expr(Source{{12, 34}}, o)));
|
||||||
|
WrapInFunction(c);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(
|
||||||
|
r()->error(),
|
||||||
|
R"(12:34 error: const initializer requires a const-expression, but expression is an override-expression)");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -1311,7 +1311,8 @@ fn f() {
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
auto* expect = R"(error: array size is an override-expression, when expected a constant-expression.
|
auto* expect =
|
||||||
|
R"(error: array size is an override-expression, when expected a constant-expression.
|
||||||
Was the SubstituteOverride transform run?)";
|
Was the SubstituteOverride transform run?)";
|
||||||
|
|
||||||
auto got = Run<Robustness>(src);
|
auto got = Run<Robustness>(src);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user