Skip to content

Commit 08f16a9

Browse files
committed
check normalization overflow in monomorphization
1 parent 52618eb commit 08f16a9

27 files changed

+266
-24
lines changed

compiler/rustc_middle/src/mir/mono.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ pub enum InstantiationMode {
4747
LocalCopy,
4848
}
4949

50+
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash, HashStable, TyEncodable, TyDecodable)]
51+
pub struct NormalizationErrorInMono;
52+
5053
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash, HashStable, TyEncodable, TyDecodable)]
5154
pub enum MonoItem<'tcx> {
5255
Fn(Instance<'tcx>),

compiler/rustc_middle/src/query/erase.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use std::intrinsics::transmute_unchecked;
33
use std::mem::MaybeUninit;
44

55
use rustc_span::ErrorGuaranteed;
6+
use rustc_span::source_map::Spanned;
67

78
use crate::mir::interpret::EvalToValTreeResult;
9+
use crate::mir::mono::{MonoItem, NormalizationErrorInMono};
810
use crate::query::CyclePlaceholder;
911
use crate::traits::solve;
1012
use crate::ty::adjustment::CoerceUnsizedInfo;
@@ -171,6 +173,17 @@ impl EraseType for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
171173
type Result = [u8; size_of::<Result<ty::EarlyBinder<'static, Ty<'_>>, CyclePlaceholder>>()];
172174
}
173175

176+
impl EraseType
177+
for Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>]), NormalizationErrorInMono>
178+
{
179+
type Result = [u8; size_of::<
180+
Result<
181+
(&'static [Spanned<MonoItem<'static>>], &'static [Spanned<MonoItem<'static>>]),
182+
NormalizationErrorInMono,
183+
>,
184+
>()];
185+
}
186+
174187
impl<T> EraseType for Option<&'_ T> {
175188
type Result = [u8; size_of::<Option<&'static ()>>()];
176189
}

compiler/rustc_middle/src/query/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ use crate::mir::interpret::{
117117
EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult,
118118
EvalToValTreeResult, GlobalId, LitToConstInput,
119119
};
120-
use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions};
120+
use crate::mir::mono::{
121+
CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono,
122+
};
121123
use crate::query::erase::{Erase, erase, restore};
122124
use crate::query::plumbing::{
123125
CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at,
@@ -2704,7 +2706,7 @@ rustc_queries! {
27042706
desc { "functions to skip for move-size check" }
27052707
}
27062708

2707-
query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> (&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]) {
2709+
query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> Result<(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]), NormalizationErrorInMono> {
27082710
desc { "collecting items used by `{}`", key.0 }
27092711
cache_on_disk_if { true }
27102712
}

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@
208208
mod autodiff;
209209

210210
use std::cell::OnceCell;
211+
use std::ops::ControlFlow;
211212

212213
use rustc_data_structures::fx::FxIndexMap;
213214
use rustc_data_structures::sync::{MTLock, par_for_each_in};
@@ -220,15 +221,17 @@ use rustc_hir::lang_items::LangItem;
220221
use rustc_hir::limit::Limit;
221222
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
222223
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar};
223-
use rustc_middle::mir::mono::{CollectionMode, InstantiationMode, MonoItem};
224+
use rustc_middle::mir::mono::{
225+
CollectionMode, InstantiationMode, MonoItem, NormalizationErrorInMono,
226+
};
224227
use rustc_middle::mir::visit::Visitor as MirVisitor;
225-
use rustc_middle::mir::{self, Location, MentionedItem, traversal};
228+
use rustc_middle::mir::{self, Body, Location, MentionedItem, traversal};
226229
use rustc_middle::query::TyCtxtAt;
227230
use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion};
228231
use rustc_middle::ty::layout::ValidityRequirement;
229232
use rustc_middle::ty::{
230233
self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable,
231-
TypeVisitableExt, VtblEntry,
234+
TypeVisitable, TypeVisitableExt, TypeVisitor, VtblEntry,
232235
};
233236
use rustc_middle::util::Providers;
234237
use rustc_middle::{bug, span_bug};
@@ -474,7 +477,22 @@ fn collect_items_rec<'tcx>(
474477
));
475478

476479
rustc_data_structures::stack::ensure_sufficient_stack(|| {
477-
let (used, mentioned) = tcx.items_of_instance((instance, mode));
480+
let Ok((used, mentioned)) = tcx.items_of_instance((instance, mode)) else {
481+
// Normalization errors here are usually due to trait solving overflow.
482+
// FIXME: I assume that there are few type errors at post-analysis stage, but not
483+
// entirely sure.
484+
// We have to emit the error outside of `items_of_instance` to access the
485+
// span of the `starting_item`.
486+
let def_id = instance.def_id();
487+
let def_span = tcx.def_span(def_id);
488+
let def_path_str = tcx.def_path_str(def_id);
489+
tcx.dcx().emit_fatal(RecursionLimit {
490+
span: starting_item.span,
491+
instance,
492+
def_span,
493+
def_path_str,
494+
});
495+
};
478496
used_items.extend(used.into_iter().copied());
479497
mentioned_items.extend(mentioned.into_iter().copied());
480498
});
@@ -603,6 +621,35 @@ fn collect_items_rec<'tcx>(
603621
}
604622
}
605623

624+
// Check whether we can normalize every type in the instantiated MIR body.
625+
fn check_normalization_error<'tcx>(
626+
tcx: TyCtxt<'tcx>,
627+
instance: Instance<'tcx>,
628+
body: &Body<'tcx>,
629+
) -> Result<(), NormalizationErrorInMono> {
630+
struct NormalizationChecker<'tcx> {
631+
tcx: TyCtxt<'tcx>,
632+
instance: Instance<'tcx>,
633+
}
634+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for NormalizationChecker<'tcx> {
635+
type Result = ControlFlow<()>;
636+
637+
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
638+
match self.instance.try_instantiate_mir_and_normalize_erasing_regions(
639+
self.tcx,
640+
ty::TypingEnv::fully_monomorphized(),
641+
ty::EarlyBinder::bind(t),
642+
) {
643+
Ok(_) => ControlFlow::Continue(()),
644+
Err(_) => ControlFlow::Break(()),
645+
}
646+
}
647+
}
648+
649+
let mut checker = NormalizationChecker { tcx, instance };
650+
if body.visit_with(&mut checker).is_break() { Err(NormalizationErrorInMono) } else { Ok(()) }
651+
}
652+
606653
fn check_recursion_limit<'tcx>(
607654
tcx: TyCtxt<'tcx>,
608655
instance: Instance<'tcx>,
@@ -1259,11 +1306,15 @@ fn collect_items_of_instance<'tcx>(
12591306
tcx: TyCtxt<'tcx>,
12601307
instance: Instance<'tcx>,
12611308
mode: CollectionMode,
1262-
) -> (MonoItems<'tcx>, MonoItems<'tcx>) {
1309+
) -> Result<(MonoItems<'tcx>, MonoItems<'tcx>), NormalizationErrorInMono> {
12631310
// This item is getting monomorphized, do mono-time checks.
1311+
let body = tcx.instance_mir(instance.def);
1312+
// Plenty of code paths later assume that everything can be normalized. So we have to check
1313+
// normalization first.
1314+
// We choose to emit the error outside to provide helpful diagnostics.
1315+
check_normalization_error(tcx, instance, body)?;
12641316
tcx.ensure_ok().check_mono_item(instance);
12651317

1266-
let body = tcx.instance_mir(instance.def);
12671318
// Naively, in "used" collection mode, all functions get added to *both* `used_items` and
12681319
// `mentioned_items`. Mentioned items processing will then notice that they have already been
12691320
// visited, but at that point each mentioned item has been monomorphized, added to the
@@ -1313,19 +1364,22 @@ fn collect_items_of_instance<'tcx>(
13131364
}
13141365
}
13151366

1316-
(used_items, mentioned_items)
1367+
Ok((used_items, mentioned_items))
13171368
}
13181369

13191370
fn items_of_instance<'tcx>(
13201371
tcx: TyCtxt<'tcx>,
13211372
(instance, mode): (Instance<'tcx>, CollectionMode),
1322-
) -> (&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]) {
1323-
let (used_items, mentioned_items) = collect_items_of_instance(tcx, instance, mode);
1373+
) -> Result<
1374+
(&'tcx [Spanned<MonoItem<'tcx>>], &'tcx [Spanned<MonoItem<'tcx>>]),
1375+
NormalizationErrorInMono,
1376+
> {
1377+
let (used_items, mentioned_items) = collect_items_of_instance(tcx, instance, mode)?;
13241378

13251379
let used_items = tcx.arena.alloc_from_iter(used_items);
13261380
let mentioned_items = tcx.arena.alloc_from_iter(mentioned_items);
13271381

1328-
(used_items, mentioned_items)
1382+
Ok((used_items, mentioned_items))
13291383
}
13301384

13311385
/// `item` must be already monomorphized.

compiler/rustc_traits/src/normalize_erasing_regions.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,18 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<TyCtxt<'tcx>> + Par
4242
// us a test case.
4343
debug_assert_eq!(normalized_value, resolved_value);
4444
let erased = infcx.tcx.erase_and_anonymize_regions(resolved_value);
45-
debug_assert!(!erased.has_infer(), "{erased:?}");
45+
if infcx.next_trait_solver() {
46+
debug_assert!(!erased.has_infer(), "{erased:?}");
47+
} else {
48+
// The old solver returns an ty var with the failed obligation in case of
49+
// selection error. And when the obligation is re-tried, the error should be
50+
// reported. However in case of overflow error, the obligation may be fulfilled
51+
// due to the original depth being dropped.
52+
// In conclusion, overflow results in an unconstrained ty var.
53+
if erased.has_infer() {
54+
return Err(NoSolution);
55+
}
56+
}
4657
Ok(erased)
4758
}
4859
Err(NoSolution) => Err(NoSolution),

tests/crashes/105275.rs renamed to tests/ui/codegen/normalization-overflow/recursion-issue-105275.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
//@ known-bug: #105275
1+
//@ build-fail
22
//@ compile-flags: -Copt-level=0
33

44
pub fn encode_num<Writer: ExampleWriter>(n: u32, mut writer: Writer) -> Result<(), Writer::Error> {
55
if n > 15 {
66
encode_num(n / 16, &mut writer)?;
7+
//~^ ERROR: reached the recursion limit while instantiating
78
}
89
Ok(())
910
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: reached the recursion limit while instantiating `encode_num::<&mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut Error>`
2+
--> $DIR/recursion-issue-105275.rs:6:9
3+
|
4+
LL | encode_num(n / 16, &mut writer)?;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: `encode_num` defined here
8+
--> $DIR/recursion-issue-105275.rs:4:1
9+
|
10+
LL | pub fn encode_num<Writer: ExampleWriter>(n: u32, mut writer: Writer) -> Result<(), Writer::Error> {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+

tests/crashes/105937.rs renamed to tests/ui/codegen/normalization-overflow/recursion-issue-105937.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
//@ known-bug: #105937
1+
//@ build-fail
22
//@ compile-flags: -Copt-level=0
33

44
pub fn encode_num<Writer: ExampleWriter>(n: u32, mut writer: Writer) -> Result<(), Writer::Error> {
55
if n > 15 {
66
encode_num(n / 16, &mut writer)?;
7+
//~^ ERROR: reached the recursion limit while instantiating
78
}
89
Ok(())
910
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: reached the recursion limit while instantiating `encode_num::<&mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut Error>`
2+
--> $DIR/recursion-issue-105937.rs:6:9
3+
|
4+
LL | encode_num(n / 16, &mut writer)?;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: `encode_num` defined here
8+
--> $DIR/recursion-issue-105937.rs:4:1
9+
|
10+
LL | pub fn encode_num<Writer: ExampleWriter>(n: u32, mut writer: Writer) -> Result<(), Writer::Error> {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+

tests/crashes/117696-1.rs renamed to tests/ui/codegen/normalization-overflow/recursion-issue-117696-1.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ known-bug: #117696
1+
//@ build-fail
22
fn main() {
33
let mut it = (Empty);
44
rec(&mut it);
@@ -23,6 +23,7 @@ where
2323
{
2424
if () == () {
2525
T::count(it);
26+
//~^ ERROR: reached the recursion limit while instantiating
2627
} else {
2728
rec(identity(&mut it))
2829
}

0 commit comments

Comments
 (0)