Skip to content

Commit 4206759

Browse files
committed
add deep normalization via the new solver
1 parent 4d42de6 commit 4206759

File tree

6 files changed

+281
-6
lines changed

6 files changed

+281
-6
lines changed

compiler/rustc_infer/src/infer/at.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub enum DefineOpaqueTypes {
4040
No,
4141
}
4242

43+
#[derive(Clone, Copy)]
4344
pub struct At<'a, 'tcx> {
4445
pub infcx: &'a InferCtxt<'tcx>,
4546
pub cause: &'a ObligationCause<'tcx>,

compiler/rustc_trait_selection/src/solve/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mod canonicalize;
2626
mod eval_ctxt;
2727
mod fulfill;
2828
pub mod inspect;
29+
mod normalize;
2930
mod opaques;
3031
mod project_goals;
3132
mod search_graph;
@@ -34,6 +35,7 @@ mod weak_types;
3435

3536
pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
3637
pub use fulfill::FulfillmentCtxt;
38+
pub(crate) use normalize::deeply_normalize;
3739

3840
#[derive(Debug, Clone, Copy)]
3941
enum SolverMode {
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
use crate::traits::error_reporting::TypeErrCtxtExt;
2+
use crate::traits::query::evaluate_obligation::InferCtxtExt;
3+
use crate::traits::{needs_normalization, TraitEngineExt as _};
4+
use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
5+
use rustc_data_structures::stack::ensure_sufficient_stack;
6+
use rustc_infer::infer::at::At;
7+
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
8+
use rustc_infer::traits::TraitEngineExt;
9+
use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine};
10+
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
11+
use rustc_middle::traits::Reveal;
12+
use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex};
13+
use rustc_middle::ty::{FallibleTypeFolder, TypeSuperFoldable};
14+
use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
15+
16+
/// Deeply normalize all aliases in `value`. This does not handle inference and expects
17+
/// its input to be already fully resolved.
18+
pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
19+
at: At<'_, 'tcx>,
20+
value: T,
21+
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
22+
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(&at.infcx);
23+
let mut folder =
24+
NormalizationFolder { at, fulfill_cx: &mut *fulfill_cx, depth: 0, universes: Vec::new() };
25+
26+
value.try_fold_with(&mut folder)
27+
}
28+
29+
struct NormalizationFolder<'me, 'tcx> {
30+
at: At<'me, 'tcx>,
31+
fulfill_cx: &'me mut dyn TraitEngine<'tcx>,
32+
depth: usize,
33+
universes: Vec<Option<UniverseIndex>>,
34+
}
35+
36+
impl<'tcx> NormalizationFolder<'_, 'tcx> {
37+
fn normalize_alias_ty(
38+
&mut self,
39+
alias: AliasTy<'tcx>,
40+
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
41+
let infcx = self.at.infcx;
42+
let tcx = infcx.tcx;
43+
let recursion_limit = tcx.recursion_limit();
44+
if !recursion_limit.value_within_limit(self.depth) {
45+
self.at.infcx.err_ctxt().report_overflow_error(
46+
&alias.to_ty(tcx),
47+
self.at.cause.span,
48+
true,
49+
|_| {},
50+
);
51+
}
52+
53+
self.depth += 1;
54+
55+
let new_infer_ty = infcx.next_ty_var(TypeVariableOrigin {
56+
kind: TypeVariableOriginKind::NormalizeProjectionType,
57+
span: self.at.cause.span,
58+
});
59+
let obligation = Obligation::new(
60+
tcx,
61+
self.at.cause.clone(),
62+
self.at.param_env,
63+
ty::Binder::dummy(ty::ProjectionPredicate {
64+
projection_ty: alias,
65+
term: new_infer_ty.into(),
66+
}),
67+
);
68+
69+
// Do not emit an error if normalization is known to fail but instead
70+
// keep the projection unnormalized. This is the case for projections
71+
// with a `T: Trait` where-clause and opaque types outside of the defining
72+
// scope.
73+
let result = if infcx.predicate_may_hold(&obligation) {
74+
self.fulfill_cx.register_predicate_obligation(infcx, obligation);
75+
let errors = self.fulfill_cx.select_all_or_error(infcx);
76+
if !errors.is_empty() {
77+
return Err(errors);
78+
}
79+
let ty = infcx.resolve_vars_if_possible(new_infer_ty);
80+
ty.try_fold_with(self)?
81+
} else {
82+
alias.to_ty(tcx).try_super_fold_with(self)?
83+
};
84+
85+
self.depth -= 1;
86+
Ok(result)
87+
}
88+
89+
fn normalize_unevaluated_const(
90+
&mut self,
91+
ty: Ty<'tcx>,
92+
uv: ty::UnevaluatedConst<'tcx>,
93+
) -> Result<ty::Const<'tcx>, Vec<FulfillmentError<'tcx>>> {
94+
let infcx = self.at.infcx;
95+
let tcx = infcx.tcx;
96+
let recursion_limit = tcx.recursion_limit();
97+
if !recursion_limit.value_within_limit(self.depth) {
98+
self.at.infcx.err_ctxt().report_overflow_error(
99+
&tcx.mk_const(uv, ty),
100+
self.at.cause.span,
101+
true,
102+
|_| {},
103+
);
104+
}
105+
106+
self.depth += 1;
107+
108+
let new_infer_ct = infcx.next_const_var(
109+
ty,
110+
ConstVariableOrigin {
111+
kind: ConstVariableOriginKind::MiscVariable,
112+
span: self.at.cause.span,
113+
},
114+
);
115+
let obligation = Obligation::new(
116+
tcx,
117+
self.at.cause.clone(),
118+
self.at.param_env,
119+
ty::Binder::dummy(ty::ProjectionPredicate {
120+
projection_ty: tcx.mk_alias_ty(uv.def, uv.substs),
121+
term: new_infer_ct.into(),
122+
}),
123+
);
124+
125+
let result = if infcx.predicate_may_hold(&obligation) {
126+
self.fulfill_cx.register_predicate_obligation(infcx, obligation);
127+
let errors = self.fulfill_cx.select_all_or_error(infcx);
128+
if !errors.is_empty() {
129+
return Err(errors);
130+
}
131+
let ct = infcx.resolve_vars_if_possible(new_infer_ct);
132+
ct.try_fold_with(self)?
133+
} else {
134+
tcx.mk_const(uv, ty).try_super_fold_with(self)?
135+
};
136+
137+
self.depth -= 1;
138+
Ok(result)
139+
}
140+
}
141+
142+
impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
143+
type Error = Vec<FulfillmentError<'tcx>>;
144+
145+
fn interner(&self) -> TyCtxt<'tcx> {
146+
self.at.infcx.tcx
147+
}
148+
149+
fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
150+
&mut self,
151+
t: ty::Binder<'tcx, T>,
152+
) -> Result<ty::Binder<'tcx, T>, Self::Error> {
153+
self.universes.push(None);
154+
let t = t.try_super_fold_with(self)?;
155+
self.universes.pop();
156+
Ok(t)
157+
}
158+
159+
fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
160+
let reveal = self.at.param_env.reveal();
161+
let infcx = self.at.infcx;
162+
if !needs_normalization(&ty, reveal) {
163+
return Ok(ty);
164+
}
165+
166+
let (kind, data) = match *ty.kind() {
167+
ty::Alias(kind, alias_ty) => (kind, alias_ty),
168+
_ => return ty.try_super_fold_with(self),
169+
};
170+
171+
// We don't normalize opaque types unless we have
172+
// `Reveal::All`, even if we're in the defining scope.
173+
if matches!(kind, ty::Opaque) && reveal == Reveal::UserFacing {
174+
return ty.try_super_fold_with(self);
175+
}
176+
177+
if data.has_escaping_bound_vars() {
178+
let (data, mapped_regions, mapped_types, mapped_consts) =
179+
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
180+
let result = ensure_sufficient_stack(|| self.normalize_alias_ty(data))?;
181+
Ok(PlaceholderReplacer::replace_placeholders(
182+
infcx,
183+
mapped_regions,
184+
mapped_types,
185+
mapped_consts,
186+
&mut self.universes,
187+
result,
188+
))
189+
} else {
190+
ensure_sufficient_stack(|| self.normalize_alias_ty(data))
191+
}
192+
}
193+
194+
fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
195+
let reveal = self.at.param_env.reveal();
196+
let infcx = self.at.infcx;
197+
if !needs_normalization(&ct, reveal) {
198+
return Ok(ct);
199+
}
200+
201+
let uv = match ct.kind() {
202+
ty::ConstKind::Unevaluated(ct) => ct,
203+
_ => return ct.try_super_fold_with(self),
204+
};
205+
206+
if uv.has_escaping_bound_vars() {
207+
let (uv, mapped_regions, mapped_types, mapped_consts) =
208+
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv);
209+
let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))?;
210+
Ok(PlaceholderReplacer::replace_placeholders(
211+
infcx,
212+
mapped_regions,
213+
mapped_types,
214+
mapped_consts,
215+
&mut self.universes,
216+
result,
217+
))
218+
} else {
219+
ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))
220+
}
221+
}
222+
}

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ use rustc_span::Span;
4040
use std::fmt::Debug;
4141
use std::ops::ControlFlow;
4242

43+
pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
44+
4345
pub use self::FulfillmentErrorCode::*;
4446
pub use self::ImplSource::*;
4547
pub use self::ObligationCauseCode::*;

compiler/rustc_trait_selection/src/traits/project.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
2020
use crate::traits::error_reporting::TypeErrCtxtExt as _;
2121
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
2222
use crate::traits::select::ProjectionMatchesProjection;
23+
use crate::traits::TraitEngineExt as _;
2324
use rustc_data_structures::sso::SsoHashSet;
2425
use rustc_data_structures::stack::ensure_sufficient_stack;
2526
use rustc_errors::ErrorGuaranteed;
@@ -28,7 +29,10 @@ use rustc_hir::lang_items::LangItem;
2829
use rustc_infer::infer::at::At;
2930
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
3031
use rustc_infer::infer::DefineOpaqueTypes;
32+
use rustc_infer::traits::FulfillmentError;
3133
use rustc_infer::traits::ObligationCauseCode;
34+
use rustc_infer::traits::TraitEngine;
35+
use rustc_infer::traits::TraitEngineExt as _;
3236
use rustc_middle::traits::select::OverflowError;
3337
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
3438
use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
@@ -53,14 +57,48 @@ pub trait NormalizeExt<'tcx> {
5357
/// This normalization should be used when the type contains inference variables or the
5458
/// projection may be fallible.
5559
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> InferOk<'tcx, T>;
60+
61+
/// Deeply normalizes `value`, replacing all aliases which can by normalized in
62+
/// the current environment. Unlike other normalization routines, this errors
63+
/// in case normalization fails or is ambiguous.
64+
///
65+
/// In the old solver this simply uses `normalize` and errors in
66+
/// case of ambiguity. The new solver only normalizes in this function and
67+
/// `normalize` is a noop.
68+
///
69+
/// This only normalize opaque types with `Reveal::All`.
70+
fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
71+
self,
72+
value: T,
73+
) -> Result<T, Vec<FulfillmentError<'tcx>>>;
5674
}
5775

5876
impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
5977
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
60-
let mut selcx = SelectionContext::new(self.infcx);
61-
let Normalized { value, obligations } =
62-
normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
63-
InferOk { value, obligations }
78+
if self.infcx.next_trait_solver() {
79+
InferOk { value, obligations: Vec::new() }
80+
} else {
81+
let mut selcx = SelectionContext::new(self.infcx);
82+
let Normalized { value, obligations } =
83+
normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
84+
InferOk { value, obligations }
85+
}
86+
}
87+
88+
fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
89+
self,
90+
value: T,
91+
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
92+
if self.infcx.next_trait_solver() {
93+
crate::solve::deeply_normalize(self, value)
94+
} else {
95+
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(&self.infcx);
96+
let value = self
97+
.normalize(value)
98+
.into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
99+
let errors = fulfill_cx.select_all_or_error(self.infcx);
100+
if errors.is_empty() { Ok(value) } else { Err(errors) }
101+
}
64102
}
65103
}
66104

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub trait QueryNormalizeExt<'tcx> {
3030
///
3131
/// After codegen, when lifetimes do not matter, it is preferable to instead
3232
/// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
33-
fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
33+
fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
3434
where
3535
T: TypeFoldable<TyCtxt<'tcx>>;
3636
}
@@ -49,7 +49,7 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
4949
/// normalizing, but for now should be used only when we actually
5050
/// know that normalization will succeed, since error reporting
5151
/// and other details are still "under development".
52-
fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
52+
fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
5353
where
5454
T: TypeFoldable<TyCtxt<'tcx>>,
5555
{
@@ -60,6 +60,16 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
6060
self.param_env,
6161
self.cause,
6262
);
63+
64+
if self.infcx.next_trait_solver() {
65+
match crate::solve::deeply_normalize(self, value) {
66+
Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
67+
Err(_errors) => {
68+
return Err(NoSolution);
69+
}
70+
}
71+
}
72+
6373
if !needs_normalization(&value, self.param_env.reveal()) {
6474
return Ok(Normalized { value, obligations: vec![] });
6575
}

0 commit comments

Comments
 (0)