Skip to content

Commit 2520ca8

Browse files
committed
Auto merge of #117131 - compiler-errors:projection-oops, r=lcnr
Add all RPITITs when augmenting param-env with GAT bounds in `check_type_bounds` When checking that associated type definitions actually satisfy their associated type bounds in `check_type_bounds`, we construct a "`normalize_param_env`" which adds a projection predicate that allows us to assume that we can project the GAT to the definition we're checking. For example, in: ```rust type Foo { type Bar: Display = i32; } ``` We would add `<Self as Foo>::Bar = i32` as a projection predicate when checking that `i32: Display` holds. That `normalize_param_env` was, for some reason, only being used to normalize the predicate before it was registered. This is sketchy, because a nested obligation may require the GAT bound to hold, and also the projection cache is broken and doesn't differentiate projection cache keys that differ by param-envs 😿. This `normalize_param_env` is also not sufficient when we have nested RPITITs and default trait methods, since we need to be able to assume we can normalize both the RPITIT and all of its child RPITITs to sufficiently prove all of its bounds. This is the cause of #117104, which only starts to fail for RPITITs that are nested 3 and above due to the projection-cache bug above.[^1] ## First fix Use the `normalize_param_env` everywhere in `check_type_bounds`. This is reflected in a test I've constructed that fixes a GAT-only failure. ## Second fix For RPITITs, install projection predicates for each RPITIT in the same function in `check_type_bounds`. This fixes #117104. not sure who to request, so... r? `@lcnr` hehe feel free to reassign :3 [^1]: The projection cache bug specifically occurs because we try normalizing the `assumed_wf_types` with the non-normalization param-env. This causes us to insert a projection cache entry that keeps the outermost RPITIT rigid, and it trivially satisifes all its own bounds. Super sketchy![^2] [^2]: I haven't actually gone and fixed the projection cache bug because it's only marginally related, but I could, and it should no longer be triggered here.
2 parents a2f5f96 + dd571e4 commit 2520ca8

File tree

7 files changed

+204
-143
lines changed

7 files changed

+204
-143
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+169-123
Original file line numberDiff line numberDiff line change
@@ -2162,128 +2162,10 @@ 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 = tcx.param_env(impl_ty.def_id);
2166-
let container_id = impl_ty.container_id(tcx);
2167-
// Given
2168-
//
2169-
// impl<A, B> Foo<u32> for (A, B) {
2170-
// type Bar<C> = Wrapper<A, B, C>
2171-
// }
2172-
//
2173-
// - `impl_trait_ref` would be `<(A, B) as Foo<u32>>`
2174-
// - `normalize_impl_ty_args` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
2175-
// - `normalize_impl_ty` would be `Wrapper<A, B, ^0.0>`
2176-
// - `rebased_args` would be `[(A, B), u32, ^0.0]`, combining the args from
2177-
// the *trait* with the generic associated type parameters (as bound vars).
2178-
//
2179-
// A note regarding the use of bound vars here:
2180-
// Imagine as an example
2181-
// ```
2182-
// trait Family {
2183-
// type Member<C: Eq>;
2184-
// }
2185-
//
2186-
// impl Family for VecFamily {
2187-
// type Member<C: Eq> = i32;
2188-
// }
2189-
// ```
2190-
// Here, we would generate
2191-
// ```notrust
2192-
// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
2193-
// ```
2194-
// when we really would like to generate
2195-
// ```notrust
2196-
// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
2197-
// ```
2198-
// But, this is probably fine, because although the first clause can be used with types C that
2199-
// do not implement Eq, for it to cause some kind of problem, there would have to be a
2200-
// VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type
2201-
// Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing
2202-
// elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in
2203-
// the trait (notably, that X: Eq and T: Family).
2204-
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
2205-
smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).params.len());
2206-
// Extend the impl's identity args with late-bound GAT vars
2207-
let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id).extend_to(
2208-
tcx,
2209-
impl_ty.def_id,
2210-
|param, _| match param.kind {
2211-
GenericParamDefKind::Type { .. } => {
2212-
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
2213-
let bound_var = ty::BoundVariableKind::Ty(kind);
2214-
bound_vars.push(bound_var);
2215-
Ty::new_bound(
2216-
tcx,
2217-
ty::INNERMOST,
2218-
ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
2219-
)
2220-
.into()
2221-
}
2222-
GenericParamDefKind::Lifetime => {
2223-
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
2224-
let bound_var = ty::BoundVariableKind::Region(kind);
2225-
bound_vars.push(bound_var);
2226-
ty::Region::new_late_bound(
2227-
tcx,
2228-
ty::INNERMOST,
2229-
ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
2230-
)
2231-
.into()
2232-
}
2233-
GenericParamDefKind::Const { .. } => {
2234-
let bound_var = ty::BoundVariableKind::Const;
2235-
bound_vars.push(bound_var);
2236-
ty::Const::new_bound(
2237-
tcx,
2238-
ty::INNERMOST,
2239-
ty::BoundVar::from_usize(bound_vars.len() - 1),
2240-
tcx.type_of(param.def_id)
2241-
.no_bound_vars()
2242-
.expect("const parameter types cannot be generic"),
2243-
)
2244-
.into()
2245-
}
2246-
},
2247-
);
2248-
// When checking something like
2249-
//
2250-
// trait X { type Y: PartialEq<<Self as X>::Y> }
2251-
// impl X for T { default type Y = S; }
2252-
//
2253-
// We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case
2254-
// we want <T as X>::Y to normalize to S. This is valid because we are
2255-
// checking the default value specifically here. Add this equality to the
2256-
// ParamEnv for normalization specifically.
2257-
let normalize_impl_ty = tcx.type_of(impl_ty.def_id).instantiate(tcx, normalize_impl_ty_args);
2258-
let rebased_args = normalize_impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
2259-
let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
2260-
let normalize_param_env = {
2261-
let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>();
2262-
match normalize_impl_ty.kind() {
2263-
ty::Alias(ty::Projection, proj)
2264-
if proj.def_id == trait_ty.def_id && proj.args == rebased_args =>
2265-
{
2266-
// Don't include this predicate if the projected type is
2267-
// exactly the same as the projection. This can occur in
2268-
// (somewhat dubious) code like this:
2269-
//
2270-
// impl<T> X for T where T: X { type Y = <T as X>::Y; }
2271-
}
2272-
_ => predicates.push(
2273-
ty::Binder::bind_with_vars(
2274-
ty::ProjectionPredicate {
2275-
projection_ty: ty::AliasTy::new(tcx, trait_ty.def_id, rebased_args),
2276-
term: normalize_impl_ty.into(),
2277-
},
2278-
bound_vars,
2279-
)
2280-
.to_predicate(tcx),
2281-
),
2282-
};
2283-
ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
2284-
};
2285-
debug!(?normalize_param_env);
2165+
let param_env = param_env_with_gat_bounds(tcx, impl_ty, impl_trait_ref);
2166+
debug!(?param_env);
22862167

2168+
let container_id = impl_ty.container_id(tcx);
22872169
let impl_ty_def_id = impl_ty.def_id.expect_local();
22882170
let impl_ty_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id);
22892171
let rebased_args = impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
@@ -2336,8 +2218,7 @@ pub(super) fn check_type_bounds<'tcx>(
23362218
debug!("check_type_bounds: item_bounds={:?}", obligations);
23372219

23382220
for mut obligation in util::elaborate(tcx, obligations) {
2339-
let normalized_predicate =
2340-
ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate);
2221+
let normalized_predicate = ocx.normalize(&normalize_cause, param_env, obligation.predicate);
23412222
debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
23422223
obligation.predicate = normalized_predicate;
23432224

@@ -2358,6 +2239,171 @@ pub(super) fn check_type_bounds<'tcx>(
23582239
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
23592240
}
23602241

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
2249+
///
2250+
/// ```ignore (example)
2251+
/// impl<A, B> Foo<u32> for (A, B) {
2252+
/// type Bar<C> = Wrapper<A, B, C>
2253+
/// }
2254+
/// ```
2255+
///
2256+
/// - `impl_trait_ref` would be `<(A, B) as Foo<u32>>`
2257+
/// - `normalize_impl_ty_args` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
2258+
/// - `normalize_impl_ty` would be `Wrapper<A, B, ^0.0>`
2259+
/// - `rebased_args` would be `[(A, B), u32, ^0.0]`, combining the args from
2260+
/// the *trait* with the generic associated type parameters (as bound vars).
2261+
///
2262+
/// A note regarding the use of bound vars here:
2263+
/// Imagine as an example
2264+
/// ```
2265+
/// trait Family {
2266+
/// type Member<C: Eq>;
2267+
/// }
2268+
///
2269+
/// impl Family for VecFamily {
2270+
/// type Member<C: Eq> = i32;
2271+
/// }
2272+
/// ```
2273+
/// Here, we would generate
2274+
/// ```ignore (pseudo-rust)
2275+
/// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
2276+
/// ```
2277+
///
2278+
/// when we really would like to generate
2279+
/// ```ignore (pseudo-rust)
2280+
/// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
2281+
/// ```
2282+
///
2283+
/// But, this is probably fine, because although the first clause can be used with types `C` that
2284+
/// do not implement `Eq`, for it to cause some kind of problem, there would have to be a
2285+
/// `VecFamily::Member<X>` for some type `X` where `!(X: Eq)`, that appears in the value of type
2286+
/// `Member<C: Eq> = ....` That type would fail a well-formedness check that we ought to be doing
2287+
/// elsewhere, which would check that any `<T as Family>::Member<X>` meets the bounds declared in
2288+
/// the trait (notably, that `X: Eq` and `T: Family`).
2289+
fn param_env_with_gat_bounds<'tcx>(
2290+
tcx: TyCtxt<'tcx>,
2291+
impl_ty: ty::AssocItem,
2292+
impl_trait_ref: ty::TraitRef<'tcx>,
2293+
) -> ty::ParamEnv<'tcx> {
2294+
let param_env = tcx.param_env(impl_ty.def_id);
2295+
let container_id = impl_ty.container_id(tcx);
2296+
let mut predicates = param_env.caller_bounds().to_vec();
2297+
2298+
// for RPITITs, we should install predicates that allow us to project all
2299+
// of the RPITITs associated with the same body. This is because checking
2300+
// the item bounds of RPITITs often involves nested RPITITs having to prove
2301+
// bounds about themselves.
2302+
let impl_tys_to_install = match impl_ty.opt_rpitit_info {
2303+
None => vec![impl_ty],
2304+
Some(
2305+
ty::ImplTraitInTraitData::Impl { fn_def_id }
2306+
| ty::ImplTraitInTraitData::Trait { fn_def_id, .. },
2307+
) => tcx
2308+
.associated_types_for_impl_traits_in_associated_fn(fn_def_id)
2309+
.iter()
2310+
.map(|def_id| tcx.associated_item(*def_id))
2311+
.collect(),
2312+
};
2313+
2314+
for impl_ty in impl_tys_to_install {
2315+
let trait_ty = match impl_ty.container {
2316+
ty::AssocItemContainer::TraitContainer => impl_ty,
2317+
ty::AssocItemContainer::ImplContainer => {
2318+
tcx.associated_item(impl_ty.trait_item_def_id.unwrap())
2319+
}
2320+
};
2321+
2322+
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
2323+
smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).params.len());
2324+
// Extend the impl's identity args with late-bound GAT vars
2325+
let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id)
2326+
.extend_to(tcx, impl_ty.def_id, |param, _| match param.kind {
2327+
GenericParamDefKind::Type { .. } => {
2328+
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
2329+
let bound_var = ty::BoundVariableKind::Ty(kind);
2330+
bound_vars.push(bound_var);
2331+
Ty::new_bound(
2332+
tcx,
2333+
ty::INNERMOST,
2334+
ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
2335+
)
2336+
.into()
2337+
}
2338+
GenericParamDefKind::Lifetime => {
2339+
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
2340+
let bound_var = ty::BoundVariableKind::Region(kind);
2341+
bound_vars.push(bound_var);
2342+
ty::Region::new_late_bound(
2343+
tcx,
2344+
ty::INNERMOST,
2345+
ty::BoundRegion {
2346+
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
2347+
kind,
2348+
},
2349+
)
2350+
.into()
2351+
}
2352+
GenericParamDefKind::Const { .. } => {
2353+
let bound_var = ty::BoundVariableKind::Const;
2354+
bound_vars.push(bound_var);
2355+
ty::Const::new_bound(
2356+
tcx,
2357+
ty::INNERMOST,
2358+
ty::BoundVar::from_usize(bound_vars.len() - 1),
2359+
tcx.type_of(param.def_id)
2360+
.no_bound_vars()
2361+
.expect("const parameter types cannot be generic"),
2362+
)
2363+
.into()
2364+
}
2365+
});
2366+
// When checking something like
2367+
//
2368+
// trait X { type Y: PartialEq<<Self as X>::Y> }
2369+
// impl X for T { default type Y = S; }
2370+
//
2371+
// We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case
2372+
// we want <T as X>::Y to normalize to S. This is valid because we are
2373+
// checking the default value specifically here. Add this equality to the
2374+
// ParamEnv for normalization specifically.
2375+
let normalize_impl_ty =
2376+
tcx.type_of(impl_ty.def_id).instantiate(tcx, normalize_impl_ty_args);
2377+
let rebased_args =
2378+
normalize_impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
2379+
let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
2380+
2381+
match normalize_impl_ty.kind() {
2382+
ty::Alias(ty::Projection, proj)
2383+
if proj.def_id == trait_ty.def_id && proj.args == rebased_args =>
2384+
{
2385+
// Don't include this predicate if the projected type is
2386+
// exactly the same as the projection. This can occur in
2387+
// (somewhat dubious) code like this:
2388+
//
2389+
// impl<T> X for T where T: X { type Y = <T as X>::Y; }
2390+
}
2391+
_ => predicates.push(
2392+
ty::Binder::bind_with_vars(
2393+
ty::ProjectionPredicate {
2394+
projection_ty: ty::AliasTy::new(tcx, trait_ty.def_id, rebased_args),
2395+
term: normalize_impl_ty.into(),
2396+
},
2397+
bound_vars,
2398+
)
2399+
.to_predicate(tcx),
2400+
),
2401+
};
2402+
}
2403+
2404+
ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
2405+
}
2406+
23612407
fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
23622408
match impl_item.kind {
23632409
ty::AssocKind::Const => "const",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// check-pass
2+
3+
#![feature(associated_type_defaults)]
4+
5+
trait Foo {
6+
type Bar<T>: Baz<Self> = i32;
7+
// We should be able to prove that `i32: Baz<Self>` because of
8+
// the impl below, which requires that `Self::Bar<()>: Eq<i32>`
9+
// which is true, because we assume `for<T> Self::Bar<T> = i32`.
10+
}
11+
12+
trait Baz<T: ?Sized> {}
13+
impl<T: Foo + ?Sized> Baz<T> for i32 where T::Bar<()>: Eq<i32> {}
14+
15+
trait Eq<T> {}
16+
impl<T> Eq<T> for T {}
17+
18+
fn main() {}
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() {}

tests/ui/traits/new-solver/specialization-transmute.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ trait Default {
1010
}
1111

1212
impl<T> Default for T {
13-
default type Id = T; //~ ERROR: type annotations needed
13+
default type Id = T;
1414
// This will be fixed by #111994
1515
fn intu(&self) -> &Self::Id { //~ ERROR type annotations needed
1616
self

tests/ui/traits/new-solver/specialization-transmute.stderr

+2-9
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,6 @@ LL | fn intu(&self) -> &Self::Id {
1616
|
1717
= note: cannot satisfy `<T as Default>::Id == _`
1818

19-
error[E0282]: type annotations needed
20-
--> $DIR/specialization-transmute.rs:13:23
21-
|
22-
LL | default type Id = T;
23-
| ^ cannot infer type for associated type `<T as Default>::Id`
24-
25-
error: aborting due to 2 previous errors; 1 warning emitted
19+
error: aborting due to previous error; 1 warning emitted
2620

27-
Some errors have detailed explanations: E0282, E0284.
28-
For more information about an error, try `rustc --explain E0282`.
21+
For more information about this error, try `rustc --explain E0284`.

tests/ui/traits/new-solver/specialization-unconstrained.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ trait Default {
1111
}
1212

1313
impl<T> Default for T {
14-
default type Id = T; //~ ERROR type annotations needed
14+
default type Id = T;
1515
}
1616

1717
fn test<T: Default<Id = U>, U>() {}

tests/ui/traits/new-solver/specialization-unconstrained.stderr

+2-9
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@ note: required by a bound in `test`
2020
LL | fn test<T: Default<Id = U>, U>() {}
2121
| ^^^^^^ required by this bound in `test`
2222

23-
error[E0282]: type annotations needed
24-
--> $DIR/specialization-unconstrained.rs:14:22
25-
|
26-
LL | default type Id = T;
27-
| ^ cannot infer type for associated type `<T as Default>::Id`
28-
29-
error: aborting due to 2 previous errors; 1 warning emitted
23+
error: aborting due to previous error; 1 warning emitted
3024

31-
Some errors have detailed explanations: E0282, E0284.
32-
For more information about an error, try `rustc --explain E0282`.
25+
For more information about this error, try `rustc --explain E0284`.

0 commit comments

Comments
 (0)