From a7527adf0855002dcbd1cb12309d4f7fbfd93e16 Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 12 Aug 2015 13:49:28 +0200 Subject: [PATCH 1/5] First (incomplete) const folding --- src/const.rs | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/const.rs diff --git a/src/const.rs b/src/const.rs new file mode 100644 index 000000000000..924a47f2e5a7 --- /dev/null +++ b/src/const.rs @@ -0,0 +1,131 @@ +use rustc::lint::Context; +use rustc::middle::const_eval::lookup_const_by_id; +use syntax::ast::*; +use syntax::ptr::P; + +/// a Lit_-like enum to fold constant `Expr`s into +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum Constant { + ConstantStr(&'static str, StrStyle), + ConstantBinary(Rc>), + ConstantByte(u8), + ConstantChar(char), + ConstantInt(u64, LitIntType), + ConstantFloat(Cow<'static, str>, FloatTy), + ConstantFloatUnsuffixed(Cow<'static, str>), + ConstantBool(bool), + ConstantVec(Vec), + ConstantTuple(Vec), +} + +/// simple constant folding +pub fn constant(cx: &Context, e: &Expr) -> Option { + match e { + &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(LitBool(true)) => constant(cx, then), + Some(LitBool(false)) => constant(cx, otherwise), + _ => None, + }, + &ExprLit(ref lit) => Some(lit_to_constant(lit)), + &ExprVec(ref vec) => constant_vec(cx, vec), + &ExprTup(ref tup) => constant_tup(cx, tup), + &ExprUnary(op, ref operand) => constant(cx, operand).and_then( + |o| match op { + UnNot => + if let ConstantBool(b) = o { + Some(ConstantBool(!b)) + } else { None }, + UnNeg => + 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)), + &LitFloatUnsuffixed(ref is) => + Some(ConstantFloatUnsuffixed(neg_float_str(is))), + _ => None, + }, + UnUniq | UnDeref => o, + }), + //TODO: add other expressions + _ => None, + } +} + +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), + &LitFloatUnsuffixed(InternedString) => + ConstantFloatUnsuffixed(Cow::Borrowed(&*is)), + &LitBool(b) => ConstantBool(b), + } +} + +/// create `Some(ConstantVec(..))` of all constants, unless there is any +/// non-constant part +fn constant_vec(cx: &Context, vec: &[&Expr]) -> Option { + Vec parts = Vec::new(); + for opt_part in vec { + match constant(cx, opt_part) { + Some(ref p) => parts.push(p), + None => { return None; }, + } + } + Some(ConstantVec(parts)) +} + +fn constant_tup(cx, &Context, tup: &[&Expr]) -> Option { + Vec parts = Vec::new(); + for opt_part in vec { + match constant(cx, opt_part) { + Some(ref p) => parts.push(p), + None => { return None; }, + } + } + Some(ConstantTuple(parts)) +} + +/// 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)) + } else { None } +} + +/// A block can only yield a constant if it only has one constant expression +fn constant_block(cx: &Context, block: &Block) -> Option { + if block.stmts.is_empty() { + block.expr.map(|b| constant(cx, b)) + } else { None } +} + +fn neg_sign(s: Sign) -> Sign { + match s: + Sign::Plus => Sign::Minus, + Sign::Minus => Sign::Plus, + } +} + +fn neg_float_str(s: &InternedString) -> Cow<'static, str> { + if s.startsWith('-') { + Cow::Borrowed(s[1..]) + } else { + Cow::Owned(format!("-{}", &*s)) + } +} From fc6dfafc306568def3b58a6a91fe56289b53242f Mon Sep 17 00:00:00 2001 From: llogiq Date: Wed, 12 Aug 2015 14:02:13 +0200 Subject: [PATCH 2/5] fixed if-condition match --- src/const.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/const.rs b/src/const.rs index 924a47f2e5a7..a353de218f48 100644 --- a/src/const.rs +++ b/src/const.rs @@ -26,8 +26,8 @@ pub fn constant(cx: &Context, e: &Expr) -> Option { &ExprBlock(ref block) => constant_block(cx, inner), &ExprIf(ref cond, ref then, ref otherwise) => match constant(cx, cond) { - Some(LitBool(true)) => constant(cx, then), - Some(LitBool(false)) => constant(cx, otherwise), + Some(ConstantBool(true)) => constant(cx, then), + Some(ConstantBool(false)) => constant(cx, otherwise), _ => None, }, &ExprLit(ref lit) => Some(lit_to_constant(lit)), From 38e8d2bc060607ebf9a37919752e1de15f1794fa Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 12 Aug 2015 13:58:55 +0200 Subject: [PATCH 3/5] methods: move misc.StrToStringPass to MethodsPass --- src/lib.rs | 4 ++-- src/methods.rs | 12 ++++++++++-- src/misc.rs | 30 ------------------------------ 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fef678f668d8..7b47edaa4965 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,7 +37,6 @@ pub mod returns; pub fn plugin_registrar(reg: &mut Registry) { reg.register_lint_pass(box types::TypePass as LintPassObject); reg.register_lint_pass(box misc::MiscPass as LintPassObject); - reg.register_lint_pass(box misc::StrToStringPass as LintPassObject); reg.register_lint_pass(box misc::TopLevelRefPass as LintPassObject); reg.register_lint_pass(box misc::CmpNan as LintPassObject); reg.register_lint_pass(box eq_op::EqOp as LintPassObject); @@ -61,7 +60,7 @@ pub fn plugin_registrar(reg: &mut Registry) { reg.register_lint_pass(box methods::MethodsPass as LintPassObject); reg.register_lint_group("clippy", vec![types::BOX_VEC, types::LINKEDLIST, - misc::SINGLE_MATCH, misc::STR_TO_STRING, + misc::SINGLE_MATCH, misc::TOPLEVEL_REF_ARG, eq_op::EQ_OP, bit_mask::BAD_BIT_MASK, bit_mask::INEFFECTIVE_BIT_MASK, @@ -83,5 +82,6 @@ pub fn plugin_registrar(reg: &mut Registry) { misc::MODULO_ONE, methods::OPTION_UNWRAP_USED, methods::RESULT_UNWRAP_USED, + methods::STR_TO_STRING, ]); } diff --git a/src/methods.rs b/src/methods.rs index 3d9aa8c6ffc1..f02e06640923 100644 --- a/src/methods.rs +++ b/src/methods.rs @@ -11,16 +11,19 @@ declare_lint!(pub OPTION_UNWRAP_USED, Warn, "Warn on using unwrap() on an Option value"); declare_lint!(pub RESULT_UNWRAP_USED, Allow, "Warn on using unwrap() on a Result value"); +declare_lint!(pub STR_TO_STRING, Warn, + "Warn when a String could use to_owned() instead of to_string()"); impl LintPass for MethodsPass { fn get_lints(&self) -> LintArray { - lint_array!(OPTION_UNWRAP_USED, RESULT_UNWRAP_USED) + lint_array!(OPTION_UNWRAP_USED, RESULT_UNWRAP_USED, STR_TO_STRING) } fn check_expr(&mut self, cx: &Context, expr: &Expr) { if let ExprMethodCall(ref ident, _, ref args) = expr.node { + let ref obj_ty = walk_ptrs_ty(cx.tcx.expr_ty(&*args[0])).sty; if ident.node.name == "unwrap" { - if let ty::TyEnum(did, _) = walk_ptrs_ty(cx.tcx.expr_ty(&*args[0])).sty { + if let ty::TyEnum(did, _) = *obj_ty { if match_def_path(cx, did.did, &["core", "option", "Option"]) { span_lint(cx, OPTION_UNWRAP_USED, expr.span, "used unwrap() on an Option value. If you don't want \ @@ -34,6 +37,11 @@ impl LintPass for MethodsPass { } } } + else if ident.node.name == "to_string" { + if let ty::TyStr = *obj_ty { + span_lint(cx, STR_TO_STRING, expr.span, "`str.to_owned()` is faster"); + } + } } } } diff --git a/src/misc.rs b/src/misc.rs index fa1847aad9a2..d5e1efe2cc3a 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -59,36 +59,6 @@ impl LintPass for MiscPass { } -declare_lint!(pub STR_TO_STRING, Warn, "Warn when a String could use to_owned() instead of to_string()"); - -#[allow(missing_copy_implementations)] -pub struct StrToStringPass; - -impl LintPass for StrToStringPass { - fn get_lints(&self) -> LintArray { - lint_array!(STR_TO_STRING) - } - - fn check_expr(&mut self, cx: &Context, expr: &ast::Expr) { - match expr.node { - ast::ExprMethodCall(ref method, _, ref args) - if method.node.name == "to_string" - && is_str(cx, &*args[0]) => { - span_lint(cx, STR_TO_STRING, expr.span, "`str.to_owned()` is faster"); - }, - _ => () - } - - fn is_str(cx: &Context, expr: &ast::Expr) -> bool { - match walk_ptrs_ty(cx.tcx.expr_ty(expr)).sty { - ty::TyStr => true, - _ => false - } - } - } -} - - declare_lint!(pub TOPLEVEL_REF_ARG, Warn, "Warn about pattern matches with top-level `ref` bindings"); #[allow(missing_copy_implementations)] From 45b95537574182be0b7ef6301cdbafe11891ec95 Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 13 Aug 2015 09:25:44 +0200 Subject: [PATCH 4/5] added follow flag --- src/const.rs | 154 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 113 insertions(+), 41 deletions(-) diff --git a/src/const.rs b/src/const.rs index a353de218f48..38044bbea5cc 100644 --- a/src/const.rs +++ b/src/const.rs @@ -3,60 +3,74 @@ use rustc::middle::const_eval::lookup_const_by_id; use syntax::ast::*; use syntax::ptr::P; +pub enum FloatWidth { + Fw32, + Fw64, + FwAny +} + +impl From for FloatWidth { + fn from(ty: FloatTy) -> FloatWidth { + match ty { + TyF32 => Fw32, + TyF64 => Fw64, + } + } +} + /// a Lit_-like enum to fold constant `Expr`s into #[derive(PartialEq, Eq, Debug, Clone)] pub enum Constant { + /// a String "abc" ConstantStr(&'static str, StrStyle), + /// a Binary String b"abc" ConstantBinary(Rc>), + /// a single byte b'a' ConstantByte(u8), + /// a single char 'a' ConstantChar(char), + /// an integer ConstantInt(u64, LitIntType), - ConstantFloat(Cow<'static, str>, FloatTy), - ConstantFloatUnsuffixed(Cow<'static, str>), + /// a float with given type + ConstantFloat(Cow<'static, str>, FloatWidth), + /// true or false ConstantBool(bool), + /// an array of constants ConstantVec(Vec), + /// also an array, but with only one constant, repeated N times + ConstantRepeat(Constant, usize), + /// a tuple of constants ConstantTuple(Vec), } /// simple constant folding -pub fn constant(cx: &Context, e: &Expr) -> Option { +pub fn constant(cx: &Context, e: &Expr, follow: bool) -> Option { match e { - &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) => + &ExprParen(ref inner) => constant(cx, inner, follow), + &ExprPath(_, _) => if follow { fetch_path(cx, e) } else { None }, + &ExprBlock(ref block) => constant_block(cx, inner, follow), + &ExprIf(ref cond, ref then, ref otherwise) => match constant(cx, cond) { - Some(ConstantBool(true)) => constant(cx, then), - Some(ConstantBool(false)) => constant(cx, otherwise), + Some(ConstantBool(true)) => constant(cx, then, follow), + Some(ConstantBool(false)) => constant(cx, otherwise, follow), _ => None, }, &ExprLit(ref lit) => Some(lit_to_constant(lit)), - &ExprVec(ref vec) => constant_vec(cx, vec), - &ExprTup(ref tup) => constant_tup(cx, tup), - &ExprUnary(op, ref operand) => constant(cx, operand).and_then( + &ExprVec(ref vec) => constant_vec(cx, vec, follow), + &ExprTup(ref tup) => constant_tup(cx, tup, follow), + &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( |o| match op { UnNot => if let ConstantBool(b) = o { Some(ConstantBool(!b)) } else { None }, - UnNeg => - 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)), - &LitFloatUnsuffixed(ref is) => - Some(ConstantFloatUnsuffixed(neg_float_str(is))), - _ => None, - }, + UnNeg => constant_negate(o), UnUniq | UnDeref => o, }), + &ExprBinary(op, ref left, ref right) => + constant_binop(cx, op, left, right, follow), //TODO: add other expressions _ => None, } @@ -69,19 +83,19 @@ fn lit_to_constant(lit: &Lit_) -> Constant { &LitByte(b) => ConstantByte(b), &LitChar(c) => ConstantChar(c), &LitInt(value, ty) => ConstantInt(value, ty), - &LitFloat(ref is, ty) => ConstantFloat(Cow::Borrowed(&*is), ty), - &LitFloatUnsuffixed(InternedString) => - ConstantFloatUnsuffixed(Cow::Borrowed(&*is)), + &LitFloat(ref is, ty) => ConstantFloat(Cow::Borrowed(&*is), ty.into()), + &LitFloatUnsuffixed(InternedString) => + ConstantFloat(Cow::Borrowed(&*is), FwAny), &LitBool(b) => ConstantBool(b), } } /// create `Some(ConstantVec(..))` of all constants, unless there is any /// non-constant part -fn constant_vec(cx: &Context, vec: &[&Expr]) -> Option { - Vec parts = Vec::new(); +fn constant_vec(cx: &Context, vec: &[&Expr], follow: bool) -> Option { + parts = Vec::new(); for opt_part in vec { - match constant(cx, opt_part) { + match constant(cx, opt_part, follow) { Some(ref p) => parts.push(p), None => { return None; }, } @@ -89,10 +103,10 @@ fn constant_vec(cx: &Context, vec: &[&Expr]) -> Option { Some(ConstantVec(parts)) } -fn constant_tup(cx, &Context, tup: &[&Expr]) -> Option { - Vec parts = Vec::new(); +fn constant_tup(cx: &Context, tup: &[&Expr], follow: bool) -> Option { + parts = Vec::new(); for opt_part in vec { - match constant(cx, opt_part) { + match constant(cx, opt_part, follow) { Some(ref p) => parts.push(p), None => { return None; }, } @@ -109,23 +123,81 @@ fn fetch_path(cx: &Context, e: &Expr) -> Option { } /// A block can only yield a constant if it only has one constant expression -fn constant_block(cx: &Context, block: &Block) -> Option { +fn constant_block(cx: &Context, block: &Block, follow: bool) -> Option { if block.stmts.is_empty() { - block.expr.map(|b| constant(cx, b)) + block.expr.map(|b| constant(cx, b, follow)) } 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, + } +} + fn neg_sign(s: Sign) -> Sign { - match s: + match s { Sign::Plus => Sign::Minus, Sign::Minus => Sign::Plus, } } fn neg_float_str(s: &InternedString) -> Cow<'static, str> { - if s.startsWith('-') { + if s.startsWith('-') { Cow::Borrowed(s[1..]) } else { Cow::Owned(format!("-{}", &*s)) } } + +fn constant_binop(cx: &Context, op: BinOp, left: &Expr, right: &Expr, + follow: bool) -> 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), + //BiBitXor, + //BiBitAnd, + //BiBitOr, + //BiShl, + //BiShr, + //BiEq, + //BiLt, + //BiLe, + //BiNe, + //BiGe, + //BiGt, + _ => None, + } +} + +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_short_circuit(cx: &Context, left: &Expr, right: &Expr, b: bool, + follow: bool) -> Option { + if let ConstantBool(lbool) = constant(cx, left, follow) { + if l == b { + Some(ConstantBool(b)) + } else { + if let ConstantBool(rbool) = constant(cx, right, follow) { + Some(ConstantBool(rbool)) + } else { None } + } + } else { None } +} From 12c974e21abc70689c59027adcf42ff3f4b29e84 Mon Sep 17 00:00:00 2001 From: llogiq Date: Thu, 13 Aug 2015 10:45:30 +0200 Subject: [PATCH 5/5] 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 38044bbea5cc..cef2cd7f9fc0 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 }