mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-15 05:26:47 +00:00
Fix stack overflow in exhaustiveness due to recursive HIR opaque type values
This commit is contained in:
parent
e643f59f6d
commit
b08e9c2a60
@ -1,5 +1,6 @@
|
||||
use std::fmt;
|
||||
use std::iter::once;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx};
|
||||
use rustc_arena::DroplessArena;
|
||||
@ -11,7 +12,8 @@ use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef,
|
||||
self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
|
||||
TypeVisitableExt, TypeVisitor, VariantDef,
|
||||
};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint;
|
||||
@ -135,11 +137,22 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
||||
/// Returns the hidden type corresponding to this key if the body under analysis is allowed to
|
||||
/// know it.
|
||||
fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
|
||||
self.typeck_results
|
||||
.concrete_opaque_types
|
||||
.get(&key.def_id)
|
||||
.map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args))
|
||||
if let Some(hidden_ty) = self.typeck_results.concrete_opaque_types.get(&key.def_id) {
|
||||
let ty = ty::EarlyBinder::bind(hidden_ty.ty).instantiate(self.tcx, key.args);
|
||||
if ty.visit_with(&mut RecursiveOpaque { def_id: key.def_id.into() }).is_continue() {
|
||||
Some(ty)
|
||||
} else {
|
||||
// HACK: We skip revealing opaque types which recursively expand
|
||||
// to themselves. This is because we may infer hidden types like
|
||||
// `Opaque<T> = Opaque<Opaque<T>>` or `Opaque<T> = Opaque<(T,)>`
|
||||
// in hir typeck.
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// This can take a non-revealed `Ty` because it reveals opaques itself.
|
||||
pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
|
||||
!ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
|
||||
@ -1105,3 +1118,20 @@ pub fn analyze_match<'p, 'tcx>(
|
||||
|
||||
Ok(report)
|
||||
}
|
||||
|
||||
struct RecursiveOpaque {
|
||||
def_id: DefId,
|
||||
}
|
||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for RecursiveOpaque {
|
||||
type Result = ControlFlow<()>;
|
||||
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
|
||||
if let ty::Alias(ty::Opaque, alias_ty) = t.kind() {
|
||||
if alias_ty.def_id == self.def_id {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
|
||||
if t.has_opaque_types() { t.super_visit_with(self) } else { ControlFlow::Continue(()) }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
warning: function cannot return without recursing
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:17:1
|
||||
|
|
||||
LL | fn build<T>(x: T) -> impl Sized {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
|
||||
LL |
|
||||
LL | let (x,) = (build(x),);
|
||||
| -------- recursive call site
|
||||
|
|
||||
= help: a `loop` may express intention better if this is on purpose
|
||||
= note: `#[warn(unconditional_recursion)]` on by default
|
||||
|
||||
warning: function cannot return without recursing
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:27:1
|
||||
|
|
||||
LL | fn build2<T>(x: T) -> impl Sized {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
|
||||
...
|
||||
LL | let (x,) = (build2(x),);
|
||||
| --------- recursive call site
|
||||
|
|
||||
= help: a `loop` may express intention better if this is on purpose
|
||||
|
||||
error[E0720]: cannot resolve opaque type
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:27:23
|
||||
|
|
||||
LL | fn build2<T>(x: T) -> impl Sized {
|
||||
| ^^^^^^^^^^ recursive opaque type
|
||||
...
|
||||
LL | (build2(x),)
|
||||
| ------------ returning here with type `(impl Sized,)`
|
||||
|
||||
warning: function cannot return without recursing
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:40:1
|
||||
|
|
||||
LL | fn build3<T>(x: T) -> impl Sized {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
|
||||
LL |
|
||||
LL | let (x,) = (build3((x,)),);
|
||||
| ------------ recursive call site
|
||||
|
|
||||
= help: a `loop` may express intention better if this is on purpose
|
||||
|
||||
error[E0792]: expected generic type parameter, found `(T,)`
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:49:5
|
||||
|
|
||||
LL | fn build3<T>(x: T) -> impl Sized {
|
||||
| - this generic parameter must be used with a generic type parameter
|
||||
...
|
||||
LL | build3(x)
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors; 3 warnings emitted
|
||||
|
||||
Some errors have detailed explanations: E0720, E0792.
|
||||
For more information about an error, try `rustc --explain E0720`.
|
80
tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr
Normal file
80
tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr
Normal file
@ -0,0 +1,80 @@
|
||||
error[E0284]: type annotations needed: cannot satisfy `impl Sized == _`
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:19:17
|
||||
|
|
||||
LL | let (x,) = (build(x),);
|
||||
| ^^^^^^^^ cannot satisfy `impl Sized == _`
|
||||
|
||||
error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:31:6
|
||||
|
|
||||
LL | (build2(x),)
|
||||
| ^^^^^^^^^ types differ
|
||||
|
||||
error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:31:5
|
||||
|
|
||||
LL | (build2(x),)
|
||||
| ^^^^^^^^^^^^ types differ
|
||||
|
||||
error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:31:5
|
||||
|
|
||||
LL | (build2(x),)
|
||||
| ^^^^^^^^^^^^ doesn't have a size known at compile-time
|
||||
|
|
||||
= help: the trait `Sized` is not implemented for `(impl Sized,)`
|
||||
= note: tuples must have a statically known size to be initialized
|
||||
|
||||
error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:42:17
|
||||
|
|
||||
LL | let (x,) = (build3((x,)),);
|
||||
| ^^^^^^^^^^^^ types differ
|
||||
|
||||
error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:42:16
|
||||
|
|
||||
LL | let (x,) = (build3((x,)),);
|
||||
| ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
||||
|
|
||||
= help: the trait `Sized` is not implemented for `(impl Sized,)`
|
||||
= note: tuples must have a statically known size to be initialized
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:42:16
|
||||
|
|
||||
LL | fn build3<T>(x: T) -> impl Sized {
|
||||
| ---------- the found opaque type
|
||||
LL |
|
||||
LL | let (x,) = (build3((x,)),);
|
||||
| ^^^^^^^^^^^^^^^ types differ
|
||||
|
|
||||
= note: expected type `_`
|
||||
found tuple `(impl Sized,)`
|
||||
|
||||
error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:42:17
|
||||
|
|
||||
LL | let (x,) = (build3((x,)),);
|
||||
| ^^^^^^^^^^^^ types differ
|
||||
|
|
||||
= note: the return type of a function must have a statically known size
|
||||
|
||||
error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:42:16
|
||||
|
|
||||
LL | let (x,) = (build3((x,)),);
|
||||
| ^^^^^^^^^^^^^^^ types differ
|
||||
|
||||
error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
|
||||
--> $DIR/recursive-in-exhaustiveness.rs:42:17
|
||||
|
|
||||
LL | let (x,) = (build3((x,)),);
|
||||
| ^^^^^^^^^^^^ types differ
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0271, E0277, E0284, E0308.
|
||||
For more information about an error, try `rustc --explain E0271`.
|
53
tests/ui/impl-trait/recursive-in-exhaustiveness.rs
Normal file
53
tests/ui/impl-trait/recursive-in-exhaustiveness.rs
Normal file
@ -0,0 +1,53 @@
|
||||
//@ revisions: current next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
// Test several spicy non-trivial recursive opaque definitions inferred from HIR typeck
|
||||
// don't cause stack overflows in exhaustiveness code, which currently reveals opaques
|
||||
// manually in a way that is not overflow aware.
|
||||
//
|
||||
// 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 :^)
|
||||
|
||||
// Opaque<T> = Opaque<Opaque<T>>
|
||||
//
|
||||
// We unfortunately accept this today, and due to how opaque type relating is implemented
|
||||
// in the NLL type relation, this defines `Opaque<T> = T`.
|
||||
fn build<T>(x: T) -> impl Sized {
|
||||
//[current]~^ WARN function cannot return without recursing
|
||||
let (x,) = (build(x),);
|
||||
//[next]~^ ERROR type annotations needed
|
||||
build(x)
|
||||
}
|
||||
|
||||
// Opaque<T> = (Opaque<T>,)
|
||||
//
|
||||
// Not allowed today. Detected as recursive.
|
||||
fn build2<T>(x: T) -> impl Sized {
|
||||
//[current]~^ ERROR cannot resolve opaque type
|
||||
//[current]~| WARN function cannot return without recursing
|
||||
let (x,) = (build2(x),);
|
||||
(build2(x),)
|
||||
//[next]~^ ERROR type mismatch resolving
|
||||
//[next]~| ERROR type mismatch resolving
|
||||
//[next]~| ERROR the size for values of type
|
||||
}
|
||||
|
||||
// Opaque<T> = Opaque<(T,)>
|
||||
//
|
||||
// Not allowed today. Detected as not defining.
|
||||
fn build3<T>(x: T) -> impl Sized {
|
||||
//[current]~^ WARN function cannot return without recursing
|
||||
let (x,) = (build3((x,)),);
|
||||
//[next]~^ ERROR type mismatch resolving
|
||||
//[next]~| ERROR type mismatch resolving
|
||||
//[next]~| ERROR type mismatch resolving
|
||||
//[next]~| ERROR type mismatch resolving
|
||||
//[next]~| ERROR the size for values of type
|
||||
//[next]~| ERROR mismatched types
|
||||
build3(x)
|
||||
//[current]~^ ERROR expected generic type parameter, found `(T,)`
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Reference in New Issue
Block a user