diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 4fbb4d1806f83fb774c2cac1d62789bc4a8ba633..84ec2e76c0b07d0975c5f061de9a6f59150e342b 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -234,7 +234,7 @@ pub fn extend_to(&self, tcx: TyCtxt<'tcx>, def_id: DefId, mut mk_kind: F) -> }) } - fn fill_item( + pub fn fill_item( substs: &mut SmallVec<[GenericArg<'tcx>; 8]>, tcx: TyCtxt<'tcx>, defs: &ty::Generics, @@ -249,7 +249,7 @@ fn fill_item( Self::fill_single(substs, defs, mk_kind) } - fn fill_single( + pub fn fill_single( 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 8d5bf98be9932b177037d98158f2376f12618a4c..c384e0dcb2cae418bf238647e14d776166823917 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 =... // } // - // - `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> + // - `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; + // } + // + // impl Family for VecFamily { + // type Member = i32; + // } + // ``` + // Here, we would generate + // ```notrust + // forall { Normalize(::Member => i32) } + // ``` + // when we really would like to generate + // ```notrust + // forall { Normalize(::Member => 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 for some type X where !(X: Eq), that appears in the value of type + // Member = .... That type would fail a well-formedness check that we ought to be doing + // elsewhere, which would check that any ::Member 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 X for T where T: X { type Y = ::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 0000000000000000000000000000000000000000..d35bb098abdf008325209ea597275c370b64c067 --- /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 0000000000000000000000000000000000000000..9ee07c2f1e1cc953a0535c22080bcfbe37faeb8d --- /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 + type Member<'a>: for<'b> PartialEq> = i32; +} + +struct Foo; +trait Family2 { + // Not fine, not Foo: PartialEq + type Member<'a>: for<'b> PartialEq> = 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 0000000000000000000000000000000000000000..01cb0bfc72cd152c9101d12a5f114ae4dcb7966d --- /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> = 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> = 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 0000000000000000000000000000000000000000..b365e07feb28a957e7e3ad9bc9bf420199591505 --- /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>; +} + +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 0000000000000000000000000000000000000000..87bd35f58788f50fcf6a2fe20c15a067a4727295 --- /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 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>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 0000000000000000000000000000000000000000..f905348ae32a30fdba2868b575b9782ac7311e9c --- /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>; +} + +struct I32; + +impl Family for I32 { + type Member<'a> = i32; +} + +fn main() {}