https://gcc.gnu.org/g:fd02ff17ae962096c419075035a183c6cd839f8f
commit r17-5146-gfd02ff17ae962096c419075035a183c6cd839f8f Author: Philip Herron <[email protected]> Date: Fri Sep 18 15:08:18 2026 +0100 gccrs: Fix bad substituion error This patch fixes hanlding of substitutions of nested generics by taking into account the already used arguments in the adjust call. For example GenericSplitN< Split <T,P>> Fixes Rust-GCC/gccrs#4884 gcc/rust/ChangeLog: * typecheck/rust-type-util.cc (normalize_projection): resolve through super traits * typecheck/rust-typecheck-context.cc: disable infer for trait-self * typecheck/rust-tyty-subst.cc: reuse to handle nested arguments * typecheck/rust-unify.cc (UnifyRules::go): relax infer on adts gcc/testsuite/ChangeLog: * rust/compile/generic-type-alias-subst-bug.rs: New test. * rust/compile/issues/4884.rs: New test. Signed-off-by: Philip Herron <[email protected]> Diff: --- gcc/rust/typecheck/rust-type-util.cc | 55 +++++++---- gcc/rust/typecheck/rust-typecheck-context.cc | 20 ++-- gcc/rust/typecheck/rust-tyty-subst.cc | 21 ++++- gcc/rust/typecheck/rust-unify.cc | 16 ++-- .../rust/compile/generic-type-alias-subst-bug.rs | 20 ++++ gcc/testsuite/rust/compile/issues/4884.rs | 102 +++++++++++++++++++++ 6 files changed, 204 insertions(+), 30 deletions(-) diff --git a/gcc/rust/typecheck/rust-type-util.cc b/gcc/rust/typecheck/rust-type-util.cc index 8f97d60e3a3f..2234f89e0e03 100644 --- a/gcc/rust/typecheck/rust-type-util.cc +++ b/gcc/rust/typecheck/rust-type-util.cc @@ -688,17 +688,28 @@ normalize_projection (TyTy::ProjectionType *proj, location_t locus, { for (auto &bound : self->get_specified_bounds ()) { - if (!bound.get ()->is_equal (*proj->get_trait_ref ())) + // The associated type can be declared on a supertrait of + // a bound: see issue-4884 + // + // I: SplitIter<Item = T> binds Iterator2::Item, + + auto item = bound.lookup_associated_item (assoc_name); + if (!item.has_value ()) + continue; + + auto &item_val = item.value (); + const auto raw_item = item_val.get_raw_item (); + if (raw_item->get_mappings ().get_defid () != item_defid) continue; - auto &binding - = bound.get_substitution_arguments ().get_binding_args (); + const auto parent = item_val.get_parent (); + const auto &arguments = parent->get_substitution_arguments (); + const auto &binding = arguments.get_binding_args (); auto it = binding.find (assoc_name); if (it != binding.end ()) return it->second; - const auto &constraints - = bound.get_substitution_arguments ().get_constraint_args (); + const auto &constraints = arguments.get_constraint_args (); auto constraint = constraints.find (assoc_name); if (constraint != constraints.end ()) { @@ -847,18 +858,28 @@ normalize_projection (TyTy::ProjectionType *proj, location_t locus, if (unify_self) { TyTy::BaseType *proj_self = proj->get_self (); - TyTy::BaseType *impl_self = frame.self; - TyTy::BaseType *self - = unify_site_and (/*id*/ 0, TyTy::TyWithLocation (proj_self, locus), - TyTy::TyWithLocation (impl_self, locus), locus, - /*emit_errors*/ false, - /*commit*/ false, - /*infer*/ false, - /*cleanup*/ true, - /*check_bounds*/ false); - - if (self->get_kind () == TyTy::TypeKind::ERROR) - return self; + bool proj_self_is_unresolved_trait_self = false; + if (auto *param = proj_self->try_as<TyTy::ParamType> ()) + { + proj_self_is_unresolved_trait_self + = !param->can_resolve () && param->is_implicit_self_trait (); + } + + if (!proj_self_is_unresolved_trait_self) + { + TyTy::BaseType *impl_self = frame.self; + TyTy::BaseType *self = unify_site_and ( + /*id*/ 0, TyTy::TyWithLocation (proj_self, locus), + TyTy::TyWithLocation (impl_self, locus), locus, + /*emit_errors*/ false, + /*commit*/ false, + /*infer*/ false, + /*cleanup*/ true, + /*check_bounds*/ false); + + if (self->get_kind () == TyTy::TypeKind::ERROR) + return self; + } } // Lookup the trait item -> impl type mapping (key = trait item DefId). diff --git a/gcc/rust/typecheck/rust-typecheck-context.cc b/gcc/rust/typecheck/rust-typecheck-context.cc index 6f364a4ac3f0..fe6bc888cc52 100644 --- a/gcc/rust/typecheck/rust-typecheck-context.cc +++ b/gcc/rust/typecheck/rust-typecheck-context.cc @@ -335,12 +335,20 @@ TypeCheckContext::find_matching_impl_trait_frame ( unresolved_trait_self = param->is_implicit_self_trait (); } - bool compatible_self - = unresolved_trait_self - || types_compatable (TyTy::TyWithLocation (i.self), - TyTy::TyWithLocation (resolved_self), - UNDEF_LOCATION, false /* emit_errors */, - false /* check_bounds */); + // Select an existing impl context without inferring a new Self + // binding + bool compatible_self = unresolved_trait_self; + if (!compatible_self) + { + auto res + = unify_site_and (UNKNOWN_HIRID, TyTy::TyWithLocation (i.self), + TyTy::TyWithLocation (resolved_self), + UNDEF_LOCATION, false /* emit_errors */, + false /* commit */, false /* infer */, + true /* cleanup */, false /* check_bounds */); + compatible_self = res->get_kind () != TyTy::TypeKind::ERROR; + } + if (compatible_self) { *find = i; diff --git a/gcc/rust/typecheck/rust-tyty-subst.cc b/gcc/rust/typecheck/rust-tyty-subst.cc index d589b25473d1..ba237bcc7447 100644 --- a/gcc/rust/typecheck/rust-tyty-subst.cc +++ b/gcc/rust/typecheck/rust-tyty-subst.cc @@ -1021,7 +1021,26 @@ SubstitutionRef::adjust_mappings_for_this ( if (subst.needs_substitution ()) { // get from passed in mappings - mappings.get_argument_for_symbol (subst.get_param_ty (), &arg); + bool found + = mappings.get_argument_for_symbol (subst.get_param_ty (), + &arg); + if (!found) + { + // This type can already be partially instantiated from an + // outer scope + SubstitutionArg bound_arg = SubstitutionArg::error (); + bool have_binding = used_arguments.get_argument_for_symbol ( + subst.get_param_ty (), &bound_arg); + + if (have_binding && !bound_arg.is_error ()) + { + BaseType *resolved + = Resolver::SubstMapperInternal::Resolve ( + bound_arg.get_tyty (), mappings); + if (resolved->get_kind () != TypeKind::ERROR) + arg = SubstitutionArg (&subst, resolved); + } + } } else { diff --git a/gcc/rust/typecheck/rust-unify.cc b/gcc/rust/typecheck/rust-unify.cc index edb6e20e857e..417804b447bb 100644 --- a/gcc/rust/typecheck/rust-unify.cc +++ b/gcc/rust/typecheck/rust-unify.cc @@ -253,15 +253,18 @@ UnifyRules::go () } if (infer_flag) { + // an impl parameter can infer to Split<T, P> without requiring T and P to + // be concrete bool rgot_param = rtype->get_kind () == TyTy::TypeKind::PARAM; bool lhs_is_infer_var = ltype->get_kind () == TyTy::TypeKind::INFER; bool lhs_is_general_infer_var = lhs_is_infer_var && static_cast<TyTy::InferType *> (ltype)->get_infer_kind () == TyTy::InferType::GENERAL; - bool expected_is_concrete - = ltype->is_concrete () && !lhs_is_general_infer_var; - bool rneeds_infer = expected_is_concrete && (rgot_param); + bool expected_can_infer_param + = (ltype->is_concrete () || ltype->get_kind () == TyTy::TypeKind::ADT) + && !lhs_is_general_infer_var; + bool rneeds_infer = expected_can_infer_param && rgot_param; bool lgot_param = ltype->get_kind () == TyTy::TypeKind::PARAM; bool rhs_is_infer_var = rtype->get_kind () == TyTy::TypeKind::INFER; @@ -269,9 +272,10 @@ UnifyRules::go () = rhs_is_infer_var && static_cast<TyTy::InferType *> (rtype)->get_infer_kind () == TyTy::InferType::GENERAL; - bool receiver_is_concrete - = rtype->is_concrete () && !rhs_is_general_infer_var; - bool lneeds_infer = receiver_is_concrete && (lgot_param); + bool receiver_can_infer_param + = (rtype->is_concrete () || rtype->get_kind () == TyTy::TypeKind::ADT) + && !rhs_is_general_infer_var; + bool lneeds_infer = receiver_can_infer_param && lgot_param; if (rneeds_infer) { diff --git a/gcc/testsuite/rust/compile/generic-type-alias-subst-bug.rs b/gcc/testsuite/rust/compile/generic-type-alias-subst-bug.rs new file mode 100644 index 000000000000..7a5e9dc852c6 --- /dev/null +++ b/gcc/testsuite/rust/compile/generic-type-alias-subst-bug.rs @@ -0,0 +1,20 @@ +// { dg-options "-w" } +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +struct Wrapper<T> { + t: T, +} + +type Alias<U> = Wrapper<U>; + +fn f<V> (x: Alias<V>) -> V { + x.t +} + +fn main() { + let x: u32 = f(Wrapper { t: 1u32 }); +} diff --git a/gcc/testsuite/rust/compile/issues/4884.rs b/gcc/testsuite/rust/compile/issues/4884.rs new file mode 100644 index 000000000000..0ca80f14d568 --- /dev/null +++ b/gcc/testsuite/rust/compile/issues/4884.rs @@ -0,0 +1,102 @@ +// { dg-options "-w" } + +#![feature(no_core, lang_items, unboxed_closures)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} + +pub enum Option<T> { + None, + Some(T), +} + +#[lang = "fn_once"] +#[rustc_paren_sugar] +pub trait FnOnce<Args> { + #[lang = "fn_once_output"] + type Output; + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; +} + +#[lang = "fn_mut"] +#[rustc_paren_sugar] +pub trait FnMut<Args>: FnOnce<Args> { + extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; +} + +pub trait Iterator2 { + type Item; + fn next(&mut self) -> Option<Self::Item>; +} + +pub trait SplitIter: Iterator2 { + fn finish(&mut self) -> Option<Self::Item>; +} + +pub struct Split<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + v: &'a [T], + pred: P, +} + +impl<'a, T, P> Iterator2 for Split<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + type Item = &'a [T]; + fn next(&mut self) -> Option<&'a [T]> { + Option::None + } +} + +impl<'a, T, P> SplitIter for Split<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + fn finish(&mut self) -> Option<&'a [T]> { + Option::None + } +} + +struct GenericSplitN<I> { + iter: I, + count: usize, +} + +impl<T, I: SplitIter<Item = T>> Iterator2 for GenericSplitN<I> { + type Item = T; + fn next(&mut self) -> Option<T> { + match self.count { + 0 => Option::None, + _ => { + self.count -= 1; + self.iter.next() + } + } + } +} + +pub struct SplitN<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + inner: GenericSplitN<Split<'a, T, P>>, +} + +impl<'a, T, P> Iterator2 for SplitN<'a, T, P> +where + P: FnMut(&T) -> bool, +{ + type Item = &'a [T]; + + fn next(&mut self) -> Option<&'a [T]> { + self.inner.next() + } +} + +fn main() -> i32 { + 0 +}
