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::{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 @@ 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 {
@ -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 @@ impl Witness {
-> 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 @@ 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,
@ -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(..) |
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;
@ -117,13 +116,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
fn report_inlining_errors(&self, patcx: PatternContext, pat_span: Span) {
for error in patcx.errors {
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) => {
span_err!(self.tcx.sess, span, E0158,
"statics cannot be referenced in patterns");
@ -230,9 +222,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",
@ -369,23 +359,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 +382,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

@ -18,7 +18,7 @@ use self::EvalHint::*;
use rustc::hir::map as ast_map;
use rustc::hir::map::blocks::FnLikeNode;
use rustc::traits;
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::def::Def;
use rustc::hir::def_id::DefId;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::util::IntTypeExt;
@ -26,16 +26,12 @@ use rustc::ty::subst::Substs;
use rustc::traits::Reveal;
use rustc::util::common::ErrorReported;
use rustc::util::nodemap::DefIdMap;
use rustc::lint;
use graphviz::IntoCow;
use syntax::ast;
use rustc::hir::{Expr, PatKind};
use rustc::hir;
use syntax::ptr::P;
use syntax::codemap;
use rustc::hir::{self, Expr};
use syntax::attr::IntType;
use syntax_pos::{self, Span};
use syntax_pos::Span;
use std::borrow::Cow;
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>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
err: &ConstEvalErr,

View File

@ -10,16 +10,17 @@
use eval;
use rustc::lint;
use rustc::middle::const_val::ConstVal;
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_id::DefId;
use rustc::hir::def::{Def, CtorKind};
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;
@ -27,7 +28,6 @@ use syntax_pos::Span;
#[derive(Clone, Debug)]
pub enum PatternError {
StaticInPattern(Span),
BadConstInPattern(Span, DefId),
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 tcx: TyCtxt<'a, 'gcx, 'tcx>,
pub errors: Vec<PatternError>,
@ -133,64 +285,20 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
let kind = match pat.node {
PatKind::Wild => PatternKind::Wild,
PatKind::Lit(ref 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::Lit(ref value) => self.lower_lit(value),
PatKind::Range(ref lo, ref hi) => {
let r_lo = eval::eval_const_expr_checked(self.tcx.global_tcx(), lo);
if let Err(ref e_lo) = r_lo {
self.errors.push(PatternError::ConstEval(e_lo.clone()));
}
let r_hi = eval::eval_const_expr_checked(self.tcx.global_tcx(), hi);
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
match (self.lower_lit(lo), self.lower_lit(hi)) {
(PatternKind::Constant { value: lo },
PatternKind::Constant { value: hi }) => {
PatternKind::Range { lo: lo, hi: hi }
}
_ => PatternKind::Wild
}
}
PatKind::Path(ref qpath) => {
let def = self.tcx.tables().qpath_def(qpath, pat.id);
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![])
}
return self.lower_path(qpath, pat.id, pat.id, pat.span);
}
PatKind::Ref(ref subpattern, _) |
@ -447,6 +555,174 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
_ => 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 {