[gccrs COMMIT] gccrs: Fix handling where clauses into two passes

gerris.rs@gmail.com gerris.rs@gmail.com
Tue Sep 8 13:14:36 GMT 2026


From: Philip Herron <herron.philip@googlemail.com>

Where constraints are annoying so we need to do it in two passes.

 1. Register bounds on the regular single segment TypePaths so like P: Deref
 2. Then register regular type bindings like I::Item or Item = U::Item

As they can be order dependant as the initial predicate needs added to then
param decls then onto whatever projections after.

Fixes Rust-GCC/gccrs#4864

gcc/rust/ChangeLog:

	* hir/tree/rust-hir-item.h: missing non const helper.
	* typecheck/rust-hir-type-check-base.h: new defer_bindings.
	* typecheck/rust-hir-type-check-implitem.cc (TypeCheckTopLevelExternItem::visit): new helper
	(TypeCheckImplItem::resolve_function_signature): Likewise.
	(TypeCheckImplItem::visit): Likewise.
	* typecheck/rust-hir-type-check-item.cc (TypeCheckItem::visit):  Likewise.
	(TypeCheckItem::resolve_function_signature):  Likewise.
	(TypeCheckItem::resolve_impl_block_substitutions):  Likewise.
	* typecheck/rust-hir-type-check-type.cc (ResolveWhereClauseItem::Resolve): new helper
	(ResolveWhereClauseItem::visit): support defer.
	* typecheck/rust-hir-type-check-type.h: update header.
	* typecheck/rust-hir-type-check.cc (TraitItemReference::get_type_from_fn): call new helper
	* typecheck/rust-tyty-bounds.cc: defer flag

gcc/testsuite/ChangeLog:

	* rust/compile/issue-4864.rs: New test.

Signed-off-by: Philip Herron <herron.philip@googlemail.com>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: https://github.com/Rust-GCC/gccrs/commit/112b46e3f8e3b81f28e90a7c8ebd1789592d31cb

The commit has been mentioned in the following issue(s):
 - Rust-GCC/gccrs#4864: https://github.com/Rust-GCC/gccrs/issues/4864

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4875

 gcc/rust/hir/tree/rust-hir-item.h             |  2 +
 gcc/rust/typecheck/rust-hir-type-check-base.h |  3 +-
 .../typecheck/rust-hir-type-check-implitem.cc | 24 ++---
 .../typecheck/rust-hir-type-check-item.cc     | 36 +++-----
 .../typecheck/rust-hir-type-check-type.cc     | 89 ++++++++++++++++++-
 gcc/rust/typecheck/rust-hir-type-check-type.h |  8 +-
 gcc/rust/typecheck/rust-hir-type-check.cc     | 10 +--
 gcc/rust/typecheck/rust-tyty-bounds.cc        | 13 ++-
 gcc/testsuite/rust/compile/issue-4864.rs      | 22 +++++
 9 files changed, 150 insertions(+), 57 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/issue-4864.rs

diff --git a/gcc/rust/hir/tree/rust-hir-item.h b/gcc/rust/hir/tree/rust-hir-item.h
index a956e0b53..9a572bf92 100644
--- a/gcc/rust/hir/tree/rust-hir-item.h
+++ b/gcc/rust/hir/tree/rust-hir-item.h
@@ -2559,6 +2559,8 @@ public:
     return where_clause;
   }
 
+  WARN_UNUSED_RESULT WhereClause &get_where_clause () { return where_clause; }
+
   ExternalFunctionItem (
     Analysis::NodeMapping mappings, Identifier item_name,
     std::vector<std::unique_ptr<GenericParam>> generic_params,
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.h b/gcc/rust/typecheck/rust-hir-type-check-base.h
index 3a9df89a5..61e545149 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-base.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.h
@@ -53,7 +53,8 @@ protected:
     HIR::TypePath &path,
     tl::optional<std::reference_wrapper<HIR::Type>> associated_self,
     BoundPolarity polarity = BoundPolarity::RegularBound,
-    bool is_qualified_type = false, bool is_super_trait = false);
+    bool is_qualified_type = false, bool is_super_trait = false,
+    bool defer_bindings = false);
 
   bool check_for_unconstrained (
     const std::vector<TyTy::SubstitutionParamMapping> &params_to_constrain,
diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.cc b/gcc/rust/typecheck/rust-hir-type-check-implitem.cc
index 9fb0d70a3..8c65390f7 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-implitem.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.cc
@@ -80,14 +80,8 @@ TypeCheckTopLevelExternItem::visit (HIR::ExternalFunctionItem &function)
     }
 
   TyTy::RegionConstraints region_constraints;
-  if (function.has_where_clause ())
-    {
-      for (auto &where_clause_item : function.get_where_clause ().get_items ())
-	{
-	  ResolveWhereClauseItem::Resolve (*where_clause_item,
-					   region_constraints);
-	}
-    }
+  ResolveWhereClauseItem::Resolve (function.get_where_clause (),
+				   region_constraints);
 
   TyTy::BaseType *ret_type = nullptr;
   if (!function.has_return_type ())
@@ -254,11 +248,8 @@ TypeCheckImplItem::resolve_function_signature (HIR::Function &function)
 			    function.get_generic_params (), substitutions);
 
   TyTy::RegionConstraints region_constraints;
-  for (auto &where_clause_item : function.get_where_clause ().get_items ())
-    {
-      ResolveWhereClauseItem::Resolve (*where_clause_item.get (),
-				       region_constraints);
-    }
+  ResolveWhereClauseItem::Resolve (function.get_where_clause (),
+				   region_constraints);
 
   TyTy::BaseType *ret_type = nullptr;
   if (!function.has_function_return_type ())
@@ -510,11 +501,8 @@ TypeCheckImplItem::visit (HIR::TypeAlias &alias)
   context->insert_type (alias.get_mappings (), actual_type);
   result = actual_type;
   TyTy::RegionConstraints region_constraints;
-  for (auto &where_clause_item : alias.get_where_clause ().get_items ())
-    {
-      ResolveWhereClauseItem::Resolve (*where_clause_item.get (),
-				       region_constraints);
-    }
+  ResolveWhereClauseItem::Resolve (alias.get_where_clause (),
+				   region_constraints);
 }
 
 TypeCheckImplItemWithTrait::TypeCheckImplItemWithTrait (
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc b/gcc/rust/typecheck/rust-hir-type-check-item.cc
index 2f2005b71..4d02e3b5c 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-item.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc
@@ -310,10 +310,8 @@ TypeCheckItem::visit (HIR::TypeAlias &alias)
   context->insert_type (alias.get_mappings (), actual_type);
 
   TyTy::RegionConstraints region_constraints;
-  for (auto &where_clause_item : alias.get_where_clause ().get_items ())
-    {
-      ResolveWhereClauseItem::Resolve (*where_clause_item, region_constraints);
-    }
+  ResolveWhereClauseItem::Resolve (alias.get_where_clause (),
+				   region_constraints);
   infered = actual_type;
 }
 
@@ -329,10 +327,8 @@ TypeCheckItem::visit (HIR::TupleStruct &struct_decl)
 			    struct_decl.get_generic_params (), substitutions);
 
   TyTy::RegionConstraints region_constraints;
-  for (auto &where_clause_item : struct_decl.get_where_clause ().get_items ())
-    {
-      ResolveWhereClauseItem::Resolve (*where_clause_item, region_constraints);
-    }
+  ResolveWhereClauseItem::Resolve (struct_decl.get_where_clause (),
+				   region_constraints);
 
   // Process #[repr(X)] attribute, if any
   const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
@@ -410,10 +406,8 @@ TypeCheckItem::visit (HIR::StructStruct &struct_decl)
 			    struct_decl.get_generic_params (), substitutions);
 
   TyTy::RegionConstraints region_constraints;
-  for (auto &where_clause_item : struct_decl.get_where_clause ().get_items ())
-    {
-      ResolveWhereClauseItem::Resolve (*where_clause_item, region_constraints);
-    }
+  ResolveWhereClauseItem::Resolve (struct_decl.get_where_clause (),
+				   region_constraints);
 
   // Process #[repr(X)] attribute, if any
   const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
@@ -577,10 +571,8 @@ TypeCheckItem::visit (HIR::Union &union_decl)
 			    union_decl.get_generic_params (), substitutions);
 
   TyTy::RegionConstraints region_constraints;
-  for (auto &where_clause_item : union_decl.get_where_clause ().get_items ())
-    {
-      ResolveWhereClauseItem::Resolve (*where_clause_item, region_constraints);
-    }
+  ResolveWhereClauseItem::Resolve (union_decl.get_where_clause (),
+				   region_constraints);
 
   std::vector<TyTy::StructFieldType *> fields;
   for (auto &variant : union_decl.get_variants ())
@@ -833,10 +825,8 @@ TypeCheckItem::resolve_function_signature (HIR::Function &function)
 			    function.get_generic_params (), substitutions);
 
   TyTy::RegionConstraints region_constraints;
-  for (auto &where_clause_item : function.get_where_clause ().get_items ())
-    {
-      ResolveWhereClauseItem::Resolve (*where_clause_item, region_constraints);
-    }
+  ResolveWhereClauseItem::Resolve (function.get_where_clause (),
+				   region_constraints);
 
   TyTy::BaseType *ret_type = nullptr;
   if (!function.has_function_return_type ())
@@ -1053,10 +1043,8 @@ TypeCheckItem::resolve_impl_block_substitutions (HIR::ImplBlock &impl_block,
 			    impl_block.get_generic_params (), substitutions);
 
   TyTy::RegionConstraints region_constraints;
-  for (auto &where_clause_item : impl_block.get_where_clause ().get_items ())
-    {
-      ResolveWhereClauseItem::Resolve (*where_clause_item, region_constraints);
-    }
+  ResolveWhereClauseItem::Resolve (impl_block.get_where_clause (),
+				   region_constraints);
 
   auto specified_bound = TyTy::TypeBoundPredicate::error ();
   TraitReference *trait_reference = &TraitReference::error_node ();
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc
index 84baab6ff..5fe75cf48 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-type.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc
@@ -1023,6 +1023,64 @@ ResolveWhereClauseItem::Resolve (HIR::WhereClauseItem &item,
     }
 }
 
+void
+ResolveWhereClauseItem::Resolve (HIR::WhereClause &clause,
+				 TyTy::RegionConstraints &region_constraints)
+{
+  ResolveWhereClauseItem resolver (region_constraints);
+
+  class PlainTypePath : public HIR::HIRTypeVisitor
+  {
+  public:
+    bool matches = false;
+
+    void visit (HIR::TypePathSegmentFunction &segment) override {}
+    void visit (HIR::QualifiedPathInType &path) override {}
+    void visit (HIR::TraitBound &bound) override {}
+    void visit (HIR::ImplTraitType &type) override {}
+    void visit (HIR::TraitObjectType &type) override {}
+    void visit (HIR::ParenthesisedType &type) override {}
+    void visit (HIR::TupleType &type) override {}
+    void visit (HIR::NeverType &type) override {}
+    void visit (HIR::RawPointerType &type) override {}
+    void visit (HIR::ReferenceType &type) override {}
+    void visit (HIR::ArrayType &type) override {}
+    void visit (HIR::SliceType &type) override {}
+    void visit (HIR::InferredType &type) override {}
+    void visit (HIR::BareFunctionType &type) override {}
+
+    void visit (HIR::TypePath &path) override
+    {
+      bool is_single = path.get_segments ().size () == 1;
+      bool final_seg_is_reg
+	= path.get_final_segment ().get_type () == HIR::TypePathSegment::REG;
+
+      matches = is_single && final_seg_is_reg;
+    }
+  };
+
+  resolver.defer_bindings = true;
+  for (auto &item : clause.get_items ())
+    if (item->get_item_type () == HIR::WhereClauseItem::TYPE_BOUND)
+      {
+	auto &bound = static_cast<HIR::TypeBoundWhereClauseItem &> (*item);
+	PlainTypePath plain;
+	bound.get_bound_type ().accept_vis (plain);
+	if (plain.matches)
+	  resolver.visit (bound);
+      }
+
+  resolver.defer_bindings = false;
+  resolver.complete_bindings = true;
+  for (auto &item : clause.get_items ())
+    {
+      if (item->get_item_type () == HIR::WhereClauseItem::TYPE_BOUND)
+	resolver.visit (static_cast<HIR::TypeBoundWhereClauseItem &> (*item));
+      else
+	Resolve (*item, region_constraints);
+    }
+}
+
 void
 ResolveWhereClauseItem::visit (HIR::LifetimeWhereClauseItem &item)
 {
@@ -1060,6 +1118,9 @@ ResolveWhereClauseItem::visit (HIR::TypeBoundWhereClauseItem &item)
     = TypeCheckType::Resolve (binding_type_path,
 			      TypeCheckType::ResolutionMode::CANONICAL);
 
+  if (defer_bindings && binding->get_kind () != TyTy::TypeKind::PARAM)
+    return;
+
   // FIXME double check there might be a trait cycle here see TypeParam handling
 
   std::vector<TyTy::TypeBoundPredicate> specified_bounds;
@@ -1072,13 +1133,17 @@ ResolveWhereClauseItem::visit (HIR::TypeBoundWhereClauseItem &item)
 	    auto *b = static_cast<HIR::TraitBound *> (bound.get ());
 
 	    TyTy::TypeBoundPredicate predicate
-	      = get_predicate_from_bound (b->get_path (), binding_type_path);
+	      = get_predicate_from_bound (b->get_path (), binding_type_path,
+					  BoundPolarity::RegularBound, false,
+					  false, defer_bindings);
 	    if (!predicate.is_error ())
 	      specified_bounds.push_back (std::move (predicate));
 	  }
 	  break;
 	case HIR::TypeParamBound::BoundType::LIFETIME:
 	  {
+	    if (defer_bindings)
+	      break;
 	    if (auto param = binding->try_as<TyTy::ParamType> ())
 	      {
 		auto *b = static_cast<HIR::Lifetime *> (bound.get ());
@@ -1098,7 +1163,27 @@ ResolveWhereClauseItem::visit (HIR::TypeBoundWhereClauseItem &item)
 	  break;
 	}
     }
-  binding->inherit_bounds (specified_bounds);
+
+  for (const auto &predicate : specified_bounds)
+    {
+      bool replaced = false;
+      if (complete_bindings)
+	{
+	  for (auto &bound : binding->get_specified_bounds ())
+	    {
+	      if (bound.get_id () == predicate.get_id ()
+		  && bound.get_locus () == predicate.get_locus ())
+		{
+		  bound = predicate;
+		  replaced = true;
+		  break;
+		}
+	    }
+	}
+
+      if (!replaced)
+	binding->inherit_bound (predicate);
+    }
 }
 
 } // namespace Resolver
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.h b/gcc/rust/typecheck/rust-hir-type-check-type.h
index 9cc0fdbc3..ee1d0c97d 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-type.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.h
@@ -132,7 +132,7 @@ class ResolveWhereClauseItem : public TypeCheckBase
   TyTy::RegionConstraints &region_constraints;
 
 public:
-  static void Resolve (HIR::WhereClauseItem &item,
+  static void Resolve (HIR::WhereClause &clause,
 		       TyTy::RegionConstraints &region_constraints);
 
 protected:
@@ -140,6 +140,12 @@ protected:
   void visit (HIR::TypeBoundWhereClauseItem &item);
 
 private:
+  static void Resolve (HIR::WhereClauseItem &item,
+		       TyTy::RegionConstraints &region_constraints);
+
+  bool defer_bindings = false;
+  bool complete_bindings = false;
+
   ResolveWhereClauseItem (TyTy::RegionConstraints &region_constraints)
     : region_constraints (region_constraints)
   {}
diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc
index d111207b7..d4c21d09c 100644
--- a/gcc/rust/typecheck/rust-hir-type-check.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check.cc
@@ -169,7 +169,6 @@ TraitItemReference::get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const
   std::vector<TyTy::SubstitutionParamMapping> substitutions
     = inherited_substitutions;
 
-  TyTy::RegionConstraints region_constraints;
   HIR::TraitFunctionDecl &function = fn.get_decl ();
   if (function.has_generics ())
     {
@@ -180,12 +179,9 @@ TraitItemReference::get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const
 					   ABI::RUST);
     }
 
-  if (function.has_where_clause ())
-    {
-      for (auto &where_clause_item : function.get_where_clause ().get_items ())
-	ResolveWhereClauseItem::Resolve (*where_clause_item,
-					 region_constraints);
-    }
+  TyTy::RegionConstraints region_constraints;
+  ResolveWhereClauseItem::Resolve (function.get_where_clause (),
+				   region_constraints);
 
   TyTy::BaseType *ret_type = nullptr;
   if (!function.has_return_type ())
diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc
index 0240a635c..bb2c5ed14 100644
--- a/gcc/rust/typecheck/rust-tyty-bounds.cc
+++ b/gcc/rust/typecheck/rust-tyty-bounds.cc
@@ -261,7 +261,8 @@ TyTy::TypeBoundPredicate
 TypeCheckBase::get_predicate_from_bound (
   HIR::TypePath &type_path,
   tl::optional<std::reference_wrapper<HIR::Type>> associated_self,
-  BoundPolarity polarity, bool is_qualified_type_path, bool is_super_trait)
+  BoundPolarity polarity, bool is_qualified_type_path, bool is_super_trait,
+  bool defer_bindings)
 {
   TyTy::TypeBoundPredicate lookup = TyTy::TypeBoundPredicate::error ();
   bool already_resolved
@@ -340,7 +341,7 @@ TypeCheckBase::get_predicate_from_bound (
 
 	std::vector<HIR::GenericArgsBinding> bindings;
 
-	if (fn.has_return_type ())
+	if (fn.has_return_type () && !defer_bindings)
 	  {
 	    TypeCheckType::Resolve (fn.get_return_type ());
 
@@ -377,6 +378,9 @@ TypeCheckBase::get_predicate_from_bound (
 			       args.get_locus ());
     }
 
+  if (defer_bindings)
+    args.get_binding_args ().clear ();
+
   // we try to apply generic arguments when they are non empty and or when the
   // predicate requires them so that we get the relevant Foo expects x number
   // arguments but got zero see test case rust/compile/traits12.rs
@@ -387,8 +391,9 @@ TypeCheckBase::get_predicate_from_bound (
 					 is_super_trait);
     }
 
-  context->insert_resolved_predicate (type_path.get_mappings ().get_hirid (),
-				      predicate);
+  if (!defer_bindings)
+    context->insert_resolved_predicate (type_path.get_mappings ().get_hirid (),
+					predicate);
 
   return predicate;
 }
diff --git a/gcc/testsuite/rust/compile/issue-4864.rs b/gcc/testsuite/rust/compile/issue-4864.rs
new file mode 100644
index 000000000..c05000a3c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-4864.rs
@@ -0,0 +1,22 @@
+#![feature(no_core, lang_items)]
+#![no_core]
+#[lang = "sized"]
+pub trait Sized {}
+trait Iterator {
+    type Item;
+}
+trait IntoIterator {
+    type Item;
+    type IntoIter: Iterator<Item = Self::Item>;
+}
+pub struct Flatten<I, U> {
+    pub inner: I,
+    pub extra: U,
+}
+impl<I, U> Iterator for Flatten<I, U>
+where
+    I: Iterator<Item: IntoIterator<IntoIter = U, Item = U::Item>>,
+    U: Iterator,
+{
+    type Item = U::Item;
+}

base-commit: cad63612b27d0e7bfc30eb8e548ad145ac16922a
-- 
2.55.0



More information about the Gcc-rust mailing list