From c001b0940c1597f422f41c308a55cc6e22f48e46 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sun, 1 Jan 2017 20:57:21 +0200 Subject: [PATCH 1/2] rustc_const_eval: build Pattern instead of hir::Pat for pretty-printing. --- src/librustc_const_eval/_match.rs | 145 ++++++++++------------- src/librustc_const_eval/check_match.rs | 36 +++--- src/librustc_const_eval/pattern.rs | 155 ++++++++++++++++++++++++- 3 files changed, 231 insertions(+), 105 deletions(-) diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index ebe10349011..6d04975f533 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -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), + UsefulWithWitness(Vec>), 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 { - 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>); +#[derive(Clone)] +pub struct Witness<'tcx>(Vec>); -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 } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 01b19e1f539..1bb0667409a 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -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 @@ 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 +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()`, 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) }; diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index e93178c89c2..03baebd7901 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -14,12 +14,13 @@ 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::{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, From c6e130e89bb6e57ce492a9551da6828cce054422 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Sun, 1 Jan 2017 22:17:18 +0200 Subject: [PATCH 2/2] rustc_const_eval: convert constants to Pattern instead of hir::Pat. --- src/librustc_const_eval/check_match.rs | 7 - src/librustc_const_eval/eval.rs | 130 +------------- src/librustc_const_eval/pattern.rs | 231 +++++++++++++++++++------ 3 files changed, 180 insertions(+), 188 deletions(-) diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 1bb0667409a..53e83815b46 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -116,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"); diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index c736b4865e7..6b8e0e34c1d 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -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, 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::>()?, 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::>()?; - 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::>()?; - 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::>()?; - 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, diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index 03baebd7901..b122d97a702 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -10,12 +10,12 @@ 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, CtorKind}; -use rustc::hir::def_id::DefId; use rustc::hir::pat_util::EnumerateAndAdjustIterator; use rustc_data_structures::indexed_vec::Idx; @@ -28,7 +28,6 @@ use syntax_pos::Span; #[derive(Clone, Debug)] pub enum PatternError { StaticInPattern(Span), - BadConstInPattern(Span, DefId), ConstEval(eval::ConstEvalErr), } @@ -286,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, _) | @@ -600,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 {