Skip to content

Commit 69563f0

Browse files
Use the normalizing param-env always in check_type_bounds
1 parent df871fb commit 69563f0

File tree

4 files changed

+164
-133
lines changed

4 files changed

+164
-133
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+143-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, trait_ty, 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,145 @@ 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+
trait_ty: ty::AssocItem,
2292+
impl_ty: ty::AssocItem,
2293+
impl_trait_ref: ty::TraitRef<'tcx>,
2294+
) -> ty::ParamEnv<'tcx> {
2295+
let param_env = tcx.param_env(impl_ty.def_id);
2296+
let container_id = impl_ty.container_id(tcx);
2297+
let mut predicates = param_env.caller_bounds().to_vec();
2298+
2299+
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
2300+
smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).params.len());
2301+
// Extend the impl's identity args with late-bound GAT vars
2302+
let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id).extend_to(
2303+
tcx,
2304+
impl_ty.def_id,
2305+
|param, _| match param.kind {
2306+
GenericParamDefKind::Type { .. } => {
2307+
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
2308+
let bound_var = ty::BoundVariableKind::Ty(kind);
2309+
bound_vars.push(bound_var);
2310+
Ty::new_bound(
2311+
tcx,
2312+
ty::INNERMOST,
2313+
ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
2314+
)
2315+
.into()
2316+
}
2317+
GenericParamDefKind::Lifetime => {
2318+
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
2319+
let bound_var = ty::BoundVariableKind::Region(kind);
2320+
bound_vars.push(bound_var);
2321+
ty::Region::new_late_bound(
2322+
tcx,
2323+
ty::INNERMOST,
2324+
ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
2325+
)
2326+
.into()
2327+
}
2328+
GenericParamDefKind::Const { .. } => {
2329+
let bound_var = ty::BoundVariableKind::Const;
2330+
bound_vars.push(bound_var);
2331+
ty::Const::new_bound(
2332+
tcx,
2333+
ty::INNERMOST,
2334+
ty::BoundVar::from_usize(bound_vars.len() - 1),
2335+
tcx.type_of(param.def_id)
2336+
.no_bound_vars()
2337+
.expect("const parameter types cannot be generic"),
2338+
)
2339+
.into()
2340+
}
2341+
},
2342+
);
2343+
// When checking something like
2344+
//
2345+
// trait X { type Y: PartialEq<<Self as X>::Y> }
2346+
// impl X for T { default type Y = S; }
2347+
//
2348+
// We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case
2349+
// we want <T as X>::Y to normalize to S. This is valid because we are
2350+
// checking the default value specifically here. Add this equality to the
2351+
// ParamEnv for normalization specifically.
2352+
let normalize_impl_ty = tcx.type_of(impl_ty.def_id).instantiate(tcx, normalize_impl_ty_args);
2353+
let rebased_args = normalize_impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
2354+
let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars);
2355+
2356+
match normalize_impl_ty.kind() {
2357+
ty::Alias(ty::Projection, proj)
2358+
if proj.def_id == trait_ty.def_id && proj.args == rebased_args =>
2359+
{
2360+
// Don't include this predicate if the projected type is
2361+
// exactly the same as the projection. This can occur in
2362+
// (somewhat dubious) code like this:
2363+
//
2364+
// impl<T> X for T where T: X { type Y = <T as X>::Y; }
2365+
}
2366+
_ => predicates.push(
2367+
ty::Binder::bind_with_vars(
2368+
ty::ProjectionPredicate {
2369+
projection_ty: ty::AliasTy::new(tcx, trait_ty.def_id, rebased_args),
2370+
term: normalize_impl_ty.into(),
2371+
},
2372+
bound_vars,
2373+
)
2374+
.to_predicate(tcx),
2375+
),
2376+
};
2377+
2378+
ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
2379+
}
2380+
23612381
fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
23622382
match impl_item.kind {
23632383
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() {}

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)