Skip to content

Commit cdd6374

Browse files
committedDec 23, 2023
Auto merge of rust-lang#119218 - Nadrieril:nested-opaque-reveal, r=compiler-errors
Exhaustiveness: Reveal empty opaques in depth Follow-up to rust-lang#116821. As noted [there](rust-lang#116821 (comment)), the current implementation doesn't detect emptiness of opaques when the opaque is nested inside a type. This doesn't matter for stable behavior (which ignores nested empty types anyway) but does matter for the [`exhaustive_patterns`](https://github.com/rust-lang/rust/issues/51085)/[`min_exhaustive_patterns`](https://github.com/rust-lang/rust/pull/118803) features. This PR fixes this behavior by adding `InhabitedPredicate::apply_reveal_opaque` that considers opaque types when determining inhabitedness. r? `@compiler-errors`
2 parents 2d7be73 + 34307ab commit cdd6374

File tree

5 files changed

+128
-36
lines changed

5 files changed

+128
-36
lines changed
 

‎compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs

+59-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use smallvec::SmallVec;
22

33
use crate::ty::context::TyCtxt;
4-
use crate::ty::{self, DefId, ParamEnv, Ty};
4+
use crate::ty::{self, DefId, OpaqueTypeKey, ParamEnv, Ty};
55

66
/// Represents whether some type is inhabited in a given context.
77
/// Examples of uninhabited types are `!`, `enum Void {}`, or a struct
@@ -23,42 +23,62 @@ pub enum InhabitedPredicate<'tcx> {
2323
/// Inhabited if some generic type is inhabited.
2424
/// These are replaced by calling [`Self::instantiate`].
2525
GenericType(Ty<'tcx>),
26+
/// Inhabited if either we don't know the hidden type or we know it and it is inhabited.
27+
OpaqueType(OpaqueTypeKey<'tcx>),
2628
/// A AND B
2729
And(&'tcx [InhabitedPredicate<'tcx>; 2]),
2830
/// A OR B
2931
Or(&'tcx [InhabitedPredicate<'tcx>; 2]),
3032
}
3133

3234
impl<'tcx> InhabitedPredicate<'tcx> {
33-
/// Returns true if the corresponding type is inhabited in the given
34-
/// `ParamEnv` and module
35+
/// Returns true if the corresponding type is inhabited in the given `ParamEnv` and module.
3536
pub fn apply(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, module_def_id: DefId) -> bool {
36-
let Ok(result) = self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|id| {
37-
Ok(tcx.is_descendant_of(module_def_id, id))
38-
});
37+
self.apply_revealing_opaque(tcx, param_env, module_def_id, &|_| None)
38+
}
39+
40+
/// Returns true if the corresponding type is inhabited in the given `ParamEnv` and module,
41+
/// revealing opaques when possible.
42+
pub fn apply_revealing_opaque(
43+
self,
44+
tcx: TyCtxt<'tcx>,
45+
param_env: ParamEnv<'tcx>,
46+
module_def_id: DefId,
47+
reveal_opaque: &impl Fn(OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>>,
48+
) -> bool {
49+
let Ok(result) = self.apply_inner::<!>(
50+
tcx,
51+
param_env,
52+
&mut Default::default(),
53+
&|id| Ok(tcx.is_descendant_of(module_def_id, id)),
54+
reveal_opaque,
55+
);
3956
result
4057
}
4158

4259
/// Same as `apply`, but returns `None` if self contains a module predicate
4360
pub fn apply_any_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
44-
self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(())).ok()
61+
self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(()), &|_| None).ok()
4562
}
4663

4764
/// Same as `apply`, but `NotInModule(_)` predicates yield `false`. That is,
4865
/// privately uninhabited types are considered always uninhabited.
4966
pub fn apply_ignore_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool {
5067
let Ok(result) =
51-
self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|_| Ok(true));
68+
self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|_| Ok(true), &|_| {
69+
None
70+
});
5271
result
5372
}
5473

55-
#[instrument(level = "debug", skip(tcx, param_env, in_module), ret)]
74+
#[instrument(level = "debug", skip(tcx, param_env, in_module, reveal_opaque), ret)]
5675
fn apply_inner<E: std::fmt::Debug>(
5776
self,
5877
tcx: TyCtxt<'tcx>,
5978
param_env: ParamEnv<'tcx>,
6079
eval_stack: &mut SmallVec<[Ty<'tcx>; 1]>, // for cycle detection
6180
in_module: &impl Fn(DefId) -> Result<bool, E>,
81+
reveal_opaque: &impl Fn(OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>>,
6282
) -> Result<bool, E> {
6383
match self {
6484
Self::False => Ok(false),
@@ -84,18 +104,41 @@ impl<'tcx> InhabitedPredicate<'tcx> {
84104
return Ok(true); // Recover; this will error later.
85105
}
86106
eval_stack.push(t);
87-
let ret = pred.apply_inner(tcx, param_env, eval_stack, in_module);
107+
let ret =
108+
pred.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque);
88109
eval_stack.pop();
89110
ret
90111
}
91112
}
92113
}
93-
Self::And([a, b]) => {
94-
try_and(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module))
95-
}
96-
Self::Or([a, b]) => {
97-
try_or(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module))
98-
}
114+
Self::OpaqueType(key) => match reveal_opaque(key) {
115+
// Unknown opaque is assumed inhabited.
116+
None => Ok(true),
117+
// Known opaque type is inspected recursively.
118+
Some(t) => {
119+
// A cyclic opaque type can happen in corner cases that would only error later.
120+
// See e.g. `tests/ui/type-alias-impl-trait/recursive-tait-conflicting-defn.rs`.
121+
if eval_stack.contains(&t) {
122+
return Ok(true); // Recover; this will error later.
123+
}
124+
eval_stack.push(t);
125+
let ret = t.inhabited_predicate(tcx).apply_inner(
126+
tcx,
127+
param_env,
128+
eval_stack,
129+
in_module,
130+
reveal_opaque,
131+
);
132+
eval_stack.pop();
133+
ret
134+
}
135+
},
136+
Self::And([a, b]) => try_and(a, b, |x| {
137+
x.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque)
138+
}),
139+
Self::Or([a, b]) => try_or(a, b, |x| {
140+
x.apply_inner(tcx, param_env, eval_stack, in_module, reveal_opaque)
141+
}),
99142
}
100143
}
101144

‎compiler/rustc_middle/src/ty/inhabitedness/mod.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
4646
use crate::query::Providers;
4747
use crate::ty::context::TyCtxt;
48-
use crate::ty::{self, DefId, Ty, VariantDef, Visibility};
48+
use crate::ty::{self, DefId, Ty, TypeVisitableExt, VariantDef, Visibility};
4949

5050
use rustc_type_ir::TyKind::*;
5151

@@ -105,6 +105,7 @@ impl<'tcx> VariantDef {
105105
impl<'tcx> Ty<'tcx> {
106106
#[instrument(level = "debug", skip(tcx), ret)]
107107
pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
108+
debug_assert!(!self.has_infer());
108109
match self.kind() {
109110
// For now, unions are always considered inhabited
110111
Adt(adt, _) if adt.is_union() => InhabitedPredicate::True,
@@ -113,7 +114,18 @@ impl<'tcx> Ty<'tcx> {
113114
InhabitedPredicate::True
114115
}
115116
Never => InhabitedPredicate::False,
116-
Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self),
117+
Param(_) | Alias(ty::Projection | ty::Weak, _) => InhabitedPredicate::GenericType(self),
118+
Alias(ty::Opaque, alias_ty) => {
119+
match alias_ty.def_id.as_local() {
120+
// Foreign opaque is considered inhabited.
121+
None => InhabitedPredicate::True,
122+
// Local opaque type may possibly be revealed.
123+
Some(local_def_id) => {
124+
let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
125+
InhabitedPredicate::OpaqueType(key)
126+
}
127+
}
128+
}
117129
// FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above.
118130
// However it's unclear if the args passed to `InhabitedPredicate::instantiate` are of the correct
119131
// format, i.e. don't contain parent args. If you hit this case, please verify this beforehand.

‎compiler/rustc_pattern_analysis/src/rustc.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::mir::interpret::Scalar;
1212
use rustc_middle::mir::{self, Const};
1313
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
1414
use rustc_middle::ty::layout::IntegerExt;
15-
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
15+
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef};
1616
use rustc_span::{Span, DUMMY_SP};
1717
use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
1818
use smallvec::SmallVec;
@@ -74,8 +74,16 @@ impl<'p, 'tcx> fmt::Debug for RustcMatchCheckCtxt<'p, 'tcx> {
7474
}
7575

7676
impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
77-
pub(crate) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
78-
!ty.is_inhabited_from(self.tcx, self.module, self.param_env)
77+
fn reveal_opaque(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
78+
self.typeck_results.concrete_opaque_types.get(&key).map(|x| x.ty)
79+
}
80+
pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
81+
!ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
82+
self.tcx,
83+
self.param_env,
84+
self.module,
85+
&|key| self.reveal_opaque(key),
86+
)
7987
}
8088

8189
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
@@ -319,7 +327,9 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
319327
let is_inhabited = v
320328
.inhabited_predicate(cx.tcx, *def)
321329
.instantiate(cx.tcx, args)
322-
.apply(cx.tcx, cx.param_env, cx.module);
330+
.apply_revealing_opaque(cx.tcx, cx.param_env, cx.module, &|key| {
331+
cx.reveal_opaque(key)
332+
});
323333
// Variants that depend on a disabled unstable feature.
324334
let is_unstable = matches!(
325335
cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),

‎tests/ui/pattern/usefulness/impl-trait.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ fn option_never(x: Void) -> Option<impl Copy> {
4646
}
4747
match option_never(x) {
4848
None => {}
49-
// FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque
50-
// types.
51-
_ => {}
49+
_ => {} //~ ERROR unreachable
5250
}
5351
}
5452
Some(x)
@@ -137,10 +135,21 @@ fn nested_empty_opaque(x: Void) -> X {
137135
let opaque_void = nested_empty_opaque(x);
138136
let secretely_void = SecretelyVoid(opaque_void);
139137
match secretely_void {
140-
// FIXME: Unreachable not detected because `is_uninhabited` does not look into opaque
141-
// types.
142-
_ => {}
138+
_ => {} //~ ERROR unreachable
143139
}
144140
}
145141
x
146142
}
143+
144+
type Y = (impl Copy, impl Copy);
145+
struct SecretelyDoubleVoid(Y);
146+
fn super_nested_empty_opaque(x: Void) -> Y {
147+
if false {
148+
let opaque_void = super_nested_empty_opaque(x);
149+
let secretely_void = SecretelyDoubleVoid(opaque_void);
150+
match secretely_void {
151+
_ => {} //~ ERROR unreachable
152+
}
153+
}
154+
(x, x)
155+
}

‎tests/ui/pattern/usefulness/impl-trait.stderr

+26-8
Original file line numberDiff line numberDiff line change
@@ -23,51 +23,69 @@ LL | Some(_) => {}
2323
| ^^^^^^^
2424

2525
error: unreachable pattern
26-
--> $DIR/impl-trait.rs:61:13
26+
--> $DIR/impl-trait.rs:49:13
27+
|
28+
LL | _ => {}
29+
| ^
30+
31+
error: unreachable pattern
32+
--> $DIR/impl-trait.rs:59:13
2733
|
2834
LL | Some(_) => {}
2935
| ^^^^^^^
3036

3137
error: unreachable pattern
32-
--> $DIR/impl-trait.rs:65:13
38+
--> $DIR/impl-trait.rs:63:13
3339
|
3440
LL | _ => {}
3541
| ^
3642

3743
error: unreachable pattern
38-
--> $DIR/impl-trait.rs:78:9
44+
--> $DIR/impl-trait.rs:76:9
3945
|
4046
LL | _ => {}
4147
| ^
4248

4349
error: unreachable pattern
44-
--> $DIR/impl-trait.rs:88:9
50+
--> $DIR/impl-trait.rs:86:9
4551
|
4652
LL | _ => {}
4753
| - matches any value
4854
LL | Some((a, b)) => {}
4955
| ^^^^^^^^^^^^ unreachable pattern
5056

5157
error: unreachable pattern
52-
--> $DIR/impl-trait.rs:96:13
58+
--> $DIR/impl-trait.rs:94:13
5359
|
5460
LL | _ => {}
5561
| ^
5662

5763
error: unreachable pattern
58-
--> $DIR/impl-trait.rs:107:9
64+
--> $DIR/impl-trait.rs:105:9
5965
|
6066
LL | Some((mut x, mut y)) => {
6167
| ^^^^^^^^^^^^^^^^^^^^
6268

6369
error: unreachable pattern
64-
--> $DIR/impl-trait.rs:126:13
70+
--> $DIR/impl-trait.rs:124:13
6571
|
6672
LL | _ => {}
6773
| - matches any value
6874
LL | Rec { n: 0, w: Some(Rec { n: 0, w: _ }) } => {}
6975
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
7076

77+
error: unreachable pattern
78+
--> $DIR/impl-trait.rs:138:13
79+
|
80+
LL | _ => {}
81+
| ^
82+
83+
error: unreachable pattern
84+
--> $DIR/impl-trait.rs:151:13
85+
|
86+
LL | _ => {}
87+
| ^
88+
7189
error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty
7290
--> $DIR/impl-trait.rs:23:11
7391
|
@@ -96,6 +114,6 @@ LL + _ => todo!(),
96114
LL + }
97115
|
98116

99-
error: aborting due to 12 previous errors
117+
error: aborting due to 15 previous errors
100118

101119
For more information about this error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)
Please sign in to comment.