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

Reply via email to