Skip to content

Commit 9ea9339

Browse files
authored
Rollup merge of #139501 - compiler-errors:suppress-stack-overflow, r=lcnr
Fix stack overflow in exhaustiveness due to recursive HIR opaque hidden types This fixes several spicy non-trivial recursive opaque definitions inferred from HIR typeck, ensuring that they don't cause stack overflows in exhaustiveness code, which currently reveals opaques manually in a way that is not overflow aware (as opposed to something like the normalizer folders). These should eventually be outright rejected, but today (some) non-trivial recursive opaque definitions are accepted, and changing that requires an FCP, so for now just make sure we don't stack overflow :^) Fixes #139402 r? lcnr
2 parents 890c6de + b08e9c2 commit 9ea9339

File tree

4 files changed

+224
-5
lines changed

4 files changed

+224
-5
lines changed

Diff for: compiler/rustc_pattern_analysis/src/rustc.rs

+35-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt;
22
use std::iter::once;
3+
use std::ops::ControlFlow;
34

45
use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx};
56
use rustc_arena::DroplessArena;
@@ -11,7 +12,8 @@ use rustc_middle::mir::{self, Const};
1112
use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
1213
use rustc_middle::ty::layout::IntegerExt;
1314
use rustc_middle::ty::{
14-
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef,
15+
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
16+
TypeVisitableExt, TypeVisitor, VariantDef,
1517
};
1618
use rustc_middle::{bug, span_bug};
1719
use rustc_session::lint;
@@ -135,11 +137,22 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
135137
/// Returns the hidden type corresponding to this key if the body under analysis is allowed to
136138
/// know it.
137139
fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
138-
self.typeck_results
139-
.concrete_opaque_types
140-
.get(&key.def_id)
141-
.map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args))
140+
if let Some(hidden_ty) = self.typeck_results.concrete_opaque_types.get(&key.def_id) {
141+
let ty = ty::EarlyBinder::bind(hidden_ty.ty).instantiate(self.tcx, key.args);
142+
if ty.visit_with(&mut RecursiveOpaque { def_id: key.def_id.into() }).is_continue() {
143+
Some(ty)
144+
} else {
145+
// HACK: We skip revealing opaque types which recursively expand
146+
// to themselves. This is because we may infer hidden types like
147+
// `Opaque<T> = Opaque<Opaque<T>>` or `Opaque<T> = Opaque<(T,)>`
148+
// in hir typeck.
149+
None
150+
}
151+
} else {
152+
None
153+
}
142154
}
155+
143156
// This can take a non-revealed `Ty` because it reveals opaques itself.
144157
pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
145158
!ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
@@ -1105,3 +1118,20 @@ pub fn analyze_match<'p, 'tcx>(
11051118

11061119
Ok(report)
11071120
}
1121+
1122+
struct RecursiveOpaque {
1123+
def_id: DefId,
1124+
}
1125+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for RecursiveOpaque {
1126+
type Result = ControlFlow<()>;
1127+
1128+
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
1129+
if let ty::Alias(ty::Opaque, alias_ty) = t.kind() {
1130+
if alias_ty.def_id == self.def_id {
1131+
return ControlFlow::Break(());
1132+
}
1133+
}
1134+
1135+
if t.has_opaque_types() { t.super_visit_with(self) } else { ControlFlow::Continue(()) }
1136+
}
1137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
warning: function cannot return without recursing
2+
--> $DIR/recursive-in-exhaustiveness.rs:17:1
3+
|
4+
LL | fn build<T>(x: T) -> impl Sized {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
6+
LL |
7+
LL | let (x,) = (build(x),);
8+
| -------- recursive call site
9+
|
10+
= help: a `loop` may express intention better if this is on purpose
11+
= note: `#[warn(unconditional_recursion)]` on by default
12+
13+
warning: function cannot return without recursing
14+
--> $DIR/recursive-in-exhaustiveness.rs:27:1
15+
|
16+
LL | fn build2<T>(x: T) -> impl Sized {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
18+
...
19+
LL | let (x,) = (build2(x),);
20+
| --------- recursive call site
21+
|
22+
= help: a `loop` may express intention better if this is on purpose
23+
24+
error[E0720]: cannot resolve opaque type
25+
--> $DIR/recursive-in-exhaustiveness.rs:27:23
26+
|
27+
LL | fn build2<T>(x: T) -> impl Sized {
28+
| ^^^^^^^^^^ recursive opaque type
29+
...
30+
LL | (build2(x),)
31+
| ------------ returning here with type `(impl Sized,)`
32+
33+
warning: function cannot return without recursing
34+
--> $DIR/recursive-in-exhaustiveness.rs:40:1
35+
|
36+
LL | fn build3<T>(x: T) -> impl Sized {
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
38+
LL |
39+
LL | let (x,) = (build3((x,)),);
40+
| ------------ recursive call site
41+
|
42+
= help: a `loop` may express intention better if this is on purpose
43+
44+
error[E0792]: expected generic type parameter, found `(T,)`
45+
--> $DIR/recursive-in-exhaustiveness.rs:49:5
46+
|
47+
LL | fn build3<T>(x: T) -> impl Sized {
48+
| - this generic parameter must be used with a generic type parameter
49+
...
50+
LL | build3(x)
51+
| ^^^^^^^^^
52+
53+
error: aborting due to 2 previous errors; 3 warnings emitted
54+
55+
Some errors have detailed explanations: E0720, E0792.
56+
For more information about an error, try `rustc --explain E0720`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
error[E0284]: type annotations needed: cannot satisfy `impl Sized == _`
2+
--> $DIR/recursive-in-exhaustiveness.rs:19:17
3+
|
4+
LL | let (x,) = (build(x),);
5+
| ^^^^^^^^ cannot satisfy `impl Sized == _`
6+
7+
error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
8+
--> $DIR/recursive-in-exhaustiveness.rs:31:6
9+
|
10+
LL | (build2(x),)
11+
| ^^^^^^^^^ types differ
12+
13+
error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
14+
--> $DIR/recursive-in-exhaustiveness.rs:31:5
15+
|
16+
LL | (build2(x),)
17+
| ^^^^^^^^^^^^ types differ
18+
19+
error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time
20+
--> $DIR/recursive-in-exhaustiveness.rs:31:5
21+
|
22+
LL | (build2(x),)
23+
| ^^^^^^^^^^^^ doesn't have a size known at compile-time
24+
|
25+
= help: the trait `Sized` is not implemented for `(impl Sized,)`
26+
= note: tuples must have a statically known size to be initialized
27+
28+
error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
29+
--> $DIR/recursive-in-exhaustiveness.rs:42:17
30+
|
31+
LL | let (x,) = (build3((x,)),);
32+
| ^^^^^^^^^^^^ types differ
33+
34+
error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time
35+
--> $DIR/recursive-in-exhaustiveness.rs:42:16
36+
|
37+
LL | let (x,) = (build3((x,)),);
38+
| ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
39+
|
40+
= help: the trait `Sized` is not implemented for `(impl Sized,)`
41+
= note: tuples must have a statically known size to be initialized
42+
43+
error[E0308]: mismatched types
44+
--> $DIR/recursive-in-exhaustiveness.rs:42:16
45+
|
46+
LL | fn build3<T>(x: T) -> impl Sized {
47+
| ---------- the found opaque type
48+
LL |
49+
LL | let (x,) = (build3((x,)),);
50+
| ^^^^^^^^^^^^^^^ types differ
51+
|
52+
= note: expected type `_`
53+
found tuple `(impl Sized,)`
54+
55+
error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
56+
--> $DIR/recursive-in-exhaustiveness.rs:42:17
57+
|
58+
LL | let (x,) = (build3((x,)),);
59+
| ^^^^^^^^^^^^ types differ
60+
|
61+
= note: the return type of a function must have a statically known size
62+
63+
error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
64+
--> $DIR/recursive-in-exhaustiveness.rs:42:16
65+
|
66+
LL | let (x,) = (build3((x,)),);
67+
| ^^^^^^^^^^^^^^^ types differ
68+
69+
error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
70+
--> $DIR/recursive-in-exhaustiveness.rs:42:17
71+
|
72+
LL | let (x,) = (build3((x,)),);
73+
| ^^^^^^^^^^^^ types differ
74+
|
75+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
76+
77+
error: aborting due to 10 previous errors
78+
79+
Some errors have detailed explanations: E0271, E0277, E0284, E0308.
80+
For more information about an error, try `rustc --explain E0271`.

Diff for: tests/ui/impl-trait/recursive-in-exhaustiveness.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
5+
// Test several spicy non-trivial recursive opaque definitions inferred from HIR typeck
6+
// don't cause stack overflows in exhaustiveness code, which currently reveals opaques
7+
// manually in a way that is not overflow aware.
8+
//
9+
// These should eventually be outright rejected, but today (some) non-trivial recursive
10+
// opaque definitions are accepted, and changing that requires an FCP, so for now just
11+
// make sure we don't stack overflow :^)
12+
13+
// Opaque<T> = Opaque<Opaque<T>>
14+
//
15+
// We unfortunately accept this today, and due to how opaque type relating is implemented
16+
// in the NLL type relation, this defines `Opaque<T> = T`.
17+
fn build<T>(x: T) -> impl Sized {
18+
//[current]~^ WARN function cannot return without recursing
19+
let (x,) = (build(x),);
20+
//[next]~^ ERROR type annotations needed
21+
build(x)
22+
}
23+
24+
// Opaque<T> = (Opaque<T>,)
25+
//
26+
// Not allowed today. Detected as recursive.
27+
fn build2<T>(x: T) -> impl Sized {
28+
//[current]~^ ERROR cannot resolve opaque type
29+
//[current]~| WARN function cannot return without recursing
30+
let (x,) = (build2(x),);
31+
(build2(x),)
32+
//[next]~^ ERROR type mismatch resolving
33+
//[next]~| ERROR type mismatch resolving
34+
//[next]~| ERROR the size for values of type
35+
}
36+
37+
// Opaque<T> = Opaque<(T,)>
38+
//
39+
// Not allowed today. Detected as not defining.
40+
fn build3<T>(x: T) -> impl Sized {
41+
//[current]~^ WARN function cannot return without recursing
42+
let (x,) = (build3((x,)),);
43+
//[next]~^ ERROR type mismatch resolving
44+
//[next]~| ERROR type mismatch resolving
45+
//[next]~| ERROR type mismatch resolving
46+
//[next]~| ERROR type mismatch resolving
47+
//[next]~| ERROR the size for values of type
48+
//[next]~| ERROR mismatched types
49+
build3(x)
50+
//[current]~^ ERROR expected generic type parameter, found `(T,)`
51+
}
52+
53+
fn main() {}

0 commit comments

Comments
 (0)