Repository: rust-lang/chalk (Rust)

Issue:
Change order of implicit type parameters
Implicit type parameters (like `Self` and others from the env) are put into a lowered item after the explicit type parameters, which is surprising.

For example, the following test fails:
```rust
program {
    trait Combine { type Item<T>; }
    struct u32 { }
    struct i32 { }
    struct Either<T, U> { }
    impl Combine for u32 { type Item<U> = Either<u32, U>; }
    impl Combine for i32 { type Item<U> = Either<i32, U>; }
}

goal {
    exists<T, U> {
        T: Combine<Item<U> = Either<u32, i32>>
    }
} yields {
    "Unique; substitution [?0 := u32, ?1 := i32]"
}
```

Instead of the expected result, we get `[?0 := i32, ?1 := u32]`, which is technically correct because our GAT parameter `U` is mapped to `?0` and `T` is mapped to `?1`. But there's no way to see this from the output of chalk.

See for example: https://github.com/rust-lang-nursery/chalk/blob/94a1941a021842a5fcb35cd043145c8faae59f08/src/ir/lowering.rs#L183-L185

This will probably have to be changed in a bunch of places.


Discussion:

Patch:
diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs
index 46c04b36837..4d2f0d8cefc 100644
--- a/chalk-integration/src/lowering.rs
+++ b/chalk-integration/src/lowering.rs
@@ -633,7 +633,10 @@ impl LowerWithEnv for ProjectionTy {
             substitution: trait_substitution,
         } = trait_ref.lower(env)?;
         let lookup = env.lookup_associated_ty(trait_id, name)?;
-        let mut args: Vec<_> = args
+
+        let mut all_args: Vec<_> = trait_substitution.iter(interner).cloned().collect();
+
+        let args: Vec<_> = args
             .iter()
             .map(|a| a.lower(env))
             .collect::<LowerResult<_>>()?;
@@ -656,11 +659,11 @@ impl LowerWithEnv for ProjectionTy {
             }
         }
 
-        args.extend(trait_substitution.iter(interner).cloned());
+        all_args.extend(args.into_iter());
 
         Ok(chalk_ir::ProjectionTy {
             associated_ty_id: lookup.id,
-            substitution: chalk_ir::Substitution::from_iter(interner, args),
+            substitution: chalk_ir::Substitution::from_iter(interner, all_args),
         })
     }
 }
@@ -1024,9 +1027,8 @@ pub fn lower_goal(goal: &Goal, program: &LoweredProgram) -> LowerResult<chalk_ir
         .map(|(&associated_ty_id, datum)| {
             let trait_datum = &program.trait_data[&datum.trait_id];
             let num_trait_params = trait_datum.binders.len(interner);
-            let num_addl_params = datum.binders.len(interner) - num_trait_params;
             let addl_variable_kinds =
-                datum.binders.binders.as_slice(interner)[..num_addl_params].to_owned();
+                datum.binders.binders.as_slice(interner)[num_trait_params..].to_owned();
             let lookup = AssociatedTyLookup {
                 id: associated_ty_id,
                 addl_variable_kinds,
diff --git a/chalk-integration/src/lowering/program_lowerer.rs b/chalk-integration/src/lowering/program_lowerer.rs
index d6cbfd4b6f6..a5dc18df640 100644
--- a/chalk-integration/src/lowering/program_lowerer.rs
+++ b/chalk-integration/src/lowering/program_lowerer.rs
@@ -292,8 +292,8 @@ impl ProgramLowerer {
                         //     type Bar<A0, A1>;
                         // }
                         // ```
-                        let mut variable_kinds = assoc_ty_defn.all_parameters();
-                        variable_kinds.extend(trait_defn.all_parameters());
+                        let mut variable_kinds = trait_defn.all_parameters();
+                        variable_kinds.extend(assoc_ty_defn.all_parameters());
 
                         let binders = empty_env.in_binders(variable_kinds, |env| {
                             Ok(rust_ir::AssociatedTyDatumBound {
@@ -330,8 +330,8 @@ impl ProgramLowerer {
                         // impl *and* those from the associated type
                         // itself. As in the "trait" case above, we begin
                         // with the parameters from the impl.
-                        let mut variable_kinds = atv.all_parameters();
-                        variable_kinds.extend(impl_defn.all_parameters());
+                        let mut variable_kinds = impl_defn.all_parameters();
+                        variable_kinds.extend(atv.all_parameters());
 
                         let value = empty_env.in_binders(variable_kinds, |env| {
                             Ok(rust_ir::AssociatedTyValueBound {
diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs
index 1e6466ffaef..329c6b984d2 100644
--- a/chalk-solve/src/clauses.rs
+++ b/chalk-solve/src/clauses.rs
@@ -866,11 +866,9 @@ fn push_alias_alias_eq_clause<I: Interner>(
                 let (_, trait_args, assoc_args) = builder.db.split_projection(&projection_ty);
                 let fresh_self_subst = Substitution::from_iter(
                     interner,
-                    assoc_args
-                        .iter()
-                        .cloned()
-                        .chain(std::iter::once(intermediate_eq_ty.clone().cast(interner)))
-                        .chain(trait_args[1..].iter().cloned()),
+                    std::iter::once(intermediate_eq_ty.clone().cast(interner))
+                        .chain(trait_args[1..].iter().cloned())
+                        .chain(assoc_args.iter().cloned()),
                 );
                 let fresh_alias = AliasTy::Projection(ProjectionTy {
                     associated_ty_id: projection_ty.associated_ty_id,
diff --git a/chalk-solve/src/split.rs b/chalk-solve/src/split.rs
index bea24044db8..5a45a833d99 100644
--- a/chalk-solve/src/split.rs
+++ b/chalk-solve/src/split.rs
@@ -93,9 +93,8 @@ pub trait Split<I: Interner>: RustIrDatabase<I> {
         // the impl parameters are a suffix
         //
         // [ P0..Pn, Pn...Pm ]
-        //           ^^^^^^^ impl parameters
-        let split_point = parameters.len() - impl_params_len;
-        let (other_params, impl_params) = parameters.split_at(split_point);
+        //   ^^^^^^ impl parameters
+        let (impl_params, other_params) = parameters.split_at(impl_params_len);
         (impl_params, other_params)
     }
 
@@ -144,9 +143,10 @@ pub trait Split<I: Interner>: RustIrDatabase<I> {
         // `<Box<!T> as Foo>::Item<'!a>`
         let projection_substitution = Substitution::from_iter(
             interner,
-            atv_parameters
-                .iter()
-                .chain(trait_ref.substitution.iter(interner))
+            trait_ref
+                .substitution
+                .iter(interner)
+                .chain(atv_parameters.iter())
                 .cloned(),
         );
 
@@ -190,8 +190,8 @@ pub trait Split<I: Interner>: RustIrDatabase<I> {
     ) -> (&'p [P], &'p [P]) {
         let trait_datum = &self.trait_datum(associated_ty_datum.trait_id);
         let trait_num_params = trait_datum.binders.len(self.interner());
-        let split_point = parameters.len() - trait_num_params;
-        let (other_params, trait_params) = parameters.split_at(split_point);
+        let split_point = trait_num_params;
+        let (trait_params, other_params) = parameters.split_at(split_point);
         (trait_params, other_params)
     }
 }


Test patch:
diff --git a/tests/lowering/mod.rs b/tests/lowering/mod.rs
index 628f7b2104e..7ddcba1fcc3 100644
--- a/tests/lowering/mod.rs
+++ b/tests/lowering/mod.rs
@@ -192,8 +192,8 @@ fn atc_accounting() {
             &r#"AssociatedTyValue {
     impl_id: ImplId(#2),
     associated_ty_id: (Iterable::Iter),
-    value: for<lifetime, type> AssociatedTyValueBound {
-        ty: Iter<'^0.0, ^0.1>
+    value: for<type, lifetime> AssociatedTyValueBound {
+        ty: Iter<'^0.1, ^0.0>
     },
 }"#
             .replace(",\n", "\n"),
diff --git a/tests/test/impls.rs b/tests/test/impls.rs
index 5576c1d5900..aa15879dd9e 100644
--- a/tests/test/impls.rs
+++ b/tests/test/impls.rs
@@ -240,8 +240,7 @@ fn normalize_rev_infer_gat() {
                 T: Combine<Item<U> = Either<A, B>>
             }
         } yields {
-            // T is ?1 and U is ?0, so this is surprising, but correct! (See #126.)
-            expect![["Unique; substitution [?0 := B, ?1 := A]"]]
+            expect![["Unique; substitution [?0 := A, ?1 := B]"]]
         }
     }
 }
diff --git a/tests/test/unify.rs b/tests/test/unify.rs
index a4bfabdbb7d..6de70054910 100644
--- a/tests/test/unify.rs
+++ b/tests/test/unify.rs
@@ -367,9 +367,7 @@ fn mixed_indices_normalize_gat_application() {
                 Normalize(<Ref<'a, T> as Foo>::T<X> -> Either<U, Y>)
             }
         } yields {
-            // Our GAT parameter <X> is mapped to ?0; all others appear left to right
-            // in our Normalize(...) goal.
-            expect![["Unique; for<?U0,?U0,?U0> { substitution [?0 := ^0.0, ?1 := '^0.1, ?2 := ^0.2, ?3 := ^0.0, ?4 := ^0.2] }"]]
+            expect![["Unique; for<?U0,?U0,?U0> { substitution [?0 := '^0.0, ?1 := ^0.1, ?2 := ^0.2, ?3 := ^0.2, ?4 := ^0.1] }"]]
         }
     }
 }