rustc_const_eval: build Pattern instead of hir::Pat for pretty-printing.

This commit is contained in:
Eduard-Mihai Burtescu 2017-01-01 20:57:21 +02:00
parent e227433dc3
commit c001b0940c
3 changed files with 231 additions and 105 deletions

View File

@ -23,18 +23,12 @@
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;
@ -74,12 +68,6 @@ fn fold_pattern(&mut self, pat: &Pattern<'tcx>) -> Pattern<'tcx> {
}
}
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 {
@ -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,
@ -255,31 +252,17 @@ 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,
@ -287,7 +270,7 @@ fn push_wild_constructor<'a, '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)
}
@ -305,7 +288,7 @@ fn push_wild_constructor<'a, 'tcx>(
///
/// 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,
@ -318,60 +301,56 @@ fn apply_constructor<'a, 'tcx>(
let mut pats = self.0.drain(len-arity..).rev();
match ty.sty {
ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None),
ty::TyAdt(..) |
ty::TyTuple(..) => {
let pats = pats.enumerate().map(|(i, p)| {
FieldPattern {
field: Field::new(i),
pattern: p
}
}).collect();
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)
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::Fn => {
PatKind::TupleStruct(qpath, pats.collect(), None)
}
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
}
@ -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
};
@ -559,7 +538,7 @@ 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);
@ -567,7 +546,7 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
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() {
@ -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| {
@ -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
}

View File

@ -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;
@ -230,9 +229,7 @@ fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) {
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",
@ -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",
@ -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)
};

View File

@ -14,12 +14,13 @@
use rustc::mir::{Field, BorrowKind, Mutability};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region};
use rustc::hir::{self, PatKind};
use rustc::hir::def::Def;
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::def_id::DefId;
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
use rustc_data_structures::indexed_vec::Idx;
use std::fmt;
use syntax::ast;
use syntax::ptr::P;
use syntax_pos::Span;
@ -105,6 +106,158 @@ pub enum PatternKind<'tcx> {
},
}
fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result {
match *value {
ConstVal::Float(ref x) => write!(f, "{}", x),
ConstVal::Integral(ref i) => write!(f, "{}", i),
ConstVal::Str(ref s) => write!(f, "{:?}", &s[..]),
ConstVal::ByteStr(ref b) => write!(f, "{:?}", &b[..]),
ConstVal::Bool(b) => write!(f, "{:?}", b),
ConstVal::Char(c) => write!(f, "{:?}", c),
ConstVal::Struct(_) |
ConstVal::Tuple(_) |
ConstVal::Function(_) |
ConstVal::Array(..) |
ConstVal::Repeat(..) |
ConstVal::Dummy => bug!("{:?} not printable in a pattern", value)
}
}
impl<'tcx> fmt::Display for Pattern<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self.kind {
PatternKind::Wild => write!(f, "_"),
PatternKind::Binding { mutability, name, mode, ref subpattern, .. } => {
let is_mut = match mode {
BindingMode::ByValue => mutability == Mutability::Mut,
BindingMode::ByRef(_, bk) => {
write!(f, "ref ")?;
bk == BorrowKind::Mut
}
};
if is_mut {
write!(f, "mut ")?;
}
write!(f, "{}", name)?;
if let Some(ref subpattern) = *subpattern {
write!(f, " @ {}", subpattern)?;
}
Ok(())
}
PatternKind::Variant { ref subpatterns, .. } |
PatternKind::Leaf { ref subpatterns } => {
let variant = match *self.kind {
PatternKind::Variant { adt_def, variant_index, .. } => {
Some(&adt_def.variants[variant_index])
}
_ => if let ty::TyAdt(adt, _) = self.ty.sty {
Some(adt.struct_variant())
} else {
None
}
};
let mut first = true;
let mut start_or_continue = || if first { first = false; "" } else { ", " };
if let Some(variant) = variant {
write!(f, "{}", variant.name)?;
// Only for TyAdt we can have `S {...}`,
// which we handle separately here.
if variant.ctor_kind == CtorKind::Fictive {
write!(f, " {{ ")?;
let mut printed = 0;
for p in subpatterns {
if let PatternKind::Wild = *p.pattern.kind {
continue;
}
let name = variant.fields[p.field.index()].name;
write!(f, "{}{}: {}", start_or_continue(), name, p.pattern)?;
printed += 1;
}
if printed < variant.fields.len() {
write!(f, "{}..", start_or_continue())?;
}
return write!(f, " }}");
}
}
let num_fields = variant.map_or(subpatterns.len(), |v| v.fields.len());
if num_fields != 0 || variant.is_none() {
write!(f, "(")?;
for i in 0..num_fields {
write!(f, "{}", start_or_continue())?;
// Common case: the field is where we expect it.
if let Some(p) = subpatterns.get(i) {
if p.field.index() == i {
write!(f, "{}", p.pattern)?;
continue;
}
}
// Otherwise, we have to go looking for it.
if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
write!(f, "{}", p.pattern)?;
} else {
write!(f, "_")?;
}
}
write!(f, ")")?;
}
Ok(())
}
PatternKind::Deref { ref subpattern } => {
match self.ty.sty {
ty::TyBox(_) => write!(f, "box ")?,
ty::TyRef(_, mt) => {
write!(f, "&")?;
if mt.mutbl == hir::MutMutable {
write!(f, "mut ")?;
}
}
_ => bug!("{} is a bad Deref pattern type", self.ty)
}
write!(f, "{}", subpattern)
}
PatternKind::Constant { ref value } => {
print_const_val(value, f)
}
PatternKind::Range { ref lo, ref hi } => {
print_const_val(lo, f)?;
write!(f, "...")?;
print_const_val(hi, f)
}
PatternKind::Slice { ref prefix, ref slice, ref suffix } |
PatternKind::Array { ref prefix, ref slice, ref suffix } => {
let mut first = true;
let mut start_or_continue = || if first { first = false; "" } else { ", " };
write!(f, "[")?;
for p in prefix {
write!(f, "{}{}", start_or_continue(), p)?;
}
if let Some(ref slice) = *slice {
write!(f, "{}", start_or_continue())?;
match *slice.kind {
PatternKind::Wild => {}
_ => write!(f, "{}", slice)?
}
write!(f, "..")?;
}
for p in suffix {
write!(f, "{}{}", start_or_continue(), p)?;
}
write!(f, "]")
}
}
}
}
pub struct PatternContext<'a, 'gcx: 'tcx, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
pub errors: Vec<PatternError>,