[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