about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-08-24 14:55:48 +0000
committerbors <bors@rust-lang.org>2021-08-24 14:55:48 +0000
commitb5fe3bc0650279e4283a0511670c0668fc0f2217 (patch)
treee0b783aca25d0bc2ef154ba5ac82063fd580c9c5
parent47ab5f7ce27397310bd8359b8db1504fbf8a9b59 (diff)
parentb0170779f5c6e9705658cda1b02cf1fd1a925205 (diff)
downloadrust-b5fe3bc0650279e4283a0511670c0668fc0f2217.tar.gz
rust-b5fe3bc0650279e4283a0511670c0668fc0f2217.zip
Auto merge of #87900 - jackh726:issue-87429, r=nikomatsakis
Use bound vars for GAT params in param_env in check_type_bounds

Fixes #87429
-rw-r--r--compiler/rustc_middle/src/ty/subst.rs4
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs103
-rw-r--r--src/test/ui/generic-associated-types/issue-87429-2.rs20
-rw-r--r--src/test/ui/generic-associated-types/issue-87429-associated-type-default.rs18
-rw-r--r--src/test/ui/generic-associated-types/issue-87429-associated-type-default.stderr16
-rw-r--r--src/test/ui/generic-associated-types/issue-87429-specialization.rs25
-rw-r--r--src/test/ui/generic-associated-types/issue-87429-specialization.stderr26
-rw-r--r--src/test/ui/generic-associated-types/issue-87429.rs15
8 files changed, 215 insertions, 12 deletions
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index 4fbb4d1806f..84ec2e76c0b 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -234,7 +234,7 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
         })
     }
 
-    fn fill_item<F>(
+    pub fn fill_item<F>(
         substs: &mut SmallVec<[GenericArg<'tcx>; 8]>,
         tcx: TyCtxt<'tcx>,
         defs: &ty::Generics,
@@ -249,7 +249,7 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
         Self::fill_single(substs, defs, mk_kind)
     }
 
-    fn fill_single<F>(
+    pub fn fill_single<F>(
         substs: &mut SmallVec<[GenericArg<'tcx>; 8]>,
         defs: &ty::Generics,
         mk_kind: &mut F,
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 8d5bf98be99..c384e0dcb2c 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -1225,6 +1225,7 @@ fn compare_type_predicate_entailment<'tcx>(
 /// For default associated types the normalization is not possible (the value
 /// from the impl could be overridden). We also can't normalize generic
 /// associated types (yet) because they contain bound parameters.
+#[tracing::instrument(level = "debug", skip(tcx))]
 pub fn check_type_bounds<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ty: &ty::AssocItem,
@@ -1238,10 +1239,83 @@ pub fn check_type_bounds<'tcx>(
     //     type Bar<C> =...
     // }
     //
-    // - `impl_substs` would be `[A, B, C]`
-    // - `rebased_substs` would be `[(A, B), u32, C]`, combining the substs from
-    //    the *trait* with the generic associated type parameters.
-    let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
+    // - `impl_trait_ref` would be `<(A, B) as Foo<u32>>
+    // - `impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
+    // - `rebased_substs` would be `[(A, B), u32, ^0.0]`, combining the substs from
+    //    the *trait* with the generic associated type parameters (as bound vars).
+    //
+    // A note regarding the use of bound vars here:
+    // Imagine as an example
+    // ```
+    // trait Family {
+    //     type Member<C: Eq>;
+    // }
+    //
+    // impl Family for VecFamily {
+    //     type Member<C: Eq> = i32;
+    // }
+    // ```
+    // Here, we would generate
+    // ```notrust
+    // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
+    // ```
+    // when we really would like to generate
+    // ```notrust
+    // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
+    // ```
+    // But, this is probably fine, because although the first clause can be used with types C that
+    // do not implement Eq, for it to cause some kind of problem, there would have to be a
+    // VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type
+    // Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing
+    // elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in
+    // the trait (notably, that X: Eq and T: Family).
+    let defs: &ty::Generics = tcx.generics_of(impl_ty.def_id);
+    let mut substs = smallvec::SmallVec::with_capacity(defs.count());
+    if let Some(def_id) = defs.parent {
+        let parent_defs = tcx.generics_of(def_id);
+        InternalSubsts::fill_item(&mut substs, tcx, parent_defs, &mut |param, _| {
+            tcx.mk_param_from_def(param)
+        });
+    }
+    let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
+        smallvec::SmallVec::with_capacity(defs.count());
+    InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param.kind {
+        GenericParamDefKind::Type { .. } => {
+            let kind = ty::BoundTyKind::Param(param.name);
+            let bound_var = ty::BoundVariableKind::Ty(kind);
+            bound_vars.push(bound_var);
+            tcx.mk_ty(ty::Bound(
+                ty::INNERMOST,
+                ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
+            ))
+            .into()
+        }
+        GenericParamDefKind::Lifetime => {
+            let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
+            let bound_var = ty::BoundVariableKind::Region(kind);
+            bound_vars.push(bound_var);
+            tcx.mk_region(ty::ReLateBound(
+                ty::INNERMOST,
+                ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
+            ))
+            .into()
+        }
+        GenericParamDefKind::Const { .. } => {
+            let bound_var = ty::BoundVariableKind::Const;
+            bound_vars.push(bound_var);
+            tcx.mk_const(ty::Const {
+                ty: tcx.type_of(param.def_id),
+                val: ty::ConstKind::Bound(
+                    ty::INNERMOST,
+                    ty::BoundVar::from_usize(bound_vars.len() - 1),
+                ),
+            })
+            .into()
+        }
+    });
+    let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter());
+    let impl_ty_substs = tcx.intern_substs(&substs);
+
     let rebased_substs =
         impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs);
     let impl_ty_value = tcx.type_of(impl_ty.def_id);
@@ -1270,18 +1344,26 @@ pub fn check_type_bounds<'tcx>(
                 // impl<T> X for T where T: X { type Y = <T as X>::Y; }
             }
             _ => predicates.push(
-                ty::Binder::dummy(ty::ProjectionPredicate {
-                    projection_ty: ty::ProjectionTy {
-                        item_def_id: trait_ty.def_id,
-                        substs: rebased_substs,
+                ty::Binder::bind_with_vars(
+                    ty::ProjectionPredicate {
+                        projection_ty: ty::ProjectionTy {
+                            item_def_id: trait_ty.def_id,
+                            substs: rebased_substs,
+                        },
+                        ty: impl_ty_value,
                     },
-                    ty: impl_ty_value,
-                })
+                    bound_vars,
+                )
                 .to_predicate(tcx),
             ),
         };
         ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing)
     };
+    debug!(?normalize_param_env);
+
+    let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
+    let rebased_substs =
+        impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs);
 
     tcx.infer_ctxt().enter(move |infcx| {
         let constness = impl_ty
@@ -1308,6 +1390,7 @@ pub fn check_type_bounds<'tcx>(
             .explicit_item_bounds(trait_ty.def_id)
             .iter()
             .map(|&(bound, span)| {
+                debug!(?bound);
                 let concrete_ty_bound = bound.subst(tcx, rebased_substs);
                 debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
 
diff --git a/src/test/ui/generic-associated-types/issue-87429-2.rs b/src/test/ui/generic-associated-types/issue-87429-2.rs
new file mode 100644
index 00000000000..d35bb098abd
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-87429-2.rs
@@ -0,0 +1,20 @@
+// Derived from `issue-87429`. A test that ensures that using bound vars in the
+// predicates in the param env when checking that an associated type satisfies
+// its bounds does not cause us to not be able to use the bounds on the parameters.
+
+// check-pass
+
+#![feature(generic_associated_types)]
+
+trait Family {
+    type Member<'a, C: Eq>: for<'b> MyBound<'b, C>;
+}
+
+trait MyBound<'a, C> { }
+impl<'a, C: Eq> MyBound<'a, C> for i32 { }
+
+impl Family for () {
+    type Member<'a, C: Eq> = i32;
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/issue-87429-associated-type-default.rs b/src/test/ui/generic-associated-types/issue-87429-associated-type-default.rs
new file mode 100644
index 00000000000..9ee07c2f1e1
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-87429-associated-type-default.rs
@@ -0,0 +1,18 @@
+// check-fail
+
+#![feature(associated_type_defaults)]
+#![feature(generic_associated_types)]
+
+trait Family {
+    // Fine, i32: PartialEq<i32>
+    type Member<'a>: for<'b> PartialEq<Self::Member<'b>> = i32;
+}
+
+struct Foo;
+trait Family2 {
+    // Not fine, not Foo: PartialEq<Foo>
+    type Member<'a>: for<'b> PartialEq<Self::Member<'b>> = Foo;
+    //~^ ERROR can't compare
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/issue-87429-associated-type-default.stderr b/src/test/ui/generic-associated-types/issue-87429-associated-type-default.stderr
new file mode 100644
index 00000000000..01cb0bfc72c
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-87429-associated-type-default.stderr
@@ -0,0 +1,16 @@
+error[E0277]: can't compare `Foo` with `Foo`
+  --> $DIR/issue-87429-associated-type-default.rs:14:5
+   |
+LL |     type Member<'a>: for<'b> PartialEq<Self::Member<'b>> = Foo;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `Foo == Foo`
+   |
+   = help: the trait `PartialEq` is not implemented for `Foo`
+note: required by a bound in `Family2::Member`
+  --> $DIR/issue-87429-associated-type-default.rs:14:22
+   |
+LL |     type Member<'a>: for<'b> PartialEq<Self::Member<'b>> = Foo;
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Family2::Member`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/generic-associated-types/issue-87429-specialization.rs b/src/test/ui/generic-associated-types/issue-87429-specialization.rs
new file mode 100644
index 00000000000..b365e07feb2
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-87429-specialization.rs
@@ -0,0 +1,25 @@
+// check-fail
+
+#![feature(specialization)]
+//~^ WARN incomplete
+#![feature(generic_associated_types)]
+
+trait Family {
+    type Member<'a>: for<'b> PartialEq<Self::Member<'b>>;
+}
+
+struct I32Family;
+
+impl Family for I32Family {
+    default type Member<'a> = i32;
+}
+
+struct Foo;
+struct FooFamily;
+
+impl Family for FooFamily {
+    default type Member<'a> = Foo;
+    //~^ ERROR can't compare
+}
+
+fn main() {}
diff --git a/src/test/ui/generic-associated-types/issue-87429-specialization.stderr b/src/test/ui/generic-associated-types/issue-87429-specialization.stderr
new file mode 100644
index 00000000000..87bd35f5878
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-87429-specialization.stderr
@@ -0,0 +1,26 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-87429-specialization.rs:3:12
+   |
+LL | #![feature(specialization)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+   = help: consider using `min_specialization` instead, which is more stable and complete
+
+error[E0277]: can't compare `Foo` with `Foo`
+  --> $DIR/issue-87429-specialization.rs:21:5
+   |
+LL |     default type Member<'a> = Foo;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `Foo == Foo`
+   |
+   = help: the trait `PartialEq` is not implemented for `Foo`
+note: required by a bound in `Family::Member`
+  --> $DIR/issue-87429-specialization.rs:8:22
+   |
+LL |     type Member<'a>: for<'b> PartialEq<Self::Member<'b>>;
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Family::Member`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/generic-associated-types/issue-87429.rs b/src/test/ui/generic-associated-types/issue-87429.rs
new file mode 100644
index 00000000000..f905348ae32
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-87429.rs
@@ -0,0 +1,15 @@
+// check-pass
+
+#![feature(generic_associated_types)]
+
+trait Family {
+    type Member<'a>: for<'b> PartialEq<Self::Member<'b>>;
+}
+
+struct I32;
+
+impl Family for I32 {
+    type Member<'a> = i32;
+}
+
+fn main() {}