Auto merge of #38766 - eddyb:less-fake-hir, r=arielb1

Stop creating fake HIR pattern nodes.

This replaces all the HIR patterns `rustc_const_eval` creates with the more appropriate HAIR equivalent.

The only place left that creates HIR nodes is the "explicit lifetimes in function signature" suggestion, which only creates type nodes while rebuilding the signature, but that is only in case of an error.

cc @arielb1
This commit is contained in:
bors 2017-01-02 13:51:43 +00:00
commit df61658c8a
4 changed files with 411 additions and 293 deletions

View File

@ -23,18 +23,12 @@ use rustc_data_structures::indexed_vec::Idx;
use pattern::{FieldPattern, Pattern, PatternKind}; use pattern::{FieldPattern, Pattern, PatternKind};
use pattern::{PatternFoldable, PatternFolder}; use pattern::{PatternFoldable, PatternFolder};
use rustc::hir::def::Def;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::hir; use rustc::mir::Field;
use rustc::hir::def::CtorKind;
use rustc::hir::{Pat, PatKind};
use rustc::util::common::ErrorReported; 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 syntax_pos::{Span, DUMMY_SP};
use arena::TypedArena; use arena::TypedArena;
@ -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> { impl<'tcx> Pattern<'tcx> {
fn is_wildcard(&self) -> bool { fn is_wildcard(&self) -> bool {
match *self.kind { match *self.kind {
@ -224,25 +212,34 @@ pub enum Constructor {
} }
impl<'tcx> 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 { match self {
&Variant(vid) => adt.variant_with_id(vid), &Variant(vid) => adt.variant_index_with_id(vid),
&Single => { &Single => {
assert_eq!(adt.variants.len(), 1); assert_eq!(adt.variants.len(), 1);
&adt.variants[0] 0
} }
_ => bug!("bad constructor {:?} for adt {:?}", self, adt) _ => bug!("bad constructor {:?} for adt {:?}", self, adt)
} }
} }
} }
#[derive(Clone, PartialEq)] #[derive(Clone)]
pub enum Usefulness { pub enum Usefulness<'tcx> {
Useful, Useful,
UsefulWithWitness(Vec<Witness>), UsefulWithWitness(Vec<Witness<'tcx>>),
NotUseful NotUseful
} }
impl<'tcx> Usefulness<'tcx> {
fn is_useful(&self) -> bool {
match *self {
NotUseful => false,
_ => true
}
}
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum WitnessPreference { pub enum WitnessPreference {
ConstructWitness, ConstructWitness,
@ -255,31 +252,17 @@ struct PatternContext<'tcx> {
max_slice_length: usize, 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 /// A stack of patterns in reverse order of construction
#[derive(Clone, PartialEq, Eq)] #[derive(Clone)]
pub struct Witness(Vec<P<Pat>>); pub struct Witness<'tcx>(Vec<Pattern<'tcx>>);
impl Witness { impl<'tcx> Witness<'tcx> {
pub fn single_pattern(&self) -> &Pat { pub fn single_pattern(&self) -> &Pattern<'tcx> {
assert_eq!(self.0.len(), 1); assert_eq!(self.0.len(), 1);
&self.0[0] &self.0[0]
} }
fn push_wild_constructor<'a, 'tcx>( fn push_wild_constructor<'a>(
mut self, mut self,
cx: &MatchCheckCtxt<'a, 'tcx>, cx: &MatchCheckCtxt<'a, 'tcx>,
ctor: &Constructor, ctor: &Constructor,
@ -287,7 +270,7 @@ impl Witness {
-> Self -> Self
{ {
let arity = constructor_arity(cx, ctor, ty); 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) self.apply_constructor(cx, ctor, ty)
} }
@ -305,7 +288,7 @@ impl Witness {
/// ///
/// left_ty: struct X { a: (bool, &'static str), b: usize} /// left_ty: struct X { a: (bool, &'static str), b: usize}
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
fn apply_constructor<'a, 'tcx>( fn apply_constructor<'a>(
mut self, mut self,
cx: &MatchCheckCtxt<'a,'tcx>, cx: &MatchCheckCtxt<'a,'tcx>,
ctor: &Constructor, ctor: &Constructor,
@ -318,60 +301,56 @@ impl Witness {
let mut pats = self.0.drain(len-arity..).rev(); let mut pats = self.0.drain(len-arity..).rev();
match ty.sty { 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, _) => { if let ty::TyAdt(adt, _) = ty.sty {
let v = ctor.variant_for_adt(adt); if adt.variants.len() > 1 {
let qpath = hir::QPath::Resolved(None, P(hir::Path { PatternKind::Variant {
span: DUMMY_SP, adt_def: adt,
def: Def::Err, variant_index: ctor.variant_index_for_adt(adt),
segments: vec![hir::PathSegment::from_name(v.name)].into(), subpatterns: pats
})); }
match v.ctor_kind { } else {
CtorKind::Fictive => { PatternKind::Leaf { subpatterns: pats }
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)
} }
CtorKind::Fn => { } else {
PatKind::TupleStruct(qpath, pats.collect(), None) PatternKind::Leaf { subpatterns: pats }
}
CtorKind::Const => PatKind::Path(qpath)
} }
} }
ty::TyRef(_, ty::TypeAndMut { mutbl, .. }) => { ty::TyRef(..) => {
PatKind::Ref(pats.nth(0).unwrap(), mutbl) PatternKind::Deref { subpattern: pats.nth(0).unwrap() }
} }
ty::TySlice(_) | ty::TyArray(..) => { ty::TySlice(_) | ty::TyArray(..) => {
PatKind::Slice(pats.collect(), None, hir::HirVec::new()) PatternKind::Slice {
prefix: pats.collect(),
slice: None,
suffix: vec![]
}
} }
_ => { _ => {
match *ctor { match *ctor {
ConstantValue(ref v) => PatKind::Lit(const_val_to_expr(v)), ConstantValue(ref v) => PatternKind::Constant { value: v.clone() },
_ => PatKind::Wild, _ => PatternKind::Wild,
} }
} }
} }
}; };
self.0.push(P(hir::Pat { self.0.push(Pattern {
id: DUMMY_NODE_ID, ty: ty,
node: pat, span: DUMMY_SP,
span: DUMMY_SP kind: Box::new(pat),
})); });
self self
} }
@ -528,13 +507,13 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix<'a, 'tcx>, matrix: &Matrix<'a, 'tcx>,
v: &[&'a Pattern<'tcx>], v: &[&'a Pattern<'tcx>],
witness: WitnessPreference) witness: WitnessPreference)
-> Usefulness { -> Usefulness<'tcx> {
let &Matrix(ref rows) = matrix; let &Matrix(ref rows) = matrix;
debug!("is_useful({:?}, {:?})", matrix, v); debug!("is_useful({:?}, {:?})", matrix, v);
if rows.is_empty() { if rows.is_empty() {
return match witness { return match witness {
ConstructWitness => UsefulWithWitness(vec![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 LeaveOutWitness => Useful
}; };
@ -559,7 +538,7 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
debug!("is_useful - expanding constructors: {:?}", constructors); debug!("is_useful - expanding constructors: {:?}", constructors);
constructors.into_iter().map(|c| constructors.into_iter().map(|c|
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) 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 { } else {
debug!("is_useful - expanding wildcard"); debug!("is_useful - expanding wildcard");
let constructors = missing_constructors(cx, matrix, pcx); 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() { if constructors.is_empty() {
all_constructors(cx, pcx).into_iter().map(|c| { all_constructors(cx, pcx).into_iter().map(|c| {
is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) 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 { } else {
let matrix = rows.iter().filter_map(|r| { let matrix = rows.iter().filter_map(|r| {
if r[0].is_wildcard() { if r[0].is_wildcard() {
@ -597,7 +576,7 @@ fn is_useful_specialized<'a, 'tcx>(
v: &[&'a Pattern<'tcx>], v: &[&'a Pattern<'tcx>],
ctor: Constructor, ctor: Constructor,
lty: Ty<'tcx>, lty: Ty<'tcx>,
witness: WitnessPreference) -> Usefulness witness: WitnessPreference) -> Usefulness<'tcx>
{ {
let arity = constructor_arity(cx, &ctor, lty); let arity = constructor_arity(cx, &ctor, lty);
let matrix = Matrix(m.iter().flat_map(|r| { 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::TyRef(..) => 1,
ty::TyAdt(adt, _) => { ty::TyAdt(adt, _) => {
ctor.variant_for_adt(adt).fields.len() adt.variants[ctor.variant_index_for_adt(adt)].fields.len()
} }
_ => 0 _ => 0
} }

View File

@ -9,11 +9,10 @@
// except according to those terms. // except according to those terms.
use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; use _match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful};
use _match::{DUMMY_WILD_PAT};
use _match::Usefulness::*; use _match::Usefulness::*;
use _match::WitnessPreference::*; use _match::WitnessPreference::*;
use pattern::{Pattern, PatternContext, PatternError}; use pattern::{Pattern, PatternContext, PatternError, PatternKind};
use eval::report_const_eval_err; use eval::report_const_eval_err;
@ -117,13 +116,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
fn report_inlining_errors(&self, patcx: PatternContext, pat_span: Span) { fn report_inlining_errors(&self, patcx: PatternContext, pat_span: Span) {
for error in patcx.errors { for error in patcx.errors {
match error { match error {
PatternError::BadConstInPattern(span, def_id) => {
self.tcx.sess.span_err(
span,
&format!("constants of the type `{}` \
cannot be used in patterns",
self.tcx.item_path_str(def_id)));
}
PatternError::StaticInPattern(span) => { PatternError::StaticInPattern(span) => {
span_err!(self.tcx.sess, span, E0158, span_err!(self.tcx.sess, span, E0158,
"statics cannot be referenced in patterns"); "statics cannot be referenced in patterns");
@ -230,9 +222,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
Useful => bug!() Useful => bug!()
}; };
let pattern_string = hir::print::to_string(&self.tcx.map, |s| { let pattern_string = witness[0].single_pattern().to_string();
s.print_pat(witness[0].single_pattern())
});
let mut diag = struct_span_err!( let mut diag = struct_span_err!(
self.tcx.sess, pat.span, E0005, self.tcx.sess, pat.span, E0005,
"refutable pattern in {}: `{}` not covered", "refutable pattern in {}: `{}` not covered",
@ -369,23 +359,21 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) { match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) {
UsefulWithWitness(pats) => { UsefulWithWitness(pats) => {
let witnesses = if pats.is_empty() { let witnesses = if pats.is_empty() {
vec![DUMMY_WILD_PAT] vec![cx.wild_pattern]
} else { } else {
pats.iter().map(|w| w.single_pattern()).collect() pats.iter().map(|w| w.single_pattern()).collect()
}; };
match source { match source {
hir::MatchSource::ForLoopDesugar => { hir::MatchSource::ForLoopDesugar => {
// `witnesses[0]` has the form `Some(<head>)`, peel off the `Some` // `witnesses[0]` has the form `Some(<head>)`, peel off the `Some`
let witness = match witnesses[0].node { let witness = match *witnesses[0].kind {
PatKind::TupleStruct(_, ref pats, _) => match &pats[..] { PatternKind::Variant { ref subpatterns, .. } => match &subpatterns[..] {
&[ref pat] => &**pat, &[ref pat] => &pat.pattern,
_ => bug!(), _ => bug!(),
}, },
_ => bug!(), _ => bug!(),
}; };
let pattern_string = hir::print::to_string(&cx.tcx.map, |s| { let pattern_string = witness.to_string();
s.print_pat(witness)
});
struct_span_err!(cx.tcx.sess, sp, E0297, struct_span_err!(cx.tcx.sess, sp, E0297,
"refutable pattern in `for` loop binding: \ "refutable pattern in `for` loop binding: \
`{}` not covered", `{}` not covered",
@ -394,24 +382,23 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
.emit(); .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; const LIMIT: usize = 3;
let joined_patterns = match pattern_strings.len() { let joined_patterns = match witnesses.len() {
0 => bug!(), 0 => bug!(),
1 => format!("`{}`", pattern_strings[0]), 1 => format!("`{}`", witnesses[0]),
2...LIMIT => { 2...LIMIT => {
let (tail, head) = pattern_strings.split_last().unwrap(); let (tail, head) = witnesses.split_last().unwrap();
format!("`{}`", head.join("`, `") + "` and `" + tail) 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()) 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), 1 => format!("pattern {} not covered", joined_patterns),
_ => format!("patterns {} not covered", joined_patterns) _ => format!("patterns {} not covered", joined_patterns)
}; };

View File

@ -18,7 +18,7 @@ use self::EvalHint::*;
use rustc::hir::map as ast_map; use rustc::hir::map as ast_map;
use rustc::hir::map::blocks::FnLikeNode; use rustc::hir::map::blocks::FnLikeNode;
use rustc::traits; use rustc::traits;
use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def::Def;
use rustc::hir::def_id::DefId; use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::util::IntTypeExt; use rustc::ty::util::IntTypeExt;
@ -26,16 +26,12 @@ use rustc::ty::subst::Substs;
use rustc::traits::Reveal; use rustc::traits::Reveal;
use rustc::util::common::ErrorReported; use rustc::util::common::ErrorReported;
use rustc::util::nodemap::DefIdMap; use rustc::util::nodemap::DefIdMap;
use rustc::lint;
use graphviz::IntoCow; use graphviz::IntoCow;
use syntax::ast; use syntax::ast;
use rustc::hir::{Expr, PatKind}; use rustc::hir::{self, Expr};
use rustc::hir;
use syntax::ptr::P;
use syntax::codemap;
use syntax::attr::IntType; use syntax::attr::IntType;
use syntax_pos::{self, Span}; use syntax_pos::Span;
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -186,126 +182,6 @@ fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
} }
} }
pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
expr: &Expr,
pat_id: ast::NodeId,
span: Span)
-> Result<P<hir::Pat>, DefId> {
let pat_ty = tcx.tables().expr_ty(expr);
debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id);
match pat_ty.sty {
ty::TyFloat(_) => {
tcx.sess.add_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN,
pat_id,
span,
format!("floating point constants cannot be used in patterns"));
}
ty::TyAdt(adt_def, _) if adt_def.is_union() => {
// Matching on union fields is unsafe, we can't hide it in constants
tcx.sess.span_err(span, "cannot use unions in constant patterns");
}
ty::TyAdt(adt_def, _) => {
if !tcx.has_attr(adt_def.did, "structural_match") {
tcx.sess.add_lint(
lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN,
pat_id,
span,
format!("to use a constant of type `{}` \
in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
tcx.item_path_str(adt_def.did),
tcx.item_path_str(adt_def.did)));
}
}
_ => { }
}
let pat = match expr.node {
hir::ExprTup(ref exprs) =>
PatKind::Tuple(exprs.iter()
.map(|expr| const_expr_to_pat(tcx, &expr, pat_id, span))
.collect::<Result<_, _>>()?, None),
hir::ExprCall(ref callee, ref args) => {
let qpath = match callee.node {
hir::ExprPath(ref qpath) => qpath,
_ => bug!()
};
let def = tcx.tables().qpath_def(qpath, callee.id);
let ctor_path = if let hir::QPath::Resolved(_, ref path) = *qpath {
match def {
Def::StructCtor(_, CtorKind::Fn) |
Def::VariantCtor(_, CtorKind::Fn) => {
Some(path.clone())
}
_ => None
}
} else {
None
};
match (def, ctor_path) {
(Def::Fn(..), None) | (Def::Method(..), None) => {
PatKind::Lit(P(expr.clone()))
}
(_, Some(ctor_path)) => {
let pats = args.iter()
.map(|expr| const_expr_to_pat(tcx, expr, pat_id, span))
.collect::<Result<_, _>>()?;
PatKind::TupleStruct(hir::QPath::Resolved(None, ctor_path), pats, None)
}
_ => bug!()
}
}
hir::ExprStruct(ref qpath, ref fields, None) => {
let field_pats =
fields.iter()
.map(|field| Ok(codemap::Spanned {
span: syntax_pos::DUMMY_SP,
node: hir::FieldPat {
name: field.name.node,
pat: const_expr_to_pat(tcx, &field.expr, pat_id, span)?,
is_shorthand: false,
},
}))
.collect::<Result<_, _>>()?;
PatKind::Struct(qpath.clone(), field_pats, false)
}
hir::ExprArray(ref exprs) => {
let pats = exprs.iter()
.map(|expr| const_expr_to_pat(tcx, &expr, pat_id, span))
.collect::<Result<_, _>>()?;
PatKind::Slice(pats, None, hir::HirVec::new())
}
hir::ExprPath(ref qpath) => {
let def = tcx.tables().qpath_def(qpath, expr.id);
match def {
Def::StructCtor(_, CtorKind::Const) |
Def::VariantCtor(_, CtorKind::Const) => {
match expr.node {
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
PatKind::Path(hir::QPath::Resolved(None, path.clone()))
}
_ => bug!()
}
}
Def::Const(def_id) | Def::AssociatedConst(def_id) => {
let substs = Some(tcx.tables().node_id_item_substs(expr.id)
.unwrap_or_else(|| tcx.intern_substs(&[])));
let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap();
return const_expr_to_pat(tcx, expr, pat_id, span);
},
_ => bug!(),
}
}
_ => PatKind::Lit(P(expr.clone()))
};
Ok(P(hir::Pat { id: expr.id, node: pat, span: span }))
}
pub fn report_const_eval_err<'a, 'tcx>( pub fn report_const_eval_err<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>,
err: &ConstEvalErr, err: &ConstEvalErr,

View File

@ -10,16 +10,17 @@
use eval; use eval;
use rustc::lint;
use rustc::middle::const_val::ConstVal; use rustc::middle::const_val::ConstVal;
use rustc::mir::{Field, BorrowKind, Mutability}; use rustc::mir::{Field, BorrowKind, Mutability};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region};
use rustc::hir::{self, PatKind}; 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::hir::pat_util::EnumerateAndAdjustIterator;
use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::indexed_vec::Idx;
use std::fmt;
use syntax::ast; use syntax::ast;
use syntax::ptr::P; use syntax::ptr::P;
use syntax_pos::Span; use syntax_pos::Span;
@ -27,7 +28,6 @@ use syntax_pos::Span;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum PatternError { pub enum PatternError {
StaticInPattern(Span), StaticInPattern(Span),
BadConstInPattern(Span, DefId),
ConstEval(eval::ConstEvalErr), ConstEval(eval::ConstEvalErr),
} }
@ -105,6 +105,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 struct PatternContext<'a, 'gcx: 'tcx, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'gcx, 'tcx>, pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
pub errors: Vec<PatternError>, pub errors: Vec<PatternError>,
@ -133,64 +285,20 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
let kind = match pat.node { let kind = match pat.node {
PatKind::Wild => PatternKind::Wild, PatKind::Wild => PatternKind::Wild,
PatKind::Lit(ref value) => { PatKind::Lit(ref value) => self.lower_lit(value),
match eval::eval_const_expr_checked(self.tcx.global_tcx(), value) {
Ok(value) => {
PatternKind::Constant { value: value }
}
Err(e) => {
self.errors.push(PatternError::ConstEval(e));
PatternKind::Wild
}
}
}
PatKind::Range(ref lo, ref hi) => { PatKind::Range(ref lo, ref hi) => {
let r_lo = eval::eval_const_expr_checked(self.tcx.global_tcx(), lo); match (self.lower_lit(lo), self.lower_lit(hi)) {
if let Err(ref e_lo) = r_lo { (PatternKind::Constant { value: lo },
self.errors.push(PatternError::ConstEval(e_lo.clone())); PatternKind::Constant { value: hi }) => {
} PatternKind::Range { lo: lo, hi: hi }
}
let r_hi = eval::eval_const_expr_checked(self.tcx.global_tcx(), hi); _ => PatternKind::Wild
if let Err(ref e_hi) = r_hi {
self.errors.push(PatternError::ConstEval(e_hi.clone()));
}
if let (Ok(lo), Ok(hi)) = (r_lo, r_hi) {
PatternKind::Range { lo: lo, hi: hi }
} else {
PatternKind::Wild
} }
} }
PatKind::Path(ref qpath) => { PatKind::Path(ref qpath) => {
let def = self.tcx.tables().qpath_def(qpath, pat.id); return self.lower_path(qpath, pat.id, pat.id, pat.span);
match def {
Def::Const(def_id) | Def::AssociatedConst(def_id) => {
let tcx = self.tcx.global_tcx();
let substs = tcx.tables().node_id_item_substs(pat.id)
.unwrap_or_else(|| tcx.intern_substs(&[]));
match eval::lookup_const_by_id(tcx, def_id, Some(substs)) {
Some((const_expr, _const_ty)) => {
match eval::const_expr_to_pat(
tcx, const_expr, pat.id, pat.span)
{
Ok(pat) => return self.lower_pattern(&pat),
Err(_) => {
self.errors.push(PatternError::BadConstInPattern(
pat.span, def_id));
PatternKind::Wild
}
}
}
None => {
self.errors.push(PatternError::StaticInPattern(pat.span));
PatternKind::Wild
}
}
}
_ => self.lower_variant_or_leaf(def, vec![])
}
} }
PatKind::Ref(ref subpattern, _) | PatKind::Ref(ref subpattern, _) |
@ -447,6 +555,174 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
_ => bug!() _ => bug!()
} }
} }
fn lower_path(&mut self,
qpath: &hir::QPath,
id: ast::NodeId,
pat_id: ast::NodeId,
span: Span)
-> Pattern<'tcx> {
let def = self.tcx.tables().qpath_def(qpath, id);
let kind = match def {
Def::Const(def_id) | Def::AssociatedConst(def_id) => {
let tcx = self.tcx.global_tcx();
let substs = tcx.tables().node_id_item_substs(id)
.unwrap_or_else(|| tcx.intern_substs(&[]));
match eval::lookup_const_by_id(tcx, def_id, Some(substs)) {
Some((const_expr, _const_ty)) => {
return self.lower_const_expr(const_expr, pat_id, span);
}
None => {
self.errors.push(PatternError::StaticInPattern(span));
PatternKind::Wild
}
}
}
_ => self.lower_variant_or_leaf(def, vec![])
};
Pattern {
span: span,
ty: self.tcx.tables().node_id_to_type(id),
kind: Box::new(kind),
}
}
fn lower_lit(&mut self, expr: &hir::Expr) -> PatternKind<'tcx> {
match eval::eval_const_expr_checked(self.tcx.global_tcx(), expr) {
Ok(value) => {
PatternKind::Constant { value: value }
}
Err(e) => {
self.errors.push(PatternError::ConstEval(e));
PatternKind::Wild
}
}
}
fn lower_const_expr(&mut self,
expr: &hir::Expr,
pat_id: ast::NodeId,
span: Span)
-> Pattern<'tcx> {
let pat_ty = self.tcx.tables().expr_ty(expr);
debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id);
match pat_ty.sty {
ty::TyFloat(_) => {
self.tcx.sess.add_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN,
pat_id,
span,
format!("floating point constants cannot be used in patterns"));
}
ty::TyAdt(adt_def, _) if adt_def.is_union() => {
// Matching on union fields is unsafe, we can't hide it in constants
self.tcx.sess.span_err(span, "cannot use unions in constant patterns");
}
ty::TyAdt(adt_def, _) => {
if !self.tcx.has_attr(adt_def.did, "structural_match") {
self.tcx.sess.add_lint(
lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN,
pat_id,
span,
format!("to use a constant of type `{}` \
in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
self.tcx.item_path_str(adt_def.did),
self.tcx.item_path_str(adt_def.did)));
}
}
_ => { }
}
let kind = match expr.node {
hir::ExprTup(ref exprs) => {
PatternKind::Leaf {
subpatterns: exprs.iter().enumerate().map(|(i, expr)| {
FieldPattern {
field: Field::new(i),
pattern: self.lower_const_expr(expr, pat_id, span)
}
}).collect()
}
}
hir::ExprCall(ref callee, ref args) => {
let qpath = match callee.node {
hir::ExprPath(ref qpath) => qpath,
_ => bug!()
};
let def = self.tcx.tables().qpath_def(qpath, callee.id);
match def {
Def::Fn(..) | Def::Method(..) => self.lower_lit(expr),
_ => {
let subpatterns = args.iter().enumerate().map(|(i, expr)| {
FieldPattern {
field: Field::new(i),
pattern: self.lower_const_expr(expr, pat_id, span)
}
}).collect();
self.lower_variant_or_leaf(def, subpatterns)
}
}
}
hir::ExprStruct(ref qpath, ref fields, None) => {
let def = self.tcx.tables().qpath_def(qpath, expr.id);
let pat_ty = self.tcx.tables().node_id_to_type(expr.id);
let adt_def = match pat_ty.sty {
ty::TyAdt(adt_def, _) => adt_def,
_ => {
span_bug!(
expr.span,
"struct expr without ADT type");
}
};
let variant_def = adt_def.variant_of_def(def);
let subpatterns =
fields.iter()
.map(|field| {
let index = variant_def.index_of_field_named(field.name.node);
let index = index.unwrap_or_else(|| {
span_bug!(
expr.span,
"no field with name {:?}",
field.name);
});
FieldPattern {
field: Field::new(index),
pattern: self.lower_const_expr(&field.expr, pat_id, span),
}
})
.collect();
self.lower_variant_or_leaf(def, subpatterns)
}
hir::ExprArray(ref exprs) => {
let pats = exprs.iter()
.map(|expr| self.lower_const_expr(expr, pat_id, span))
.collect();
PatternKind::Array {
prefix: pats,
slice: None,
suffix: vec![]
}
}
hir::ExprPath(ref qpath) => {
return self.lower_path(qpath, expr.id, pat_id, span);
}
_ => self.lower_lit(expr)
};
Pattern {
span: span,
ty: pat_ty,
kind: Box::new(kind),
}
}
} }
pub trait PatternFoldable<'tcx> : Sized { pub trait PatternFoldable<'tcx> : Sized {