From: Philip Herron <philip.herron@embecosm.com>
When we have a closure block referencing variables in a parent function,
we must track what these are. We do this by having a context of closures
so if we have a variable reference and its declared in a rib whose node id
is less than the node id of the closure's node id we know it must be a
captured variable. We also need to iterate all possible closure contexts
as we might be in the case of a nested closure.
Addresses #195
gcc/rust/ChangeLog:
* resolve/rust-ast-resolve-expr.cc (ResolveExpr::visit): Use proper closure
contexts.
* resolve/rust-name-resolver.cc (Scope::lookup_decl_type): New function.
(Scope::lookup_rib_for_decl): Likewise.
(Resolver::insert_resolved_name): Insert captured items.
(Resolver::push_closure_context): New function.
(Resolver::pop_closure_context): Likewise.
(Resolver::insert_captured_item): Likewise.
(Resolver::decl_needs_capture): Likewise.
(Resolver::get_captures): Likewise.
* resolve/rust-name-resolver.h: Declare new functions.
---
gcc/rust/resolve/rust-ast-resolve-expr.cc | 9 ++
gcc/rust/resolve/rust-name-resolver.cc | 133 ++++++++++++++++++++++
gcc/rust/resolve/rust-name-resolver.h | 17 +++
3 files changed, 159 insertions(+)
@@ -581,9 +581,13 @@ ResolveExpr::visit (AST::ClosureExprInner &expr)
resolve_closure_param (p);
}
+ resolver->push_closure_context (expr.get_node_id ());
+
ResolveExpr::go (expr.get_definition_expr ().get (), prefix,
canonical_prefix);
+ resolver->pop_closure_context ();
+
resolver->get_name_scope ().pop ();
resolver->get_type_scope ().pop ();
resolver->get_label_scope ().pop ();
@@ -606,9 +610,14 @@ ResolveExpr::visit (AST::ClosureExprInnerTyped &expr)
}
ResolveType::go (expr.get_return_type ().get ());
+
+ resolver->push_closure_context (expr.get_node_id ());
+
ResolveExpr::go (expr.get_definition_block ().get (), prefix,
canonical_prefix);
+ resolver->pop_closure_context ();
+
resolver->get_name_scope ().pop ();
resolver->get_type_scope ().pop ();
resolver->get_label_scope ().pop ();
@@ -169,6 +169,39 @@ Scope::lookup (const CanonicalPath &ident, NodeId *id)
return lookup != UNKNOWN_NODEID;
}
+bool
+Scope::lookup_decl_type (NodeId id, Rib::ItemType *type)
+{
+ bool found = false;
+ iterate ([&] (const Rib *r) -> bool {
+ if (r->decl_was_declared_here (id))
+ {
+ bool ok = r->lookup_decl_type (id, type);
+ rust_assert (ok);
+ found = true;
+ return false;
+ }
+ return true;
+ });
+ return found;
+}
+
+bool
+Scope::lookup_rib_for_decl (NodeId id, const Rib **rib)
+{
+ bool found = false;
+ iterate ([&] (const Rib *r) -> bool {
+ if (r->decl_was_declared_here (id))
+ {
+ *rib = r;
+ found = true;
+ return false;
+ }
+ return true;
+ });
+ return found;
+}
+
void
Scope::iterate (std::function<bool (Rib *)> cb)
{
@@ -435,6 +468,7 @@ Resolver::insert_resolved_name (NodeId refId, NodeId defId)
{
resolved_names[refId] = defId;
get_name_scope ().append_reference_for_def (refId, defId);
+ insert_captured_item (defId);
}
bool
@@ -531,5 +565,104 @@ Resolver::lookup_resolved_misc (NodeId refId, NodeId *defId)
return true;
}
+void
+Resolver::push_closure_context (NodeId closure_expr_id)
+{
+ auto it = closures_capture_mappings.find (closure_expr_id);
+ rust_assert (it == closures_capture_mappings.end ());
+
+ closures_capture_mappings.insert ({closure_expr_id, {}});
+ closure_context.push_back (closure_expr_id);
+}
+
+void
+Resolver::pop_closure_context ()
+{
+ rust_assert (!closure_context.empty ());
+ closure_context.pop_back ();
+}
+
+void
+Resolver::insert_captured_item (NodeId id)
+{
+ // nothing to do unless we are in a closure context
+ if (closure_context.empty ())
+ return;
+
+ // check that this is a VAR_DECL?
+ Scope &name_scope = get_name_scope ();
+ Rib::ItemType type = Rib::ItemType::Unknown;
+ bool found = name_scope.lookup_decl_type (id, &type);
+ if (!found)
+ return;
+
+ // RIB Function { let a, let b } id = 1;
+ // RIB Closure { let c } id = 2;
+ // RIB IfStmt { <bind a>} id = 3;
+ // RIB ... { ... } id = 4
+ //
+ // if we have a resolved_node_id of 'a' and the current rib is '3' we know
+ // this is binding exists in a rib with id < the closure rib id, other wise
+ // its just a normal binding and we don't care
+ //
+ // Problem the node id's dont work like this because the inner most items are
+ // created first so this means the root will have a larger id and a simple
+ // less than or greater than check wont work for more complex scoping cases
+ // but we can use our current rib context to figure this out by checking if
+ // the rib id the decl we care about exists prior to the rib for the closure
+ // id
+
+ const Rib *r = nullptr;
+ bool ok = name_scope.lookup_rib_for_decl (id, &r);
+ rust_assert (ok);
+ NodeId decl_rib_node_id = r->get_node_id ();
+
+ // iterate the closure context and add in the mapping for all to handle the
+ // case of nested closures
+ for (auto &closure_expr_id : closure_context)
+ {
+ if (!decl_needs_capture (decl_rib_node_id, closure_expr_id, name_scope))
+ continue;
+
+ // is this a valid binding to take
+ bool is_var_decl_p = type == Rib::ItemType::Var;
+ if (!is_var_decl_p)
+ {
+ // FIXME is this an error case?
+ return;
+ }
+
+ // append it to the context info
+ auto it = closures_capture_mappings.find (closure_expr_id);
+ rust_assert (it != closures_capture_mappings.end ());
+
+ it->second.insert (id);
+ }
+}
+
+bool
+Resolver::decl_needs_capture (NodeId decl_rib_node_id,
+ NodeId closure_rib_node_id, const Scope &scope)
+{
+ for (const auto &rib : scope.get_context ())
+ {
+ bool rib_is_closure = rib->get_node_id () == closure_rib_node_id;
+ bool rib_is_decl = rib->get_node_id () == decl_rib_node_id;
+ if (rib_is_closure)
+ return false;
+ else if (rib_is_decl)
+ return true;
+ }
+ return false;
+}
+
+const std::set<NodeId> &
+Resolver::get_captures (NodeId id) const
+{
+ auto it = closures_capture_mappings.find (id);
+ rust_assert (it != closures_capture_mappings.end ());
+ return it->second;
+}
+
} // namespace Resolver
} // namespace Rust
@@ -96,6 +96,8 @@ public:
void insert (const CanonicalPath &ident, NodeId id, Location locus,
Rib::ItemType type = Rib::ItemType::Unknown);
bool lookup (const CanonicalPath &ident, NodeId *id);
+ bool lookup_decl_type (NodeId id, Rib::ItemType *type);
+ bool lookup_rib_for_decl (NodeId id, const Rib **rib);
void iterate (std::function<bool (Rib *)> cb);
void iterate (std::function<bool (const Rib *)> cb) const;
@@ -109,6 +111,8 @@ public:
CrateNum get_crate_num () const { return crate_num; }
+ const std::vector<Rib *> &get_context () const { return stack; };
+
private:
CrateNum crate_num;
std::vector<Rib *> stack;
@@ -191,6 +195,15 @@ public:
return current_module_stack.at (current_module_stack.size () - 2);
}
+ void push_closure_context (NodeId closure_expr_id);
+ void pop_closure_context ();
+ void insert_captured_item (NodeId id);
+ const std::set<NodeId> &get_captures (NodeId id) const;
+
+protected:
+ bool decl_needs_capture (NodeId decl_rib_node_id, NodeId closure_rib_node_id,
+ const Scope &scope);
+
private:
Resolver ();
@@ -234,6 +247,10 @@ private:
// keep track of the current module scope ids
std::vector<NodeId> current_module_stack;
+
+ // captured variables mappings
+ std::vector<NodeId> closure_context;
+ std::map<NodeId, std::set<NodeId>> closures_capture_mappings;
};
} // namespace Resolver