Skip to content

Commit 36e4f5d

Browse files
committed
Auto merge of #146096 - adwinwhite:handle_normalization_overflow_in_mono1, r=saethlin
Fix normalization overflow ICEs in monomorphization Fixes#92004Fixes#92470Fixes#95134Fixes#105275Fixes#105937Fixes#117696-2 Fixes#118590Fixes#122823Fixes#131342Fixes#139659 ## Analysis: The causes of these issues are similar. They contain generic recursive functions that can be instantiated with different args infinitely at monomorphization stage. Ideally this should be caught by the [`check_recursion_limit`](https://github.com/rust-lang/rust/blob/c0bb3b98bb7aac24a37635e5d36d961e0b14f435/compiler/rustc_monomorphize/src/collector.rs#L468) function. The reality is that normalization can reach recursion limit earlier than monomorphization's check because they calculate depths in different ways. Since normalization is called everywhere, ICEs appear in different locations. ## Fix: If we abort on overflow with `TypingMode::PostAnalysis` in the trait solver, it would also catch these errors. The main challenge is providing good diagnostics for them. So it's quite natural to put the check right before these normalization happening. I first tried to check the whole MIR body's normalization and `references_error`. (As elaborate_drop handles normalization failure by [returning `ty::Error`](https://github.com/rust-lang/rust/blob/c0bb3b98bb7aac24a37635e5d36d961e0b14f435/compiler/rustc_mir_transform/src/elaborate_drop.rs#L514-L519).) It turns out that checking all `Local`s seems sufficient. These types are gonna be normalized anyway. So with cache, these checks shouldn't be expensive. This fixes these ICEs for both the next and old solver, though I'm not sure the change I made to the old solver is proper. Its overflow handling looks convoluted thus I didn't try to fix it more "upstream".
2 parents 2300c2a + 08f16a9 commit 36e4f5d

27 files changed

Lines changed: 266 additions & 24 deletions

‎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+
pubstructNormalizationErrorInMono;
52+
5053
#[derive(PartialEq,Eq,Clone,Copy,Debug,Hash,HashStable,TyEncodable,TyDecodable)]
5154
pubenumMonoItem<'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
usecrate::mir::interpret::EvalToValTreeResult;
9+
usecrate::mir::mono::{MonoItem,NormalizationErrorInMono};
810
usecrate::query::CyclePlaceholder;
911
usecrate::traits::solve;
1012
usecrate::ty::adjustment::CoerceUnsizedInfo;
@@ -171,6 +173,17 @@ impl EraseType for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
171173
typeResult = [u8;size_of::<Result<ty::EarlyBinder<'static,Ty<'_>>,CyclePlaceholder>>()];
172174
}
173175

176+
implEraseType
177+
forResult<(&'_[Spanned<MonoItem<'_>>],&'_[Spanned<MonoItem<'_>>]),NormalizationErrorInMono>
178+
{
179+
typeResult = [u8;size_of::<
180+
Result<
181+
(&'static[Spanned<MonoItem<'static>>],&'static[Spanned<MonoItem<'static>>]),
182+
NormalizationErrorInMono,
183+
>,
184+
>()];
185+
}
186+
174187
impl<T>EraseTypeforOption<&'_T>{
175188
typeResult = [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
@@ -116,7 +116,9 @@ use crate::mir::interpret::{
116116
EvalStaticInitializerRawResult,EvalToAllocationRawResult,EvalToConstValueResult,
117117
EvalToValTreeResult,GlobalId,LitToConstInput,
118118
};
119-
usecrate::mir::mono::{CodegenUnit,CollectionMode,MonoItem,MonoItemPartitions};
119+
usecrate::mir::mono::{
120+
CodegenUnit,CollectionMode,MonoItem,MonoItemPartitions,NormalizationErrorInMono,
121+
};
120122
usecrate::query::erase::{Erase, erase, restore};
121123
usecrate::query::plumbing::{CyclePlaceholder,DynamicQuery};
122124
usecrate::traits::query::{
@@ -2702,7 +2704,7 @@ rustc_queries! {
27022704
desc {"functions to skip for move-size check"}
27032705
}
27042706

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

‎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::VisitorasMirVisitor;
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+
letOk((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+
fncheck_normalization_error<'tcx>(
626+
tcx:TyCtxt<'tcx>,
627+
instance:Instance<'tcx>,
628+
body:&Body<'tcx>,
629+
) -> Result<(),NormalizationErrorInMono>{
630+
structNormalizationChecker<'tcx>{
631+
tcx:TyCtxt<'tcx>,
632+
instance:Instance<'tcx>,
633+
}
634+
impl<'tcx>TypeVisitor<TyCtxt<'tcx>>forNormalizationChecker<'tcx>{
635+
typeResult = ControlFlow<()>;
636+
637+
fnvisit_ty(&mutself,t:Ty<'tcx>) -> Self::Result{
638+
matchself.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+
letmut checker = NormalizationChecker{ tcx, instance };
650+
if body.visit_with(&mut checker).is_break(){Err(NormalizationErrorInMono)}else{Ok(())}
651+
}
652+
606653
fncheck_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
fnitems_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+
returnErr(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
pubfnencode_num<Writer:ExampleWriter>(n:u32,mutwriter: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
pubfnencode_num<Writer:ExampleWriter>(n:u32,mutwriter: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
fnmain(){
33
letmut 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)