Auto merge of #28748 - nikomatsakis:universal-mir, r=pnkfelix
I had to fix a few things. Notable changes: 1. I removed the MIR support for constants, instead falling back to the existing `ConstVal`. I still think we ought to reform how we handle constants, but it's not clear to me that the approach I was taking is correct, and anyway I think we ought to do it separately. 2. I adjusted how we handle bindings in matches: we now *declare* all the bindings up front, rather than doing it as we encounter them. This is not only simpler, since we don't have to check if a binding has already been declared, it avoids ICEs if any of the arms turn out to be unreachable. 3. I do MIR construction *after* `check_match`, because it detects various broken cases. I'd like for `check_match` to be subsumed by MIR construction, but we can do that as a separate PR (if indeed it makes sense). I did a crater run and found no regressions in the wild: https://gist.github.com/nikomatsakis/0038f90e10c8ad00f2f8
This commit is contained in:
commit
c298efdb1f
@ -38,6 +38,7 @@ use std::borrow::{Cow, IntoCow};
|
||||
use std::num::wrapping::OverflowingOps;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::Entry::Vacant;
|
||||
use std::mem::transmute;
|
||||
use std::{i8, i16, i32, i64, u8, u16, u32, u64};
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -242,7 +243,7 @@ pub fn lookup_const_fn_by_id<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: DefId)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ConstVal {
|
||||
Float(f64),
|
||||
Int(i64),
|
||||
@ -254,6 +255,27 @@ pub enum ConstVal {
|
||||
Tuple(ast::NodeId),
|
||||
}
|
||||
|
||||
/// Note that equality for `ConstVal` means that the it is the same
|
||||
/// constant, not that the rust values are equal. In particular, `NaN
|
||||
/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
|
||||
/// are considering unequal).
|
||||
impl PartialEq for ConstVal {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn eq(&self, other: &ConstVal) -> bool {
|
||||
match (self, other) {
|
||||
(&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)},
|
||||
(&Int(a), &Int(b)) => a == b,
|
||||
(&Uint(a), &Uint(b)) => a == b,
|
||||
(&Str(ref a), &Str(ref b)) => a == b,
|
||||
(&ByteStr(ref a), &ByteStr(ref b)) => a == b,
|
||||
(&Bool(a), &Bool(b)) => a == b,
|
||||
(&Struct(a), &Struct(b)) => a == b,
|
||||
(&Tuple(a), &Tuple(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstVal {
|
||||
pub fn description(&self) -> &'static str {
|
||||
match *self {
|
||||
|
@ -718,9 +718,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session,
|
||||
// passes are timed inside typeck
|
||||
typeck::check_crate(tcx, trait_map);
|
||||
|
||||
time(time_passes, "MIR dump", ||
|
||||
mir::dump::dump_crate(tcx));
|
||||
|
||||
time(time_passes, "const checking", ||
|
||||
middle::check_const::check_crate(tcx));
|
||||
|
||||
@ -741,6 +738,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: Session,
|
||||
time(time_passes, "match checking", ||
|
||||
middle::check_match::check_crate(tcx));
|
||||
|
||||
time(time_passes, "MIR dump", ||
|
||||
mir::dump::dump_crate(tcx));
|
||||
|
||||
time(time_passes, "liveness checking", ||
|
||||
middle::liveness::check_crate(tcx));
|
||||
|
||||
|
@ -10,8 +10,6 @@
|
||||
|
||||
//! See docs in build/expr/mod.rs
|
||||
|
||||
use rustc_data_structures::fnv::FnvHashMap;
|
||||
|
||||
use build::{Builder};
|
||||
use hair::*;
|
||||
use repr::*;
|
||||
@ -28,93 +26,16 @@ impl<H:Hair> Builder<H> {
|
||||
|
||||
fn expr_as_constant(&mut self, expr: Expr<H>) -> Constant<H> {
|
||||
let this = self;
|
||||
let Expr { ty: _, temp_lifetime: _, span, kind } = expr;
|
||||
let kind = match kind {
|
||||
ExprKind::Scope { extent: _, value } => {
|
||||
return this.as_constant(value);
|
||||
}
|
||||
ExprKind::Literal { literal } => {
|
||||
ConstantKind::Literal(literal)
|
||||
}
|
||||
ExprKind::Vec { fields } => {
|
||||
let fields = this.as_constants(fields);
|
||||
ConstantKind::Aggregate(AggregateKind::Vec, fields)
|
||||
}
|
||||
ExprKind::Tuple { fields } => {
|
||||
let fields = this.as_constants(fields);
|
||||
ConstantKind::Aggregate(AggregateKind::Tuple, fields)
|
||||
}
|
||||
ExprKind::Adt { adt_def, variant_index, substs, fields, base: None } => {
|
||||
let field_names = this.hir.fields(adt_def, variant_index);
|
||||
let fields = this.named_field_constants(field_names, fields);
|
||||
ConstantKind::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs), fields)
|
||||
}
|
||||
ExprKind::Repeat { value, count } => {
|
||||
let value = Box::new(this.as_constant(value));
|
||||
let count = Box::new(this.as_constant(count));
|
||||
ConstantKind::Repeat(value, count)
|
||||
}
|
||||
ExprKind::Binary { op, lhs, rhs } => {
|
||||
let lhs = Box::new(this.as_constant(lhs));
|
||||
let rhs = Box::new(this.as_constant(rhs));
|
||||
ConstantKind::BinaryOp(op, lhs, rhs)
|
||||
}
|
||||
ExprKind::Unary { op, arg } => {
|
||||
let arg = Box::new(this.as_constant(arg));
|
||||
ConstantKind::UnaryOp(op, arg)
|
||||
}
|
||||
ExprKind::Field { lhs, name } => {
|
||||
let lhs = this.as_constant(lhs);
|
||||
ConstantKind::Projection(
|
||||
Box::new(ConstantProjection {
|
||||
base: lhs,
|
||||
elem: ProjectionElem::Field(name),
|
||||
}))
|
||||
}
|
||||
ExprKind::Deref { arg } => {
|
||||
let arg = this.as_constant(arg);
|
||||
ConstantKind::Projection(
|
||||
Box::new(ConstantProjection {
|
||||
base: arg,
|
||||
elem: ProjectionElem::Deref,
|
||||
}))
|
||||
}
|
||||
ExprKind::Call { fun, args } => {
|
||||
let fun = this.as_constant(fun);
|
||||
let args = this.as_constants(args);
|
||||
ConstantKind::Call(Box::new(fun), args)
|
||||
}
|
||||
_ => {
|
||||
let Expr { ty, temp_lifetime: _, span, kind } = expr;
|
||||
match kind {
|
||||
ExprKind::Scope { extent: _, value } =>
|
||||
this.as_constant(value),
|
||||
ExprKind::Literal { literal } =>
|
||||
Constant { span: span, ty: ty, literal: literal },
|
||||
_ =>
|
||||
this.hir.span_bug(
|
||||
span,
|
||||
&format!("expression is not a valid constant {:?}", kind));
|
||||
}
|
||||
};
|
||||
Constant { span: span, kind: kind }
|
||||
}
|
||||
|
||||
fn as_constants(&mut self,
|
||||
exprs: Vec<ExprRef<H>>)
|
||||
-> Vec<Constant<H>>
|
||||
{
|
||||
exprs.into_iter().map(|expr| self.as_constant(expr)).collect()
|
||||
}
|
||||
|
||||
fn named_field_constants(&mut self,
|
||||
field_names: Vec<Field<H>>,
|
||||
field_exprs: Vec<FieldExprRef<H>>)
|
||||
-> Vec<Constant<H>>
|
||||
{
|
||||
let fields_map: FnvHashMap<_, _> =
|
||||
field_exprs.into_iter()
|
||||
.map(|f| (f.name, self.as_constant(f.expr)))
|
||||
.collect();
|
||||
|
||||
let fields: Vec<_> =
|
||||
field_names.into_iter()
|
||||
.map(|n| fields_map[&n].clone())
|
||||
.collect();
|
||||
|
||||
fields
|
||||
&format!("expression is not a valid constant {:?}", kind)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,14 +99,16 @@ impl<H:Hair> Builder<H> {
|
||||
true_block, expr_span, destination,
|
||||
Constant {
|
||||
span: expr_span,
|
||||
kind: ConstantKind::Literal(Literal::Bool { value: true }),
|
||||
ty: this.hir.bool_ty(),
|
||||
literal: this.hir.true_literal(),
|
||||
});
|
||||
|
||||
this.cfg.push_assign_constant(
|
||||
false_block, expr_span, destination,
|
||||
Constant {
|
||||
span: expr_span,
|
||||
kind: ConstantKind::Literal(Literal::Bool { value: false }),
|
||||
ty: this.hir.bool_ty(),
|
||||
literal: this.hir.false_literal(),
|
||||
});
|
||||
|
||||
this.cfg.terminate(true_block, Terminator::Goto { target: join_block });
|
||||
|
@ -34,10 +34,20 @@ impl<H:Hair> Builder<H> {
|
||||
let discriminant_lvalue =
|
||||
unpack!(block = self.as_lvalue(block, discriminant));
|
||||
|
||||
let arm_blocks: Vec<BasicBlock> =
|
||||
arms.iter()
|
||||
.map(|_| self.cfg.start_new_block())
|
||||
.collect();
|
||||
// Before we do anything, create uninitialized variables with
|
||||
// suitable extent for all of the bindings in this match. It's
|
||||
// easiest to do this up front because some of these arms may
|
||||
// be unreachable or reachable multiple times.
|
||||
let var_extent = self.extent_of_innermost_scope().unwrap();
|
||||
for arm in &arms {
|
||||
self.declare_bindings(var_extent, arm.patterns[0].clone());
|
||||
}
|
||||
|
||||
let mut arm_blocks = ArmBlocks {
|
||||
blocks: arms.iter()
|
||||
.map(|_| self.cfg.start_new_block())
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let arm_bodies: Vec<ExprRef<H>> =
|
||||
arms.iter()
|
||||
@ -51,35 +61,33 @@ impl<H:Hair> Builder<H> {
|
||||
// reverse of the order in which candidates are written in the
|
||||
// source.
|
||||
let candidates: Vec<Candidate<H>> =
|
||||
arms.into_iter()
|
||||
.zip(arm_blocks.iter())
|
||||
arms.iter()
|
||||
.enumerate()
|
||||
.rev() // highest priority comes last
|
||||
.flat_map(|(arm, &arm_block)| {
|
||||
let guard = arm.guard;
|
||||
arm.patterns.into_iter()
|
||||
.flat_map(|(arm_index, arm)| {
|
||||
arm.patterns.iter()
|
||||
.rev()
|
||||
.map(move |pat| (arm_block, pat, guard.clone()))
|
||||
.map(move |pat| (arm_index, pat.clone(), arm.guard.clone()))
|
||||
})
|
||||
.map(|(arm_block, pattern, guard)| {
|
||||
.map(|(arm_index, pattern, guard)| {
|
||||
Candidate {
|
||||
match_pairs: vec![self.match_pair(discriminant_lvalue.clone(), pattern)],
|
||||
bindings: vec![],
|
||||
guard: guard,
|
||||
arm_block: arm_block,
|
||||
arm_index: arm_index,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// this will generate code to test discriminant_lvalue and
|
||||
// branch to the appropriate arm block
|
||||
let var_extent = self.extent_of_innermost_scope().unwrap();
|
||||
self.match_candidates(span, var_extent, candidates, block);
|
||||
self.match_candidates(span, &mut arm_blocks, candidates, block);
|
||||
|
||||
// all the arm blocks will rejoin here
|
||||
let end_block = self.cfg.start_new_block();
|
||||
|
||||
for (arm_body, &arm_block) in arm_bodies.into_iter().zip(arm_blocks.iter()) {
|
||||
let mut arm_block = arm_block;
|
||||
for (arm_index, arm_body) in arm_bodies.into_iter().enumerate() {
|
||||
let mut arm_block = arm_blocks.blocks[arm_index];
|
||||
unpack!(arm_block = self.into(destination, arm_block, arm_body));
|
||||
self.cfg.terminate(arm_block, Terminator::Goto { target: end_block });
|
||||
}
|
||||
@ -122,12 +130,15 @@ impl<H:Hair> Builder<H> {
|
||||
initializer: &Lvalue<H>)
|
||||
-> BlockAnd<()>
|
||||
{
|
||||
// first, creating the bindings
|
||||
self.declare_bindings(var_extent, irrefutable_pat.clone());
|
||||
|
||||
// create a dummy candidate
|
||||
let mut candidate = Candidate::<H> {
|
||||
match_pairs: vec![self.match_pair(initializer.clone(), irrefutable_pat)],
|
||||
match_pairs: vec![self.match_pair(initializer.clone(), irrefutable_pat.clone())],
|
||||
bindings: vec![],
|
||||
guard: None,
|
||||
arm_block: block
|
||||
arm_index: 0, // since we don't call `match_candidates`, this field is unused
|
||||
};
|
||||
|
||||
// Simplify the candidate. Since the pattern is irrefutable, this should
|
||||
@ -142,44 +153,50 @@ impl<H:Hair> Builder<H> {
|
||||
}
|
||||
|
||||
// now apply the bindings, which will also declare the variables
|
||||
self.bind_matched_candidate(block, var_extent, candidate.bindings);
|
||||
self.bind_matched_candidate(block, candidate.bindings);
|
||||
|
||||
block.unit()
|
||||
}
|
||||
|
||||
pub fn declare_uninitialized_variables(&mut self,
|
||||
var_extent: H::CodeExtent,
|
||||
pattern: PatternRef<H>)
|
||||
pub fn declare_bindings(&mut self,
|
||||
var_extent: H::CodeExtent,
|
||||
pattern: PatternRef<H>)
|
||||
{
|
||||
let pattern = self.hir.mirror(pattern);
|
||||
match pattern.kind {
|
||||
PatternKind::Binding { mutability, name, mode: _, var, ty, subpattern } => {
|
||||
self.declare_binding(var_extent, mutability, name, var, ty, pattern.span);
|
||||
if let Some(subpattern) = subpattern {
|
||||
self.declare_uninitialized_variables(var_extent, subpattern);
|
||||
self.declare_bindings(var_extent, subpattern);
|
||||
}
|
||||
}
|
||||
PatternKind::Array { prefix, slice, suffix } |
|
||||
PatternKind::Slice { prefix, slice, suffix } => {
|
||||
for subpattern in prefix.into_iter().chain(slice).chain(suffix) {
|
||||
self.declare_uninitialized_variables(var_extent, subpattern);
|
||||
self.declare_bindings(var_extent, subpattern);
|
||||
}
|
||||
}
|
||||
PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {
|
||||
}
|
||||
PatternKind::Deref { subpattern } => {
|
||||
self.declare_uninitialized_variables(var_extent, subpattern);
|
||||
self.declare_bindings(var_extent, subpattern);
|
||||
}
|
||||
PatternKind::Leaf { subpatterns } |
|
||||
PatternKind::Variant { subpatterns, .. } => {
|
||||
for subpattern in subpatterns {
|
||||
self.declare_uninitialized_variables(var_extent, subpattern.pattern);
|
||||
self.declare_bindings(var_extent, subpattern.pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// List of blocks for each arm (and potentially other metadata in the
|
||||
/// future).
|
||||
struct ArmBlocks {
|
||||
blocks: Vec<BasicBlock>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Candidate<H:Hair> {
|
||||
// all of these must be satisfied...
|
||||
@ -191,8 +208,8 @@ struct Candidate<H:Hair> {
|
||||
// ...and the guard must be evaluated...
|
||||
guard: Option<ExprRef<H>>,
|
||||
|
||||
// ...and then we branch here.
|
||||
arm_block: BasicBlock,
|
||||
// ...and then we branch to arm with this index.
|
||||
arm_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -221,10 +238,10 @@ enum TestKind<H:Hair> {
|
||||
Switch { adt_def: H::AdtDef },
|
||||
|
||||
// test for equality
|
||||
Eq { value: Constant<H>, ty: H::Ty },
|
||||
Eq { value: Literal<H>, ty: H::Ty },
|
||||
|
||||
// test whether the value falls within an inclusive range
|
||||
Range { lo: Constant<H>, hi: Constant<H>, ty: H::Ty },
|
||||
Range { lo: Literal<H>, hi: Literal<H>, ty: H::Ty },
|
||||
|
||||
// test length of the slice is equal to len
|
||||
Len { len: usize, op: BinOp },
|
||||
@ -242,12 +259,12 @@ struct Test<H:Hair> {
|
||||
impl<H:Hair> Builder<H> {
|
||||
fn match_candidates(&mut self,
|
||||
span: H::Span,
|
||||
var_extent: H::CodeExtent,
|
||||
arm_blocks: &mut ArmBlocks,
|
||||
mut candidates: Vec<Candidate<H>>,
|
||||
mut block: BasicBlock)
|
||||
{
|
||||
debug!("matched_candidate(span={:?}, var_extent={:?}, block={:?}, candidates={:?})",
|
||||
span, var_extent, block, candidates);
|
||||
debug!("matched_candidate(span={:?}, block={:?}, candidates={:?})",
|
||||
span, block, candidates);
|
||||
|
||||
// Start by simplifying candidates. Once this process is
|
||||
// complete, all the match pairs which remain require some
|
||||
@ -267,9 +284,12 @@ impl<H:Hair> Builder<H> {
|
||||
// If so, apply any bindings, test the guard (if any), and
|
||||
// branch to the arm.
|
||||
let candidate = candidates.pop().unwrap();
|
||||
match self.bind_and_guard_matched_candidate(block, var_extent, candidate) {
|
||||
None => { return; }
|
||||
Some(b) => { block = b; }
|
||||
if let Some(b) = self.bind_and_guard_matched_candidate(block, arm_blocks, candidate) {
|
||||
block = b;
|
||||
} else {
|
||||
// if None is returned, then any remaining candidates
|
||||
// are unreachable (at least not through this path).
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,7 +317,7 @@ impl<H:Hair> Builder<H> {
|
||||
candidate))
|
||||
})
|
||||
.collect();
|
||||
self.match_candidates(span, var_extent, applicable_candidates, target_block);
|
||||
self.match_candidates(span, arm_blocks, applicable_candidates, target_block);
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,15 +335,17 @@ impl<H:Hair> Builder<H> {
|
||||
/// MIR).
|
||||
fn bind_and_guard_matched_candidate(&mut self,
|
||||
mut block: BasicBlock,
|
||||
var_extent: H::CodeExtent,
|
||||
arm_blocks: &mut ArmBlocks,
|
||||
candidate: Candidate<H>)
|
||||
-> Option<BasicBlock> {
|
||||
debug!("bind_and_guard_matched_candidate(block={:?}, var_extent={:?}, candidate={:?})",
|
||||
block, var_extent, candidate);
|
||||
debug!("bind_and_guard_matched_candidate(block={:?}, candidate={:?})",
|
||||
block, candidate);
|
||||
|
||||
debug_assert!(candidate.match_pairs.is_empty());
|
||||
|
||||
self.bind_matched_candidate(block, var_extent, candidate.bindings);
|
||||
self.bind_matched_candidate(block, candidate.bindings);
|
||||
|
||||
let arm_block = arm_blocks.blocks[candidate.arm_index];
|
||||
|
||||
if let Some(guard) = candidate.guard {
|
||||
// the block to branch to if the guard fails; if there is no
|
||||
@ -331,36 +353,26 @@ impl<H:Hair> Builder<H> {
|
||||
let cond = unpack!(block = self.as_operand(block, guard));
|
||||
let otherwise = self.cfg.start_new_block();
|
||||
self.cfg.terminate(block, Terminator::If { cond: cond,
|
||||
targets: [candidate.arm_block, otherwise]});
|
||||
targets: [arm_block, otherwise]});
|
||||
Some(otherwise)
|
||||
} else {
|
||||
self.cfg.terminate(block, Terminator::Goto { target: candidate.arm_block });
|
||||
self.cfg.terminate(block, Terminator::Goto { target: arm_block });
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_matched_candidate(&mut self,
|
||||
block: BasicBlock,
|
||||
var_extent: H::CodeExtent,
|
||||
bindings: Vec<Binding<H>>) {
|
||||
debug!("bind_matched_candidate(block={:?}, var_extent={:?}, bindings={:?})",
|
||||
block, var_extent, bindings);
|
||||
debug!("bind_matched_candidate(block={:?}, bindings={:?})",
|
||||
block, bindings);
|
||||
|
||||
// Assign each of the bindings. This may trigger moves out of the candidate.
|
||||
for binding in bindings {
|
||||
// Create a variable for the `var_id` being bound. In the
|
||||
// case where there are multiple patterns for a single
|
||||
// arm, it may already exist.
|
||||
let var_index = if !self.var_indices.contains_key(&binding.var_id) {
|
||||
self.declare_binding(var_extent,
|
||||
binding.mutability,
|
||||
binding.name,
|
||||
binding.var_id,
|
||||
binding.var_ty,
|
||||
binding.span)
|
||||
} else {
|
||||
self.var_indices[&binding.var_id]
|
||||
};
|
||||
// Find the variable for the `var_id` being bound. It
|
||||
// should have been created by a previous call to
|
||||
// `declare_bindings`.
|
||||
let var_index = self.var_indices[&binding.var_id];
|
||||
|
||||
let rvalue = match binding.binding_mode {
|
||||
BindingMode::ByValue =>
|
||||
|
@ -33,20 +33,20 @@ impl<H:Hair> Builder<H> {
|
||||
}
|
||||
}
|
||||
|
||||
PatternKind::Constant { ref expr } => {
|
||||
let expr = self.as_constant(expr.clone());
|
||||
PatternKind::Constant { ref value } => {
|
||||
Test {
|
||||
span: match_pair.pattern.span,
|
||||
kind: TestKind::Eq { value: expr, ty: match_pair.pattern.ty.clone() },
|
||||
kind: TestKind::Eq { value: value.clone(),
|
||||
ty: match_pair.pattern.ty.clone() },
|
||||
}
|
||||
}
|
||||
|
||||
PatternKind::Range { ref lo, ref hi } => {
|
||||
let lo = self.as_constant(lo.clone());
|
||||
let hi = self.as_constant(hi.clone());
|
||||
Test {
|
||||
span: match_pair.pattern.span,
|
||||
kind: TestKind::Range { lo: lo, hi: hi, ty: match_pair.pattern.ty.clone() },
|
||||
kind: TestKind::Range { lo: lo.clone(),
|
||||
hi: hi.clone(),
|
||||
ty: match_pair.pattern.ty.clone() },
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,15 +90,15 @@ impl<H:Hair> Builder<H> {
|
||||
|
||||
TestKind::Eq { value, ty } => {
|
||||
// call PartialEq::eq(discrim, constant)
|
||||
let constant = self.push_constant(block, test.span, ty.clone(), value);
|
||||
let constant = self.push_literal(block, test.span, ty.clone(), value);
|
||||
let item_ref = self.hir.partial_eq(ty);
|
||||
self.call_comparison_fn(block, test.span, item_ref, lvalue.clone(), constant)
|
||||
}
|
||||
|
||||
TestKind::Range { lo, hi, ty } => {
|
||||
// Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`.
|
||||
let lo = self.push_constant(block, test.span, ty.clone(), lo);
|
||||
let hi = self.push_constant(block, test.span, ty.clone(), hi);
|
||||
let lo = self.push_literal(block, test.span, ty.clone(), lo);
|
||||
let hi = self.push_literal(block, test.span, ty.clone(), hi);
|
||||
let item_ref = self.hir.partial_le(ty);
|
||||
|
||||
let lo_blocks =
|
||||
|
@ -33,13 +33,14 @@ impl<H:Hair> Builder<H> {
|
||||
lvalue
|
||||
}
|
||||
|
||||
pub fn push_constant(&mut self,
|
||||
block: BasicBlock,
|
||||
span: H::Span,
|
||||
ty: H::Ty,
|
||||
constant: Constant<H>)
|
||||
-> Lvalue<H> {
|
||||
let temp = self.temp(ty);
|
||||
pub fn push_literal(&mut self,
|
||||
block: BasicBlock,
|
||||
span: H::Span,
|
||||
ty: H::Ty,
|
||||
literal: Literal<H>)
|
||||
-> Lvalue<H> {
|
||||
let temp = self.temp(ty.clone());
|
||||
let constant = Constant { span: span, ty: ty, literal: literal };
|
||||
self.cfg.push_assign_constant(block, span, &temp, constant);
|
||||
temp
|
||||
}
|
||||
@ -55,8 +56,8 @@ impl<H:Hair> Builder<H> {
|
||||
block, span, &temp,
|
||||
Constant {
|
||||
span: span,
|
||||
kind: ConstantKind::Literal(Literal::Uint { bits: IntegralBits::BSize,
|
||||
value: value as u64 }),
|
||||
ty: self.hir.usize_ty(),
|
||||
literal: self.hir.usize_literal(value),
|
||||
});
|
||||
temp
|
||||
}
|
||||
@ -66,13 +67,7 @@ impl<H:Hair> Builder<H> {
|
||||
span: H::Span,
|
||||
item_ref: ItemRef<H>)
|
||||
-> Lvalue<H> {
|
||||
let constant = Constant {
|
||||
span: span,
|
||||
kind: ConstantKind::Literal(Literal::Item {
|
||||
def_id: item_ref.def_id,
|
||||
substs: item_ref.substs
|
||||
})
|
||||
};
|
||||
self.push_constant(block, span, item_ref.ty, constant)
|
||||
let literal = Literal::Item { def_id: item_ref.def_id, substs: item_ref.substs };
|
||||
self.push_literal(block, span, item_ref.ty, literal)
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl<H:Hair> Builder<H> {
|
||||
StmtKind::Let { remainder_scope, init_scope, pattern, initializer: None, stmts } => {
|
||||
this.in_scope(remainder_scope, block, |this| {
|
||||
unpack!(block = this.in_scope(init_scope, block, |this| {
|
||||
this.declare_uninitialized_variables(remainder_scope, pattern);
|
||||
this.declare_bindings(remainder_scope, pattern);
|
||||
block.unit()
|
||||
}));
|
||||
this.stmts(block, stmts)
|
||||
|
@ -62,7 +62,7 @@ impl<'a, 'tcx> OuterDump<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
let always_build_mir = self.tcx.sess.opts.always_build_mir;
|
||||
let always_build_mir = true;
|
||||
if !built_mir && always_build_mir {
|
||||
let mut closure_dump = InnerDump { tcx: self.tcx, attr: None };
|
||||
walk_op(&mut closure_dump);
|
||||
|
@ -38,6 +38,7 @@ pub trait Hair: Sized+Debug+Clone+Eq+Hash { // (*)
|
||||
type Ty: Clone+Debug+Eq; // e.g., ty::Ty<'tcx>
|
||||
type Region: Copy+Debug; // e.g., ty::Region
|
||||
type CodeExtent: Copy+Debug+Hash+Eq; // e.g., region::CodeExtent
|
||||
type ConstVal: Clone+Debug+PartialEq; // e.g., ConstVal
|
||||
type Pattern: Clone+Debug+Mirror<Self,Output=Pattern<Self>>; // e.g., &P<ast::Pat>
|
||||
type Expr: Clone+Debug+Mirror<Self,Output=Expr<Self>>; // e.g., &P<ast::Expr>
|
||||
type Stmt: Clone+Debug+Mirror<Self,Output=Stmt<Self>>; // e.g., &P<ast::Stmt>
|
||||
@ -55,9 +56,18 @@ pub trait Hair: Sized+Debug+Clone+Eq+Hash { // (*)
|
||||
/// Returns the type `usize`.
|
||||
fn usize_ty(&mut self) -> Self::Ty;
|
||||
|
||||
/// Returns the literal for `true`
|
||||
fn usize_literal(&mut self, value: usize) -> Literal<Self>;
|
||||
|
||||
/// Returns the type `bool`.
|
||||
fn bool_ty(&mut self) -> Self::Ty;
|
||||
|
||||
/// Returns the literal for `true`
|
||||
fn true_literal(&mut self) -> Literal<Self>;
|
||||
|
||||
/// Returns the literal for `true`
|
||||
fn false_literal(&mut self) -> Literal<Self>;
|
||||
|
||||
/// Returns a reference to `PartialEq::<T,T>::eq`
|
||||
fn partial_eq(&mut self, ty: Self::Ty) -> ItemRef<Self>;
|
||||
|
||||
@ -261,9 +271,9 @@ pub enum PatternKind<H:Hair> {
|
||||
|
||||
Deref { subpattern: PatternRef<H> }, // box P, &P, &mut P, etc
|
||||
|
||||
Constant { expr: ExprRef<H> },
|
||||
Constant { value: Literal<H> },
|
||||
|
||||
Range { lo: ExprRef<H>, hi: ExprRef<H> },
|
||||
Range { lo: Literal<H>, hi: Literal<H> },
|
||||
|
||||
// matches against a slice, checking the length and extracting elements
|
||||
Slice { prefix: Vec<PatternRef<H>>,
|
||||
|
@ -642,48 +642,21 @@ impl<H:Hair> Debug for Rvalue<H> {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Constants
|
||||
//
|
||||
// Two constants are equal if they are the same constant. Note that
|
||||
// this does not necessarily mean that they are "==" in Rust -- in
|
||||
// particular one must be wary of `NaN`!
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Constant<H:Hair> {
|
||||
pub span: H::Span,
|
||||
pub kind: ConstantKind<H>
|
||||
pub ty: H::Ty,
|
||||
pub literal: Literal<H>
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ConstantKind<H:Hair> {
|
||||
Literal(Literal<H>),
|
||||
Aggregate(AggregateKind<H>, Vec<Constant<H>>),
|
||||
Call(Box<Constant<H>>, Vec<Constant<H>>),
|
||||
Cast(Box<Constant<H>>, H::Ty),
|
||||
Repeat(Box<Constant<H>>, Box<Constant<H>>),
|
||||
Ref(BorrowKind, Box<Constant<H>>),
|
||||
BinaryOp(BinOp, Box<Constant<H>>, Box<Constant<H>>),
|
||||
UnaryOp(UnOp, Box<Constant<H>>),
|
||||
Projection(Box<ConstantProjection<H>>)
|
||||
}
|
||||
|
||||
pub type ConstantProjection<H> =
|
||||
Projection<H,Constant<H>,Constant<H>>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Literal<H:Hair> {
|
||||
Item { def_id: H::DefId, substs: H::Substs },
|
||||
Projection { projection: H::Projection },
|
||||
Int { bits: IntegralBits, value: i64 },
|
||||
Uint { bits: IntegralBits, value: u64 },
|
||||
Float { bits: FloatBits, value: f64 },
|
||||
Char { c: char },
|
||||
Bool { value: bool },
|
||||
Bytes { value: H::Bytes },
|
||||
String { value: H::InternedString },
|
||||
Value { value: H::ConstVal },
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub enum IntegralBits {
|
||||
B8, B16, B32, B64, BSize
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||
pub enum FloatBits {
|
||||
F32, F64
|
||||
}
|
||||
|
@ -16,14 +16,13 @@ use tcx::Cx;
|
||||
use tcx::block;
|
||||
use tcx::pattern::PatNode;
|
||||
use tcx::rustc::front::map;
|
||||
use tcx::rustc::middle::const_eval;
|
||||
use tcx::rustc::middle::def;
|
||||
use tcx::rustc::middle::region::CodeExtent;
|
||||
use tcx::rustc::middle::pat_util;
|
||||
use tcx::rustc::middle::ty::{self, Ty};
|
||||
use tcx::rustc_front::hir;
|
||||
use tcx::rustc_front::util as hir_util;
|
||||
use tcx::syntax::ast;
|
||||
use tcx::syntax::codemap::Span;
|
||||
use tcx::syntax::parse::token;
|
||||
use tcx::syntax::ptr::P;
|
||||
use tcx::to_ref::ToRef;
|
||||
@ -83,9 +82,9 @@ impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for &'tcx hir::Expr {
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprLit(ref lit) => {
|
||||
let literal = convert_literal(cx, self.span, expr_ty, lit);
|
||||
ExprKind::Literal { literal: literal }
|
||||
hir::ExprLit(..) => {
|
||||
let value = const_eval::eval_const_expr(cx.tcx, self);
|
||||
ExprKind::Literal { literal: Literal::Value { value: value } }
|
||||
}
|
||||
|
||||
hir::ExprBinary(op, ref lhs, ref rhs) => {
|
||||
@ -452,67 +451,6 @@ fn to_borrow_kind(m: hir::Mutability) -> BorrowKind {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_literal<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||
expr_span: Span,
|
||||
expr_ty: Ty<'tcx>,
|
||||
literal: &ast::Lit)
|
||||
-> Literal<Cx<'a,'tcx>>
|
||||
{
|
||||
use repr::IntegralBits::*;
|
||||
match (&literal.node, &expr_ty.sty) {
|
||||
(&ast::LitStr(ref text, _), _) =>
|
||||
Literal::String { value: text.clone() },
|
||||
(&ast::LitByteStr(ref bytes), _) =>
|
||||
Literal::Bytes { value: bytes.clone() },
|
||||
(&ast::LitByte(c), _) =>
|
||||
Literal::Uint { bits: B8, value: c as u64 },
|
||||
(&ast::LitChar(c), _) =>
|
||||
Literal::Char { c: c },
|
||||
(&ast::LitInt(v, _), &ty::TyUint(ast::TyU8)) =>
|
||||
Literal::Uint { bits: B8, value: v },
|
||||
(&ast::LitInt(v, _), &ty::TyUint(ast::TyU16)) =>
|
||||
Literal::Uint { bits: B16, value: v },
|
||||
(&ast::LitInt(v, _), &ty::TyUint(ast::TyU32)) =>
|
||||
Literal::Uint { bits: B32, value: v },
|
||||
(&ast::LitInt(v, _), &ty::TyUint(ast::TyU64)) =>
|
||||
Literal::Uint { bits: B64, value: v },
|
||||
(&ast::LitInt(v, _), &ty::TyUint(ast::TyUs)) =>
|
||||
Literal::Uint { bits: BSize, value: v },
|
||||
(&ast::LitInt(v, ast::SignedIntLit(_, ast::Sign::Minus)), &ty::TyInt(ast::TyI8)) =>
|
||||
Literal::Int { bits: B8, value: -(v as i64) },
|
||||
(&ast::LitInt(v, ast::SignedIntLit(_, ast::Sign::Minus)), &ty::TyInt(ast::TyI16)) =>
|
||||
Literal::Int { bits: B16, value: -(v as i64) },
|
||||
(&ast::LitInt(v, ast::SignedIntLit(_, ast::Sign::Minus)), &ty::TyInt(ast::TyI32)) =>
|
||||
Literal::Int { bits: B32, value: -(v as i64) },
|
||||
(&ast::LitInt(v, ast::SignedIntLit(_, ast::Sign::Minus)), &ty::TyInt(ast::TyI64)) =>
|
||||
Literal::Int { bits: B64, value: -(v as i64) },
|
||||
(&ast::LitInt(v, ast::SignedIntLit(_, ast::Sign::Minus)), &ty::TyInt(ast::TyIs)) =>
|
||||
Literal::Int { bits: BSize, value: -(v as i64) },
|
||||
(&ast::LitInt(v, _), &ty::TyInt(ast::TyI8)) =>
|
||||
Literal::Int { bits: B8, value: v as i64 },
|
||||
(&ast::LitInt(v, _), &ty::TyInt(ast::TyI16)) =>
|
||||
Literal::Int { bits: B16, value: v as i64 },
|
||||
(&ast::LitInt(v, _), &ty::TyInt(ast::TyI32)) =>
|
||||
Literal::Int { bits: B32, value: v as i64 },
|
||||
(&ast::LitInt(v, _), &ty::TyInt(ast::TyI64)) =>
|
||||
Literal::Int { bits: B64, value: v as i64 },
|
||||
(&ast::LitInt(v, _), &ty::TyInt(ast::TyIs)) =>
|
||||
Literal::Int { bits: BSize, value: v as i64 },
|
||||
(&ast::LitFloat(ref v, _), &ty::TyFloat(ast::TyF32)) |
|
||||
(&ast::LitFloatUnsuffixed(ref v), &ty::TyFloat(ast::TyF32)) =>
|
||||
Literal::Float { bits: FloatBits::F32, value: v.parse::<f64>().unwrap() },
|
||||
(&ast::LitFloat(ref v, _), &ty::TyFloat(ast::TyF64)) |
|
||||
(&ast::LitFloatUnsuffixed(ref v), &ty::TyFloat(ast::TyF64)) =>
|
||||
Literal::Float { bits: FloatBits::F64, value: v.parse::<f64>().unwrap() },
|
||||
(&ast::LitBool(v), _) =>
|
||||
Literal::Bool { value: v },
|
||||
(ref l, ref t) =>
|
||||
cx.tcx.sess.span_bug(
|
||||
expr_span,
|
||||
&format!("Invalid literal/type combination: {:?},{:?}", l, t))
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_arm<'a,'tcx:'a>(cx: &Cx<'a,'tcx>, arm: &'tcx hir::Arm) -> Arm<Cx<'a,'tcx>> {
|
||||
let map = if arm.pats.len() == 1 {
|
||||
None
|
||||
|
@ -14,6 +14,7 @@ use std::fmt::{Debug, Formatter, Error};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::rc::Rc;
|
||||
|
||||
use self::rustc::middle::const_eval::ConstVal;
|
||||
use self::rustc::middle::def_id::DefId;
|
||||
use self::rustc::middle::infer::InferCtxt;
|
||||
use self::rustc::middle::region::CodeExtent;
|
||||
@ -56,6 +57,7 @@ impl<'a,'tcx:'a> Hair for Cx<'a, 'tcx> {
|
||||
type Ty = Ty<'tcx>;
|
||||
type Region = ty::Region;
|
||||
type CodeExtent = CodeExtent;
|
||||
type ConstVal = ConstVal;
|
||||
type Pattern = PatNode<'tcx>;
|
||||
type Expr = &'tcx hir::Expr;
|
||||
type Stmt = &'tcx hir::Stmt;
|
||||
@ -70,10 +72,22 @@ impl<'a,'tcx:'a> Hair for Cx<'a, 'tcx> {
|
||||
self.tcx.types.usize
|
||||
}
|
||||
|
||||
fn usize_literal(&mut self, value: usize) -> Literal<Self> {
|
||||
Literal::Value { value: ConstVal::Uint(value as u64) }
|
||||
}
|
||||
|
||||
fn bool_ty(&mut self) -> Ty<'tcx> {
|
||||
self.tcx.types.bool
|
||||
}
|
||||
|
||||
fn true_literal(&mut self) -> Literal<Self> {
|
||||
Literal::Value { value: ConstVal::Bool(true) }
|
||||
}
|
||||
|
||||
fn false_literal(&mut self) -> Literal<Self> {
|
||||
Literal::Value { value: ConstVal::Bool(false) }
|
||||
}
|
||||
|
||||
fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<Self> {
|
||||
let eq_def_id = self.tcx.lang_items.eq_trait().unwrap();
|
||||
self.cmp_method_ref(eq_def_id, "eq", ty)
|
||||
|
@ -14,9 +14,10 @@ use repr::*;
|
||||
use rustc_data_structures::fnv::FnvHashMap;
|
||||
use std::rc::Rc;
|
||||
use tcx::Cx;
|
||||
use tcx::rustc::middle::const_eval::lookup_const_by_id;
|
||||
use tcx::rustc::middle::const_eval;
|
||||
use tcx::rustc::middle::def;
|
||||
use tcx::rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding};
|
||||
use tcx::rustc::middle::subst::Substs;
|
||||
use tcx::rustc::middle::ty::{self, Ty};
|
||||
use tcx::rustc_front::hir;
|
||||
use tcx::syntax::ast;
|
||||
@ -145,12 +146,19 @@ impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for PatNode<'tcx> {
|
||||
hir::PatWild(..) =>
|
||||
PatternKind::Wild,
|
||||
|
||||
hir::PatLit(ref lt) =>
|
||||
PatternKind::Constant { expr: lt.to_ref() },
|
||||
hir::PatLit(ref value) => {
|
||||
let value = const_eval::eval_const_expr(cx.tcx, value);
|
||||
let value = Literal::Value { value: value };
|
||||
PatternKind::Constant { value: value }
|
||||
},
|
||||
|
||||
hir::PatRange(ref begin, ref end) =>
|
||||
PatternKind::Range { lo: begin.to_ref(),
|
||||
hi: end.to_ref() },
|
||||
hir::PatRange(ref lo, ref hi) => {
|
||||
let lo = const_eval::eval_const_expr(cx.tcx, lo);
|
||||
let lo = Literal::Value { value: lo };
|
||||
let hi = const_eval::eval_const_expr(cx.tcx, hi);
|
||||
let hi = Literal::Value { value: hi };
|
||||
PatternKind::Range { lo: lo, hi: hi }
|
||||
},
|
||||
|
||||
hir::PatEnum(..) | hir::PatIdent(..) | hir::PatQPath(..)
|
||||
if pat_is_resolved_const(&cx.tcx.def_map, self.pat) =>
|
||||
@ -158,13 +166,25 @@ impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for PatNode<'tcx> {
|
||||
let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def();
|
||||
match def {
|
||||
def::DefConst(def_id) | def::DefAssociatedConst(def_id) =>
|
||||
match lookup_const_by_id(cx.tcx, def_id, Some(self.pat.id)) {
|
||||
Some(const_expr) =>
|
||||
PatternKind::Constant { expr: const_expr.to_ref() },
|
||||
None =>
|
||||
match const_eval::lookup_const_by_id(cx.tcx, def_id, Some(self.pat.id)) {
|
||||
Some(const_expr) => {
|
||||
let opt_value =
|
||||
const_eval::eval_const_expr_partial(
|
||||
cx.tcx, const_expr,
|
||||
const_eval::EvalHint::ExprTypeChecked);
|
||||
let literal = if let Ok(value) = opt_value {
|
||||
Literal::Value { value: value }
|
||||
} else {
|
||||
let substs = cx.tcx.mk_substs(Substs::empty());
|
||||
Literal::Item { def_id: def_id, substs: substs }
|
||||
};
|
||||
PatternKind::Constant { value: literal }
|
||||
}
|
||||
None => {
|
||||
cx.tcx.sess.span_bug(
|
||||
self.pat.span,
|
||||
&format!("cannot eval constant: {:?}", def_id)),
|
||||
&format!("cannot eval constant: {:?}", def_id))
|
||||
}
|
||||
},
|
||||
_ =>
|
||||
cx.tcx.sess.span_bug(
|
||||
|
Loading…
Reference in New Issue
Block a user