From: Philip Herron <[email protected]>

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 <[email protected]>
---
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

Reply via email to