[committed,095/103] gccrs: Add closure binding's tracking to name resolution

Message ID 20230221120230.596966-96-arthur.cohen@embecosm.com
State Unresolved
Headers
Series [committed,001/103] gccrs: Fix missing dead code analysis ICE on local enum definition |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Arthur Cohen Feb. 21, 2023, 12:02 p.m. UTC
  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(+)
  

Patch

diff --git a/gcc/rust/resolve/rust-ast-resolve-expr.cc b/gcc/rust/resolve/rust-ast-resolve-expr.cc
index 293c63f5628..536c828beb4 100644
--- a/gcc/rust/resolve/rust-ast-resolve-expr.cc
+++ b/gcc/rust/resolve/rust-ast-resolve-expr.cc
@@ -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 ();
diff --git a/gcc/rust/resolve/rust-name-resolver.cc b/gcc/rust/resolve/rust-name-resolver.cc
index cf3028ef271..b8ed3538f74 100644
--- a/gcc/rust/resolve/rust-name-resolver.cc
+++ b/gcc/rust/resolve/rust-name-resolver.cc
@@ -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
diff --git a/gcc/rust/resolve/rust-name-resolver.h b/gcc/rust/resolve/rust-name-resolver.h
index 54d59544b1d..f38cbb941bf 100644
--- a/gcc/rust/resolve/rust-name-resolver.h
+++ b/gcc/rust/resolve/rust-name-resolver.h
@@ -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