Skip to content

Commit

Permalink
rustc_const_eval: build Pattern instead of hir::Pat for pretty-printing.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Jan 1, 2017
1 parent e227433 commit c001b09
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 105 deletions.
145 changes: 62 additions & 83 deletions src/librustc_const_eval/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,12 @@ use rustc_data_structures::indexed_vec::Idx;
use pattern::{FieldPattern, Pattern, PatternKind};
use pattern::{PatternFoldable, PatternFolder};

use rustc::hir::def::Def;
use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};

use rustc::hir;
use rustc::hir::def::CtorKind;
use rustc::hir::{Pat, PatKind};
use rustc::mir::Field;
use rustc::util::common::ErrorReported;

use syntax::ast::{self, DUMMY_NODE_ID};
use syntax::codemap::Spanned;
use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP};

use arena::TypedArena;
Expand Down Expand Up @@ -74,12 +68,6 @@ impl<'tcx> PatternFolder<'tcx> for LiteralExpander {
}
}

pub const DUMMY_WILD_PAT: &'static Pat = &Pat {
id: DUMMY_NODE_ID,
node: PatKind::Wild,
span: DUMMY_SP
};

impl<'tcx> Pattern<'tcx> {
fn is_wildcard(&self) -> bool {
match *self.kind {
Expand Down Expand Up @@ -224,25 +212,34 @@ pub enum Constructor {
}

impl<'tcx> Constructor {
fn variant_for_adt(&self, adt: &'tcx ty::AdtDef) -> &'tcx ty::VariantDef {
fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> usize {
match self {
&Variant(vid) => adt.variant_with_id(vid),
&Variant(vid) => adt.variant_index_with_id(vid),
&Single => {
assert_eq!(adt.variants.len(), 1);
&adt.variants[0]
0
}
_ => bug!("bad constructor {:?} for adt {:?}", self, adt)
}
}
}

#[derive(Clone, PartialEq)]
pub enum Usefulness {
#[derive(Clone)]
pub enum Usefulness<'tcx> {
Useful,
UsefulWithWitness(Vec<Witness>),
UsefulWithWitness(Vec<Witness<'tcx>>),
NotUseful
}

impl<'tcx> Usefulness<'tcx> {
fn is_useful(&self) -> bool {
match *self {
NotUseful => false,
_ => true
}
}
}

#[derive(Copy, Clone)]
pub enum WitnessPreference {
ConstructWitness,
Expand All @@ -255,39 +252,25 @@ struct PatternContext<'tcx> {
max_slice_length: usize,
}


fn const_val_to_expr(value: &ConstVal) -> P<hir::Expr> {
let node = match value {
&ConstVal::Bool(b) => ast::LitKind::Bool(b),
_ => bug!()
};
P(hir::Expr {
id: DUMMY_NODE_ID,
node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
span: DUMMY_SP,
attrs: ast::ThinVec::new(),
})
}

/// A stack of patterns in reverse order of construction
#[derive(Clone, PartialEq, Eq)]
pub struct Witness(Vec<P<Pat>>);
#[derive(Clone)]
pub struct Witness<'tcx>(Vec<Pattern<'tcx>>);

impl Witness {
pub fn single_pattern(&self) -> &Pat {
impl<'tcx> Witness<'tcx> {
pub fn single_pattern(&self) -> &Pattern<'tcx> {
assert_eq!(self.0.len(), 1);
&self.0[0]
}

fn push_wild_constructor<'a, 'tcx>(
fn push_wild_constructor<'a>(
mut self,
cx: &MatchCheckCtxt<'a, 'tcx>,
ctor: &Constructor,
ty: Ty<'tcx>)
-> Self
{
let arity = constructor_arity(cx, ctor, ty);
self.0.extend(repeat(DUMMY_WILD_PAT).take(arity).map(|p| P(p.clone())));
self.0.extend(repeat(cx.wild_pattern).take(arity).cloned());
self.apply_constructor(cx, ctor, ty)
}

Expand All @@ -305,7 +288,7 @@ impl Witness {
///
/// left_ty: struct X { a: (bool, &'static str), b: usize}
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
fn apply_constructor<'a, 'tcx>(
fn apply_constructor<'a>(
mut self,
cx: &MatchCheckCtxt<'a,'tcx>,
ctor: &Constructor,
Expand All @@ -318,60 +301,56 @@ impl Witness {
let mut pats = self.0.drain(len-arity..).rev();

match ty.sty {
ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),

ty::TyAdt(adt, _) => {
let v = ctor.variant_for_adt(adt);
let qpath = hir::QPath::Resolved(None, P(hir::Path {
span: DUMMY_SP,
def: Def::Err,
segments: vec![hir::PathSegment::from_name(v.name)].into(),
}));
match v.ctor_kind {
CtorKind::Fictive => {
let field_pats: hir::HirVec<_> = v.fields.iter()
.zip(pats)
.filter(|&(_, ref pat)| pat.node != PatKind::Wild)
.map(|(field, pat)| Spanned {
span: DUMMY_SP,
node: hir::FieldPat {
name: field.name,
pat: pat,
is_shorthand: false,
}
}).collect();
let has_more_fields = field_pats.len() < arity;
PatKind::Struct(qpath, field_pats, has_more_fields)
ty::TyAdt(..) |
ty::TyTuple(..) => {
let pats = pats.enumerate().map(|(i, p)| {
FieldPattern {
field: Field::new(i),
pattern: p
}
CtorKind::Fn => {
PatKind::TupleStruct(qpath, pats.collect(), None)
}).collect();

if let ty::TyAdt(adt, _) = ty.sty {
if adt.variants.len() > 1 {
PatternKind::Variant {
adt_def: adt,
variant_index: ctor.variant_index_for_adt(adt),
subpatterns: pats
}
} else {
PatternKind::Leaf { subpatterns: pats }
}
CtorKind::Const => PatKind::Path(qpath)
} else {
PatternKind::Leaf { subpatterns: pats }
}
}

ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => {
PatKind::Ref(pats.nth(0).unwrap(), mutbl)
ty::TyRef(..) => {
PatternKind::Deref { subpattern: pats.nth(0).unwrap() }
}

ty::TySlice(_) | ty::TyArray(..) => {
PatKind::Slice(pats.collect(), None, hir::HirVec::new())
PatternKind::Slice {
prefix: pats.collect(),
slice: None,
suffix: vec![]
}
}

_ => {
match *ctor {
ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)),
_ => PatKind::Wild,
ConstantValue(ref v) => PatternKind::Constant { value: v.clone() },
_ => PatternKind::Wild,
}
}
}
};

self.0.push(P(hir::Pat {
id: DUMMY_NODE_ID,
node: pat,
span: DUMMY_SP
}));
self.0.push(Pattern {
ty: ty,
span: DUMMY_SP,
kind: Box::new(pat),
});

self
}
Expand Down Expand Up @@ -528,13 +507,13 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix<'a, 'tcx>,
v: &[&'a Pattern<'tcx>],
witness: WitnessPreference)
-> Usefulness {
-> Usefulness<'tcx> {
let &Matrix(ref rows) = matrix;
debug!("is_useful({:?}, {:?})", matrix, v);
if rows.is_empty() {
return match witness {
ConstructWitness => UsefulWithWitness(vec![Witness(
repeat(DUMMY_WILD_PAT).take(v.len()).map(|p| P(p.clone())).collect()
repeat(cx.wild_pattern).take(v.len()).cloned().collect()
)]),
LeaveOutWitness => Useful
};
Expand All @@ -559,15 +538,15 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
debug!("is_useful - expanding constructors: {:?}", constructors);
constructors.into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
).find(|result| result != &NotUseful).unwrap_or(NotUseful)
).find(|result| result.is_useful()).unwrap_or(NotUseful)
} else {
debug!("is_useful - expanding wildcard");
let constructors = missing_constructors(cx, matrix, pcx);
debug!("is_useful - missing_constructors = {:?}", constructors);
if constructors.is_empty() {
all_constructors(cx, pcx).into_iter().map(|c| {
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness)
}).find(|result| result != &NotUseful).unwrap_or(NotUseful)
}).find(|result| result.is_useful()).unwrap_or(NotUseful)
} else {
let matrix = rows.iter().filter_map(|r| {
if r[0].is_wildcard() {
Expand Down Expand Up @@ -597,7 +576,7 @@ fn is_useful_specialized<'a, 'tcx>(
v: &[&'a Pattern<'tcx>],
ctor: Constructor,
lty: Ty<'tcx>,
witness: WitnessPreference) -> Usefulness
witness: WitnessPreference) -> Usefulness<'tcx>
{
let arity = constructor_arity(cx, &ctor, lty);
let matrix = Matrix(m.iter().flat_map(|r| {
Expand Down Expand Up @@ -672,7 +651,7 @@ fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize
},
ty::TyRef(..) => 1,
ty::TyAdt(adt, _) => {
ctor.variant_for_adt(adt).fields.len()
adt.variants[ctor.variant_index_for_adt(adt)].fields.len()
}
_ => 0
}
Expand Down
36 changes: 15 additions & 21 deletions src/librustc_const_eval/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
// except according to those terms.

use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful};
use _match::{DUMMY_WILD_PAT};
use _match::Usefulness::*;
use _match::WitnessPreference::*;

use pattern::{Pattern, PatternContext, PatternError};
use pattern::{Pattern, PatternContext, PatternError, PatternKind};

use eval::report_const_eval_err;

Expand Down Expand Up @@ -230,9 +229,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
Useful => bug!()
};

let pattern_string = hir::print::to_string(&self.tcx.map, |s| {
s.print_pat(witness[0].single_pattern())
});
let pattern_string = witness[0].single_pattern().to_string();
let mut diag = struct_span_err!(
self.tcx.sess, pat.span, E0005,
"refutable pattern in {}: `{}` not covered",
Expand Down Expand Up @@ -369,23 +366,21 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) {
UsefulWithWitness(pats) => {
let witnesses = if pats.is_empty() {
vec![DUMMY_WILD_PAT]
vec![cx.wild_pattern]
} else {
pats.iter().map(|w| w.single_pattern()).collect()
};
match source {
hir::MatchSource::ForLoopDesugar => {
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
let witness = match witnesses[0].node {
PatKind::TupleStruct(_, ref pats, _) => match &pats[..] {
&[ref pat] => &**pat,
let witness = match *witnesses[0].kind {
PatternKind::Variant { ref subpatterns, .. } => match &subpatterns[..] {
&[ref pat] => &pat.pattern,
_ => bug!(),
},
_ => bug!(),
};
let pattern_string = hir::print::to_string(&cx.tcx.map, |s| {
s.print_pat(witness)
});
let pattern_string = witness.to_string();
struct_span_err!(cx.tcx.sess, sp, E0297,
"refutable pattern in `for` loop binding: \
`{}` not covered",
Expand All @@ -394,24 +389,23 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
.emit();
},
_ => {
let pattern_strings: Vec<_> = witnesses.iter().map(|w| {
hir::print::to_string(&cx.tcx.map, |s| s.print_pat(w))
}).collect();
const LIMIT: usize = 3;
let joined_patterns = match pattern_strings.len() {
let joined_patterns = match witnesses.len() {
0 => bug!(),
1 => format!("`{}`", pattern_strings[0]),
1 => format!("`{}`", witnesses[0]),
2...LIMIT => {
let (tail, head) = pattern_strings.split_last().unwrap();
format!("`{}`", head.join("`, `") + "` and `" + tail)
let (tail, head) = witnesses.split_last().unwrap();
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
format!("`{}` and `{}`", head.join("`, `"), tail)
},
_ => {
let (head, tail) = pattern_strings.split_at(LIMIT);
let (head, tail) = witnesses.split_at(LIMIT);
let head: Vec<_> = head.iter().map(|w| w.to_string()).collect();
format!("`{}` and {} more", head.join("`, `"), tail.len())
}
};

let label_text = match pattern_strings.len(){
let label_text = match witnesses.len() {
1 => format!("pattern {} not covered", joined_patterns),
_ => format!("patterns {} not covered", joined_patterns)
};
Expand Down
Loading

0 comments on commit c001b09

Please sign in to comment.