Skip to content

Commit 672e3aa

Browse files
committed
Auto merge of rust-lang#136074 - compiler-errors:deeply-normalize-next-solver, r=lcnr
Properly deeply normalize in the next solver Turn deep normalization into a `TypeOp`. In the old solver, just dispatch to the `Normalize` type op, but in the new solver call `deeply_normalize`. I chose to separate it into a different type op b/c some normalization is a no-op in the new solver, so this distinguishes just the normalization we need for correctness. Then use `DeeplyNormalize` in the callsites we used to be using a `CustomTypeOp` (for normalizing known type outlives obligations), and also use it to normalize function args and impl headers in the new solver. Finally, use it to normalize signatures for WF checks in the new solver as well. This addresses rust-lang/trait-system-refactor-initiative#146.
2 parents 34a5ea9 + d5be3ba commit 672e3aa

12 files changed

+250
-47
lines changed

compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs

+57-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use rustc_infer::infer::{
99
};
1010
use rustc_infer::traits::ObligationCause;
1111
use rustc_infer::traits::query::{
12-
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal,
13-
CanonicalTypeOpProvePredicateGoal,
12+
CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpDeeplyNormalizeGoal,
13+
CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal,
1414
};
1515
use rustc_middle::ty::error::TypeError;
1616
use rustc_middle::ty::{
@@ -109,6 +109,14 @@ impl<'tcx, T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx> ToUnivers
109109
}
110110
}
111111

112+
impl<'tcx, T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx> ToUniverseInfo<'tcx>
113+
for CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T>
114+
{
115+
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
116+
UniverseInfo::TypeOp(Rc::new(DeeplyNormalizeQuery { canonical_query: self, base_universe }))
117+
}
118+
}
119+
112120
impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpAscribeUserTypeGoal<'tcx> {
113121
fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
114122
UniverseInfo::TypeOp(Rc::new(AscribeUserTypeQuery { canonical_query: self, base_universe }))
@@ -285,6 +293,53 @@ where
285293
}
286294
}
287295

296+
struct DeeplyNormalizeQuery<'tcx, T> {
297+
canonical_query: CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T>,
298+
base_universe: ty::UniverseIndex,
299+
}
300+
301+
impl<'tcx, T> TypeOpInfo<'tcx> for DeeplyNormalizeQuery<'tcx, T>
302+
where
303+
T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx,
304+
{
305+
fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> {
306+
tcx.dcx().create_err(HigherRankedLifetimeError {
307+
cause: Some(HigherRankedErrorCause::CouldNotNormalize {
308+
value: self.canonical_query.canonical.value.value.value.to_string(),
309+
}),
310+
span,
311+
})
312+
}
313+
314+
fn base_universe(&self) -> ty::UniverseIndex {
315+
self.base_universe
316+
}
317+
318+
fn nice_error<'infcx>(
319+
&self,
320+
mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
321+
cause: ObligationCause<'tcx>,
322+
placeholder_region: ty::Region<'tcx>,
323+
error_region: Option<ty::Region<'tcx>>,
324+
) -> Option<Diag<'infcx>> {
325+
let (infcx, key, _) =
326+
mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
327+
let ocx = ObligationCtxt::new(&infcx);
328+
329+
let (param_env, value) = key.into_parts();
330+
let _ = ocx.deeply_normalize(&cause, param_env, value.value);
331+
332+
let diag = try_extract_error_from_fulfill_cx(
333+
&ocx,
334+
mbcx.mir_def_id(),
335+
placeholder_region,
336+
error_region,
337+
)?
338+
.with_dcx(mbcx.dcx());
339+
Some(diag)
340+
}
341+
}
342+
288343
struct AscribeUserTypeQuery<'tcx> {
289344
canonical_query: CanonicalTypeOpAscribeUserTypeGoal<'tcx>,
290345
base_universe: ty::UniverseIndex,

compiler/rustc_borrowck/src/type_check/canonical.rs

+12
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
149149
self.normalize_with_category(value, location, ConstraintCategory::Boring)
150150
}
151151

152+
pub(super) fn deeply_normalize<T>(&mut self, value: T, location: impl NormalizeLocation) -> T
153+
where
154+
T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx,
155+
{
156+
let result: Result<_, ErrorGuaranteed> = self.fully_perform_op(
157+
location.to_locations(),
158+
ConstraintCategory::Boring,
159+
self.infcx.param_env.and(type_op::normalize::DeeplyNormalize { value }),
160+
);
161+
result.unwrap_or(value)
162+
}
163+
152164
#[instrument(skip(self), level = "debug")]
153165
pub(super) fn normalize_with_category<T>(
154166
&mut self,

compiler/rustc_borrowck/src/type_check/constraint_conversion.rs

+3-18
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs;
44
use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
55
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
66
use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
7+
use rustc_infer::traits::query::type_op::DeeplyNormalize;
78
use rustc_middle::bug;
89
use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory};
9-
use rustc_middle::traits::ObligationCause;
10-
use rustc_middle::traits::query::NoSolution;
1110
use rustc_middle::ty::fold::fold_regions;
1211
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
1312
use rustc_span::Span;
14-
use rustc_trait_selection::traits::ScrubbedTraitError;
15-
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
1613
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
1714
use tracing::{debug, instrument};
1815

@@ -270,20 +267,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
270267
ConstraintCategory<'tcx>,
271268
)>,
272269
) -> Ty<'tcx> {
273-
let result = CustomTypeOp::new(
274-
|ocx| {
275-
ocx.deeply_normalize(
276-
&ObligationCause::dummy_with_span(self.span),
277-
self.param_env,
278-
ty,
279-
)
280-
.map_err(|_: Vec<ScrubbedTraitError<'tcx>>| NoSolution)
281-
},
282-
"normalize type outlives obligation",
283-
)
284-
.fully_perform(self.infcx, self.span);
285-
286-
match result {
270+
match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span)
271+
{
287272
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
288273
if let Some(QueryRegionConstraints { outlives }) = constraints {
289274
next_outlives_predicates.extend(outlives.iter().copied());

compiler/rustc_borrowck/src/type_check/free_region_relations.rs

+8-20
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,11 @@ use rustc_infer::infer::canonical::QueryRegionConstraints;
55
use rustc_infer::infer::outlives::env::RegionBoundPairs;
66
use rustc_infer::infer::region_constraints::GenericKind;
77
use rustc_infer::infer::{InferCtxt, outlives};
8-
use rustc_infer::traits::ScrubbedTraitError;
8+
use rustc_infer::traits::query::type_op::DeeplyNormalize;
99
use rustc_middle::mir::ConstraintCategory;
10-
use rustc_middle::traits::ObligationCause;
1110
use rustc_middle::traits::query::OutlivesBound;
1211
use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt};
1312
use rustc_span::{ErrorGuaranteed, Span};
14-
use rustc_trait_selection::solve::NoSolution;
15-
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
1613
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
1714
use tracing::{debug, instrument};
1815
use type_op::TypeOpOutput;
@@ -267,7 +264,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
267264
}
268265
let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } =
269266
param_env
270-
.and(type_op::normalize::Normalize { value: ty })
267+
.and(DeeplyNormalize { value: ty })
271268
.fully_perform(self.infcx, span)
272269
.unwrap_or_else(|guar| TypeOpOutput {
273270
output: Ty::new_error(self.infcx.tcx, guar),
@@ -303,9 +300,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
303300
// Add implied bounds from impl header.
304301
if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) {
305302
for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) {
306-
let result: Result<_, ErrorGuaranteed> = param_env
307-
.and(type_op::normalize::Normalize { value: ty })
308-
.fully_perform(self.infcx, span);
303+
let result: Result<_, ErrorGuaranteed> =
304+
param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, span);
309305
let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else {
310306
continue;
311307
};
@@ -360,18 +356,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
360356
output: normalized_outlives,
361357
constraints: constraints_normalize,
362358
error_info: _,
363-
}) = CustomTypeOp::new(
364-
|ocx| {
365-
ocx.deeply_normalize(
366-
&ObligationCause::dummy_with_span(span),
367-
self.param_env,
368-
outlives,
369-
)
370-
.map_err(|_: Vec<ScrubbedTraitError<'tcx>>| NoSolution)
371-
},
372-
"normalize type outlives obligation",
373-
)
374-
.fully_perform(self.infcx, span)
359+
}) = self
360+
.param_env
361+
.and(DeeplyNormalize { value: outlives })
362+
.fully_perform(self.infcx, span)
375363
else {
376364
self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}"));
377365
return;

compiler/rustc_borrowck/src/type_check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
11161116
ConstraintCategory::Boring,
11171117
);
11181118

1119-
let sig = self.normalize(unnormalized_sig, term_location);
1119+
let sig = self.deeply_normalize(unnormalized_sig, term_location);
11201120
// HACK(#114936): `WF(sig)` does not imply `WF(normalized(sig))`
11211121
// with built-in `Fn` implementations, since the impl may not be
11221122
// well-formed itself.

compiler/rustc_middle/src/traits/query.rs

+10
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,18 @@ pub mod type_op {
4141
pub predicate: Predicate<'tcx>,
4242
}
4343

44+
/// Normalizes, but not in the new solver.
4445
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
4546
pub struct Normalize<T> {
4647
pub value: T,
4748
}
4849

50+
/// Normalizes, and deeply normalizes in the new solver.
51+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
52+
pub struct DeeplyNormalize<T> {
53+
pub value: T,
54+
}
55+
4956
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
5057
pub struct ImpliedOutlivesBounds<'tcx> {
5158
pub ty: Ty<'tcx>,
@@ -80,6 +87,9 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> =
8087
pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
8188
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
8289

90+
pub type CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T> =
91+
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::DeeplyNormalize<T>>>;
92+
8393
pub type CanonicalImpliedOutlivesBoundsGoal<'tcx> =
8494
CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::ImpliedOutlivesBounds<'tcx>>>;
8595

compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs

+55-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt;
22

33
use rustc_middle::traits::ObligationCause;
44
use rustc_middle::traits::query::NoSolution;
5-
pub use rustc_middle::traits::query::type_op::Normalize;
5+
pub use rustc_middle::traits::query::type_op::{DeeplyNormalize, Normalize};
66
use rustc_middle::ty::fold::TypeFoldable;
77
use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
88
use rustc_span::Span;
@@ -27,13 +27,54 @@ where
2727
T::type_op_method(tcx, canonicalized)
2828
}
2929

30+
fn perform_locally_with_next_solver(
31+
_ocx: &ObligationCtxt<'_, 'tcx>,
32+
key: ParamEnvAnd<'tcx, Self>,
33+
_span: Span,
34+
) -> Result<Self::QueryResponse, NoSolution> {
35+
Ok(key.value.value)
36+
}
37+
}
38+
39+
impl<'tcx, T> super::QueryTypeOp<'tcx> for DeeplyNormalize<T>
40+
where
41+
T: Normalizable<'tcx> + 'tcx,
42+
{
43+
type QueryResponse = T;
44+
45+
fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<T> {
46+
if !key.value.value.has_aliases() { Some(key.value.value) } else { None }
47+
}
48+
49+
fn perform_query(
50+
tcx: TyCtxt<'tcx>,
51+
canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>,
52+
) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
53+
T::type_op_method(
54+
tcx,
55+
CanonicalQueryInput {
56+
typing_mode: canonicalized.typing_mode,
57+
canonical: canonicalized.canonical.unchecked_map(
58+
|ty::ParamEnvAnd { param_env, value }| ty::ParamEnvAnd {
59+
param_env,
60+
value: Normalize { value: value.value },
61+
},
62+
),
63+
},
64+
)
65+
}
66+
3067
fn perform_locally_with_next_solver(
3168
ocx: &ObligationCtxt<'_, 'tcx>,
3269
key: ParamEnvAnd<'tcx, Self>,
3370
span: Span,
3471
) -> Result<Self::QueryResponse, NoSolution> {
35-
// FIXME(-Znext-solver): shouldn't be using old normalizer
36-
Ok(ocx.normalize(&ObligationCause::dummy_with_span(span), key.param_env, key.value.value))
72+
ocx.deeply_normalize(
73+
&ObligationCause::dummy_with_span(span),
74+
key.param_env,
75+
key.value.value,
76+
)
77+
.map_err(|_| NoSolution)
3778
}
3879
}
3980

@@ -81,3 +122,14 @@ impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> {
81122
tcx.type_op_normalize_fn_sig(canonicalized)
82123
}
83124
}
125+
126+
/// This impl is not needed, since we never normalize type outlives predicates
127+
/// in the old solver, but is required by trait bounds to be happy.
128+
impl<'tcx> Normalizable<'tcx> for ty::PolyTypeOutlivesPredicate<'tcx> {
129+
fn type_op_method(
130+
_tcx: TyCtxt<'tcx>,
131+
_canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
132+
) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
133+
unreachable!("we never normalize PolyTypeOutlivesPredicate")
134+
}
135+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ check-pass
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
5+
6+
// Make sure that we can normalize `<T as Ref<'a>>::Assoc` to `&'a T` and get
7+
// its implied bounds in impl header.
8+
9+
trait Ref<'a> {
10+
type Assoc;
11+
}
12+
impl<'a, T> Ref<'a> for T where T: 'a {
13+
type Assoc = &'a T;
14+
}
15+
16+
fn outlives<'a, T: 'a>() {}
17+
18+
trait Trait<'a, T> {
19+
fn test();
20+
}
21+
22+
impl<'a, T> Trait<'a, T> for <T as Ref<'a>>::Assoc {
23+
fn test() {
24+
outlives::<'a, T>();
25+
}
26+
}
27+
28+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@ check-pass
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
5+
6+
// Make sure that we can normalize `<T as Ref<'a>>::Assoc` to `&'a T` and get
7+
// its implied bounds.
8+
9+
trait Ref<'a> {
10+
type Assoc;
11+
}
12+
impl<'a, T> Ref<'a> for T where T: 'a {
13+
type Assoc = &'a T;
14+
}
15+
16+
fn outlives<'a, T: 'a>() {}
17+
18+
fn test<'a, T>(_: <T as Ref<'a>>::Assoc) {
19+
outlives::<'a, T>();
20+
}
21+
22+
fn main() {}

tests/ui/nll/check-normalized-sig-for-wf.stderr tests/ui/nll/check-normalized-sig-for-wf.current.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0597]: `s` does not live long enough
2-
--> $DIR/check-normalized-sig-for-wf.rs:7:7
2+
--> $DIR/check-normalized-sig-for-wf.rs:11:7
33
|
44
LL | s: String,
55
| - binding `s` declared here
@@ -14,7 +14,7 @@ LL | }
1414
| - `s` dropped here while still borrowed
1515

1616
error[E0521]: borrowed data escapes outside of function
17-
--> $DIR/check-normalized-sig-for-wf.rs:15:5
17+
--> $DIR/check-normalized-sig-for-wf.rs:19:5
1818
|
1919
LL | fn extend<T>(input: &T) -> &'static T {
2020
| ----- - let's call the lifetime of this reference `'1`
@@ -28,7 +28,7 @@ LL | n(input).0
2828
| argument requires that `'1` must outlive `'static`
2929

3030
error[E0521]: borrowed data escapes outside of function
31-
--> $DIR/check-normalized-sig-for-wf.rs:23:5
31+
--> $DIR/check-normalized-sig-for-wf.rs:27:5
3232
|
3333
LL | fn extend_mut<'a, T>(input: &'a mut T) -> &'static mut T {
3434
| -- ----- `input` is a reference that is only valid in the function body

0 commit comments

Comments
 (0)