Skip to content

Commit a84eb95

Browse files
committed
check_match: don't treat privately uninhabited types as uninhabited
Fixes rust-lang#38972.
1 parent 306035c commit a84eb95

File tree

5 files changed

+144
-37
lines changed

5 files changed

+144
-37
lines changed

src/librustc_const_eval/_match.rs

+62-30
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,28 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
196196
}
197197
}).clone()
198198
}
199+
200+
fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
201+
if self.tcx.sess.features.borrow().never_type {
202+
ty.is_uninhabited_from(self.module, self.tcx)
203+
} else {
204+
false
205+
}
206+
}
207+
208+
fn is_variant_uninhabited(&self,
209+
variant: &'tcx ty::VariantDef,
210+
substs: &'tcx ty::subst::Substs<'tcx>) -> bool
211+
{
212+
if self.tcx.sess.features.borrow().never_type {
213+
let forest = variant.uninhabited_from(
214+
&mut FxHashMap::default(), self.tcx, substs, AdtKind::Enum
215+
);
216+
forest.contains(self.tcx, self.module)
217+
} else {
218+
false
219+
}
220+
}
199221
}
200222

201223
#[derive(Clone, Debug, PartialEq)]
@@ -379,48 +401,32 @@ impl<'tcx> Witness<'tcx> {
379401
fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
380402
pcx: PatternContext<'tcx>) -> Vec<Constructor>
381403
{
382-
let check_inhabited = cx.tcx.sess.features.borrow().never_type;
383404
debug!("all_constructors({:?})", pcx.ty);
384405
match pcx.ty.sty {
385406
ty::TyBool =>
386407
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
387408
ty::TySlice(ref sub_ty) => {
388-
if sub_ty.is_uninhabited_from(cx.module, cx.tcx)
389-
&& check_inhabited
390-
{
409+
if cx.is_uninhabited(sub_ty) {
391410
vec![Slice(0)]
392411
} else {
393412
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect()
394413
}
395414
}
396415
ty::TyArray(ref sub_ty, length) => {
397-
if length == 0 || !(sub_ty.is_uninhabited_from(cx.module, cx.tcx)
398-
&& check_inhabited)
399-
{
400-
vec![Slice(length)]
401-
} else {
416+
if length > 0 && cx.is_uninhabited(sub_ty) {
402417
vec![]
418+
} else {
419+
vec![Slice(length)]
403420
}
404421
}
405422
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
406-
def.variants.iter().filter_map(|v| {
407-
let mut visited = FxHashMap::default();
408-
let forest = v.uninhabited_from(&mut visited,
409-
cx.tcx, substs,
410-
AdtKind::Enum);
411-
if forest.contains(cx.tcx, cx.module)
412-
&& check_inhabited
413-
{
414-
None
415-
} else {
416-
Some(Variant(v.did))
417-
}
418-
}).collect()
423+
def.variants.iter()
424+
.filter(|v| !cx.is_variant_uninhabited(v, substs))
425+
.map(|v| Variant(v.did))
426+
.collect()
419427
}
420428
_ => {
421-
if pcx.ty.is_uninhabited_from(cx.module, cx.tcx)
422-
&& check_inhabited
423-
{
429+
if cx.is_uninhabited(pcx.ty) {
424430
vec![]
425431
} else {
426432
vec![Single]
@@ -564,7 +570,6 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
564570

565571
assert!(rows.iter().all(|r| r.len() == v.len()));
566572

567-
568573
let pcx = PatternContext {
569574
ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error())
570575
.unwrap_or(v[0].ty),
@@ -590,7 +595,6 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
590595
let missing_ctors: Vec<Constructor> = all_ctors.iter().filter(|c| {
591596
!used_ctors.contains(*c)
592597
}).cloned().collect();
593-
debug!("missing_ctors = {:?}", missing_ctors);
594598

595599
// `missing_ctors` is the set of constructors from the same type as the
596600
// first column of `matrix` that are matched only by wildcard patterns
@@ -599,8 +603,23 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
599603
// Therefore, if there is some pattern that is unmatched by `matrix`,
600604
// it will still be unmatched if the first constructor is replaced by
601605
// any of the constructors in `missing_ctors`
602-
603-
if missing_ctors.is_empty() {
606+
//
607+
// However, if our scrutinee is *privately* an empty enum, we
608+
// must treat it as though it had an "unknown" constructor (in
609+
// that case, all other patterns obviously can't be variants)
610+
// to avoid exposing its emptyness. See the `match_privately_empty`
611+
// test for details.
612+
//
613+
// FIXME: currently the only way I know of something can
614+
// be a privately-empty enum is when the never_type
615+
// feature flag is not present, so this is only
616+
// needed for that case.
617+
618+
let is_privately_empty =
619+
all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty);
620+
debug!("missing_ctors={:?} is_privately_empty={:?}", missing_ctors,
621+
is_privately_empty);
622+
if missing_ctors.is_empty() && !is_privately_empty {
604623
all_ctors.into_iter().map(|c| {
605624
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
606625
}).find(|result| result.is_useful()).unwrap_or(NotUseful)
@@ -649,6 +668,7 @@ fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>(
649668
lty: Ty<'tcx>,
650669
witness: WitnessPreference) -> Usefulness<'tcx>
651670
{
671+
debug!("is_useful_specialized({:?}, {:?}, {:?})", v, ctor, lty);
652672
let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty);
653673
let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| {
654674
Pattern {
@@ -754,7 +774,19 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>,
754774
ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty],
755775
ty::TyAdt(adt, substs) => {
756776
adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| {
757-
field.ty(cx.tcx, substs)
777+
let is_visible = adt.is_enum()
778+
|| field.vis.is_accessible_from(cx.module, cx.tcx);
779+
if is_visible {
780+
field.ty(cx.tcx, substs)
781+
} else {
782+
// Treat all non-visible fields as nil. They
783+
// can't appear in any other pattern from
784+
// this match (because they are private),
785+
// so their type does not matter - but
786+
// we don't want to know they are
787+
// uninhabited.
788+
cx.tcx.mk_nil()
789+
}
758790
}).collect()
759791
}
760792
_ => vec![],

src/librustc_const_eval/check_match.rs

+20
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,26 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
177177
// Fourth, check for unreachable arms.
178178
check_arms(cx, &inlined_arms, source);
179179

180+
// Then, if the match has no arms, check whether the scrutinee
181+
// is uninhabited.
182+
let pat_ty = self.tables.node_id_to_type(scrut.id);
183+
let module = self.tcx.hir.local_def_id(self.tcx.hir.get_module_parent(scrut.id));
184+
if inlined_arms.is_empty() {
185+
if !pat_ty.is_uninhabited_from(module, self.tcx) {
186+
// We know the type is inhabited, so this must be wrong
187+
let mut err = create_e0004(self.tcx.sess, scrut.span,
188+
format!("non-exhaustive patterns: type {} \
189+
is non-empty",
190+
pat_ty));
191+
span_help!(&mut err, scrut.span,
192+
"Please ensure that all possible cases are being handled; \
193+
possibly adding wildcards or more match arms.");
194+
err.emit();
195+
}
196+
// If the type *is* uninhabited, it's vacuously exhaustive
197+
return;
198+
}
199+
180200
let matrix: Matrix = inlined_arms
181201
.iter()
182202
.filter(|&&(_, guard)| guard.is_none())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(never_type)]
12+
13+
mod private {
14+
pub struct Private {
15+
_bot: !,
16+
pub misc: bool,
17+
}
18+
pub const DATA: Option<Private> = None;
19+
}
20+
21+
fn main() {
22+
match private::DATA {
23+
//~^ ERROR non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered
24+
None => {}
25+
Some(private::Private {
26+
misc: false,
27+
..
28+
}) => {}
29+
}
30+
}

src/test/compile-fail/uninhabited-matches-feature-gated.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,13 @@ fn main() {
1919
};
2020

2121
let x: &Void = unsafe { std::mem::uninitialized() };
22-
let _ = match x {};
23-
//~^ ERROR non-exhaustive
22+
let _ = match x {}; // okay
2423

2524
let x: (Void,) = unsafe { std::mem::uninitialized() };
26-
let _ = match x {};
27-
//~^ ERROR non-exhaustive
25+
let _ = match x {}; // okay
2826

2927
let x: [Void; 1] = unsafe { std::mem::uninitialized() };
30-
let _ = match x {};
31-
//~^ ERROR non-exhaustive
28+
let _ = match x {}; // okay
3229

3330
let x: &[Void] = unsafe { std::mem::uninitialized() };
3431
let _ = match x { //~ ERROR non-exhaustive
@@ -47,4 +44,3 @@ fn main() {
4744
let Ok(x) = x;
4845
//~^ ERROR refutable
4946
}
50-

src/test/run-pass/issue-38972.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// This issue tracks a regression (a new warning) without
12+
// feature(never_type). When we make that the default, please
13+
// remove this test.
14+
15+
enum Foo { }
16+
17+
fn make_foo() -> Option<Foo> { None }
18+
19+
fn use_foo(v: &Foo) -> ! {
20+
match v { }
21+
}
22+
23+
#[deny(warnings)]
24+
fn main() {
25+
match make_foo() {
26+
None => {},
27+
Some(ref v) => use_foo(v),
28+
}
29+
}

0 commit comments

Comments
 (0)