resolver: Error when calling shadowed functions
The fix involves unifying the dependency logic of DependencyScanner, which also cleans up the code quite a bit.
Also:
* Correctly resolve parameter type before declaring the parameter variable.
* Fix bonkers typos in utils::Lookup.
Fixed: tint:1442
Change-Id: I77b548e148b461f87ec10e8272c398f6fee297bd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/81102
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/resolver/call_validation_test.cc b/src/resolver/call_validation_test.cc
index e4a479b..71cca02 100644
--- a/src/resolver/call_validation_test.cc
+++ b/src/resolver/call_validation_test.cc
@@ -265,6 +265,24 @@
note: 'v' declared here)");
}
+TEST_F(ResolverCallValidationTest, CallVariableShadowsFunction) {
+ // fn x() {}
+ // fn f() {
+ // var x : i32;
+ // x();
+ // }
+ Func("x", {}, ty.void_(), {});
+ Func("f", {}, ty.void_(),
+ {
+ Decl(Var(Source{{56, 78}}, "x", ty.i32())),
+ CallStmt(Call(Source{{12, 34}}, "x")),
+ });
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(error: cannot call variable 'x'
+56:78 note: 'x' declared here)");
+}
+
} // namespace
} // namespace resolver
} // namespace tint
diff --git a/src/resolver/dependency_graph.cc b/src/resolver/dependency_graph.cc
index d3ac5f4..cbb574d 100644
--- a/src/resolver/dependency_graph.cc
+++ b/src/resolver/dependency_graph.cc
@@ -173,6 +173,16 @@
/// Traverses the function, performing symbol resolution and determining
/// global dependencies.
void TraverseFunction(const ast::Function* func) {
+ // Perform symbol resolution on all the parameter types before registering
+ // the parameters themselves. This allows the case of declaring a parameter
+ // with the same identifier as its type.
+ for (auto* param : func->params) {
+ TraverseType(param->type);
+ }
+ // Resolve the return type
+ TraverseType(func->return_type);
+
+ // Push the scope stack for the parameters and function body.
scope_stack_.Push();
TINT_DEFER(scope_stack_.Pop());
@@ -181,12 +191,10 @@
graph_.shadows.emplace(param, shadows);
}
Declare(param->symbol, param);
- TraverseType(param->type);
}
if (func->body) {
TraverseStatements(func->body->statements);
}
- TraverseType(func->return_type);
}
/// Traverses the statements, performing symbol resolution and determining
@@ -295,38 +303,15 @@
ast::TraverseExpressions(
root, diagnostics_, [&](const ast::Expression* expr) {
if (auto* ident = expr->As<ast::IdentifierExpression>()) {
- auto* node = scope_stack_.Get(ident->symbol);
- if (node == nullptr) {
- if (!IsBuiltin(ident->symbol)) {
- UnknownSymbol(ident->symbol, ident->source, "identifier");
- }
- return ast::TraverseAction::Descend;
- }
- auto global_it = globals_.find(ident->symbol);
- if (global_it != globals_.end() &&
- node == global_it->second->node) {
- AddGlobalDependency(ident, ident->symbol, "identifier",
- "references");
- } else {
- graph_.resolved_symbols.emplace(ident, node);
- }
+ AddDependency(ident, ident->symbol, "identifier", "references");
}
if (auto* call = expr->As<ast::CallExpression>()) {
if (call->target.name) {
- if (!IsBuiltin(call->target.name->symbol)) {
- AddGlobalDependency(call->target.name,
- call->target.name->symbol, "function",
- "calls");
- graph_.resolved_symbols.emplace(
- call,
- utils::Lookup(graph_.resolved_symbols, call->target.name));
- }
+ AddDependency(call->target.name, call->target.name->symbol,
+ "function", "calls");
}
if (call->target.type) {
TraverseType(call->target.type);
- graph_.resolved_symbols.emplace(
- call,
- utils::Lookup(graph_.resolved_symbols, call->target.type));
}
}
if (auto* cast = expr->As<ast::BitcastExpression>()) {
@@ -360,7 +345,7 @@
return;
}
if (auto* tn = ty->As<ast::TypeName>()) {
- AddGlobalDependency(tn, tn->name, "type", "references");
+ AddDependency(tn, tn->name, "type", "references");
return;
}
if (auto* vec = ty->As<ast::Vector>()) {
@@ -416,24 +401,31 @@
UnhandledNode(diagnostics_, attr);
}
- /// Adds the dependency to the currently processed global
- void AddGlobalDependency(const ast::Node* from,
- Symbol to,
- const char* use,
- const char* action) {
- auto global_it = globals_.find(to);
- if (global_it != globals_.end()) {
- auto* global = global_it->second;
+ /// Adds the dependency from `from` to `to`, erroring if `to` cannot be
+ /// resolved.
+ void AddDependency(const ast::Node* from,
+ Symbol to,
+ const char* use,
+ const char* action) {
+ auto* resolved = scope_stack_.Get(to);
+ if (!resolved) {
+ if (!IsBuiltin(to)) {
+ UnknownSymbol(to, from->source, use);
+ return;
+ }
+ }
+
+ if (auto* global = utils::Lookup(globals_, to);
+ global && global->node == resolved) {
if (dependency_edges_
.emplace(DependencyEdge{current_global_, global},
DependencyInfo{from->source, action})
.second) {
current_global_->deps.emplace_back(global);
}
- graph_.resolved_symbols.emplace(from, global->node);
- } else {
- UnknownSymbol(to, from->source, use);
}
+
+ graph_.resolved_symbols.emplace(from, resolved);
}
/// @returns true if `name` is the name of a builtin function
diff --git a/src/resolver/dependency_graph_test.cc b/src/resolver/dependency_graph_test.cc
index 2329d86..102d996 100644
--- a/src/resolver/dependency_graph_test.cc
+++ b/src/resolver/dependency_graph_test.cc
@@ -993,9 +993,9 @@
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- R"(3:1 error: cyclic dependency found: 'S' -> 'A' -> 'S'
-3:10 note: struct 'S' references alias 'A' here
+ R"(2:1 error: cyclic dependency found: 'A' -> 'S' -> 'A'
2:10 note: alias 'A' references struct 'S' here
+3:10 note: struct 'S' references alias 'A' here
4:1 error: cyclic dependency found: 'Z' -> 'L' -> 'Z'
4:10 note: var 'Z' references let 'L' here
5:10 note: let 'L' references var 'Z' here)");
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 9869755..f2f516a 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -268,8 +268,9 @@
return builder_->create<sem::ExternalTexture>();
},
[&](Default) -> sem::Type* {
+ auto* resolved = ResolvedSymbol(ty);
return Switch(
- ResolvedSymbol(ty), //
+ resolved, //
[&](sem::Type* type) { return type; },
[&](sem::Variable* var) {
auto name =
@@ -291,7 +292,10 @@
},
[&](Default) {
TINT_UNREACHABLE(Resolver, diagnostics_)
- << "Unhandled ast::Type: " << ty->TypeInfo().name;
+ << "Unhandled resolved type '"
+ << (resolved ? resolved->TypeInfo().name : "<null>")
+ << "' resolved from ast::Type '" << ty->TypeInfo().name
+ << "'";
return nullptr;
});
});
diff --git a/src/utils/map.h b/src/utils/map.h
index db1b153..a84c9ba 100644
--- a/src/utils/map.h
+++ b/src/utils/map.h
@@ -29,9 +29,9 @@
/// @return the map item value, or `if_missing` if the map does not contain the
/// given key
template <typename K, typename V, typename H, typename C, typename KV = K>
-V Lookup(std::unordered_map<K, V, H, C>& map,
+V Lookup(const std::unordered_map<K, V, H, C>& map,
const KV& key,
- const KV& if_missing = {}) {
+ const V& if_missing = {}) {
auto it = map.find(key);
return it != map.end() ? it->second : if_missing;
}