Skip to content

Commit 2a38e39

Browse files
Use the normalizing param-env always in check_type_bounds
1 parent cee6db1 commit 2a38e39

File tree

4 files changed

+152
-133
lines changed

4 files changed

+152
-133
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+133-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,135 @@ pub(super) fn check_type_bounds<'tcx>(
23582239
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
23592240
}
23602241

2242+
/// Given
2243+
///
2244+
/// impl<A, B> Foo<u32> for (A, B) {
2245+
/// type Bar<C> = Wrapper<A, B, C>
2246+
/// }
2247+
///
2248+
/// - `impl_trait_ref` would be `<(A, B) as Foo<u32>>`
2249+
/// - `normalize_impl_ty_args` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
2250+
/// - `normalize_impl_ty` would be `Wrapper<A, B, ^0.0>`
2251+
/// - `rebased_args` would be `[(A, B), u32, ^0.0]`, combining the args from
2252+
/// the *trait* with the generic associated type parameters (as bound vars).
2253+
///
2254+
/// A note regarding the use of bound vars here:
2255+
/// Imagine as an example
2256+
/// ```
2257+
/// trait Family {
2258+
/// type Member<C: Eq>;
2259+
/// }
2260+
///
2261+
/// impl Family for VecFamily {
2262+
/// type Member<C: Eq> = i32;
2263+
/// }
2264+
/// ```
2265+
/// Here, we would generate
2266+
/// ```notrust
2267+
/// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
2268+
/// ```
2269+
/// when we really would like to generate
2270+
/// ```notrust
2271+
/// forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
2272+
/// ```
2273+
/// But, this is probably fine, because although the first clause can be used with types C that
2274+
/// do not implement Eq, for it to cause some kind of problem, there would have to be a
2275+
/// VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type
2276+
/// Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing
2277+
/// elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in
2278+
/// the trait (notably, that X: Eq and T: Family).
2279+
fn param_env_with_gat_bounds<'tcx>(
2280+
tcx: TyCtxt<'tcx>,
2281+
trait_ty: ty::AssocItem,
2282+
impl_ty: ty::AssocItem,
2283+
impl_trait_ref: ty::TraitRef<'tcx>,
2284+
) -> ty::ParamEnv<'tcx> {
2285+
let param_env = tcx.param_env(impl_ty.def_id);
2286+
let container_id = impl_ty.container_id(tcx);
2287+
let mut predicates = param_env.caller_bounds().to_vec();
2288+
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()
2306+
}
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()
2317+
}
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"),
2328+
)
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+
};
2367+
2368+
ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
2369+
}
2370+
23612371
fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
23622372
match impl_item.kind {
23632373
ty::AssocKind::Const => "const",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![feature(associated_type_defaults)]
2+
3+
trait Foo {
4+
type Bar<T>: Baz<Self> = i32;
5+
// We should be able to prove that `i32: Baz<Self>` because of
6+
// the impl below, which requires that `Self::Bar<()>: Eq<i32>`
7+
// which is true, because we assume `for<T> Self::Bar<T> = i32`.
8+
}
9+
10+
trait Baz<T: ?Sized> {}
11+
impl<T: Foo + ?Sized> Baz<T> for i32 where T::Bar<()>: Eq<i32> {}
12+
13+
trait Eq<T> {}
14+
impl<T> Eq<T> for T {}
15+
16+
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)