Skip to content

Commit d8f3d49

Browse files
philbertyCohenArthur
authored andcommitted
Add closure binding's tracking to name resolution
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
1 parent 814e20b commit d8f3d49

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

gcc/rust/resolve/rust-ast-resolve-expr.cc

+9
Original file line numberDiff line numberDiff line change
@@ -581,9 +581,13 @@ ResolveExpr::visit (AST::ClosureExprInner &expr)
581581
resolve_closure_param (p);
582582
}
583583

584+
resolver->push_closure_context (expr.get_node_id ());
585+
584586
ResolveExpr::go (expr.get_definition_expr ().get (), prefix,
585587
canonical_prefix);
586588

589+
resolver->pop_closure_context ();
590+
587591
resolver->get_name_scope ().pop ();
588592
resolver->get_type_scope ().pop ();
589593
resolver->get_label_scope ().pop ();
@@ -606,9 +610,14 @@ ResolveExpr::visit (AST::ClosureExprInnerTyped &expr)
606610
}
607611

608612
ResolveType::go (expr.get_return_type ().get ());
613+
614+
resolver->push_closure_context (expr.get_node_id ());
615+
609616
ResolveExpr::go (expr.get_definition_block ().get (), prefix,
610617
canonical_prefix);
611618

619+
resolver->pop_closure_context ();
620+
612621
resolver->get_name_scope ().pop ();
613622
resolver->get_type_scope ().pop ();
614623
resolver->get_label_scope ().pop ();

gcc/rust/resolve/rust-name-resolver.cc

+133
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,39 @@ Scope::lookup (const CanonicalPath &ident, NodeId *id)
169169
return lookup != UNKNOWN_NODEID;
170170
}
171171

172+
bool
173+
Scope::lookup_decl_type (NodeId id, Rib::ItemType *type)
174+
{
175+
bool found = false;
176+
iterate ([&] (const Rib *r) -> bool {
177+
if (r->decl_was_declared_here (id))
178+
{
179+
bool ok = r->lookup_decl_type (id, type);
180+
rust_assert (ok);
181+
found = true;
182+
return false;
183+
}
184+
return true;
185+
});
186+
return found;
187+
}
188+
189+
bool
190+
Scope::lookup_rib_for_decl (NodeId id, const Rib **rib)
191+
{
192+
bool found = false;
193+
iterate ([&] (const Rib *r) -> bool {
194+
if (r->decl_was_declared_here (id))
195+
{
196+
*rib = r;
197+
found = true;
198+
return false;
199+
}
200+
return true;
201+
});
202+
return found;
203+
}
204+
172205
void
173206
Scope::iterate (std::function<bool (Rib *)> cb)
174207
{
@@ -435,6 +468,7 @@ Resolver::insert_resolved_name (NodeId refId, NodeId defId)
435468
{
436469
resolved_names[refId] = defId;
437470
get_name_scope ().append_reference_for_def (refId, defId);
471+
insert_captured_item (defId);
438472
}
439473

440474
bool
@@ -531,5 +565,104 @@ Resolver::lookup_resolved_misc (NodeId refId, NodeId *defId)
531565
return true;
532566
}
533567

568+
void
569+
Resolver::push_closure_context (NodeId closure_expr_id)
570+
{
571+
auto it = closures_capture_mappings.find (closure_expr_id);
572+
rust_assert (it == closures_capture_mappings.end ());
573+
574+
closures_capture_mappings.insert ({closure_expr_id, {}});
575+
closure_context.push_back (closure_expr_id);
576+
}
577+
578+
void
579+
Resolver::pop_closure_context ()
580+
{
581+
rust_assert (!closure_context.empty ());
582+
closure_context.pop_back ();
583+
}
584+
585+
void
586+
Resolver::insert_captured_item (NodeId id)
587+
{
588+
// nothing to do unless we are in a closure context
589+
if (closure_context.empty ())
590+
return;
591+
592+
// check that this is a VAR_DECL?
593+
Scope &name_scope = get_name_scope ();
594+
Rib::ItemType type = Rib::ItemType::Unknown;
595+
bool found = name_scope.lookup_decl_type (id, &type);
596+
if (!found)
597+
return;
598+
599+
// RIB Function { let a, let b } id = 1;
600+
// RIB Closure { let c } id = 2;
601+
// RIB IfStmt { <bind a>} id = 3;
602+
// RIB ... { ... } id = 4
603+
//
604+
// if we have a resolved_node_id of 'a' and the current rib is '3' we know
605+
// this is binding exists in a rib with id < the closure rib id, other wise
606+
// its just a normal binding and we don't care
607+
//
608+
// Problem the node id's dont work like this because the inner most items are
609+
// created first so this means the root will have a larger id and a simple
610+
// less than or greater than check wont work for more complex scoping cases
611+
// but we can use our current rib context to figure this out by checking if
612+
// the rib id the decl we care about exists prior to the rib for the closure
613+
// id
614+
615+
const Rib *r = nullptr;
616+
bool ok = name_scope.lookup_rib_for_decl (id, &r);
617+
rust_assert (ok);
618+
NodeId decl_rib_node_id = r->get_node_id ();
619+
620+
// iterate the closure context and add in the mapping for all to handle the
621+
// case of nested closures
622+
for (auto &closure_expr_id : closure_context)
623+
{
624+
if (!decl_needs_capture (decl_rib_node_id, closure_expr_id, name_scope))
625+
continue;
626+
627+
// is this a valid binding to take
628+
bool is_var_decl_p = type == Rib::ItemType::Var;
629+
if (!is_var_decl_p)
630+
{
631+
// FIXME is this an error case?
632+
return;
633+
}
634+
635+
// append it to the context info
636+
auto it = closures_capture_mappings.find (closure_expr_id);
637+
rust_assert (it != closures_capture_mappings.end ());
638+
639+
it->second.insert (id);
640+
}
641+
}
642+
643+
bool
644+
Resolver::decl_needs_capture (NodeId decl_rib_node_id,
645+
NodeId closure_rib_node_id, const Scope &scope)
646+
{
647+
for (const auto &rib : scope.get_context ())
648+
{
649+
bool rib_is_closure = rib->get_node_id () == closure_rib_node_id;
650+
bool rib_is_decl = rib->get_node_id () == decl_rib_node_id;
651+
if (rib_is_closure)
652+
return false;
653+
else if (rib_is_decl)
654+
return true;
655+
}
656+
return false;
657+
}
658+
659+
const std::set<NodeId> &
660+
Resolver::get_captures (NodeId id) const
661+
{
662+
auto it = closures_capture_mappings.find (id);
663+
rust_assert (it != closures_capture_mappings.end ());
664+
return it->second;
665+
}
666+
534667
} // namespace Resolver
535668
} // namespace Rust

gcc/rust/resolve/rust-name-resolver.h

+17
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class Scope
9696
void insert (const CanonicalPath &ident, NodeId id, Location locus,
9797
Rib::ItemType type = Rib::ItemType::Unknown);
9898
bool lookup (const CanonicalPath &ident, NodeId *id);
99+
bool lookup_decl_type (NodeId id, Rib::ItemType *type);
100+
bool lookup_rib_for_decl (NodeId id, const Rib **rib);
99101

100102
void iterate (std::function<bool (Rib *)> cb);
101103
void iterate (std::function<bool (const Rib *)> cb) const;
@@ -109,6 +111,8 @@ class Scope
109111

110112
CrateNum get_crate_num () const { return crate_num; }
111113

114+
const std::vector<Rib *> &get_context () const { return stack; };
115+
112116
private:
113117
CrateNum crate_num;
114118
std::vector<Rib *> stack;
@@ -191,6 +195,15 @@ class Resolver
191195
return current_module_stack.at (current_module_stack.size () - 2);
192196
}
193197

198+
void push_closure_context (NodeId closure_expr_id);
199+
void pop_closure_context ();
200+
void insert_captured_item (NodeId id);
201+
const std::set<NodeId> &get_captures (NodeId id) const;
202+
203+
protected:
204+
bool decl_needs_capture (NodeId decl_rib_node_id, NodeId closure_rib_node_id,
205+
const Scope &scope);
206+
194207
private:
195208
Resolver ();
196209

@@ -234,6 +247,10 @@ class Resolver
234247

235248
// keep track of the current module scope ids
236249
std::vector<NodeId> current_module_stack;
250+
251+
// captured variables mappings
252+
std::vector<NodeId> closure_context;
253+
std::map<NodeId, std::set<NodeId>> closures_capture_mappings;
237254
};
238255

239256
} // namespace Resolver

0 commit comments

Comments
 (0)