Skip to content

Commit

Permalink
gccrs: Add closure binding's tracking to name resolution
Browse files Browse the repository at this point in the history
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 Rust-GCC#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.
  • Loading branch information
philberty authored and CohenArthur committed Feb 14, 2023
1 parent c833686 commit 5cdfabe
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 0 deletions.
9 changes: 9 additions & 0 deletions gcc/rust/resolve/rust-ast-resolve-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();
Expand All @@ -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 ();
Expand Down
133 changes: 133 additions & 0 deletions gcc/rust/resolve/rust-name-resolver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
17 changes: 17 additions & 0 deletions gcc/rust/resolve/rust-name-resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ class Scope
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;
Expand All @@ -109,6 +111,8 @@ class Scope

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;
Expand Down Expand Up @@ -191,6 +195,15 @@ class Resolver
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 ();

Expand Down Expand Up @@ -234,6 +247,10 @@ class Resolver

// 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
Expand Down

0 comments on commit 5cdfabe

Please sign in to comment.