From: Philip Herron <[email protected]>
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]>
---
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/5870b08eac1603169553d896c128fd886523f6af
The commit has been mentioned in the following issue(s):
- Rust-GCC/gccrs#4884: https://github.com/Rust-GCC/gccrs/issues/4884
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4908
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 +--
.../compile/generic-type-alias-subst-bug.rs | 20 ++++
gcc/testsuite/rust/compile/issues/4884.rs | 102 ++++++++++++++++++
6 files changed, 204 insertions(+), 30 deletions(-)
create mode 100644 gcc/testsuite/rust/compile/generic-type-alias-subst-bug.rs
create mode 100644 gcc/testsuite/rust/compile/issues/4884.rs
diff --git a/gcc/rust/typecheck/rust-type-util.cc
b/gcc/rust/typecheck/rust-type-util.cc
index 8f97d60e3..2234f89e0 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 6f364a4ac..fe6bc888c 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 d589b2547..ba237bcc7 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 edb6e20e8..417804b44 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 000000000..7a5e9dc85
--- /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 000000000..0ca80f14d
--- /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
+}
--
2.55.0