rustc_const_eval: convert constants to Pattern instead of hir::Pat.

This commit is contained in:
Eduard-Mihai Burtescu 2017-01-01 22:17:18 +02:00
parent c001b0940c
commit c6e130e89b
3 changed files with 180 additions and 188 deletions

View File

@ -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");

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,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 {