From: Philip Herron Date: Tue, 23 May 2023 16:55:32 +0000 (+0100) Subject: gccrs: support deref cycles to resolve to trait items X-Git-Tag: basepoints/gcc-15~2522 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6ed987d5bc6a11735a3244d6f69d8f3e8879fbc0;p=thirdparty%2Fgcc.git gccrs: support deref cycles to resolve to trait items Fixes #2190 gcc/rust/ChangeLog: * backend/rust-compile-expr.cc (HIRCompileBase::resolve_deref_adjustment): reuse op overload code * typecheck/rust-autoderef.cc (Adjuster::try_deref_type): update (Adjuster::try_raw_deref_type): likewise * typecheck/rust-autoderef.h: likewise * typecheck/rust-hir-dot-operator.cc (MethodResolver::select): improve debug loging gcc/testsuite/ChangeLog: * rust/compile/issue-2190-1.rs: New test. * rust/compile/issue-2190-2.rs: New test. Signed-off-by: Philip Herron --- diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index 00b9a0700088..93dd937da6f7 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -2367,15 +2367,8 @@ HIRCompileBase::resolve_deref_adjustment (Resolver::Adjustment &adjustment, rust_assert (adjustment.has_operator_overload ()); TyTy::FnType *lookup = adjustment.get_deref_operator_fn (); - HIR::ImplItem *resolved_item = adjustment.get_deref_hir_item (); - - tree fn_address = error_mark_node; - if (!lookup->has_subsititions_defined ()) - fn_address = CompileInherentImplItem::Compile (resolved_item, ctx, nullptr, - true, locus); - else - fn_address = CompileInherentImplItem::Compile (resolved_item, ctx, lookup, - true, locus); + TyTy::BaseType *receiver = adjustment.get_actual (); + tree fn_address = resolve_method_address (lookup, receiver, locus); // does it need a reference to call tree adjusted_argument = expression; diff --git a/gcc/rust/typecheck/rust-autoderef.cc b/gcc/rust/typecheck/rust-autoderef.cc index adaf575b2c15..2f21d2685e4f 100644 --- a/gcc/rust/typecheck/rust-autoderef.cc +++ b/gcc/rust/typecheck/rust-autoderef.cc @@ -28,7 +28,7 @@ namespace Resolver { static bool resolve_operator_overload_fn ( Analysis::RustLangItem::ItemType lang_item_type, TyTy::BaseType *ty, - TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item, + TyTy::FnType **resolved_fn, Adjustment::AdjustmentType *requires_ref_adjustment); TyTy::BaseType * @@ -44,19 +44,18 @@ Adjustment Adjuster::try_deref_type (TyTy::BaseType *ty, Analysis::RustLangItem::ItemType deref_lang_item) { - HIR::ImplItem *impl_item = nullptr; TyTy::FnType *fn = nullptr; Adjustment::AdjustmentType requires_ref_adjustment = Adjustment::AdjustmentType::ERROR; bool operator_overloaded - = resolve_operator_overload_fn (deref_lang_item, ty, &fn, &impl_item, + = resolve_operator_overload_fn (deref_lang_item, ty, &fn, &requires_ref_adjustment); if (!operator_overloaded) { return Adjustment::get_error (); } - auto resolved_base = fn->get_return_type ()->clone (); + auto resolved_base = fn->get_return_type ()->destructure (); bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF; if (!is_valid_type) return Adjustment::get_error (); @@ -81,7 +80,7 @@ Adjuster::try_deref_type (TyTy::BaseType *ty, } return Adjustment::get_op_overload_deref_adjustment (adjustment_type, ty, - ref_base, fn, impl_item, + ref_base, fn, requires_ref_adjustment); } @@ -94,7 +93,7 @@ Adjuster::try_raw_deref_type (TyTy::BaseType *ty) const TyTy::ReferenceType *ref_base = static_cast (ty); - auto infered = ref_base->get_base ()->clone (); + auto infered = ref_base->get_base ()->destructure (); return Adjustment (Adjustment::AdjustmentType::INDIRECTION, ty, infered); } @@ -122,8 +121,8 @@ Adjuster::try_unsize_type (TyTy::BaseType *ty) static bool resolve_operator_overload_fn ( - Analysis::RustLangItem::ItemType lang_item_type, TyTy::BaseType *ty, - TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item, + Analysis::RustLangItem::ItemType lang_item_type, TyTy::BaseType *lhs, + TyTy::FnType **resolved_fn, Adjustment::AdjustmentType *requires_ref_adjustment) { auto context = TypeCheckContext::get (); @@ -139,35 +138,69 @@ resolve_operator_overload_fn ( if (!lang_item_defined) return false; + // we might be in a static or const context and unknown is fine + TypeCheckContextItem current_context = TypeCheckContextItem::get_error (); + if (context->have_function_context ()) + { + current_context = context->peek_context (); + } + + // this flags stops recurisve calls to try and deref when none is available + // which will cause an infinite loop + bool autoderef_flag = true; auto segment = HIR::PathIdentSegment (associated_item_name); - auto candidates - = MethodResolver::Probe (ty, HIR::PathIdentSegment (associated_item_name), - true); + auto candidates = MethodResolver::Probe (lhs, segment, autoderef_flag); + + // remove any recursive candidates + std::set resolved_candidates; + for (auto &c : candidates) + { + const TyTy::BaseType *candidate_type = c.candidate.ty; + rust_assert (candidate_type->get_kind () == TyTy::TypeKind::FNDEF); + + const TyTy::FnType &fn + = *static_cast (candidate_type); - bool have_implementation_for_lang_item = !candidates.empty (); + DefId current_fn_defid = current_context.get_defid (); + bool recursive_candidated = fn.get_id () == current_fn_defid; + if (!recursive_candidated) + { + resolved_candidates.insert (c); + } + } + + bool have_implementation_for_lang_item = resolved_candidates.size () > 0; if (!have_implementation_for_lang_item) return false; - // multiple candidates? - if (candidates.size () > 1) + if (resolved_candidates.size () > 1) { - // error out? probably not for this case + // no need to error out as we are just trying to see if there is a fit return false; } // Get the adjusted self - auto candidate = *candidates.begin (); - Adjuster adj (ty); + MethodCandidate candidate = *resolved_candidates.begin (); + Adjuster adj (lhs); TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments); - // is this the case we are recursive - // handle the case where we are within the impl block for this - // lang_item otherwise we end up with a recursive operator overload - // such as the i32 operator overload trait - TypeCheckContextItem fn_context = context->peek_context (); - if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM) + PathProbeCandidate &resolved_candidate = candidate.candidate; + TyTy::BaseType *lookup_tyty = candidate.candidate.ty; + rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF); + TyTy::BaseType *lookup = lookup_tyty; + TyTy::FnType *fn = static_cast (lookup); + rust_assert (fn->is_method ()); + + rust_debug ("is_impl_item_candidate: %s", + resolved_candidate.is_impl_candidate () ? "true" : "false"); + + // in the case where we resolve to a trait bound we have to be careful we are + // able to do so there is a case where we are currently resolving the deref + // operator overload function which is generic and this might resolve to the + // trait item of deref which is not valid as its just another recursive case + if (current_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM) { - auto &impl_item = fn_context.get_impl_item (); + auto &impl_item = current_context.get_impl_item (); HIR::ImplBlock *parent = impl_item.first; HIR::Function *fn = impl_item.second; @@ -201,24 +234,17 @@ resolve_operator_overload_fn ( } } - TyTy::BaseType *lookup_tyty = candidate.candidate.ty; - - // rust only support impl item deref operator overloading ie you must have an - // impl block for it - rust_assert (candidate.candidate.type - == PathProbeCandidate::CandidateType::IMPL_FUNC); - *impl_item = candidate.candidate.item.impl.impl_item; - - rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF); - TyTy::BaseType *lookup = lookup_tyty; - TyTy::FnType *fn = static_cast (lookup); - rust_assert (fn->is_method ()); + // we found a valid operator overload + fn->prepare_higher_ranked_bounds (); + rust_debug ("resolved operator overload to: {%u} {%s}", + candidate.candidate.ty->get_ref (), + candidate.candidate.ty->debug_str ().c_str ()); if (fn->needs_substitution ()) { - if (ty->get_kind () == TyTy::TypeKind::ADT) + if (lhs->get_kind () == TyTy::TypeKind::ADT) { - const TyTy::ADTType *adt = static_cast (ty); + const TyTy::ADTType *adt = static_cast (lhs); auto s = fn->get_self_type ()->get_root (); rust_assert (s->can_eq (adt, false)); @@ -265,8 +291,8 @@ resolve_operator_overload_fn ( rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); fn = static_cast (lookup); - Location unify_locus = mappings->lookup_location (ty->get_ref ()); - unify_site (ty->get_ref (), + Location unify_locus = mappings->lookup_location (lhs->get_ref ()); + unify_site (lhs->get_ref (), TyTy::TyWithLocation (fn->get_self_type ()), TyTy::TyWithLocation (adjusted_self), unify_locus); diff --git a/gcc/rust/typecheck/rust-autoderef.h b/gcc/rust/typecheck/rust-autoderef.h index 0c00c5a82cd8..bdb1abc92d2f 100644 --- a/gcc/rust/typecheck/rust-autoderef.h +++ b/gcc/rust/typecheck/rust-autoderef.h @@ -42,18 +42,15 @@ public: // ctor for all adjustments except derefs Adjustment (AdjustmentType type, TyTy::BaseType *actual, TyTy::BaseType *expected) - : Adjustment (type, actual, expected, nullptr, nullptr, - AdjustmentType::ERROR) + : Adjustment (type, actual, expected, nullptr, AdjustmentType::ERROR) {} static Adjustment get_op_overload_deref_adjustment ( AdjustmentType type, TyTy::BaseType *actual, TyTy::BaseType *expected, - TyTy::FnType *fn, HIR::ImplItem *deref_item, - Adjustment::AdjustmentType requires_ref_adjustment) + TyTy::FnType *fn, Adjustment::AdjustmentType requires_ref_adjustment) { rust_assert (type == DEREF || type == DEREF_MUT); - return Adjustment (type, actual, expected, fn, deref_item, - requires_ref_adjustment); + return Adjustment (type, actual, expected, fn, requires_ref_adjustment); } AdjustmentType get_type () const { return type; } @@ -107,15 +104,12 @@ public: return requires_ref_adjustment; } - HIR::ImplItem *get_deref_hir_item () const { return deref_item; } - private: Adjustment (AdjustmentType type, TyTy::BaseType *actual, TyTy::BaseType *expected, TyTy::FnType *deref_operator_fn, - HIR::ImplItem *deref_item, Adjustment::AdjustmentType requires_ref_adjustment) : type (type), actual (actual), expected (expected), - deref_operator_fn (deref_operator_fn), deref_item (deref_item), + deref_operator_fn (deref_operator_fn), requires_ref_adjustment (requires_ref_adjustment) {} @@ -127,7 +121,6 @@ private: // // the fn that we are calling TyTy::FnType *deref_operator_fn; - HIR::ImplItem *deref_item; // operator overloads can requre a reference Adjustment::AdjustmentType requires_ref_adjustment; }; diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.cc b/gcc/rust/typecheck/rust-hir-dot-operator.cc index 251cf8a02e58..28bc0c0c9a5e 100644 --- a/gcc/rust/typecheck/rust-hir-dot-operator.cc +++ b/gcc/rust/typecheck/rust-hir-dot-operator.cc @@ -66,8 +66,9 @@ MethodResolver::try_hook (const TyTy::BaseType &r) bool MethodResolver::select (TyTy::BaseType &receiver) { - rust_debug ("MethodResolver::select reciever=[%s]", - receiver.debug_str ().c_str ()); + rust_debug ("MethodResolver::select reciever=[%s] path=[%s]", + receiver.debug_str ().c_str (), + segment_name.as_string ().c_str ()); struct impl_item_candidate { diff --git a/gcc/testsuite/rust/compile/issue-2190-1.rs b/gcc/testsuite/rust/compile/issue-2190-1.rs new file mode 100644 index 000000000000..85091b22e3b6 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-2190-1.rs @@ -0,0 +1,11 @@ +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +fn foo>(t: &T) -> i32 { + t.max(2) + // { dg-error "failed to resolve method for .max." "" { target *-*-* } .-1 } + // { dg-error "failed to type resolve expression" "" { target *-*-* } .-2 } +} diff --git a/gcc/testsuite/rust/compile/issue-2190-2.rs b/gcc/testsuite/rust/compile/issue-2190-2.rs new file mode 100644 index 000000000000..48ab72b92001 --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-2190-2.rs @@ -0,0 +1,20 @@ +// { dg-options "-w" } +#[lang = "deref"] +trait Deref { + type Target; + fn deref(&self) -> &Self::Target; +} + +fn foo>(t: &T) -> i32 { + t.max(2) +} + +impl i32 { + fn max(self, other: i32) -> i32 { + if self > other { + self + } else { + other + } + } +}