Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove runtime failure from pattern matching #15700

Merged
merged 1 commit into from Jul 21, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 22 additions & 83 deletions src/librustc/middle/trans/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,12 @@ use middle::resolve::DefMap;
use middle::trans::adt;
use middle::trans::base::*;
use middle::trans::build::*;
use middle::trans::build;
use middle::trans::callee;
use middle::trans::cleanup;
use middle::trans::cleanup::CleanupMethods;
use middle::trans::common::*;
use middle::trans::consts;
use middle::trans::controlflow;
use middle::trans::datum::*;
use middle::trans::expr::Dest;
use middle::trans::expr;
Expand All @@ -220,14 +220,12 @@ use util::ppaux::{Repr, vec_map_to_string};

use std;
use std::collections::HashMap;
use std::cell::Cell;
use std::rc::Rc;
use std::gc::{Gc};
use syntax::ast;
use syntax::ast::Ident;
use syntax::codemap::Span;
use syntax::fold::Folder;
use syntax::parse::token::InternedString;

#[deriving(PartialEq)]
pub enum VecLenOpt {
Expand Down Expand Up @@ -301,20 +299,6 @@ fn trans_opt<'a>(mut bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> {
}
}

fn variant_opt(bcx: &Block, pat_id: ast::NodeId) -> Opt {
let ccx = bcx.ccx();
let def = ccx.tcx.def_map.borrow().get_copy(&pat_id);
match def {
def::DefVariant(enum_id, var_id, _) => {
let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id);
var(variant.disr_val, adt::represent_node(bcx, pat_id), var_id)
}
_ => {
ccx.sess().bug("non-variant or struct in variant_opt()");
}
}
}

#[deriving(Clone)]
pub enum TransBindingMode {
TrByCopy(/* llbinding */ ValueRef),
Expand Down Expand Up @@ -630,26 +614,15 @@ fn get_options(bcx: &Block, m: &[Match], col: uint) -> Vec<Opt> {
ast::PatLit(l) => {
add_to_set(ccx.tcx(), &mut found, lit(l));
}
ast::PatIdent(..) => {
ast::PatIdent(..) | ast::PatEnum(..) | ast::PatStruct(..) => {
// This is either an enum variant or a variable binding.
let opt_def = ccx.tcx.def_map.borrow().find_copy(&cur.id);
match opt_def {
Some(def::DefVariant(..)) => {
add_to_set(ccx.tcx(), &mut found,
variant_opt(bcx, cur.id));
}
_ => {}
}
}
ast::PatEnum(..) | ast::PatStruct(..) => {
// This could be one of: a tuple-like enum variant, a
// struct-like enum variant, or a struct.
let opt_def = ccx.tcx.def_map.borrow().find_copy(&cur.id);
match opt_def {
Some(def::DefFn(..)) |
Some(def::DefVariant(..)) => {
Some(def::DefVariant(enum_id, var_id, _)) => {
let variant = ty::enum_variant_with_id(ccx.tcx(), enum_id, var_id);
add_to_set(ccx.tcx(), &mut found,
variant_opt(bcx, cur.id));
var(variant.disr_val,
adt::represent_node(bcx, cur.id), var_id));
}
_ => {}
}
Expand Down Expand Up @@ -795,56 +768,33 @@ fn any_irrefutable_adt_pat(bcx: &Block, m: &[Match], col: uint) -> bool {
})
}

struct DynamicFailureHandler<'a> {
bcx: &'a Block<'a>,
sp: Span,
msg: InternedString,
finished: Cell<Option<BasicBlockRef>>,
}

impl<'a> DynamicFailureHandler<'a> {
fn handle_fail(&self) -> BasicBlockRef {
match self.finished.get() {
Some(bb) => return bb,
_ => (),
}

let fcx = self.bcx.fcx;
let fail_cx = fcx.new_block(false, "case_fallthrough", None);
controlflow::trans_fail(fail_cx, self.sp, self.msg.clone());
self.finished.set(Some(fail_cx.llbb));
fail_cx.llbb
}
}

/// What to do when the pattern match fails.
enum FailureHandler<'a> {
Infallible,
JumpToBasicBlock(BasicBlockRef),
DynamicFailureHandlerClass(Box<DynamicFailureHandler<'a>>),
Unreachable
}

impl<'a> FailureHandler<'a> {
fn is_infallible(&self) -> bool {
match *self {
Infallible => true,
_ => false,
_ => false
}
}

fn is_fallible(&self) -> bool {
!self.is_infallible()
}

fn handle_fail(&self) -> BasicBlockRef {
fn handle_fail(&self, bcx: &Block) {
match *self {
Infallible => {
fail!("attempted to fail in infallible failure handler!")
}
JumpToBasicBlock(basic_block) => basic_block,
DynamicFailureHandlerClass(ref dynamic_failure_handler) => {
dynamic_failure_handler.handle_fail()
}
Infallible =>
fail!("attempted to fail in infallible failure handler!"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume "attempted to fail" doesn't mean fail!(), but rather "attempted to not match". (aka "infallable" is being used as a synonym for "irrefutable" here?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, where irrefutable refers to a specific branch of the decision tree. Unless guards or slice patterns are involved, only one branch in the tree has to be explored. And not matching is impossible because of the exhaustiveness guarantee. It is possible when guards or var-length slice patterns are involved, however, in which case it will jump to the next appropriate branch.

JumpToBasicBlock(basic_block) =>
Br(bcx, basic_block),
Unreachable =>
build::Unreachable(bcx)
}
}
}
Expand Down Expand Up @@ -1005,7 +955,7 @@ fn compile_guard<'a, 'b>(
// condition explicitly rather than (possibly) falling back to
// the default arm.
&JumpToBasicBlock(_) if m.len() == 1 && has_genuine_default => {
Br(bcx, chk.handle_fail());
chk.handle_fail(bcx);
}
_ => {
compile_submatch(bcx, m, vals, chk, has_genuine_default);
Expand All @@ -1030,7 +980,7 @@ fn compile_submatch<'a, 'b>(
let mut bcx = bcx;
if m.len() == 0u {
if chk.is_fallible() {
Br(bcx, chk.handle_fail());
chk.handle_fail(bcx);
}
return;
}
Expand Down Expand Up @@ -1301,7 +1251,7 @@ fn compile_submatch_continue<'a, 'b>(
// condition explicitly rather than (eventually) falling back to
// the last default arm.
&JumpToBasicBlock(_) if defaults.len() == 1 && has_genuine_default => {
Br(else_cx, chk.handle_fail());
chk.handle_fail(else_cx);
}
_ => {
compile_submatch(else_cx,
Expand Down Expand Up @@ -1395,21 +1345,10 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>,
}

let t = node_id_type(bcx, discr_expr.id);
let chk = {
if ty::type_is_empty(tcx, t) {
// Special case for empty types
let fail_cx = Cell::new(None);
let fail_handler = box DynamicFailureHandler {
bcx: scope_cx,
sp: discr_expr.span,
msg: InternedString::new("scrutinizing value that can't \
exist"),
finished: fail_cx,
};
DynamicFailureHandlerClass(fail_handler)
} else {
Infallible
}
let chk = if ty::type_is_empty(tcx, t) {
Unreachable
} else {
Infallible
};

let arm_datas: Vec<ArmData> = arms.iter().map(|arm| ArmData {
Expand Down