Skip to content

Commit d40fd3d

Browse files
Add all RPITITs when augmenting param-env with GAT bounds in check_type_bounds
1 parent 2a38e39 commit d40fd3d

File tree

3 files changed

+125
-78
lines changed

3 files changed

+125
-78
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+112-78
Original file line numberDiff line numberDiff line change
@@ -2162,7 +2162,7 @@ pub(super) fn check_type_bounds<'tcx>(
21622162
impl_ty: ty::AssocItem,
21632163
impl_trait_ref: ty::TraitRef<'tcx>,
21642164
) -> Result<(), ErrorGuaranteed> {
2165-
let param_env = param_env_with_gat_bounds(tcx, trait_ty, impl_ty, impl_trait_ref);
2165+
let param_env = param_env_with_gat_bounds(tcx, impl_ty, impl_trait_ref);
21662166
debug!(?param_env);
21672167

21682168
let container_id = impl_ty.container_id(tcx);
@@ -2239,7 +2239,13 @@ pub(super) fn check_type_bounds<'tcx>(
22392239
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
22402240
}
22412241

2242-
/// Given
2242+
/// Install projection predicates that allow GATs to project to their own
2243+
/// definition types. This is not allowed in general in cases of default
2244+
/// associated types in trait definitions, or when specialization is involved,
2245+
/// but is needed when checking these definition types actually satisfy the
2246+
/// trait bounds of the GAT.
2247+
///
2248+
/// # How it works
22432249
///
22442250
/// impl<A, B> Foo<u32> for (A, B) {
22452251
/// type Bar<C> = Wrapper<A, B, C>
@@ -2266,10 +2272,12 @@ pub(super) fn check_type_bounds<'tcx>(
22662272
/// ```notrust
22672273
/// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
22682274
/// ```
2275+
///
22692276
/// when we really would like to generate
22702277
/// ```notrust
22712278
/// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
22722279
/// ```
2280+
///
22732281
/// But, this is probably fine, because although the first clause can be used with types C that
22742282
/// do not implement Eq, for it to cause some kind of problem, there would have to be a
22752283
/// VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type
@@ -2278,92 +2286,118 @@ pub(super) fn check_type_bounds<'tcx>(
22782286
/// the trait (notably, that X: Eq and T: Family).
22792287
fn param_env_with_gat_bounds<'tcx>(
22802288
tcx: TyCtxt<'tcx>,
2281-
trait_ty: ty::AssocItem,
22822289
impl_ty: ty::AssocItem,
22832290
impl_trait_ref: ty::TraitRef<'tcx>,
22842291
) -> ty::ParamEnv<'tcx> {
22852292
let param_env = tcx.param_env(impl_ty.def_id);
22862293
let container_id = impl_ty.container_id(tcx);
22872294
let mut predicates = param_env.caller_bounds().to_vec();
22882295

2289-
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
2290-
smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).params.len());
2291-
// Extend the impl's identity args with late-bound GAT vars
2292-
let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id).extend_to(
2293-
tcx,
2294-
impl_ty.def_id,
2295-
|param, _| match param.kind {
2296-
GenericParamDefKind::Type { .. } => {
2297-
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
2298-
let bound_var = ty::BoundVariableKind::Ty(kind);
2299-
bound_vars.push(bound_var);
2300-
Ty::new_bound(
2301-
tcx,
2302-
ty::INNERMOST,
2303-
ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
2304-
)
2305-
.into()
2296+
// for RPITITs, we should install predicates that allow us to project all
2297+
// of the RPITITs associated with the same body. This is because checking
2298+
// the item bounds of RPITITs often involves nested RPITITs having to prove
2299+
// bounds about themselves.
2300+
let impl_tys_to_install = match impl_ty.opt_rpitit_info {
2301+
None => vec![impl_ty],
2302+
Some(
2303+
ty::ImplTraitInTraitData::Impl { fn_def_id }
2304+
| ty::ImplTraitInTraitData::Trait { fn_def_id, .. },
2305+
) => tcx
2306+
.associated_types_for_impl_traits_in_associated_fn(fn_def_id)
2307+
.iter()
2308+
.map(|def_id| tcx.associated_item(*def_id))
2309+
.collect(),
2310+
};
2311+
2312+
for impl_ty in impl_tys_to_install {
2313+
let trait_ty = match impl_ty.container {
2314+
ty::AssocItemContainer::TraitContainer => impl_ty,
2315+
ty::AssocItemContainer::ImplContainer => {
2316+
tcx.associated_item(impl_ty.trait_item_def_id.unwrap())
23062317
}
2307-
GenericParamDefKind::Lifetime => {
2308-
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
2309-
let bound_var = ty::BoundVariableKind::Region(kind);
2310-
bound_vars.push(bound_var);
2311-
ty::Region::new_late_bound(
2312-
tcx,
2313-
ty::INNERMOST,
2314-
ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
2315-
)
2316-
.into()
2318+
};
2319+
2320+
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
2321+
smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).params.len());
2322+
// Extend the impl's identity args with late-bound GAT vars
2323+
let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id)
2324+
.extend_to(tcx, impl_ty.def_id, |param, _| match param.kind {
2325+
GenericParamDefKind::Type { .. } => {
2326+
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
2327+
let bound_var = ty::BoundVariableKind::Ty(kind);
2328+
bound_vars.push(bound_var);
2329+
Ty::new_bound(
2330+
tcx,
2331+
ty::INNERMOST,
2332+
ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
2333+
)
2334+
.into()
2335+
}
2336+
GenericParamDefKind::Lifetime => {
2337+
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
2338+
let bound_var = ty::BoundVariableKind::Region(kind);
2339+
bound_vars.push(bound_var);
2340+
ty::Region::new_late_bound(
2341+
tcx,
2342+
ty::INNERMOST,
2343+
ty::BoundRegion {
2344+
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
2345+
kind,
2346+
},
2347+
)
2348+
.into()
2349+
}
2350+
GenericParamDefKind::Const { .. } => {
2351+
let bound_var = ty::BoundVariableKind::Const;
2352+
bound_vars.push(bound_var);
2353+
ty::Const::new_bound(
2354+
tcx,
2355+
ty::INNERMOST,
2356+
ty::BoundVar::from_usize(bound_vars.len() - 1),
2357+
tcx.type_of(param.def_id)
2358+
.no_bound_vars()
2359+
.expect("const parameter types cannot be generic"),
2360+
)
2361+
.into()
2362+
}
2363+
});
2364+
// When checking something like
2365+
//
2366+
// trait X { type Y: PartialEq<<Self as X>::Y> }
2367+
// impl X for T { default type Y = S; }
2368+
//
2369+
// We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case
2370+
// we want <T as X>::Y to normalize to S. This is valid because we are
2371+
// checking the default value specifically here. Add this equality to the
2372+
// ParamEnv for normalization specifically.
2373+
let normalize_impl_ty =
2374+
tcx.type_of(impl_ty.def_id).instantiate(tcx, normalize_impl_ty_args);
2375+
let rebased_args =
2376+
normalize_impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
2377+
let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
2378+
2379+
match normalize_impl_ty.kind() {
2380+
ty::Alias(ty::Projection, proj)
2381+
if proj.def_id == trait_ty.def_id && proj.args == rebased_args =>
2382+
{
2383+
// Don't include this predicate if the projected type is
2384+
// exactly the same as the projection. This can occur in
2385+
// (somewhat dubious) code like this:
2386+
//
2387+
// impl<T> X for T where T: X { type Y = <T as X>::Y; }
23172388
}
2318-
GenericParamDefKind::Const { .. } => {
2319-
let bound_var = ty::BoundVariableKind::Const;
2320-
bound_vars.push(bound_var);
2321-
ty::Const::new_bound(
2322-
tcx,
2323-
ty::INNERMOST,
2324-
ty::BoundVar::from_usize(bound_vars.len() - 1),
2325-
tcx.type_of(param.def_id)
2326-
.no_bound_vars()
2327-
.expect("const parameter types cannot be generic"),
2389+
_ => predicates.push(
2390+
ty::Binder::bind_with_vars(
2391+
ty::ProjectionPredicate {
2392+
projection_ty: ty::AliasTy::new(tcx, trait_ty.def_id, rebased_args),
2393+
term: normalize_impl_ty.into(),
2394+
},
2395+
bound_vars,
23282396
)
2329-
.into()
2330-
}
2331-
},
2332-
);
2333-
// When checking something like
2334-
//
2335-
// trait X { type Y: PartialEq<<Self as X>::Y> }
2336-
// impl X for T { default type Y = S; }
2337-
//
2338-
// We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case
2339-
// we want <T as X>::Y to normalize to S. This is valid because we are
2340-
// checking the default value specifically here. Add this equality to the
2341-
// ParamEnv for normalization specifically.
2342-
let normalize_impl_ty = tcx.type_of(impl_ty.def_id).instantiate(tcx, normalize_impl_ty_args);
2343-
let rebased_args = normalize_impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
2344-
let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
2345-
2346-
match normalize_impl_ty.kind() {
2347-
ty::Alias(ty::Projection, proj)
2348-
if proj.def_id == trait_ty.def_id && proj.args == rebased_args =>
2349-
{
2350-
// Don't include this predicate if the projected type is
2351-
// exactly the same as the projection. This can occur in
2352-
// (somewhat dubious) code like this:
2353-
//
2354-
// impl<T> X for T where T: X { type Y = <T as X>::Y; }
2355-
}
2356-
_ => predicates.push(
2357-
ty::Binder::bind_with_vars(
2358-
ty::ProjectionPredicate {
2359-
projection_ty: ty::AliasTy::new(tcx, trait_ty.def_id, rebased_args),
2360-
term: normalize_impl_ty.into(),
2361-
},
2362-
bound_vars,
2363-
)
2364-
.to_predicate(tcx),
2365-
),
2366-
};
2397+
.to_predicate(tcx),
2398+
),
2399+
};
2400+
}
23672401

23682402
ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
23692403
}

tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// check-pass
2+
13
#![feature(associated_type_defaults)]
24

35
trait Foo {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// check-pass
2+
3+
use std::ops::Deref;
4+
5+
trait Foo {
6+
fn foo() -> impl Deref<Target = impl Deref<Target = impl Sized>> {
7+
&&()
8+
}
9+
}
10+
11+
fn main() {}

0 commit comments

Comments
 (0)