diff options
| author | nils <48135649+Nilstrieb@users.noreply.github.com> | 2023-03-28 12:51:13 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-28 12:51:13 +0200 |
| commit | 7e90732abe9da454a7177971c55fa069c8d56e8d (patch) | |
| tree | ca189cdc2109dc2e275ff4f897ce9e9870d50d07 | |
| parent | 64710790d6ce6fce7aa33a07932fb46bdf54d7b3 (diff) | |
| parent | 5456eecab0eb17bd34d3368db3bc6002ba619a64 (diff) | |
| download | rust-7e90732abe9da454a7177971c55fa069c8d56e8d.tar.gz rust-7e90732abe9da454a7177971c55fa069c8d56e8d.zip | |
Rollup merge of #109470 - compiler-errors:gat-normalize-bound, r=jackh726
Correctly substitute GAT's type used in `normalize_param_env` in `check_type_bounds`
Given:
```rust
trait Foo {
type Assoc<T>: PartialEq<Self::Assoc<i32>>;
}
impl Foo for () {
type Assoc<T> = Wrapper<T>;
}
struct Wrapper<T>(T);
impl<T> PartialEq<Wrapper<i32>> for Wrapper<T> { }
```
We add an additional predicate in the `normalize_param_env` in `check_type_bounds` that is used to normalize the GAT's bounds to check them in the impl. Problematically, though, that predicate is constructed to be `for<^0> <() as Foo>::Assoc<^0> => Wrapper<T>`, instead of `for<^0> <() as Foo>::Assoc<^0> => Wrapper<^0>`.
That means `Self::Assoc<i32>` in the bounds that we're checking normalizes to `Wrapper<T>`, instead of `Wrapper<i32>`, and so the bound `Self::Assoc<T>: PartialEq<Self::Assoc<i32>>` normalizes to `Wrapper<T>: PartialEq<Wrapper<T>>`, which does not hold.
Fixes this by properly substituting the RHS of that normalizes predicate that we add to the `normalize_param_env`. That means the bound is properly normalized to `Wrapper<T>: PartialEq<Wrapper<i32>>`, which *does* hold.
---
The second commit in this PR just cleans up some substs stuff and some naming.
r? `@jackh726` cc #87900
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/compare_impl_item.rs | 103 | ||||
| -rw-r--r-- | tests/ui/generic-associated-types/gat-bounds-normalize-pred.rs | 17 |
2 files changed, 67 insertions, 53 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 49665525967..17abb6ef75a 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1876,14 +1876,17 @@ pub(super) fn check_type_bounds<'tcx>( impl_ty: ty::AssocItem, impl_trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), ErrorGuaranteed> { + let param_env = tcx.param_env(impl_ty.def_id); + let container_id = impl_ty.container_id(tcx); // Given // // impl<A, B> Foo<u32> for (A, B) { - // type Bar<C> =... + // type Bar<C> = Wrapper<A, B, C> // } // // - `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) + // - `normalize_impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0) + // - `normalize_impl_ty` would be `Wrapper<A, B, ^0.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). // @@ -1912,56 +1915,46 @@ pub(super) fn check_type_bounds<'tcx>( // 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.def_id, param.name); - let bound_var = ty::BoundVariableKind::Ty(kind); - bound_vars.push(bound_var); - tcx.mk_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_re_late_bound( - 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::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_usize(bound_vars.len() - 1)), - tcx.type_of(param.def_id).subst_identity(), - ) - .into() - } - }); - let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars); - let impl_ty_substs = tcx.mk_substs(&substs); - let container_id = impl_ty.container_id(tcx); - - let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs); - let impl_ty_value = tcx.type_of(impl_ty.def_id).subst_identity(); - - let param_env = tcx.param_env(impl_ty.def_id); - + smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).params.len()); + // Extend the impl's identity substs with late-bound GAT vars + let normalize_impl_ty_substs = ty::InternalSubsts::identity_for_item(tcx, container_id) + .extend_to(tcx, impl_ty.def_id, |param, _| match param.kind { + GenericParamDefKind::Type { .. } => { + let kind = ty::BoundTyKind::Param(param.def_id, param.name); + let bound_var = ty::BoundVariableKind::Ty(kind); + bound_vars.push(bound_var); + tcx.mk_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_re_late_bound( + 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::ConstKind::Bound( + ty::INNERMOST, + ty::BoundVar::from_usize(bound_vars.len() - 1), + ), + tcx.type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + ) + .into() + } + }); // When checking something like // // trait X { type Y: PartialEq<<Self as X>::Y> } @@ -1971,9 +1964,13 @@ pub(super) fn check_type_bounds<'tcx>( // we want <T as X>::Y to normalize to S. This is valid because we are // checking the default value specifically here. Add this equality to the // ParamEnv for normalization specifically. + let normalize_impl_ty = tcx.type_of(impl_ty.def_id).subst(tcx, normalize_impl_ty_substs); + let rebased_substs = + normalize_impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs); + let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars); let normalize_param_env = { let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>(); - match impl_ty_value.kind() { + match normalize_impl_ty.kind() { ty::Alias(ty::Projection, proj) if proj.def_id == trait_ty.def_id && proj.substs == rebased_substs => { @@ -1987,7 +1984,7 @@ pub(super) fn check_type_bounds<'tcx>( ty::Binder::bind_with_vars( ty::ProjectionPredicate { projection_ty: tcx.mk_alias_ty(trait_ty.def_id, rebased_substs), - term: impl_ty_value.into(), + term: normalize_impl_ty.into(), }, bound_vars, ) diff --git a/tests/ui/generic-associated-types/gat-bounds-normalize-pred.rs b/tests/ui/generic-associated-types/gat-bounds-normalize-pred.rs new file mode 100644 index 00000000000..b43f982283b --- /dev/null +++ b/tests/ui/generic-associated-types/gat-bounds-normalize-pred.rs @@ -0,0 +1,17 @@ +// check-pass + +trait Foo { + type Assoc<T>: PartialEq<Self::Assoc<i32>>; +} + +impl Foo for () { + type Assoc<T> = Wrapper<T>; +} + +struct Wrapper<T>(T); + +impl<T> PartialEq<Wrapper<i32>> for Wrapper<T> { + fn eq(&self, _other: &Wrapper<i32>) -> bool { true } +} + +fn main() {} |
