From 12c974e21abc70689c59027adcf42ff3f4b29e84 Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 13 Aug 2015 10:45:30 +0200 Subject: [PATCH] changed Constant to a struct with 'needed_resolution' bool --- src/const.rs | 202 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 138 insertions(+), 64 deletions(-) diff --git a/src/const.rs b/src/const.rs index 38044bbea5c..cef2cd7f9fc 100644 --- a/src/const.rs +++ b/src/const.rs @@ -18,9 +18,25 @@ impl From for FloatWidth { } } +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Constant { + constant: ConstantVariant, + needed_resolution: bool +} + +impl Constant { + fn new(variant: ConstantVariant) -> Constant { + Constant { constant: variant, needed_resolution: false } + } + + fn new_resolved(variant: ConstantVariant) -> Constant { + Constant { constant: variant, needed_resolution: true } + } +} + /// a Lit_-like enum to fold constant `Expr`s into #[derive(PartialEq, Eq, Debug, Clone)] -pub enum Constant { +pub enum ConstantVariant { /// a String "abc" ConstantStr(&'static str, StrStyle), /// a Binary String b"abc" @@ -43,34 +59,51 @@ pub enum Constant { ConstantTuple(Vec), } -/// simple constant folding -pub fn constant(cx: &Context, e: &Expr, follow: bool) -> Option { +impl ConstantVariant { + /// convert to u64 if possible + /// + /// # panics + /// + /// if the constant could not be converted to u64 losslessly + fn as_u64(&self) -> u64 { + if let &ConstantInt(val, _) = self { + val // TODO we may want to check the sign if any + } else { + panic!("Could not convert a {:?} to u64"); + } + } +} + +/// simple constant folding: Insert an expression, get a constant or none. +pub fn constant(cx: &Context, e: &Expr) -> Option { match e { - &ExprParen(ref inner) => constant(cx, inner, follow), - &ExprPath(_, _) => if follow { fetch_path(cx, e) } else { None }, - &ExprBlock(ref block) => constant_block(cx, inner, follow), + &ExprParen(ref inner) => constant(cx, inner), + &ExprPath(_, _) => fetch_path(cx, e), + &ExprBlock(ref block) => constant_block(cx, inner), &ExprIf(ref cond, ref then, ref otherwise) => - match constant(cx, cond) { - Some(ConstantBool(true)) => constant(cx, then, follow), - Some(ConstantBool(false)) => constant(cx, otherwise, follow), - _ => None, - }, + constant_if(cx, cond, then, otherwise), &ExprLit(ref lit) => Some(lit_to_constant(lit)), - &ExprVec(ref vec) => constant_vec(cx, vec, follow), - &ExprTup(ref tup) => constant_tup(cx, tup, follow), + &ExprVec(ref vec) => constant_vec(cx, vec), + &ExprTup(ref tup) => constant_tup(cx, tup), &ExprRepeat(ref value, ref number) => - constant_binop_apply(cx, value, number,|v, n| ConstantRepeat(v, n)), - &ExprUnary(op, ref operand) => constant(cx, operand, follow).and_then( + constant_binop_apply(cx, value, number,|v, n| Constant { + constant: ConstantRepeat(v, n.constant.as_u64()), + needed_resolution: v.needed_resolution || n.needed_resolution + }), + &ExprUnary(op, ref operand) => constant(cx, operand).and_then( |o| match op { UnNot => - if let ConstantBool(b) = o { - Some(ConstantBool(!b)) + if let ConstantBool(b) = o.variant { + Some(Constant{ + needed_resolution: o.needed_resolution, + constant: ConstantBool(!b), + }) } else { None }, UnNeg => constant_negate(o), UnUniq | UnDeref => o, }), &ExprBinary(op, ref left, ref right) => - constant_binop(cx, op, left, right, follow), + constant_binop(op, left, right), //TODO: add other expressions _ => None, } @@ -78,68 +111,100 @@ pub fn constant(cx: &Context, e: &Expr, follow: bool) -> Option { fn lit_to_constant(lit: &Lit_) -> Constant { match lit { - &LitStr(ref is, style) => ConstantStr(&*is, style), - &LitBinary(ref blob) => ConstantBinary(blob.clone()), - &LitByte(b) => ConstantByte(b), - &LitChar(c) => ConstantChar(c), - &LitInt(value, ty) => ConstantInt(value, ty), - &LitFloat(ref is, ty) => ConstantFloat(Cow::Borrowed(&*is), ty.into()), + &LitStr(ref is, style) => Constant::new(ConstantStr(&*is, style)), + &LitBinary(ref blob) => Constant::new(ConstantBinary(blob.clone())), + &LitByte(b) => Constant::new(ConstantByte(b)), + &LitChar(c) => Constant::new(ConstantChar(c)), + &LitInt(value, ty) => Constant::new(ConstantInt(value, ty)), + &LitFloat(ref is, ty) => + Constant::new(ConstantFloat(Cow::Borrowed(&*is), ty.into())), &LitFloatUnsuffixed(InternedString) => - ConstantFloat(Cow::Borrowed(&*is), FwAny), - &LitBool(b) => ConstantBool(b), + Constant::new(ConstantFloat(Cow::Borrowed(&*is), FwAny)), + &LitBool(b) => Constant::new(ConstantBool(b)), } } /// create `Some(ConstantVec(..))` of all constants, unless there is any /// non-constant part -fn constant_vec(cx: &Context, vec: &[&Expr], follow: bool) -> Option { - parts = Vec::new(); +fn constant_vec(cx: &Context, vec: &[&Expr]) -> Option { + let mut parts = Vec::new(); + let mut resolved = false; for opt_part in vec { - match constant(cx, opt_part, follow) { - Some(ref p) => parts.push(p), + match constant(cx, opt_part) { + Some(ref p) => { + resolved |= p.needed_resolution; + parts.push(p) + }, None => { return None; }, } } - Some(ConstantVec(parts)) + Some(Constant { + constant: ConstantVec(parts), + needed_resolution: resolved + }) } -fn constant_tup(cx: &Context, tup: &[&Expr], follow: bool) -> Option { - parts = Vec::new(); +fn constant_tup(cx: &Context, tup: &[&Expr]) -> Option { + let mut parts = Vec::new(); + let mut resolved = false; for opt_part in vec { - match constant(cx, opt_part, follow) { - Some(ref p) => parts.push(p), + match constant(cx, opt_part) { + Some(ref p) => { + resolved |= p.needed_resolution; + parts.push(p) + }, None => { return None; }, } } - Some(ConstantTuple(parts)) + Some(Constant { + constant: ConstantTuple(parts), + needed_resolution: resolved + }) } /// lookup a possibly constant expression from a ExprPath fn fetch_path(cx: &Context, e: &Expr) -> Option { if let Some(&PathResolution { base_def: DefConst(id), ..}) = cx.tcx.def_map.borrow().get(&e.id) { - lookup_const_by_id(cx.tcx, id, None).map(|l| constant(cx, l)) + lookup_const_by_id(cx.tcx, id, None).map( + |l| Constant::new_resolved(constant(cx, l).constant)) } else { None } } /// A block can only yield a constant if it only has one constant expression -fn constant_block(cx: &Context, block: &Block, follow: bool) -> Option { +fn constant_block(cx: &Context, block: &Block) -> Option { if block.stmts.is_empty() { - block.expr.map(|b| constant(cx, b, follow)) + block.expr.map(|b| constant(cx, b)) + } else { None } +} + +fn constant_if(cx: &Context, cond: &Expr, then: &Expr, otherwise: &Expr) -> + Option { + if let Some(Constant{ constant: ConstantBool(b), needed_resolution: res }) = + constant(cx, cond) { + let part = constant(cx, if b { then } else { otherwise }); + Some(Constant { + constant: part.constant, + needed_resolution: res || part.needed_resolution, + }) } else { None } } fn constant_negate(o: Constant) -> Option { - match o { - &ConstantInt(value, ty) => - Some(ConstantInt(value, match ty { - SignedIntLit(ity, sign) => SignedIntLit(ity, neg_sign(sign)), - UnsuffixedIntLit(sign) => UnsuffixedIntLit(neg_sign(sign)), - _ => { return None; }, - })), - &LitFloat(ref is, ref ty) => Some(ConstantFloat(neg_float_str(is), ty)), - _ => None, - } + Some(Constant{ + needed_resolution: o.needed_resolution, + constant: match o.constant { + &ConstantInt(value, ty) => + ConstantInt(value, match ty { + SignedIntLit(ity, sign) => + SignedIntLit(ity, neg_sign(sign)), + UnsuffixedIntLit(sign) => UnsuffixedIntLit(neg_sign(sign)), + _ => { return None; }, + }), + &LitFloat(ref is, ref ty) => ConstantFloat(neg_float_str(is), ty), + _ => { return None; }, + } + }) } fn neg_sign(s: Sign) -> Sign { @@ -157,16 +222,16 @@ fn neg_float_str(s: &InternedString) -> Cow<'static, str> { } } -fn constant_binop(cx: &Context, op: BinOp, left: &Expr, right: &Expr, - follow: bool) -> Option { +fn constant_binop(cx: &Context, op: BinOp, left: &Expr, right: &Expr) + -> Option { match op.node { //BiAdd, //BiSub, //BiMul, //BiDiv, //BiRem, - BiAnd => constant_short_circuit(cx, left, right, false, follow), - BiOr => constant_short_circuit(cx, left, right, true, follow), + BiAnd => constant_short_circuit(cx, left, right, false), + BiOr => constant_short_circuit(cx, left, right, true), //BiBitXor, //BiBitAnd, //BiBitOr, @@ -182,21 +247,30 @@ fn constant_binop(cx: &Context, op: BinOp, left: &Expr, right: &Expr, } } -fn constant_binop_apply(cx: &Context, left: &Expr, right: &Expr, op: F, - follow: bool) -> Option -where F: FnMut(Constant, Constant) -> Option { - constant(cx, left, follow).and_then(|l| constant(cx, right, follow) - .and_then(|r| op(l, r))) +fn constant_binop_apply(cx: &Context, left: &Expr, right: &Expr, op: F) + -> Option +where F: FnMut(ConstantVariant, ConstantVariant) -> Option { + constant(cx, left).and_then(|l| constant(cx, right).and_then( + |r| Constant { + needed_resolution: l.needed_resolution || r.needed_resolution, + constant: op(l.constant, r.constant) + })) } -fn constant_short_circuit(cx: &Context, left: &Expr, right: &Expr, b: bool, - follow: bool) -> Option { - if let ConstantBool(lbool) = constant(cx, left, follow) { +fn constant_short_circuit(cx: &Context, left: &Expr, right: &Expr, b: bool) -> + Option { + let leftconst = constant(cx, left); + if let ConstantBool(lbool) = leftconst.constant { if l == b { - Some(ConstantBool(b)) + Some(leftconst) } else { - if let ConstantBool(rbool) = constant(cx, right, follow) { - Some(ConstantBool(rbool)) + let rightconst = constant(cx, right); + if let ConstantBool(rbool) = rightconst.constant { + Some(Constant { + constant: rightconst.constant, + needed_resolution: leftconst.needed_resolution || + rightconst.needed_resolution, + }) } else { None } } } else { None }