[gccrs COMMIT 10/17] gccrs: add new type path prob mechanism

gerris.rs@gmail.com gerris.rs@gmail.com
Sun Aug 30 21:46:06 GMT 2026


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

gcc/rust/ChangeLog:

	* Make-lang.in: new object
	* typecheck/rust-hir-type-check-type.cc (TypeCheckType::resolve_associated_type): helper
	* typecheck/rust-hir-type-check-type.h: new interface
	* typecheck/rust-hir-path-probe-type.cc: New file.
	* typecheck/rust-hir-path-probe-type.h: New file.

gcc/testsuite/ChangeLog:

	* rust/compile/issue-4166.rs:

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/00ea8a7c4129cfb5e2a000ed8b2c2b93213e22bd

The commit has NOT been mentioned in any issue.

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

 gcc/rust/Make-lang.in                         |   1 +
 .../typecheck/rust-hir-path-probe-type.cc     | 232 ++++++++++++++++++
 gcc/rust/typecheck/rust-hir-path-probe-type.h |  81 ++++++
 .../typecheck/rust-hir-type-check-type.cc     | 120 ++++-----
 gcc/rust/typecheck/rust-hir-type-check-type.h |   4 +
 gcc/testsuite/rust/compile/issue-4166.rs      |   4 +-
 6 files changed, 382 insertions(+), 60 deletions(-)
 create mode 100644 gcc/rust/typecheck/rust-hir-path-probe-type.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-path-probe-type.h

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index f50e7d0c0..3f79477b7 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -187,6 +187,7 @@ GRS_OBJS = \
     rust/rust-polonius.o\
     rust/rust-hir-dot-operator.o \
     rust/rust-hir-path-probe.o \
+    rust/rust-hir-path-probe-type.o \
     rust/rust-hir-path.o \
     rust/rust-hir-type.o \
     rust/rust-hir-expr.o \
diff --git a/gcc/rust/typecheck/rust-hir-path-probe-type.cc b/gcc/rust/typecheck/rust-hir-path-probe-type.cc
new file mode 100644
index 000000000..98cd7adc4
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-path-probe-type.cc
@@ -0,0 +1,232 @@
+// Copyright (C) 2020-2026 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-hir-path-probe-type.h"
+#include "rust-hir-map.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir.h"
+#include "rust-type-util.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypePathProbeResult
+TypePathProbe::Probe (TyTy::BaseType *receiver,
+		      const HIR::PathIdentSegment &segment_name)
+{
+  TypePathProbe probe (receiver, segment_name);
+  return probe.probe ();
+}
+
+TypePathProbeResult
+TypePathProbe::probe ()
+{
+  switch (receiver->get_kind ())
+    {
+    case TyTy::TypeKind::PARAM:
+    case TyTy::TypeKind::DYNAMIC:
+      probe_generic ();
+      break;
+
+    case TyTy::TypeKind::ADT:
+      probe_adt (static_cast<TyTy::ADTType *> (receiver));
+      break;
+
+    default:
+      probe_fallback ();
+      break;
+    }
+
+  return std::move (result);
+}
+
+void
+TypePathProbe::probe_generic ()
+{
+  for (const TyTy::TypeBoundPredicate &predicate :
+       receiver->get_specified_bounds ())
+    {
+      auto candidate = process_predicate_for_candidates (predicate);
+      insert_candidate (std::move (candidate));
+    }
+}
+
+void
+TypePathProbe::probe_adt (TyTy::ADTType *adt)
+{
+  auto adt_item = mappings.lookup_defid (adt->get_id ());
+  if (!adt_item.has_value ())
+    {
+      probe_fallback ();
+      return;
+    }
+
+  NodeId adt_node_id = adt_item.value ()->get_mappings ().get_nodeid ();
+  mappings.iterate_adt_impl_items (adt_node_id,
+				   [this] (HirId id, HIR::ImplItem *item,
+					   HIR::ImplBlock *impl) -> bool {
+				     return process_impl_item (id, item, impl);
+				   });
+}
+
+void
+TypePathProbe::probe_fallback ()
+{
+  mappings.iterate_impl_items (
+    [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
+      return process_impl_item (id, item, impl);
+    });
+}
+
+bool
+TypePathProbe::process_impl_item (HirId id, HIR::ImplItem *item,
+				  HIR::ImplBlock *impl)
+{
+  auto item_name = item->get_impl_item_name ();
+  if (search.to_string () != item_name)
+    return true;
+
+  HirId impl_ty_id = impl->get_type ().get_mappings ().get_hirid ();
+  TyTy::BaseType *impl_block_ty = nullptr;
+  if (!query_type (impl_ty_id, &impl_block_ty))
+    return true;
+
+  if (!types_compatable (TyTy::TyWithLocation (receiver),
+			 TyTy::TyWithLocation (impl_block_ty),
+			 impl->get_locus (), false))
+    return true;
+
+  // Keep trait impl items at trait position.  In particular, do not query the
+  // type of an impl associated-type alias here: projection normalization will
+  // select and evaluate the concrete alias later.
+  if (impl->has_trait_ref ())
+    {
+      process_trait_impl_item (impl);
+      return true;
+    }
+
+  TyTy::BaseType *item_ty = nullptr;
+  if (!query_type (id, &item_ty))
+    return true;
+
+  PathProbeCandidate::CandidateType candidate_type;
+  switch (item->get_impl_item_type ())
+    {
+    case HIR::ImplItem::FUNCTION:
+      candidate_type = PathProbeCandidate::IMPL_FUNC;
+      break;
+
+    case HIR::ImplItem::TYPE_ALIAS:
+      candidate_type = PathProbeCandidate::IMPL_TYPE_ALIAS;
+      break;
+
+    case HIR::ImplItem::CONSTANT:
+      candidate_type = PathProbeCandidate::IMPL_CONST;
+      break;
+
+    default:
+      return true;
+    }
+
+  PathProbeCandidate::ImplItemCandidate impl_candidate{item, impl};
+
+  insert_candidate (
+    {candidate_type, item_ty, item->get_locus (), impl_candidate});
+
+  return true;
+}
+
+void
+TypePathProbe::process_trait_impl_item (HIR::ImplBlock *impl)
+{
+  HIR::TypePath &trait_path = impl->get_trait_ref ();
+  TraitReference *trait_ref = TraitResolver::Lookup (trait_path);
+  if (trait_ref->is_error ())
+    trait_ref = TraitResolver::Resolve (trait_path);
+  if (trait_ref->is_error ())
+    return;
+
+  TyTy::TypeBoundPredicate predicate (*trait_ref, BoundPolarity::RegularBound,
+				      impl->get_locus ());
+  auto candidate = process_predicate_for_candidates (predicate);
+  if (candidate.is_error ())
+    return;
+
+  rust_assert (candidate.is_trait_candidate ());
+  candidate.item.trait.impl = impl;
+  insert_candidate (std::move (candidate));
+}
+
+PathProbeCandidate
+TypePathProbe::process_predicate_for_candidates (
+  const TyTy::TypeBoundPredicate &predicate)
+{
+  tl::optional<TyTy::TypeBoundPredicateItem> item
+    = predicate.lookup_associated_item (search.to_string ());
+  if (!item.has_value ())
+    return PathProbeCandidate::get_error ();
+
+  const TraitReference *trait_ref = item->get_parent ()->get ();
+  const TraitItemReference *trait_item_ref = item->get_raw_item ();
+  PathProbeCandidate::CandidateType candidate_type;
+  switch (trait_item_ref->get_trait_item_type ())
+    {
+    case TraitItemReference::TraitItemType::FN:
+      candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC;
+      break;
+    case TraitItemReference::TraitItemType::CONST:
+      candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST;
+      break;
+    case TraitItemReference::TraitItemType::TYPE:
+      candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS;
+      break;
+
+    case TraitItemReference::TraitItemType::ERROR:
+    default:
+      return PathProbeCandidate::get_error ();
+    }
+
+  TyTy::BaseType *trait_item_tyty = item->get_raw_item ()->get_tyty ();
+  if (receiver->get_kind () != TyTy::DYNAMIC)
+    trait_item_tyty = item->get_tyty_for_receiver (receiver);
+
+  PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref,
+							      trait_item_ref,
+							      nullptr};
+  return {candidate_type, trait_item_tyty, trait_item_ref->get_locus (),
+	  trait_item_candidate};
+}
+
+void
+TypePathProbe::insert_candidate (PathProbeCandidate candidate)
+{
+  if (candidate.is_error ())
+    return;
+
+  bool is_type
+    = candidate.type == PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS
+      || candidate.type == PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS;
+  if (is_type)
+    result.type_candidates.insert (std::move (candidate));
+  else
+    result.non_type_matches.insert (std::move (candidate));
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-path-probe-type.h b/gcc/rust/typecheck/rust-hir-path-probe-type.h
new file mode 100644
index 000000000..ece23030b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-path-probe-type.h
@@ -0,0 +1,81 @@
+// Copyright (C) 2020-2026 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_HIR_PATH_PROBE_TYPE_H
+#define RUST_HIR_PATH_PROBE_TYPE_H
+
+#include "rust-hir-map.h"
+#include "rust-hir-path-probe.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+struct TypePathProbeResult
+{
+  std::set<PathProbeCandidate> type_candidates;
+  std::set<PathProbeCandidate> non_type_matches;
+
+  bool has_type_candidates () const { return !type_candidates.empty (); }
+
+  bool has_non_type_matches () const { return !non_type_matches.empty (); }
+
+  bool is_empty () const
+  {
+    return type_candidates.empty () && non_type_matches.empty ();
+  }
+};
+
+class TypePathProbe
+{
+public:
+  static TypePathProbeResult Probe (TyTy::BaseType *receiver,
+				    const HIR::PathIdentSegment &segment_name);
+
+private:
+  TypePathProbe (TyTy::BaseType *receiver,
+		 const HIR::PathIdentSegment &segment_name)
+    : mappings (Analysis::Mappings::get ()), receiver (receiver),
+      search (segment_name)
+  {}
+
+  TypePathProbeResult probe ();
+
+  void probe_generic ();
+  void probe_adt (TyTy::ADTType *adt);
+  void probe_fallback ();
+
+  bool process_impl_item (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl);
+
+  void process_trait_impl_item (HIR::ImplBlock *impl);
+
+  PathProbeCandidate
+  process_predicate_for_candidates (const TyTy::TypeBoundPredicate &predicate);
+
+  void insert_candidate (PathProbeCandidate candidate);
+
+  Analysis::Mappings &mappings;
+  TyTy::BaseType *receiver;
+  const HIR::PathIdentSegment &search;
+  TypePathProbeResult result;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_PATH_PROBE_TYPE_H
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc
index c6fbb2ae1..112aba4c0 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-type.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc
@@ -19,11 +19,11 @@
 #include "rust-hir-type-check-type.h"
 #include "options.h"
 #include "optional.h"
+#include "rich-location.h"
 #include "rust-hir-map.h"
 #include "rust-hir-trait-resolve.h"
 #include "rust-hir-type-check-expr.h"
-#include "rust-hir-path-probe.h"
-#include "rust-hir-type-bounds.h"
+#include "rust-hir-path-probe-type.h"
 #include "rust-finalized-name-resolution-context.h"
 #include "rust-mapping-common.h"
 #include "rust-rib.h"
@@ -32,6 +32,8 @@
 #include "rust-system.h"
 #include "rust-compile-base.h"
 #include "rust-resolve-builtins.h"
+#include "rust-tyty.h"
+#include "text-range-label.h"
 
 namespace Rust {
 namespace Resolver {
@@ -507,6 +509,19 @@ TypeCheckType::resolve_associated_type (const std::string &search,
   return false;
 }
 
+bool
+TypeCheckType::try_resolve_contextual_self_associated_type (
+  const HIR::TypePathSegment &segment, bool first_segment,
+  bool ty_seg_is_big_self, TyTy::BaseType **result)
+{
+  if (!first_segment || !ty_seg_is_big_self
+      || !context->block_context ().is_in_context ())
+    return false;
+
+  TypeCheckBlockContextItem ctx = context->block_context ().peek ();
+  return resolve_associated_type (segment.to_string (), ctx, result);
+}
+
 TyTy::BaseType *
 TypeCheckType::resolve_segments (
   HirId expr_id, std::vector<std::unique_ptr<HIR::TypePathSegment>> &segments,
@@ -514,82 +529,71 @@ TypeCheckType::resolve_segments (
   const Analysis::NodeMapping &expr_mappings, location_t expr_locus,
   bool tySegIsBigSelf)
 {
-  TyTy::BaseType *prev_segment = tyseg;
   for (size_t i = offset; i < segments.size (); i++)
     {
-      std::unique_ptr<HIR::TypePathSegment> &seg = segments.at (i);
-
-      bool reciever_is_generic
-	= prev_segment->get_kind () == TyTy::TypeKind::PARAM;
-      bool probe_bounds = true;
-      bool probe_impls = !reciever_is_generic;
-      bool ignore_mandatory_trait_items = !reciever_is_generic;
+      auto &seg = segments.at (i);
+      const auto &ident_segment = seg->get_ident_segment ();
       bool first_segment = i == offset;
-      bool selfResolveOk = false;
+      TyTy::BaseType *associated_type = nullptr;
 
-      if (first_segment && tySegIsBigSelf
-	  && context->block_context ().is_in_context ())
+      bool selfResolveOk
+	= try_resolve_contextual_self_associated_type (*seg, first_segment,
+						       tySegIsBigSelf,
+						       &associated_type);
+      if (selfResolveOk)
 	{
-	  TypeCheckBlockContextItem ctx = context->block_context ().peek ();
-	  TyTy::BaseType *lookup = nullptr;
-	  selfResolveOk
-	    = resolve_associated_type (seg->to_string (), ctx, &lookup);
-	  if (selfResolveOk)
-	    {
-	      prev_segment = tyseg;
-	      tyseg = lookup;
-	    }
+	  tyseg = associated_type;
 	}
-      if (!selfResolveOk)
+      else
 	{
-	  // probe the path is done in two parts one where we search impls if no
-	  // candidate is found then we search extensions from traits
-	  auto candidates
-	    = PathProbeType::Probe (prev_segment, seg->get_ident_segment (),
-				    probe_impls, false,
-				    ignore_mandatory_trait_items);
-	  if (candidates.size () == 0)
+	  if (auto adt = tyseg->try_as<TyTy::ADTType> ())
 	    {
-	      candidates
-		= PathProbeType::Probe (prev_segment, seg->get_ident_segment (),
-					false, probe_bounds,
-					ignore_mandatory_trait_items);
-	      if (candidates.size () == 0)
+	      if (adt->is_enum ())
 		{
-		  rust_error_at (
-		    seg->get_locus (),
-		    "failed to resolve path segment using an impl Probe");
-		  return new TyTy::ErrorType (expr_id);
+		  rich_location r (line_table, seg->get_locus ());
+		  text_range_label label ("enum declared here");
+
+		  auto item_lookup = mappings.lookup_defid (adt->get_id ());
+		  if (item_lookup.has_value ())
+		    {
+		      auto &item = item_lookup.value ();
+		      r.add_range (item->get_locus (), SHOW_RANGE_WITHOUT_CARET,
+				   &label);
+		    }
+
+		  TyTy::VariantDef *v;
+		  if (adt->lookup_variant (ident_segment.to_string (), &v))
+		    {
+		      rust_error_at (
+			r, ErrorCode::E0573,
+			"expected type, found variant of %<%s::%s%>",
+			adt->get_name ().c_str (),
+			v->get_identifier ().c_str ());
+		      return new TyTy::ErrorType (expr_id);
+		    }
 		}
 	    }
 
+	  auto result = TypePathProbe::Probe (tyseg, ident_segment);
+	  auto &candidates = result.type_candidates;
+
+	  if (candidates.empty ())
+	    {
+	      rust_error_at (seg->get_locus (),
+			     "failed to resolve path segment %<%s%> as a type",
+			     ident_segment.to_string ().c_str ());
+	      return new TyTy::ErrorType (expr_id);
+	    }
+
 	  if (candidates.size () > 1)
 	    {
-	      ReportMultipleCandidateError::Report (candidates,
-						    seg->get_ident_segment (),
+	      ReportMultipleCandidateError::Report (candidates, ident_segment,
 						    seg->get_locus ());
 	      return new TyTy::ErrorType (expr_id);
 	    }
 
 	  auto &candidate = *candidates.begin ();
-	  prev_segment = tyseg;
 	  tyseg = candidate.ty;
-
-	  if (candidate.is_enum_candidate ())
-	    {
-	      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (tyseg);
-	      auto last_variant = adt->get_variants ();
-	      TyTy::VariantDef *variant = last_variant.back ();
-
-	      rich_location richloc (line_table, seg->get_locus ());
-	      richloc.add_fixit_replace ("not a type");
-
-	      rust_error_at (richloc, ErrorCode::E0573,
-			     "expected type, found variant of %<%s::%s%>",
-			     adt->get_name ().c_str (),
-			     variant->get_identifier ().c_str ());
-	      return new TyTy::ErrorType (expr_id);
-	    }
 	}
 
       if (seg->is_generic_segment ())
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.h b/gcc/rust/typecheck/rust-hir-type-check-type.h
index 7683d5bda..27d2ae40a 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-type.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.h
@@ -84,6 +84,10 @@ private:
 				TypeCheckBlockContextItem &ctx,
 				TyTy::BaseType **result);
 
+  bool try_resolve_contextual_self_associated_type (
+    const HIR::TypePathSegment &segment, bool first_segment,
+    bool ty_seg_is_big_self, TyTy::BaseType **result);
+
   TyTy::BaseType *translated;
 };
 
diff --git a/gcc/testsuite/rust/compile/issue-4166.rs b/gcc/testsuite/rust/compile/issue-4166.rs
index d9f87909b..3a28dc501 100644
--- a/gcc/testsuite/rust/compile/issue-4166.rs
+++ b/gcc/testsuite/rust/compile/issue-4166.rs
@@ -4,7 +4,7 @@
 
 pub trait Foo {
     type Bar;
-    fn foo(bar: Self::bar); // { dg-error "failed to resolve path segment using an impl Probe" }
+    fn foo(bar: Self::bar); // { dg-error "failed to resolve path segment .bar. as a type" }
 }
 
 pub struct FooImpl;
@@ -19,4 +19,4 @@ const foo_impl: () = {
     }
 };
 
-fn main() {}
\ No newline at end of file
+fn main() {}
-- 
2.55.0



More information about the Gcc-rust mailing list