Skip to content

Commit c2c27fa

Browse files
author
Jakub Wieczorek
committed
Fix #12285
Unit-like struct patterns are irrefutable, no need for a branch.
1 parent eda75bc commit c2c27fa

File tree

3 files changed

+78
-93
lines changed

3 files changed

+78
-93
lines changed

src/librustc/middle/check_match.rs

+51-73
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,10 @@ pub enum Constructor {
9393
Slice(uint)
9494
}
9595

96-
#[deriving(Clone)]
96+
#[deriving(Clone, PartialEq)]
9797
enum Usefulness {
98-
Useful(Vec<Gc<Pat>>),
98+
Useful,
99+
UsefulWithWitness(Vec<Gc<Pat>>),
99100
NotUseful
100101
}
101102

@@ -104,15 +105,6 @@ enum WitnessPreference {
104105
LeaveOutWitness
105106
}
106107

107-
impl Usefulness {
108-
fn useful(self) -> Option<Vec<Gc<Pat>>> {
109-
match self {
110-
Useful(pats) => Some(pats),
111-
_ => None
112-
}
113-
}
114-
}
115-
116108
impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
117109
fn visit_expr(&mut self, ex: &Expr, _: ()) {
118110
check_expr(self, ex);
@@ -203,7 +195,8 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
203195
let v = vec!(*pat);
204196
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
205197
NotUseful => cx.tcx.sess.span_err(pat.span, "unreachable pattern"),
206-
_ => ()
198+
Useful => (),
199+
UsefulWithWitness(_) => unreachable!()
207200
}
208201
if arm.guard.is_none() {
209202
let Matrix(mut rows) = seen;
@@ -223,7 +216,7 @@ fn raw_pat(p: Gc<Pat>) -> Gc<Pat> {
223216

224217
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &Matrix) {
225218
match is_useful(cx, m, [wild()], ConstructWitness) {
226-
Useful(pats) => {
219+
UsefulWithWitness(pats) => {
227220
let witness = match pats.as_slice() {
228221
[witness] => witness,
229222
[] => wild(),
@@ -234,7 +227,8 @@ fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &Matrix) {
234227
}
235228
NotUseful => {
236229
// This is good, wildcard pattern isn't reachable
237-
}
230+
},
231+
_ => unreachable!()
238232
}
239233
}
240234

@@ -404,11 +398,14 @@ fn all_constructors(cx: &MatchCheckCtxt, left_ty: ty::t,
404398

405399
// Note: is_useful doesn't work on empty types, as the paper notes.
406400
// So it assumes that v is non-empty.
407-
fn is_useful(cx: &MatchCheckCtxt, m @ &Matrix(ref rows): &Matrix,
401+
fn is_useful(cx: &MatchCheckCtxt, matrix @ &Matrix(ref rows): &Matrix,
408402
v: &[Gc<Pat>], witness: WitnessPreference) -> Usefulness {
409-
debug!("{:}", m);
403+
debug!("{:}", matrix);
410404
if rows.len() == 0u {
411-
return Useful(vec!());
405+
return match witness {
406+
ConstructWitness => UsefulWithWitness(vec!()),
407+
LeaveOutWitness => Useful
408+
};
412409
}
413410
if rows.get(0).len() == 0u {
414411
return NotUseful;
@@ -438,53 +435,46 @@ fn is_useful(cx: &MatchCheckCtxt, m @ &Matrix(ref rows): &Matrix,
438435

439436
let constructors = pat_constructors(cx, v[0], left_ty, max_slice_length);
440437
if constructors.is_empty() {
441-
match missing_constructor(cx, m, left_ty, max_slice_length) {
438+
match missing_constructor(cx, matrix, left_ty, max_slice_length) {
442439
None => {
443-
all_constructors(cx, left_ty, max_slice_length).move_iter().filter_map(|c| {
444-
is_useful_specialized(cx, m, v, c.clone(),
445-
left_ty, witness).useful().map(|pats| {
446-
Useful(match witness {
447-
ConstructWitness => {
448-
let arity = constructor_arity(cx, &c, left_ty);
449-
let subpats = {
450-
let pat_slice = pats.as_slice();
451-
Vec::from_fn(arity, |i| {
452-
pat_slice.get(i).map(|p| p.clone())
453-
.unwrap_or_else(|| wild())
454-
})
455-
};
456-
let mut result = vec!(construct_witness(cx, &c, subpats, left_ty));
457-
result.extend(pats.move_iter().skip(arity));
458-
result
459-
}
460-
LeaveOutWitness => vec!()
461-
})
462-
})
463-
}).nth(0).unwrap_or(NotUseful)
440+
all_constructors(cx, left_ty, max_slice_length).move_iter().map(|c| {
441+
match is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness) {
442+
UsefulWithWitness(pats) => UsefulWithWitness({
443+
let arity = constructor_arity(cx, &c, left_ty);
444+
let subpats = {
445+
let pat_slice = pats.as_slice();
446+
Vec::from_fn(arity, |i| {
447+
pat_slice.get(i).map(|p| p.clone())
448+
.unwrap_or_else(|| wild())
449+
})
450+
};
451+
let mut result = vec!(construct_witness(cx, &c, subpats, left_ty));
452+
result.extend(pats.move_iter().skip(arity));
453+
result
454+
}),
455+
result => result
456+
}
457+
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
464458
},
465459

466460
Some(constructor) => {
467461
let matrix = Matrix(rows.iter().filter_map(|r|
468462
default(cx, r.as_slice())).collect());
469463
match is_useful(cx, &matrix, v.tail(), witness) {
470-
Useful(pats) => Useful(match witness {
471-
ConstructWitness => {
472-
let arity = constructor_arity(cx, &constructor, left_ty);
473-
let wild_pats = Vec::from_elem(arity, wild());
474-
let enum_pat = construct_witness(cx, &constructor, wild_pats, left_ty);
475-
(vec!(enum_pat)).append(pats.as_slice())
476-
}
477-
LeaveOutWitness => vec!()
478-
}),
464+
UsefulWithWitness(pats) => {
465+
let arity = constructor_arity(cx, &constructor, left_ty);
466+
let wild_pats = Vec::from_elem(arity, wild());
467+
let enum_pat = construct_witness(cx, &constructor, wild_pats, left_ty);
468+
UsefulWithWitness(vec!(enum_pat).append(pats.as_slice()))
469+
},
479470
result => result
480471
}
481472
}
482473
}
483474
} else {
484-
constructors.move_iter().filter_map(|c| {
485-
is_useful_specialized(cx, m, v, c.clone(), left_ty, witness)
486-
.useful().map(|pats| Useful(pats))
487-
}).nth(0).unwrap_or(NotUseful)
475+
constructors.move_iter().map(|c|
476+
is_useful_specialized(cx, matrix, v, c.clone(), left_ty, witness)
477+
).find(|result| result != &NotUseful).unwrap_or(NotUseful)
488478
}
489479
}
490480

@@ -519,6 +509,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: Gc<Pat>,
519509
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
520510
vec!(ConstantValue(eval_const_expr(cx.tcx, &*const_expr)))
521511
},
512+
Some(&DefStruct(_)) => vec!(Single),
522513
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
523514
_ => vec!()
524515
},
@@ -560,21 +551,6 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: Gc<Pat>,
560551
}
561552
}
562553

563-
fn is_wild(cx: &MatchCheckCtxt, p: Gc<Pat>) -> bool {
564-
let pat = raw_pat(p);
565-
match pat.node {
566-
PatWild | PatWildMulti => true,
567-
PatIdent(_, _, _) =>
568-
match cx.tcx.def_map.borrow().find(&pat.id) {
569-
Some(&DefVariant(_, _, _)) | Some(&DefStatic(..)) => false,
570-
_ => true
571-
},
572-
PatVec(ref before, Some(_), ref after) =>
573-
before.is_empty() && after.is_empty(),
574-
_ => false
575-
}
576-
}
577-
578554
/// This computes the arity of a constructor. The arity of a constructor
579555
/// is how many subpattern patterns of that constructor should be expanded to.
580556
///
@@ -780,7 +756,7 @@ pub fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
780756
}
781757

782758
fn default(cx: &MatchCheckCtxt, r: &[Gc<Pat>]) -> Option<Vec<Gc<Pat>>> {
783-
if is_wild(cx, r[0]) {
759+
if pat_is_binding_or_wild(&cx.tcx.def_map, &*raw_pat(r[0])) {
784760
Some(Vec::from_slice(r.tail()))
785761
} else {
786762
None
@@ -833,12 +809,14 @@ fn check_fn(cx: &mut MatchCheckCtxt,
833809

834810
fn is_refutable(cx: &MatchCheckCtxt, pat: Gc<Pat>) -> Option<Gc<Pat>> {
835811
let pats = Matrix(vec!(vec!(pat)));
836-
is_useful(cx, &pats, [wild()], ConstructWitness)
837-
.useful()
838-
.map(|pats| {
812+
match is_useful(cx, &pats, [wild()], ConstructWitness) {
813+
UsefulWithWitness(pats) => {
839814
assert_eq!(pats.len(), 1);
840-
pats.get(0).clone()
841-
})
815+
Some(pats.get(0).clone())
816+
},
817+
NotUseful => None,
818+
Useful => unreachable!()
819+
}
842820
}
843821

844822
// Legality of move bindings checking

src/librustc/middle/trans/_match.rs

+5-20
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ use middle::trans::cleanup::CleanupMethods;
212212
use middle::trans::common::*;
213213
use middle::trans::consts;
214214
use middle::trans::controlflow;
215-
use middle::trans::datum;
216215
use middle::trans::datum::*;
217216
use middle::trans::expr::Dest;
218217
use middle::trans::expr;
@@ -236,10 +235,8 @@ use syntax::ast_util;
236235
use syntax::codemap::Span;
237236
use syntax::parse::token::InternedString;
238237

239-
// An option identifying a literal: either a unit-like struct or an
240-
// expression.
238+
// An option identifying a literal: either an expression or a DefId of a static expression.
241239
enum Lit {
242-
UnitLikeStructLit(ast::NodeId), // the node ID of the pattern
243240
ExprLit(Gc<ast::Expr>),
244241
ConstLit(ast::DefId), // the def ID of the constant
245242
}
@@ -262,14 +259,12 @@ enum Opt {
262259
fn lit_to_expr(tcx: &ty::ctxt, a: &Lit) -> Gc<ast::Expr> {
263260
match *a {
264261
ExprLit(existing_a_expr) => existing_a_expr,
265-
ConstLit(a_const) => const_eval::lookup_const_by_id(tcx, a_const).unwrap(),
266-
UnitLikeStructLit(_) => fail!("lit_to_expr: unexpected struct lit"),
262+
ConstLit(a_const) => const_eval::lookup_const_by_id(tcx, a_const).unwrap()
267263
}
268264
}
269265

270266
fn opt_eq(tcx: &ty::ctxt, a: &Opt, b: &Opt) -> bool {
271267
match (a, b) {
272-
(&lit(UnitLikeStructLit(a)), &lit(UnitLikeStructLit(b))) => a == b,
273268
(&lit(a), &lit(b)) => {
274269
let a_expr = lit_to_expr(tcx, &a);
275270
let b_expr = lit_to_expr(tcx, &b);
@@ -310,11 +305,6 @@ fn trans_opt<'a>(bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> {
310305
let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
311306
return single_result(Result::new(bcx, lit_datum.val));
312307
}
313-
lit(UnitLikeStructLit(pat_id)) => {
314-
let struct_ty = ty::node_id_to_type(bcx.tcx(), pat_id);
315-
let datum = datum::rvalue_scratch_datum(bcx, struct_ty, "");
316-
return single_result(Result::new(bcx, datum.val));
317-
}
318308
lit(l @ ConstLit(ref def_id)) => {
319309
let lit_ty = ty::node_id_to_type(bcx.tcx(), lit_to_expr(bcx.tcx(), &l).id);
320310
let (llval, _) = consts::get_const_val(bcx.ccx(), *def_id);
@@ -347,9 +337,6 @@ fn variant_opt(bcx: &Block, pat_id: ast::NodeId) -> Opt {
347337
let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id);
348338
var(variant.disr_val, adt::represent_node(bcx, pat_id), var_id)
349339
}
350-
def::DefFn(..) | def::DefStruct(_) => {
351-
lit(UnitLikeStructLit(pat_id))
352-
}
353340
_ => {
354341
ccx.sess().bug("non-variant or struct in variant_opt()");
355342
}
@@ -567,7 +554,6 @@ fn enter_opt<'a, 'b>(
567554
let _indenter = indenter();
568555

569556
let ctor = match opt {
570-
&lit(UnitLikeStructLit(_)) => check_match::Single,
571557
&lit(x) => check_match::ConstantValue(const_eval::eval_const_expr(
572558
bcx.tcx(), lit_to_expr(bcx.tcx(), &x))),
573559
&range(ref lo, ref hi) => check_match::ConstantRange(
@@ -672,11 +658,10 @@ fn get_options(bcx: &Block, m: &[Match], col: uint) -> Vec<Opt> {
672658
add_to_set(ccx.tcx(), &mut found, lit(ExprLit(l)));
673659
}
674660
ast::PatIdent(..) => {
675-
// This is one of: an enum variant, a unit-like struct, or a
676-
// variable binding.
661+
// This is either an enum variant or a variable binding.
677662
let opt_def = ccx.tcx.def_map.borrow().find_copy(&cur.id);
678663
match opt_def {
679-
Some(def::DefVariant(..)) | Some(def::DefStruct(..)) => {
664+
Some(def::DefVariant(..)) => {
680665
add_to_set(ccx.tcx(), &mut found,
681666
variant_opt(bcx, cur.id));
682667
}
@@ -827,7 +812,7 @@ fn any_irrefutable_adt_pat(bcx: &Block, m: &[Match], col: uint) -> bool {
827812
let pat = *br.pats.get(col);
828813
match pat.node {
829814
ast::PatTup(_) => true,
830-
ast::PatStruct(_, _, _) | ast::PatEnum(_, _) =>
815+
ast::PatEnum(..) | ast::PatIdent(_, _, None) | ast::PatStruct(..) =>
831816
match bcx.tcx().def_map.borrow().find(&pat.id) {
832817
Some(&def::DefFn(..)) |
833818
Some(&def::DefStruct(..)) => true,

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

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2014 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+
struct S;
12+
13+
fn main() {
14+
match Some(&S) {
15+
Some(&S) => {},
16+
_x => unreachable!()
17+
}
18+
match Some(&S) {
19+
Some(&S) => {},
20+
None => unreachable!()
21+
}
22+
}

0 commit comments

Comments
 (0)