mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-13 07:06:11 +00:00
resolver: Support shadowing
Add transform::Unshadow to renamed shadowed symbols. Required by a number of other transforms. Replace Resolver symbol resolution with dep-graph. The dependency graph now performs full symbol resolution before the regular resolver pass. Make use of this instead of duplicating the effort. Simplfies code, and actually performs variable shadowing consistently. Fixed: tint:819 Bug: tint:1266 Change-Id: I595d1812aebe1d79d2d32e724ff90de36e74cf4b Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/70523 Reviewed-by: David Neto <dneto@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
@@ -177,6 +177,9 @@ class DependencyScanner {
|
||||
TINT_DEFER(scope_stack_.Pop());
|
||||
|
||||
for (auto* param : func->params) {
|
||||
if (auto* shadows = scope_stack_.Get(param->symbol)) {
|
||||
graph_.shadows.emplace(param, shadows);
|
||||
}
|
||||
Declare(param->symbol, param);
|
||||
TraverseType(param->type);
|
||||
}
|
||||
@@ -255,6 +258,9 @@ class DependencyScanner {
|
||||
return;
|
||||
}
|
||||
if (auto* v = stmt->As<ast::VariableDeclStatement>()) {
|
||||
if (auto* shadows = scope_stack_.Get(v->variable->symbol)) {
|
||||
graph_.shadows.emplace(v->variable, shadows);
|
||||
}
|
||||
TraverseType(v->variable->type);
|
||||
TraverseExpression(v->variable->constructor);
|
||||
Declare(v->variable->symbol, v->variable);
|
||||
|
||||
@@ -55,6 +55,12 @@ struct DependencyGraph {
|
||||
/// Map of ast::IdentifierExpression or ast::TypeName to a type, function, or
|
||||
/// variable that declares the symbol.
|
||||
std::unordered_map<const ast::Node*, const ast::Node*> resolved_symbols;
|
||||
|
||||
/// Map of ast::Variable to a type, function, or variable that is shadowed by
|
||||
/// the variable key. A declaration (X) shadows another (Y) if X and Y use
|
||||
/// the same symbol, and X is declared in a sub-scope of the scope that
|
||||
/// declares Y.
|
||||
std::unordered_map<const ast::Variable*, const ast::Node*> shadows;
|
||||
};
|
||||
|
||||
} // namespace resolver
|
||||
|
||||
@@ -88,6 +88,17 @@ static constexpr SymbolDeclKind kValueDeclKinds[] = {
|
||||
SymbolDeclKind::NestedLocalLet,
|
||||
};
|
||||
|
||||
static constexpr SymbolDeclKind kGlobalDeclKinds[] = {
|
||||
SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalLet, SymbolDeclKind::Alias,
|
||||
SymbolDeclKind::Struct, SymbolDeclKind::Function,
|
||||
};
|
||||
|
||||
static constexpr SymbolDeclKind kLocalDeclKinds[] = {
|
||||
SymbolDeclKind::Parameter, SymbolDeclKind::LocalVar,
|
||||
SymbolDeclKind::LocalLet, SymbolDeclKind::NestedLocalVar,
|
||||
SymbolDeclKind::NestedLocalLet,
|
||||
};
|
||||
|
||||
static constexpr SymbolDeclKind kGlobalValueDeclKinds[] = {
|
||||
SymbolDeclKind::GlobalVar,
|
||||
SymbolDeclKind::GlobalLet,
|
||||
@@ -1171,6 +1182,53 @@ INSTANTIATE_TEST_SUITE_P(Functions,
|
||||
|
||||
} // namespace resolved_symbols
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Shadowing tests
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
namespace shadowing {
|
||||
|
||||
using ResolverDependencyShadowTest = ResolverDependencyGraphTestWithParam<
|
||||
std::tuple<SymbolDeclKind, SymbolDeclKind>>;
|
||||
|
||||
TEST_P(ResolverDependencyShadowTest, Test) {
|
||||
const Symbol symbol = Sym("SYMBOL");
|
||||
const auto outer_kind = std::get<0>(GetParam());
|
||||
const auto inner_kind = std::get<1>(GetParam());
|
||||
|
||||
// Build a symbol declaration and a use of that symbol
|
||||
SymbolTestHelper helper(this);
|
||||
auto* outer = helper.Add(outer_kind, symbol, Source{{12, 34}});
|
||||
helper.Add(inner_kind, symbol, Source{{56, 78}});
|
||||
auto* inner_var = helper.nested_statements.size()
|
||||
? helper.nested_statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable
|
||||
: helper.statements.size()
|
||||
? helper.statements[0]
|
||||
->As<ast::VariableDeclStatement>()
|
||||
->variable
|
||||
: helper.parameters[0];
|
||||
helper.Build();
|
||||
|
||||
EXPECT_EQ(Build().shadows[inner_var], outer);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(LocalShadowGlobal,
|
||||
ResolverDependencyShadowTest,
|
||||
testing::Combine(testing::ValuesIn(kGlobalDeclKinds),
|
||||
testing::ValuesIn(kLocalDeclKinds)));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NestedLocalShadowLocal,
|
||||
ResolverDependencyShadowTest,
|
||||
testing::Combine(testing::Values(SymbolDeclKind::Parameter,
|
||||
SymbolDeclKind::LocalVar,
|
||||
SymbolDeclKind::LocalLet),
|
||||
testing::Values(SymbolDeclKind::NestedLocalVar,
|
||||
SymbolDeclKind::NestedLocalLet)));
|
||||
|
||||
} // namespace shadowing
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AST traversal tests
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -26,25 +26,46 @@ namespace {
|
||||
class ResolverFunctionValidationTest : public resolver::TestHelper,
|
||||
public testing::Test {};
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, ParameterNamesMustBeUnique_pass) {
|
||||
TEST_F(ResolverFunctionValidationTest, DuplicateParameterName) {
|
||||
// fn func_a(common_name : f32) { }
|
||||
// fn func_b(common_name : f32) { }
|
||||
Func("func_a", {Param("common_name", ty.f32())}, ty.void_(), {});
|
||||
Func("func_b", {Param("common_name", ty.f32())}, ty.void_(), {});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "");
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest,
|
||||
ParameterNamesMustBeUniqueShadowsGlobal_pass) {
|
||||
TEST_F(ResolverFunctionValidationTest, ParameterMayShadowGlobal) {
|
||||
// var<private> common_name : f32;
|
||||
// fn func(common_name : f32) { }
|
||||
Global("common_name", ty.f32(), ast::StorageClass::kPrivate);
|
||||
Func("func", {Param("common_name", ty.f32())}, ty.void_(), {});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "");
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, LocalConflictsWithParameter) {
|
||||
// fn func(common_name : f32) {
|
||||
// let common_name = 1;
|
||||
// }
|
||||
Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
|
||||
{Decl(Const(Source{{56, 78}}, "common_name", nullptr, Expr(1)))});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), R"(56:78 error: redeclaration of 'common_name'
|
||||
12:34 note: 'common_name' previously declared here)");
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, NestedLocalMayShadowParameter) {
|
||||
// fn func(common_name : f32) {
|
||||
// {
|
||||
// let common_name = 1;
|
||||
// }
|
||||
// }
|
||||
Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
|
||||
{Block(Decl(Const(Source{{56, 78}}, "common_name", nullptr, Expr(1))))});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest,
|
||||
@@ -57,7 +78,7 @@ TEST_F(ResolverFunctionValidationTest,
|
||||
Decl(var),
|
||||
});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, FunctionUsingSameVariableName_Pass) {
|
||||
@@ -74,7 +95,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionUsingSameVariableName_Pass) {
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest,
|
||||
@@ -95,7 +116,7 @@ TEST_F(ResolverFunctionValidationTest,
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, UnreachableCode_return) {
|
||||
@@ -189,7 +210,7 @@ TEST_F(ResolverFunctionValidationTest,
|
||||
Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest,
|
||||
@@ -213,7 +234,7 @@ TEST_F(ResolverFunctionValidationTest,
|
||||
Return(),
|
||||
});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest,
|
||||
@@ -269,7 +290,7 @@ TEST_F(ResolverFunctionValidationTest,
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest,
|
||||
@@ -298,7 +319,7 @@ TEST_F(ResolverFunctionValidationTest,
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest,
|
||||
@@ -362,7 +383,7 @@ TEST_F(ResolverFunctionValidationTest, NoPipelineEntryPoints) {
|
||||
},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, FunctionVarInitWithParam) {
|
||||
@@ -376,7 +397,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionVarInitWithParam) {
|
||||
Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, FunctionConstInitWithParam) {
|
||||
@@ -390,7 +411,7 @@ TEST_F(ResolverFunctionValidationTest, FunctionConstInitWithParam) {
|
||||
Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
|
||||
ast::DecorationList{});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, FunctionParamsConst) {
|
||||
@@ -414,7 +435,7 @@ TEST_F(ResolverFunctionValidationTest, WorkgroupSize_GoodType_ConstU32) {
|
||||
{Stage(ast::PipelineStage::kCompute),
|
||||
WorkgroupSize(Expr("x"), Expr("y"), Expr(16u))});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_GoodType_U32) {
|
||||
@@ -425,7 +446,7 @@ TEST_F(ResolverFunctionValidationTest, WorkgroupSize_GoodType_U32) {
|
||||
{Stage(ast::PipelineStage::kCompute),
|
||||
WorkgroupSize(Source{{12, 34}}, Expr(1u), Expr(2u), Expr(3u))});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_MismatchTypeU32) {
|
||||
@@ -696,7 +717,7 @@ TEST_F(ResolverFunctionValidationTest, ParameterSotreType_AtomicFree) {
|
||||
auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
|
||||
Func("f", ast::VariableList{bar}, ty.void_(), {});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, ParametersAtLimit) {
|
||||
@@ -706,7 +727,7 @@ TEST_F(ResolverFunctionValidationTest, ParametersAtLimit) {
|
||||
}
|
||||
Func(Source{{12, 34}}, "f", params, ty.void_(), {});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverFunctionValidationTest, ParametersOverLimit) {
|
||||
@@ -736,7 +757,7 @@ TEST_P(ResolverFunctionParameterValidationTest, StorageClass) {
|
||||
Func("f", ast::VariableList{arg}, ty.void_(), {});
|
||||
|
||||
if (param.should_pass) {
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << param.storage_class;
|
||||
|
||||
@@ -74,7 +74,6 @@
|
||||
#include "src/sem/type_conversion.h"
|
||||
#include "src/sem/variable.h"
|
||||
#include "src/utils/defer.h"
|
||||
#include "src/utils/map.h"
|
||||
#include "src/utils/math.h"
|
||||
#include "src/utils/reverse.h"
|
||||
#include "src/utils/scoped_assignment.h"
|
||||
@@ -142,6 +141,8 @@ bool Resolver::ResolveInternal() {
|
||||
|
||||
AllocateOverridableConstantIds();
|
||||
|
||||
SetShadows();
|
||||
|
||||
if (!ValidatePipelineStages()) {
|
||||
return false;
|
||||
}
|
||||
@@ -258,14 +259,8 @@ sem::Type* Resolver::Type(const ast::Type* ty) {
|
||||
if (ty->As<ast::ExternalTexture>()) {
|
||||
return builder_->create<sem::ExternalTexture>();
|
||||
}
|
||||
if (auto* t = ty->As<ast::TypeName>()) {
|
||||
auto it = named_type_info_.find(t->name);
|
||||
if (it == named_type_info_.end()) {
|
||||
AddError("unknown type '" + builder_->Symbols().NameFor(t->name) + "'",
|
||||
t->source);
|
||||
return nullptr;
|
||||
}
|
||||
return it->second.sem;
|
||||
if (auto* type = ResolvedSymbol<sem::Type>(ty)) {
|
||||
return type;
|
||||
}
|
||||
TINT_UNREACHABLE(Resolver, diagnostics_)
|
||||
<< "Unhandled ast::Type: " << ty->TypeInfo().name;
|
||||
@@ -423,7 +418,7 @@ sem::Variable* Resolver::Variable(const ast::Variable* var,
|
||||
}
|
||||
case VariableKind::kLocal: {
|
||||
auto* local = builder_->create<sem::LocalVariable>(
|
||||
var, var_ty, storage_class, access,
|
||||
var, var_ty, storage_class, access, current_statement_,
|
||||
(rhs && var->is_const) ? rhs->ConstantValue() : sem::Constant{});
|
||||
builder_->Sem().Add(var, local);
|
||||
return local;
|
||||
@@ -496,17 +491,23 @@ void Resolver::AllocateOverridableConstantIds() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Resolver::GlobalVariable(const ast::Variable* var) {
|
||||
if (!ValidateNoDuplicateDefinition(var->symbol, var->source,
|
||||
/* check_global_scope_only */ true)) {
|
||||
return false;
|
||||
void Resolver::SetShadows() {
|
||||
for (auto it : dependencies_.shadows) {
|
||||
auto* var = Sem(it.first);
|
||||
if (auto* local = var->As<sem::LocalVariable>()) {
|
||||
local->SetShadows(Sem(it.second));
|
||||
}
|
||||
if (auto* param = var->As<sem::Parameter>()) {
|
||||
param->SetShadows(Sem(it.second));
|
||||
}
|
||||
}
|
||||
} // namespace resolver
|
||||
|
||||
bool Resolver::GlobalVariable(const ast::Variable* var) {
|
||||
auto* sem = Variable(var, VariableKind::kGlobal);
|
||||
if (!sem) {
|
||||
return false;
|
||||
}
|
||||
variable_stack_.Set(var->symbol, sem);
|
||||
|
||||
auto storage_class = sem->StorageClass();
|
||||
if (!var->is_const && storage_class == ast::StorageClass::kNone) {
|
||||
@@ -547,9 +548,6 @@ bool Resolver::GlobalVariable(const ast::Variable* var) {
|
||||
}
|
||||
|
||||
sem::Function* Resolver::Function(const ast::Function* decl) {
|
||||
variable_stack_.Push();
|
||||
TINT_DEFER(variable_stack_.Pop());
|
||||
|
||||
uint32_t parameter_index = 0;
|
||||
std::unordered_map<Symbol, Source> parameter_names;
|
||||
std::vector<sem::Parameter*> parameters;
|
||||
@@ -581,7 +579,6 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
variable_stack_.Set(param->symbol, var);
|
||||
parameters.emplace_back(var);
|
||||
|
||||
auto* var_ty = const_cast<sem::Type*>(var->Type());
|
||||
@@ -684,11 +681,6 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Register the function information _after_ processing the statements. This
|
||||
// allows us to catch a function calling itself when determining the call
|
||||
// information as this function doesn't exist until it's finished.
|
||||
symbol_to_function_[decl->symbol] = func;
|
||||
|
||||
// If this is an entry point, mark all transitively called functions as being
|
||||
// used by this entry point.
|
||||
if (decl->IsEntryPoint()) {
|
||||
@@ -1241,22 +1233,28 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
|
||||
auto* ident = expr->target.name;
|
||||
Mark(ident);
|
||||
|
||||
auto it = named_type_info_.find(ident->symbol);
|
||||
if (it != named_type_info_.end()) {
|
||||
// We have a type.
|
||||
return type_ctor_or_conv(it->second.sem);
|
||||
auto* resolved = ResolvedSymbol(ident);
|
||||
if (auto* ty = As<sem::Type>(resolved)) {
|
||||
return type_ctor_or_conv(ty);
|
||||
}
|
||||
|
||||
if (auto* fn = As<sem::Function>(resolved)) {
|
||||
return FunctionCall(expr, fn, std::move(args));
|
||||
}
|
||||
|
||||
// Not a type, treat as a intrinsic / function call.
|
||||
auto name = builder_->Symbols().NameFor(ident->symbol);
|
||||
auto intrinsic_type = sem::ParseIntrinsicType(name);
|
||||
auto* call = (intrinsic_type != sem::IntrinsicType::kNone)
|
||||
? IntrinsicCall(expr, intrinsic_type, std::move(args),
|
||||
std::move(arg_tys))
|
||||
: FunctionCall(expr, std::move(args));
|
||||
if (intrinsic_type != sem::IntrinsicType::kNone) {
|
||||
return IntrinsicCall(expr, intrinsic_type, std::move(args),
|
||||
std::move(arg_tys));
|
||||
}
|
||||
|
||||
current_function_->AddDirectCall(call);
|
||||
return call;
|
||||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< expr->source << " unresolved CallExpression target:\n"
|
||||
<< "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>")
|
||||
<< "\n"
|
||||
<< "name: " << builder_->Symbols().NameFor(ident->symbol);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sem::Call* Resolver::IntrinsicCall(
|
||||
@@ -1288,37 +1286,27 @@ sem::Call* Resolver::IntrinsicCall(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
current_function_->AddDirectCall(call);
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
sem::Call* Resolver::FunctionCall(
|
||||
const ast::CallExpression* expr,
|
||||
sem::Function* target,
|
||||
const std::vector<const sem::Expression*> args) {
|
||||
auto sym = expr->target.name->symbol;
|
||||
auto name = builder_->Symbols().NameFor(sym);
|
||||
|
||||
auto target_it = symbol_to_function_.find(sym);
|
||||
if (target_it == symbol_to_function_.end()) {
|
||||
if (current_function_ && current_function_->Declaration()->symbol == sym) {
|
||||
AddError("recursion is not permitted. '" + name +
|
||||
"' attempted to call itself.",
|
||||
expr->source);
|
||||
} else {
|
||||
AddError("unable to find called function: " + name, expr->source);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
auto* target = target_it->second;
|
||||
auto* call = builder_->create<sem::Call>(expr, target, std::move(args),
|
||||
current_statement_, sem::Constant{});
|
||||
|
||||
if (current_function_) {
|
||||
target->AddCallSite(call);
|
||||
|
||||
// Note: Requires called functions to be resolved first.
|
||||
// This is currently guaranteed as functions must be declared before
|
||||
// use.
|
||||
current_function_->AddTransitivelyCalledFunction(target);
|
||||
current_function_->AddDirectCall(call);
|
||||
for (auto* transitive_call : target->TransitivelyCalledFunctions()) {
|
||||
current_function_->AddTransitivelyCalledFunction(transitive_call);
|
||||
}
|
||||
@@ -1329,6 +1317,8 @@ sem::Call* Resolver::FunctionCall(
|
||||
}
|
||||
}
|
||||
|
||||
target->AddCallSite(call);
|
||||
|
||||
if (!ValidateFunctionCall(call)) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1476,7 +1466,8 @@ sem::Expression* Resolver::Literal(const ast::LiteralExpression* literal) {
|
||||
|
||||
sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
|
||||
auto symbol = expr->symbol;
|
||||
if (auto* var = variable_stack_.Get(symbol)) {
|
||||
auto* resolved = ResolvedSymbol(expr);
|
||||
if (auto* var = As<sem::Variable>(resolved)) {
|
||||
auto* user =
|
||||
builder_->create<sem::VariableUser>(expr, current_statement_, var);
|
||||
|
||||
@@ -1526,18 +1517,26 @@ sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
|
||||
return user;
|
||||
}
|
||||
|
||||
if (symbol_to_function_.count(symbol)) {
|
||||
if (Is<sem::Function>(resolved)) {
|
||||
AddError("missing '(' for function call", expr->source.End());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string name = builder_->Symbols().NameFor(symbol);
|
||||
if (sem::ParseIntrinsicType(name) != sem::IntrinsicType::kNone) {
|
||||
if (IsIntrinsic(symbol)) {
|
||||
AddError("missing '(' for intrinsic call", expr->source.End());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AddError("unknown identifier: '" + name + "'", expr->source);
|
||||
if (resolved->Is<sem::Type>()) {
|
||||
AddError("missing '(' for type constructor or cast", expr->source.End());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< expr->source << " unresolved identifier:\n"
|
||||
<< "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>")
|
||||
<< "\n"
|
||||
<< "name: " << builder_->Symbols().NameFor(symbol);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1934,11 +1933,6 @@ sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
|
||||
bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
|
||||
Mark(stmt->variable);
|
||||
|
||||
if (!ValidateNoDuplicateDefinition(stmt->variable->symbol,
|
||||
stmt->variable->source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* var = Variable(stmt->variable, VariableKind::kLocal);
|
||||
if (!var) {
|
||||
return false;
|
||||
@@ -1952,7 +1946,6 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
|
||||
}
|
||||
}
|
||||
|
||||
variable_stack_.Set(stmt->variable->symbol, var);
|
||||
if (current_block_) { // Not all statements are inside a block
|
||||
current_block_->AddDecl(stmt->variable);
|
||||
}
|
||||
@@ -1978,12 +1971,6 @@ sem::Type* Resolver::TypeDecl(const ast::TypeDecl* named_type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
named_type_info_.emplace(named_type->name, TypeDeclInfo{named_type, result});
|
||||
|
||||
if (!ValidateTypeDecl(named_type)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
builder_->Sem().Add(named_type, result);
|
||||
return result;
|
||||
}
|
||||
@@ -2081,7 +2068,7 @@ sem::Array* Resolver::Array(const ast::Array* arr) {
|
||||
|
||||
if (auto* ident = count_expr->As<ast::IdentifierExpression>()) {
|
||||
// Make sure the identifier is a non-overridable module-scope constant.
|
||||
auto* var = variable_stack_.Get(ident->symbol);
|
||||
auto* var = ResolvedSymbol<sem::Variable>(ident);
|
||||
if (!var || !var->Is<sem::GlobalVariable>() ||
|
||||
!var->Declaration()->is_const) {
|
||||
AddError("array size identifier must be a module-scope constant",
|
||||
@@ -2244,13 +2231,11 @@ bool Resolver::Scope(sem::CompoundStatement* stmt, F&& callback) {
|
||||
current_statement_ = stmt;
|
||||
current_compound_statement_ = stmt;
|
||||
current_block_ = stmt->As<sem::BlockStatement>();
|
||||
variable_stack_.Push();
|
||||
|
||||
TINT_DEFER({
|
||||
current_block_ = prev_current_block;
|
||||
current_compound_statement_ = prev_current_compound_statement;
|
||||
current_statement_ = prev_current_statement;
|
||||
variable_stack_.Pop();
|
||||
});
|
||||
|
||||
return callback();
|
||||
@@ -2330,6 +2315,11 @@ bool Resolver::IsHostShareable(const sem::Type* type) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Resolver::IsIntrinsic(Symbol symbol) const {
|
||||
std::string name = builder_->Symbols().NameFor(symbol);
|
||||
return sem::ParseIntrinsicType(name) != sem::IntrinsicType::kNone;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Resolver::TypeConversionSig
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "src/sem/constant.h"
|
||||
#include "src/sem/function.h"
|
||||
#include "src/sem/struct.h"
|
||||
#include "src/utils/map.h"
|
||||
#include "src/utils/unique_vector.h"
|
||||
|
||||
namespace tint {
|
||||
@@ -176,6 +177,7 @@ class Resolver {
|
||||
sem::Expression* Expression(const ast::Expression*);
|
||||
sem::Function* Function(const ast::Function*);
|
||||
sem::Call* FunctionCall(const ast::CallExpression*,
|
||||
sem::Function* target,
|
||||
const std::vector<const sem::Expression*> args);
|
||||
sem::Expression* Identifier(const ast::IdentifierExpression*);
|
||||
sem::Call* IntrinsicCall(const ast::CallExpression*,
|
||||
@@ -239,9 +241,6 @@ class Resolver {
|
||||
bool ValidateMatrix(const sem::Matrix* ty, const Source& source);
|
||||
bool ValidateFunctionParameter(const ast::Function* func,
|
||||
const sem::Variable* var);
|
||||
bool ValidateNoDuplicateDefinition(Symbol sym,
|
||||
const Source& source,
|
||||
bool check_global_scope_only = false);
|
||||
bool ValidateParameter(const ast::Function* func, const sem::Variable* var);
|
||||
bool ValidateReturn(const ast::ReturnStatement* ret);
|
||||
bool ValidateStatements(const ast::StatementList& stmts);
|
||||
@@ -264,7 +263,6 @@ class Resolver {
|
||||
const sem::Type* type);
|
||||
bool ValidateArrayConstructorOrCast(const ast::CallExpression* ctor,
|
||||
const sem::Array* arr_type);
|
||||
bool ValidateTypeDecl(const ast::TypeDecl* named_type) const;
|
||||
bool ValidateTextureIntrinsicFunction(const sem::Call* call);
|
||||
bool ValidateNoDuplicateDecorations(const ast::DecorationList& decorations);
|
||||
// sem::Struct is assumed to have at least one member
|
||||
@@ -343,6 +341,10 @@ class Resolver {
|
||||
/// Allocate constant IDs for pipeline-overridable constants.
|
||||
void AllocateOverridableConstantIds();
|
||||
|
||||
/// Set the shadowing information on variable declarations.
|
||||
/// @note this method must only be called after all semantic nodes are built.
|
||||
void SetShadows();
|
||||
|
||||
/// @returns the resolved type of the ast::Expression `expr`
|
||||
/// @param expr the expression
|
||||
sem::Type* TypeOf(const ast::Expression* expr);
|
||||
@@ -410,14 +412,28 @@ class Resolver {
|
||||
template <typename SEM = sem::Info::InferFromAST,
|
||||
typename AST_OR_TYPE = CastableBase>
|
||||
auto* Sem(const AST_OR_TYPE* ast) {
|
||||
auto* sem = builder_->Sem().Get<SEM>(ast);
|
||||
using T = sem::Info::GetResultType<SEM, AST_OR_TYPE>;
|
||||
auto* sem = builder_->Sem().Get(ast);
|
||||
if (!sem) {
|
||||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< "AST node '" << ast->TypeInfo().name << "' had no semantic info\n"
|
||||
<< "At: " << ast->source << "\n"
|
||||
<< "Pointer: " << ast;
|
||||
}
|
||||
return sem;
|
||||
return const_cast<T*>(As<T>(sem));
|
||||
}
|
||||
|
||||
/// @returns true if the symbol is the name of an intrinsic (builtin)
|
||||
/// function.
|
||||
bool IsIntrinsic(Symbol) const;
|
||||
|
||||
/// @returns the resolved symbol (function, type or variable) for the given
|
||||
/// ast::Identifier or ast::TypeName cast to the given semantic type.
|
||||
template <typename SEM = sem::Node>
|
||||
SEM* ResolvedSymbol(const ast::Node* node) {
|
||||
auto* resolved = utils::Lookup(dependencies_.resolved_symbols, node);
|
||||
return resolved ? const_cast<SEM*>(builder_->Sem().Get<SEM>(resolved))
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
struct TypeConversionSig {
|
||||
@@ -456,12 +472,8 @@ class Resolver {
|
||||
diag::List& diagnostics_;
|
||||
std::unique_ptr<IntrinsicTable> const intrinsic_table_;
|
||||
DependencyGraph dependencies_;
|
||||
ScopeStack<sem::Variable*> variable_stack_;
|
||||
std::unordered_map<Symbol, sem::Function*> symbol_to_function_;
|
||||
std::vector<sem::Function*> entry_points_;
|
||||
std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
|
||||
std::unordered_map<Symbol, TypeDeclInfo> named_type_info_;
|
||||
|
||||
std::unordered_set<const ast::Node*> marked_;
|
||||
std::unordered_map<uint32_t, const sem::Variable*> constant_ids_;
|
||||
std::unordered_map<TypeConversionSig,
|
||||
|
||||
@@ -942,11 +942,6 @@ bool Resolver::ValidateInterpolateDecoration(
|
||||
|
||||
bool Resolver::ValidateFunction(const sem::Function* func) {
|
||||
auto* decl = func->Declaration();
|
||||
if (!ValidateNoDuplicateDefinition(decl->symbol, decl->source,
|
||||
/* check_global_scope_only */ true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto workgroup_deco_count = 0;
|
||||
for (auto* deco : decl->decorations) {
|
||||
if (deco->Is<ast::WorkgroupDecoration>()) {
|
||||
@@ -1444,7 +1439,7 @@ bool Resolver::ValidateFunctionCall(const sem::Call* call) {
|
||||
if (param_type->Is<sem::Pointer>()) {
|
||||
auto is_valid = false;
|
||||
if (auto* ident_expr = arg_expr->As<ast::IdentifierExpression>()) {
|
||||
auto* var = variable_stack_.Get(ident_expr->symbol);
|
||||
auto* var = ResolvedSymbol<sem::Variable>(ident_expr);
|
||||
if (!var) {
|
||||
TINT_ICE(Resolver, diagnostics_) << "failed to resolve identifier";
|
||||
return false;
|
||||
@@ -1456,7 +1451,7 @@ bool Resolver::ValidateFunctionCall(const sem::Call* call) {
|
||||
if (unary->op == ast::UnaryOp::kAddressOf) {
|
||||
if (auto* ident_unary =
|
||||
unary->expr->As<ast::IdentifierExpression>()) {
|
||||
auto* var = variable_stack_.Get(ident_unary->symbol);
|
||||
auto* var = ResolvedSymbol<sem::Variable>(ident_unary);
|
||||
if (!var) {
|
||||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< "failed to resolve identifier";
|
||||
@@ -1755,23 +1750,6 @@ bool Resolver::ValidateScalarConstructorOrCast(const ast::CallExpression* ctor,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateTypeDecl(const ast::TypeDecl* named_type) const {
|
||||
auto iter = named_type_info_.find(named_type->name);
|
||||
if (iter == named_type_info_.end()) {
|
||||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< "ValidateTypeDecl called() before TypeDecl()";
|
||||
}
|
||||
if (iter->second.ast != named_type) {
|
||||
AddError("type with the name '" +
|
||||
builder_->Symbols().NameFor(named_type->name) +
|
||||
"' was already declared",
|
||||
named_type->source);
|
||||
AddNote("first declared here", iter->second.ast->source);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidatePipelineStages() {
|
||||
auto check_workgroup_storage = [&](const sem::Function* func,
|
||||
const sem::Function* entry_point) {
|
||||
@@ -2338,22 +2316,21 @@ bool Resolver::ValidateAssignment(const ast::AssignmentStatement* a) {
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#assignment-statement
|
||||
auto const* lhs_ty = TypeOf(a->lhs);
|
||||
|
||||
if (auto* ident = a->lhs->As<ast::IdentifierExpression>()) {
|
||||
if (auto* var = variable_stack_.Get(ident->symbol)) {
|
||||
if (var->Is<sem::Parameter>()) {
|
||||
AddError("cannot assign to function parameter", a->lhs->source);
|
||||
AddNote("'" + builder_->Symbols().NameFor(ident->symbol) +
|
||||
"' is declared here:",
|
||||
var->Declaration()->source);
|
||||
return false;
|
||||
}
|
||||
if (var->Declaration()->is_const) {
|
||||
AddError("cannot assign to const", a->lhs->source);
|
||||
AddNote("'" + builder_->Symbols().NameFor(ident->symbol) +
|
||||
"' is declared here:",
|
||||
var->Declaration()->source);
|
||||
return false;
|
||||
}
|
||||
if (auto* var = ResolvedSymbol<sem::Variable>(a->lhs)) {
|
||||
auto* decl = var->Declaration();
|
||||
if (var->Is<sem::Parameter>()) {
|
||||
AddError("cannot assign to function parameter", a->lhs->source);
|
||||
AddNote("'" + builder_->Symbols().NameFor(decl->symbol) +
|
||||
"' is declared here:",
|
||||
decl->source);
|
||||
return false;
|
||||
}
|
||||
if (decl->is_const) {
|
||||
AddError("cannot assign to const", a->lhs->source);
|
||||
AddNote("'" + builder_->Symbols().NameFor(decl->symbol) +
|
||||
"' is declared here:",
|
||||
decl->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2388,36 +2365,6 @@ bool Resolver::ValidateAssignment(const ast::AssignmentStatement* a) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateNoDuplicateDefinition(Symbol sym,
|
||||
const Source& source,
|
||||
bool check_global_scope_only) {
|
||||
if (check_global_scope_only) {
|
||||
if (auto* var = variable_stack_.Get(sym)) {
|
||||
if (var->Is<sem::GlobalVariable>()) {
|
||||
AddError("redefinition of '" + builder_->Symbols().NameFor(sym) + "'",
|
||||
source);
|
||||
AddNote("previous definition is here", var->Declaration()->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto it = symbol_to_function_.find(sym);
|
||||
if (it != symbol_to_function_.end()) {
|
||||
AddError("redefinition of '" + builder_->Symbols().NameFor(sym) + "'",
|
||||
source);
|
||||
AddNote("previous definition is here", it->second->Declaration()->source);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (auto* var = variable_stack_.Get(sym)) {
|
||||
AddError("redefinition of '" + builder_->Symbols().NameFor(sym) + "'",
|
||||
source);
|
||||
AddNote("previous definition is here", var->Declaration()->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateNoDuplicateDecorations(
|
||||
const ast::DecorationList& decorations) {
|
||||
std::unordered_map<const TypeInfo*, Source> seen;
|
||||
|
||||
@@ -150,25 +150,28 @@ TEST_F(ResolverValidationTest, Expr_ErrUnknownExprType) {
|
||||
|
||||
TEST_F(ResolverValidationTest, Expr_DontCall_Function) {
|
||||
Func("func", {}, ty.void_(), {}, {});
|
||||
auto* ident = create<ast::IdentifierExpression>(
|
||||
Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
|
||||
Symbols().Register("func"));
|
||||
WrapInFunction(ident);
|
||||
WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "func"));
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "3:8 error: missing '(' for function call");
|
||||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, Expr_DontCall_Intrinsic) {
|
||||
auto* ident = create<ast::IdentifierExpression>(
|
||||
Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
|
||||
Symbols().Register("round"));
|
||||
WrapInFunction(ident);
|
||||
WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "round"));
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "3:8 error: missing '(' for intrinsic call");
|
||||
}
|
||||
|
||||
TEST_F(ResolverValidationTest, Expr_DontCall_Type) {
|
||||
Alias("T", ty.u32());
|
||||
WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "T"));
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"3:8 error: missing '(' for type constructor or cast");
|
||||
}
|
||||
|
||||
TEST_F(ResolverValidationTest,
|
||||
AssignmentStmt_InvalidLHS_IntrinsicFunctionName) {
|
||||
// normalize = 2;
|
||||
@@ -220,7 +223,7 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariableGlobalVariable_Pass) {
|
||||
|
||||
Func("my_func", ast::VariableList{}, ty.void_(),
|
||||
{
|
||||
Assign(Expr(Source{Source::Location{12, 34}}, "global_var"), 3.14f),
|
||||
Assign(Expr(Source{{12, 34}}, "global_var"), 3.14f),
|
||||
Return(),
|
||||
});
|
||||
|
||||
@@ -237,7 +240,7 @@ TEST_F(ResolverValidationTest, UsingUndefinedVariableInnerScope_Fail) {
|
||||
auto* cond = Expr(true);
|
||||
auto* body = Block(Decl(var));
|
||||
|
||||
SetSource(Source{Source::Location{12, 34}});
|
||||
SetSource(Source{{12, 34}});
|
||||
auto* lhs = Expr(Source{{12, 34}}, "a");
|
||||
auto* rhs = Expr(3.14f);
|
||||
|
||||
@@ -350,9 +353,7 @@ TEST_F(ResolverValidationTest, StorageClass_TextureExplicitStorageClass) {
|
||||
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
|
||||
Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
|
||||
|
||||
auto* ident = create<ast::IdentifierExpression>(
|
||||
Source{{Source::Location{3, 3}, Source::Location{3, 7}}},
|
||||
Symbols().Register("xyqz"));
|
||||
auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "xyqz");
|
||||
|
||||
auto* mem = MemberAccessor("my_vec", ident);
|
||||
WrapInFunction(mem);
|
||||
@@ -364,9 +365,7 @@ TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
|
||||
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
|
||||
Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
|
||||
|
||||
auto* ident = create<ast::IdentifierExpression>(
|
||||
Source{{Source::Location{3, 3}, Source::Location{3, 7}}},
|
||||
Symbols().Register("rgyw"));
|
||||
auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "rgyw");
|
||||
|
||||
auto* mem = MemberAccessor("my_vec", ident);
|
||||
WrapInFunction(mem);
|
||||
@@ -380,9 +379,7 @@ TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
|
||||
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
|
||||
Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
|
||||
|
||||
auto* ident = create<ast::IdentifierExpression>(
|
||||
Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
|
||||
Symbols().Register("zzzzz"));
|
||||
auto* ident = Expr(Source{{{3, 3}, {3, 8}}}, "zzzzz");
|
||||
auto* mem = MemberAccessor("my_vec", ident);
|
||||
WrapInFunction(mem);
|
||||
|
||||
@@ -393,8 +390,7 @@ TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
|
||||
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadIndex) {
|
||||
Global("my_vec", ty.vec2<f32>(), ast::StorageClass::kPrivate);
|
||||
|
||||
auto* ident = create<ast::IdentifierExpression>(Source{{3, 3}},
|
||||
Symbols().Register("z"));
|
||||
auto* ident = Expr(Source{{3, 3}}, "z");
|
||||
auto* mem = MemberAccessor("my_vec", ident);
|
||||
WrapInFunction(mem);
|
||||
|
||||
@@ -406,9 +402,7 @@ TEST_F(ResolverValidationTest, Expr_MemberAccessor_BadParent) {
|
||||
// var param: vec4<f32>
|
||||
// let ret: f32 = *(¶m).x;
|
||||
auto* param = Var("param", ty.vec4<f32>());
|
||||
auto* x = create<ast::IdentifierExpression>(
|
||||
Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
|
||||
Symbols().Register("x"));
|
||||
auto* x = Expr(Source{{{3, 3}, {3, 8}}}, "x");
|
||||
|
||||
auto* addressOf_expr = AddressOf(Source{{12, 34}}, param);
|
||||
auto* accessor_expr = MemberAccessor(addressOf_expr, x);
|
||||
@@ -430,9 +424,7 @@ TEST_F(ResolverValidationTest, EXpr_MemberAccessor_FuncGoodParent) {
|
||||
auto* p =
|
||||
Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
|
||||
auto* star_p = Deref(p);
|
||||
auto* z = create<ast::IdentifierExpression>(
|
||||
Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
|
||||
Symbols().Register("z"));
|
||||
auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
|
||||
auto* accessor_expr = MemberAccessor(star_p, z);
|
||||
auto* x = Var("x", ty.f32(), accessor_expr);
|
||||
Func("func", {p}, ty.f32(), {Decl(x), Return(x)});
|
||||
@@ -446,9 +438,7 @@ TEST_F(ResolverValidationTest, EXpr_MemberAccessor_FuncBadParent) {
|
||||
// }
|
||||
auto* p =
|
||||
Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
|
||||
auto* z = create<ast::IdentifierExpression>(
|
||||
Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
|
||||
Symbols().Register("z"));
|
||||
auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
|
||||
auto* accessor_expr = MemberAccessor(p, z);
|
||||
auto* star_p = Deref(accessor_expr);
|
||||
auto* x = Var("x", ty.f32(), star_p);
|
||||
@@ -473,7 +463,7 @@ TEST_F(ResolverValidationTest,
|
||||
// }
|
||||
// }
|
||||
|
||||
auto error_loc = Source{Source::Location{12, 34}};
|
||||
auto error_loc = Source{{12, 34}};
|
||||
auto* body =
|
||||
Block(create<ast::ContinueStatement>(),
|
||||
Decl(error_loc, Var("z", ty.i32(), ast::StorageClass::kNone)),
|
||||
@@ -524,9 +514,9 @@ TEST_F(ResolverValidationTest,
|
||||
// }
|
||||
// }
|
||||
|
||||
auto cont_loc = Source{Source::Location{12, 34}};
|
||||
auto decl_loc = Source{Source::Location{56, 78}};
|
||||
auto ref_loc = Source{Source::Location{90, 12}};
|
||||
auto cont_loc = Source{{12, 34}};
|
||||
auto decl_loc = Source{{56, 78}};
|
||||
auto ref_loc = Source{{90, 12}};
|
||||
auto* body =
|
||||
Block(If(Expr(true), Block(create<ast::ContinueStatement>(cont_loc))),
|
||||
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
||||
@@ -556,9 +546,9 @@ TEST_F(
|
||||
// }
|
||||
// }
|
||||
|
||||
auto cont_loc = Source{Source::Location{12, 34}};
|
||||
auto decl_loc = Source{Source::Location{56, 78}};
|
||||
auto ref_loc = Source{Source::Location{90, 12}};
|
||||
auto cont_loc = Source{{12, 34}};
|
||||
auto decl_loc = Source{{56, 78}};
|
||||
auto ref_loc = Source{{90, 12}};
|
||||
auto* body =
|
||||
Block(If(Expr(true), Block(create<ast::ContinueStatement>(cont_loc))),
|
||||
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
||||
@@ -590,9 +580,9 @@ TEST_F(ResolverValidationTest,
|
||||
// }
|
||||
// }
|
||||
|
||||
auto cont_loc = Source{Source::Location{12, 34}};
|
||||
auto decl_loc = Source{Source::Location{56, 78}};
|
||||
auto ref_loc = Source{Source::Location{90, 12}};
|
||||
auto cont_loc = Source{{12, 34}};
|
||||
auto decl_loc = Source{{56, 78}};
|
||||
auto ref_loc = Source{{90, 12}};
|
||||
auto* body =
|
||||
Block(If(Expr(true), Block(create<ast::ContinueStatement>(cont_loc))),
|
||||
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
||||
@@ -623,9 +613,9 @@ TEST_F(ResolverValidationTest,
|
||||
// }
|
||||
// }
|
||||
|
||||
auto cont_loc = Source{Source::Location{12, 34}};
|
||||
auto decl_loc = Source{Source::Location{56, 78}};
|
||||
auto ref_loc = Source{Source::Location{90, 12}};
|
||||
auto cont_loc = Source{{12, 34}};
|
||||
auto decl_loc = Source{{56, 78}};
|
||||
auto ref_loc = Source{{90, 12}};
|
||||
auto* body =
|
||||
Block(If(Expr(true), Block(create<ast::ContinueStatement>(cont_loc))),
|
||||
Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
|
||||
@@ -724,7 +714,7 @@ TEST_F(ResolverTest, Stmt_Loop_ContinueInLoopBodyAfterDecl_UsageInContinuing) {
|
||||
// }
|
||||
// }
|
||||
|
||||
auto error_loc = Source{Source::Location{12, 34}};
|
||||
auto error_loc = Source{{12, 34}};
|
||||
auto* body = Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
|
||||
create<ast::ContinueStatement>());
|
||||
auto* continuing = Block(Assign(Expr(error_loc, "z"), 2));
|
||||
|
||||
@@ -58,7 +58,7 @@ TEST_F(ResolverVarLetTest, TypeOfVar) {
|
||||
Decl(a),
|
||||
});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
// `var` declarations are always of reference type
|
||||
ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
|
||||
@@ -114,7 +114,7 @@ TEST_F(ResolverVarLetTest, TypeOfLet) {
|
||||
Decl(p),
|
||||
});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
// `let` declarations are always of the storage type
|
||||
EXPECT_TRUE(TypeOf(i)->Is<sem::I32>());
|
||||
@@ -153,7 +153,7 @@ TEST_F(ResolverVarLetTest, DefaultVarStorageClass) {
|
||||
|
||||
WrapInFunction(function);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
ASSERT_TRUE(TypeOf(function)->Is<sem::Reference>());
|
||||
ASSERT_TRUE(TypeOf(private_)->Is<sem::Reference>());
|
||||
@@ -187,7 +187,7 @@ TEST_F(ResolverVarLetTest, ExplicitVarStorageClass) {
|
||||
create<ast::GroupDecoration>(0),
|
||||
});
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
|
||||
|
||||
@@ -222,7 +222,7 @@ TEST_F(ResolverVarLetTest, LetInheritsAccessFromOriginatingVariable) {
|
||||
|
||||
WrapInFunction(ptr);
|
||||
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
|
||||
ASSERT_TRUE(TypeOf(ptr)->Is<sem::Pointer>());
|
||||
@@ -232,6 +232,384 @@ TEST_F(ResolverVarLetTest, LetInheritsAccessFromOriginatingVariable) {
|
||||
EXPECT_EQ(TypeOf(ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, LocalShadowsAlias) {
|
||||
// type a = i32;
|
||||
//
|
||||
// fn X() {
|
||||
// var a = false;
|
||||
// }
|
||||
//
|
||||
// fn Y() {
|
||||
// let a = true;
|
||||
// }
|
||||
|
||||
auto* t = Alias("a", ty.i32());
|
||||
auto* v = Var("a", nullptr, Expr(false));
|
||||
auto* l = Const("a", nullptr, Expr(false));
|
||||
Func("X", {}, ty.void_(), {Decl(v)});
|
||||
Func("Y", {}, ty.void_(), {Decl(l)});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* type_t = Sem().Get(t);
|
||||
auto* local_v = Sem().Get<sem::LocalVariable>(v);
|
||||
auto* local_l = Sem().Get<sem::LocalVariable>(l);
|
||||
|
||||
ASSERT_NE(local_v, nullptr);
|
||||
ASSERT_NE(local_l, nullptr);
|
||||
|
||||
EXPECT_EQ(local_v->Shadows(), type_t);
|
||||
EXPECT_EQ(local_l->Shadows(), type_t);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, LocalShadowsStruct) {
|
||||
// struct a {
|
||||
// m : i32;
|
||||
// };
|
||||
//
|
||||
// fn X() {
|
||||
// var a = true;
|
||||
// }
|
||||
//
|
||||
// fn Y() {
|
||||
// let a = false;
|
||||
// }
|
||||
|
||||
auto* t = Structure("a", {Member("m", ty.i32())});
|
||||
auto* v = Var("a", nullptr, Expr(false));
|
||||
auto* l = Const("a", nullptr, Expr(false));
|
||||
Func("X", {}, ty.void_(), {Decl(v)});
|
||||
Func("Y", {}, ty.void_(), {Decl(l)});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* type_t = Sem().Get(t);
|
||||
auto* local_v = Sem().Get<sem::LocalVariable>(v);
|
||||
auto* local_l = Sem().Get<sem::LocalVariable>(l);
|
||||
|
||||
ASSERT_NE(local_v, nullptr);
|
||||
ASSERT_NE(local_l, nullptr);
|
||||
|
||||
EXPECT_EQ(local_v->Shadows(), type_t);
|
||||
EXPECT_EQ(local_l->Shadows(), type_t);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, LocalShadowsFunction) {
|
||||
// fn a() {
|
||||
// var a = true;
|
||||
// }
|
||||
//
|
||||
// fn b() {
|
||||
// let b = false;
|
||||
// }
|
||||
|
||||
auto* v = Var("a", nullptr, Expr(false));
|
||||
auto* l = Const("b", nullptr, Expr(false));
|
||||
auto* fa = Func("a", {}, ty.void_(), {Decl(v)});
|
||||
auto* fb = Func("b", {}, ty.void_(), {Decl(l)});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* local_v = Sem().Get<sem::LocalVariable>(v);
|
||||
auto* local_l = Sem().Get<sem::LocalVariable>(l);
|
||||
auto* func_a = Sem().Get(fa);
|
||||
auto* func_b = Sem().Get(fb);
|
||||
|
||||
ASSERT_NE(local_v, nullptr);
|
||||
ASSERT_NE(local_l, nullptr);
|
||||
ASSERT_NE(func_a, nullptr);
|
||||
ASSERT_NE(func_b, nullptr);
|
||||
|
||||
EXPECT_EQ(local_v->Shadows(), func_a);
|
||||
EXPECT_EQ(local_l->Shadows(), func_b);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, LocalShadowsGlobalVar) {
|
||||
// var<private> a : i32;
|
||||
//
|
||||
// fn X() {
|
||||
// var a = a;
|
||||
// }
|
||||
//
|
||||
// fn Y() {
|
||||
// let a = a;
|
||||
// }
|
||||
|
||||
auto* g = Global("a", ty.i32(), ast::StorageClass::kPrivate);
|
||||
auto* v = Var("a", nullptr, Expr("a"));
|
||||
auto* l = Const("a", nullptr, Expr("a"));
|
||||
Func("X", {}, ty.void_(), {Decl(v)});
|
||||
Func("Y", {}, ty.void_(), {Decl(l)});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* global = Sem().Get(g);
|
||||
auto* local_v = Sem().Get<sem::LocalVariable>(v);
|
||||
auto* local_l = Sem().Get<sem::LocalVariable>(l);
|
||||
|
||||
ASSERT_NE(local_v, nullptr);
|
||||
ASSERT_NE(local_l, nullptr);
|
||||
|
||||
EXPECT_EQ(local_v->Shadows(), global);
|
||||
EXPECT_EQ(local_l->Shadows(), global);
|
||||
|
||||
auto* user_v =
|
||||
Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
|
||||
auto* user_l =
|
||||
Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
|
||||
|
||||
ASSERT_NE(user_v, nullptr);
|
||||
ASSERT_NE(user_l, nullptr);
|
||||
|
||||
EXPECT_EQ(user_v->Variable(), global);
|
||||
EXPECT_EQ(user_l->Variable(), global);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, LocalShadowsGlobalLet) {
|
||||
// let a : i32 = 1;
|
||||
//
|
||||
// fn X() {
|
||||
// var a = (a == 123);
|
||||
// }
|
||||
//
|
||||
// fn Y() {
|
||||
// let a = (a == 321);
|
||||
// }
|
||||
|
||||
auto* g = GlobalConst("a", ty.i32(), Expr(1));
|
||||
auto* v = Var("a", nullptr, Expr("a"));
|
||||
auto* l = Const("a", nullptr, Expr("a"));
|
||||
Func("X", {}, ty.void_(), {Decl(v)});
|
||||
Func("Y", {}, ty.void_(), {Decl(l)});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* global = Sem().Get(g);
|
||||
auto* local_v = Sem().Get<sem::LocalVariable>(v);
|
||||
auto* local_l = Sem().Get<sem::LocalVariable>(l);
|
||||
|
||||
ASSERT_NE(local_v, nullptr);
|
||||
ASSERT_NE(local_l, nullptr);
|
||||
|
||||
EXPECT_EQ(local_v->Shadows(), global);
|
||||
EXPECT_EQ(local_l->Shadows(), global);
|
||||
|
||||
auto* user_v =
|
||||
Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
|
||||
auto* user_l =
|
||||
Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
|
||||
|
||||
ASSERT_NE(user_v, nullptr);
|
||||
ASSERT_NE(user_l, nullptr);
|
||||
|
||||
EXPECT_EQ(user_v->Variable(), global);
|
||||
EXPECT_EQ(user_l->Variable(), global);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, LocalShadowsLocalVar) {
|
||||
// fn X() {
|
||||
// var a : i32;
|
||||
// {
|
||||
// var a = a;
|
||||
// }
|
||||
// {
|
||||
// let a = a;
|
||||
// }
|
||||
// }
|
||||
|
||||
auto* s = Var("a", ty.i32(), Expr(1));
|
||||
auto* v = Var("a", nullptr, Expr("a"));
|
||||
auto* l = Const("a", nullptr, Expr("a"));
|
||||
Func("X", {}, ty.void_(), {Decl(s), Block(Decl(v)), Block(Decl(l))});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* local_s = Sem().Get<sem::LocalVariable>(s);
|
||||
auto* local_v = Sem().Get<sem::LocalVariable>(v);
|
||||
auto* local_l = Sem().Get<sem::LocalVariable>(l);
|
||||
|
||||
ASSERT_NE(local_s, nullptr);
|
||||
ASSERT_NE(local_v, nullptr);
|
||||
ASSERT_NE(local_l, nullptr);
|
||||
|
||||
EXPECT_EQ(local_v->Shadows(), local_s);
|
||||
EXPECT_EQ(local_l->Shadows(), local_s);
|
||||
|
||||
auto* user_v =
|
||||
Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
|
||||
auto* user_l =
|
||||
Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
|
||||
|
||||
ASSERT_NE(user_v, nullptr);
|
||||
ASSERT_NE(user_l, nullptr);
|
||||
|
||||
EXPECT_EQ(user_v->Variable(), local_s);
|
||||
EXPECT_EQ(user_l->Variable(), local_s);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, LocalShadowsLocalLet) {
|
||||
// fn X() {
|
||||
// let a = 1;
|
||||
// {
|
||||
// var a = (a == 123);
|
||||
// }
|
||||
// {
|
||||
// let a = (a == 321);
|
||||
// }
|
||||
// }
|
||||
|
||||
auto* s = Const("a", ty.i32(), Expr(1));
|
||||
auto* v = Var("a", nullptr, Expr("a"));
|
||||
auto* l = Const("a", nullptr, Expr("a"));
|
||||
Func("X", {}, ty.void_(), {Decl(s), Block(Decl(v)), Block(Decl(l))});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* local_s = Sem().Get<sem::LocalVariable>(s);
|
||||
auto* local_v = Sem().Get<sem::LocalVariable>(v);
|
||||
auto* local_l = Sem().Get<sem::LocalVariable>(l);
|
||||
|
||||
ASSERT_NE(local_s, nullptr);
|
||||
ASSERT_NE(local_v, nullptr);
|
||||
ASSERT_NE(local_l, nullptr);
|
||||
|
||||
EXPECT_EQ(local_v->Shadows(), local_s);
|
||||
EXPECT_EQ(local_l->Shadows(), local_s);
|
||||
|
||||
auto* user_v =
|
||||
Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
|
||||
auto* user_l =
|
||||
Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
|
||||
|
||||
ASSERT_NE(user_v, nullptr);
|
||||
ASSERT_NE(user_l, nullptr);
|
||||
|
||||
EXPECT_EQ(user_v->Variable(), local_s);
|
||||
EXPECT_EQ(user_l->Variable(), local_s);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, LocalShadowsParam) {
|
||||
// fn F(a : i32) {
|
||||
// {
|
||||
// var a = a;
|
||||
// }
|
||||
// {
|
||||
// let a = a;
|
||||
// }
|
||||
// }
|
||||
|
||||
auto* p = Param("a", ty.i32());
|
||||
auto* v = Var("a", nullptr, Expr("a"));
|
||||
auto* l = Const("a", nullptr, Expr("a"));
|
||||
Func("X", {p}, ty.void_(), {Block(Decl(v)), Block(Decl(l))});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* param = Sem().Get<sem::Parameter>(p);
|
||||
auto* local_v = Sem().Get<sem::LocalVariable>(v);
|
||||
auto* local_l = Sem().Get<sem::LocalVariable>(l);
|
||||
|
||||
ASSERT_NE(param, nullptr);
|
||||
ASSERT_NE(local_v, nullptr);
|
||||
ASSERT_NE(local_l, nullptr);
|
||||
|
||||
EXPECT_EQ(local_v->Shadows(), param);
|
||||
EXPECT_EQ(local_l->Shadows(), param);
|
||||
|
||||
auto* user_v =
|
||||
Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
|
||||
auto* user_l =
|
||||
Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
|
||||
|
||||
ASSERT_NE(user_v, nullptr);
|
||||
ASSERT_NE(user_l, nullptr);
|
||||
|
||||
EXPECT_EQ(user_v->Variable(), param);
|
||||
EXPECT_EQ(user_l->Variable(), param);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, ParamShadowsFunction) {
|
||||
// fn a(a : bool) {
|
||||
// }
|
||||
|
||||
auto* p = Param("a", ty.bool_());
|
||||
auto* f = Func("a", {p}, ty.void_(), {});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* func = Sem().Get(f);
|
||||
auto* param = Sem().Get<sem::Parameter>(p);
|
||||
|
||||
ASSERT_NE(func, nullptr);
|
||||
ASSERT_NE(param, nullptr);
|
||||
|
||||
EXPECT_EQ(param->Shadows(), func);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, ParamShadowsGlobalVar) {
|
||||
// var<private> a : i32;
|
||||
//
|
||||
// fn F(a : bool) {
|
||||
// }
|
||||
|
||||
auto* g = Global("a", ty.i32(), ast::StorageClass::kPrivate);
|
||||
auto* p = Param("a", ty.bool_());
|
||||
Func("F", {p}, ty.void_(), {});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* global = Sem().Get(g);
|
||||
auto* param = Sem().Get<sem::Parameter>(p);
|
||||
|
||||
ASSERT_NE(global, nullptr);
|
||||
ASSERT_NE(param, nullptr);
|
||||
|
||||
EXPECT_EQ(param->Shadows(), global);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, ParamShadowsGlobalLet) {
|
||||
// let a : i32 = 1;
|
||||
//
|
||||
// fn F(a : bool) {
|
||||
// }
|
||||
|
||||
auto* g = GlobalConst("a", ty.i32(), Expr(1));
|
||||
auto* p = Param("a", ty.bool_());
|
||||
Func("F", {p}, ty.void_(), {});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* global = Sem().Get(g);
|
||||
auto* param = Sem().Get<sem::Parameter>(p);
|
||||
|
||||
ASSERT_NE(global, nullptr);
|
||||
ASSERT_NE(param, nullptr);
|
||||
|
||||
EXPECT_EQ(param->Shadows(), global);
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetTest, ParamShadowsAlias) {
|
||||
// type a = i32;
|
||||
//
|
||||
// fn F(a : a) {
|
||||
// }
|
||||
|
||||
auto* a = Alias("a", ty.i32());
|
||||
auto* p = Param("a", ty.type_name("a"));
|
||||
Func("F", {p}, ty.void_(), {});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* alias = Sem().Get(a);
|
||||
auto* param = Sem().Get<sem::Parameter>(p);
|
||||
|
||||
ASSERT_NE(alias, nullptr);
|
||||
ASSERT_NE(param, nullptr);
|
||||
|
||||
EXPECT_EQ(param->Shadows(), alias);
|
||||
EXPECT_EQ(param->Type(), alias);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace resolver
|
||||
} // namespace tint
|
||||
|
||||
@@ -175,10 +175,7 @@ TEST_F(ResolverVarLetValidationTest, GlobalVarRedeclaredAsLocal) {
|
||||
WrapInFunction(Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone,
|
||||
Expr(2.0f)));
|
||||
|
||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error: redefinition of 'v'\nnote: previous definition is here");
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetValidationTest, VarRedeclaredInInnerBlock) {
|
||||
@@ -194,10 +191,7 @@ TEST_F(ResolverVarLetValidationTest, VarRedeclaredInInnerBlock) {
|
||||
|
||||
WrapInFunction(outer_body);
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error: redefinition of 'v'\nnote: previous definition is here");
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetValidationTest, VarRedeclaredInIfBlock) {
|
||||
@@ -219,10 +213,7 @@ TEST_F(ResolverVarLetValidationTest, VarRedeclaredInIfBlock) {
|
||||
|
||||
WrapInFunction(outer_body);
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
"12:34 error: redefinition of 'v'\nnote: previous definition is here");
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverVarLetValidationTest, InferredPtrStorageAccessMismatch) {
|
||||
|
||||
Reference in New Issue
Block a user