https://gcc.gnu.org/g:c15916235ba51159bebf1d4a88a3b44d5d5a632c
commit r17-4300-gc15916235ba51159bebf1d4a88a3b44d5d5a632c Author: Philip Herron <[email protected]> Date: Tue Aug 25 22:19:58 2026 +0100 gccrs: add new type path prob mechanism 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]> Diff: --- gcc/rust/Make-lang.in | 1 + gcc/rust/typecheck/rust-hir-path-probe-type.cc | 232 +++++++++++++++++++++++++ gcc/rust/typecheck/rust-hir-path-probe-type.h | 81 +++++++++ gcc/rust/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(-) diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index f50e7d0c0184..3f79477b79c0 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 000000000000..98cd7adc431c --- /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 000000000000..ece23030b64c --- /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 c6fbb2ae1bd7..112aba4c00f6 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 7683d5bda0df..27d2ae40af0c 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 d9f87909b0e0..3a28dc50174f 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() {}
