Skip to content

Commit ff448cf

Browse files
committed
implement version of normalize_erasing_regions that doesn't assume value is normalizable
1 parent 2446a21 commit ff448cf

File tree

6 files changed

+203
-3
lines changed

6 files changed

+203
-3
lines changed

compiler/rustc_lint/src/types.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,9 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
13371337
let layout = match cx.layout_of(ty) {
13381338
Ok(layout) => layout,
13391339
Err(
1340-
ty::layout::LayoutError::Unknown(_) | ty::layout::LayoutError::SizeOverflow(_),
1340+
ty::layout::LayoutError::Unknown(_)
1341+
| ty::layout::LayoutError::SizeOverflow(_)
1342+
| ty::layout::LayoutError::NormalizationFailure(_, _),
13411343
) => return,
13421344
};
13431345
let (variants, tag) = match layout.variants {

compiler/rustc_middle/src/mir/interpret/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ impl dyn MachineStopType {
493493
}
494494

495495
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
496-
static_assert_size!(InterpError<'_>, 64);
496+
static_assert_size!(InterpError<'_>, 88);
497497

498498
pub enum InterpError<'tcx> {
499499
/// The program caused undefined behavior.

compiler/rustc_middle/src/query/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,20 @@ rustc_queries! {
16581658
desc { "normalizing `{}`", goal.value }
16591659
}
16601660

1661+
/// Do not call this query directly: invoke `try_normalize_erasing_regions` instead.
1662+
query try_normalize_generic_arg_after_erasing_regions(
1663+
goal: ParamEnvAnd<'tcx, GenericArg<'tcx>>
1664+
) -> Result<GenericArg<'tcx>, NoSolution> {
1665+
desc { "trying to normalize `{}`", goal.value }
1666+
}
1667+
1668+
/// Do not call this query directly: invoke `try_normalize_erasing_regions` instead.
1669+
query try_normalize_mir_const_after_erasing_regions(
1670+
goal: ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>
1671+
) -> Result<mir::ConstantKind<'tcx>, NoSolution> {
1672+
desc { "trying to normalize `{}`", goal.value }
1673+
}
1674+
16611675
query implied_outlives_bounds(
16621676
goal: CanonicalTyGoal<'tcx>
16631677
) -> Result<

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
22
use crate::mir::{GeneratorLayout, GeneratorSavedLocal};
3+
use crate::ty::normalize_erasing_regions::NormalizationError;
34
use crate::ty::subst::Subst;
45
use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable};
56
use rustc_ast as ast;
@@ -199,6 +200,7 @@ pub const MAX_SIMD_LANES: u64 = 1 << 0xF;
199200
pub enum LayoutError<'tcx> {
200201
Unknown(Ty<'tcx>),
201202
SizeOverflow(Ty<'tcx>),
203+
NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>),
202204
}
203205

204206
impl<'tcx> fmt::Display for LayoutError<'tcx> {
@@ -208,16 +210,24 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
208210
LayoutError::SizeOverflow(ty) => {
209211
write!(f, "values of the type `{}` are too big for the current architecture", ty)
210212
}
213+
LayoutError::NormalizationFailure(t, e) => write!(
214+
f,
215+
"unable to determine layout for `{}` because `{}` cannot be normalized",
216+
t,
217+
e.get_type_for_failure()
218+
),
211219
}
212220
}
213221
}
214222

223+
#[instrument(skip(tcx, query), level = "debug")]
215224
fn layout_of<'tcx>(
216225
tcx: TyCtxt<'tcx>,
217226
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
218227
) -> Result<TyAndLayout<'tcx>, LayoutError<'tcx>> {
219228
ty::tls::with_related_context(tcx, move |icx| {
220229
let (param_env, ty) = query.into_parts();
230+
debug!(?ty);
221231

222232
if !tcx.recursion_limit().value_within_limit(icx.layout_depth) {
223233
tcx.sess.fatal(&format!("overflow representing the type `{}`", ty));
@@ -229,7 +239,14 @@ fn layout_of<'tcx>(
229239
ty::tls::enter_context(&icx, |_| {
230240
let param_env = param_env.with_reveal_all_normalized(tcx);
231241
let unnormalized_ty = ty;
232-
let ty = tcx.normalize_erasing_regions(param_env, ty);
242+
243+
let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
244+
Ok(t) => t,
245+
Err(normalization_error) => {
246+
return Err(LayoutError::NormalizationFailure(ty, normalization_error));
247+
}
248+
};
249+
233250
if ty != unnormalized_ty {
234251
// Ensure this layout is also cached for the normalized type.
235252
return tcx.layout_of(param_env.and(ty));

compiler/rustc_middle/src/ty/normalize_erasing_regions.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,28 @@
88
//! or constant found within. (This underlying query is what is cached.)
99
1010
use crate::mir;
11+
use crate::traits::query::NoSolution;
1112
use crate::ty::fold::{TypeFoldable, TypeFolder};
1213
use crate::ty::subst::{Subst, SubstsRef};
1314
use crate::ty::{self, Ty, TyCtxt};
1415

16+
#[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)]
17+
pub enum NormalizationError<'tcx> {
18+
Type(Ty<'tcx>),
19+
Const(ty::Const<'tcx>),
20+
ConstantKind(mir::ConstantKind<'tcx>),
21+
}
22+
23+
impl<'tcx> NormalizationError<'tcx> {
24+
pub fn get_type_for_failure(&self) -> String {
25+
match self {
26+
NormalizationError::Type(t) => format!("{}", t),
27+
NormalizationError::Const(c) => format!("{}", c),
28+
NormalizationError::ConstantKind(ck) => format!("{}", ck),
29+
}
30+
}
31+
}
32+
1533
impl<'tcx> TyCtxt<'tcx> {
1634
/// Erase the regions in `value` and then fully normalize all the
1735
/// types found within. The result will also have regions erased.
@@ -32,6 +50,8 @@ impl<'tcx> TyCtxt<'tcx> {
3250
// Erase first before we do the real query -- this keeps the
3351
// cache from being too polluted.
3452
let value = self.erase_regions(value);
53+
debug!(?value);
54+
3555
if !value.has_projections() {
3656
value
3757
} else {
@@ -41,6 +61,44 @@ impl<'tcx> TyCtxt<'tcx> {
4161
}
4262
}
4363

64+
/// Tries to erase the regions in `value` and then fully normalize all the
65+
/// types found within. The result will also have regions erased.
66+
///
67+
/// Contrary to `normalize_erasing_regions` this function does not assume that normalization
68+
/// succeeds.
69+
pub fn try_normalize_erasing_regions<T>(
70+
self,
71+
param_env: ty::ParamEnv<'tcx>,
72+
value: T,
73+
) -> Result<T, NormalizationError<'tcx>>
74+
where
75+
T: TypeFoldable<'tcx>,
76+
{
77+
debug!(
78+
"try_normalize_erasing_regions::<{}>(value={:?}, param_env={:?})",
79+
std::any::type_name::<T>(),
80+
value,
81+
param_env,
82+
);
83+
84+
// Erase first before we do the real query -- this keeps the
85+
// cache from being too polluted.
86+
let value = self.erase_regions(value);
87+
debug!(?value);
88+
89+
if !value.has_projections() {
90+
Ok(value)
91+
} else {
92+
let mut folder = TryNormalizeAfterErasingRegionsFolder::new(self, param_env);
93+
let result = value.fold_with(&mut folder);
94+
95+
match folder.found_normalization_error() {
96+
Some(e) => Err(e),
97+
None => Ok(result),
98+
}
99+
}
100+
}
101+
44102
/// If you have a `Binder<'tcx, T>`, you can do this to strip out the
45103
/// late-bound regions and then normalize the result, yielding up
46104
/// a `T` (with regions erased). This is appropriate when the
@@ -91,11 +149,14 @@ struct NormalizeAfterErasingRegionsFolder<'tcx> {
91149
}
92150

93151
impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
152+
#[instrument(skip(self), level = "debug")]
94153
fn normalize_generic_arg_after_erasing_regions(
95154
&self,
96155
arg: ty::GenericArg<'tcx>,
97156
) -> ty::GenericArg<'tcx> {
98157
let arg = self.param_env.and(arg);
158+
debug!(?arg);
159+
99160
self.tcx.normalize_generic_arg_after_erasing_regions(arg)
100161
}
101162
}
@@ -126,3 +187,69 @@ impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> {
126187
Ok(self.tcx.normalize_mir_const_after_erasing_regions(arg))
127188
}
128189
}
190+
191+
struct TryNormalizeAfterErasingRegionsFolder<'tcx> {
192+
tcx: TyCtxt<'tcx>,
193+
param_env: ty::ParamEnv<'tcx>,
194+
normalization_error: Option<NormalizationError<'tcx>>,
195+
}
196+
197+
impl<'tcx> TryNormalizeAfterErasingRegionsFolder<'tcx> {
198+
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
199+
TryNormalizeAfterErasingRegionsFolder { tcx, param_env, normalization_error: None }
200+
}
201+
202+
#[instrument(skip(self), level = "debug")]
203+
fn try_normalize_generic_arg_after_erasing_regions(
204+
&self,
205+
arg: ty::GenericArg<'tcx>,
206+
) -> Result<ty::GenericArg<'tcx>, NoSolution> {
207+
let arg = self.param_env.and(arg);
208+
debug!(?arg);
209+
210+
self.tcx.try_normalize_generic_arg_after_erasing_regions(arg)
211+
}
212+
213+
pub fn found_normalization_error(&self) -> Option<NormalizationError<'tcx>> {
214+
self.normalization_error
215+
}
216+
}
217+
218+
impl TypeFolder<'tcx> for TryNormalizeAfterErasingRegionsFolder<'tcx> {
219+
fn tcx(&self) -> TyCtxt<'tcx> {
220+
self.tcx
221+
}
222+
223+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
224+
match self.try_normalize_generic_arg_after_erasing_regions(ty.into()) {
225+
Ok(t) => t.expect_ty(),
226+
Err(_) => {
227+
self.normalization_error = Some(NormalizationError::Type(ty));
228+
ty
229+
}
230+
}
231+
}
232+
233+
fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
234+
match self.try_normalize_generic_arg_after_erasing_regions(c.into()) {
235+
Ok(t) => t.expect_const(),
236+
Err(_) => {
237+
self.normalization_error = Some(NormalizationError::Const(*c));
238+
c
239+
}
240+
}
241+
}
242+
243+
#[inline]
244+
fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
245+
// FIXME: This *probably* needs canonicalization too!
246+
let arg = self.param_env.and(c);
247+
match self.tcx.try_normalize_mir_const_after_erasing_regions(arg) {
248+
Ok(c) => c,
249+
Err(_) => {
250+
self.normalization_error = Some(NormalizationError::ConstantKind(c));
251+
c
252+
}
253+
}
254+
}
255+
}

compiler/rustc_traits/src/normalize_erasing_regions.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ crate fn provide(p: &mut Providers) {
2020
normalize_mir_const_after_erasing_regions: |tcx, goal| {
2121
normalize_after_erasing_regions(tcx, goal)
2222
},
23+
try_normalize_generic_arg_after_erasing_regions: |tcx, goal| {
24+
debug!("try_normalize_generic_arg_after_erasing_regions(goal={:#?}", goal);
25+
26+
try_normalize_after_erasing_regions(tcx, goal)
27+
},
28+
try_normalize_mir_const_after_erasing_regions: |tcx, goal| {
29+
try_normalize_after_erasing_regions(tcx, goal)
30+
},
2331
..*p
2432
};
2533
}
@@ -56,6 +64,38 @@ fn normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + Cop
5664
})
5765
}
5866

67+
#[instrument(level = "debug", skip(tcx))]
68+
fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + Copy>(
69+
tcx: TyCtxt<'tcx>,
70+
goal: ParamEnvAnd<'tcx, T>,
71+
) -> Result<T, NoSolution> {
72+
let ParamEnvAnd { param_env, value } = goal;
73+
tcx.infer_ctxt().enter(|infcx| {
74+
let cause = ObligationCause::dummy();
75+
match infcx.at(&cause, param_env).normalize(value) {
76+
Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => {
77+
// We don't care about the `obligations`; they are
78+
// always only region relations, and we are about to
79+
// erase those anyway:
80+
debug_assert_eq!(
81+
normalized_obligations.iter().find(|p| not_outlives_predicate(&p.predicate)),
82+
None,
83+
);
84+
85+
let resolved_value = infcx.resolve_vars_if_possible(normalized_value);
86+
// It's unclear when `resolve_vars` would have an effect in a
87+
// fresh `InferCtxt`. If this assert does trigger, it will give
88+
// us a test case.
89+
debug_assert_eq!(normalized_value, resolved_value);
90+
let erased = infcx.tcx.erase_regions(resolved_value);
91+
debug_assert!(!erased.needs_infer(), "{:?}", erased);
92+
Ok(erased)
93+
}
94+
Err(NoSolution) => Err(NoSolution),
95+
}
96+
})
97+
}
98+
5999
fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool {
60100
match p.kind().skip_binder() {
61101
ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::TypeOutlives(..) => false,

0 commit comments

Comments
 (0)