From 432d9fec38be7b8b5abe57f002dc3f71d84e63c3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 16 Mar 2016 12:38:26 +0100 Subject: [PATCH 1/2] refactor clippy-consts to use ConstInt --- src/consts.rs | 278 +++++++++------------------------------------ src/identity_op.rs | 11 +- tests/consts.rs | 10 +- 3 files changed, 66 insertions(+), 233 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index eae9747d6..06f790d83 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -4,13 +4,14 @@ use rustc::lint::LateContext; use rustc::middle::const_eval::lookup_const_by_id; use rustc::middle::def::{Def, PathResolution}; use rustc_front::hir::*; -use std::cmp::Ordering::{self, Greater, Less, Equal}; +use rustc_const_eval::{ConstInt, ConstUsize, ConstIsize}; +use std::cmp::Ordering::{self, Equal}; use std::cmp::PartialOrd; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; use std::rc::Rc; -use syntax::ast::{FloatTy, LitIntType, LitKind, StrStyle, UintTy}; +use syntax::ast::{FloatTy, LitIntType, LitKind, StrStyle, UintTy, IntTy}; use syntax::ptr::P; #[derive(Debug, Copy, Clone)] @@ -29,12 +30,6 @@ impl From for FloatWidth { } } -#[derive(Copy, Eq, Debug, Clone, PartialEq, Hash)] -pub enum Sign { - Plus, - Minus, -} - /// a Lit_-like enum to fold constant `Expr`s into #[derive(Debug, Clone)] pub enum Constant { @@ -42,12 +37,10 @@ pub enum Constant { Str(String, StrStyle), /// a Binary String b"abc" Binary(Rc>), - /// a single byte b'a' - Byte(u8), /// a single char 'a' Char(char), /// an integer, third argument is whether the value is negated - Int(u64, LitIntType, Sign), + Int(ConstInt), /// a float with given type Float(String, FloatWidth), /// true or false @@ -67,21 +60,20 @@ impl Constant { /// /// if the constant could not be converted to u64 losslessly fn as_u64(&self) -> u64 { - if let Constant::Int(val, _, _) = *self { - val // TODO we may want to check the sign if any + if let Constant::Int(val) = *self { + val.to_u64().expect("negative constant can't be casted to u64") } else { panic!("Could not convert a {:?} to u64", self); } } /// convert this constant to a f64, if possible - #[allow(cast_precision_loss)] + #[allow(cast_precision_loss, cast_possible_wrap)] pub fn as_float(&self) -> Option { match *self { - Constant::Byte(b) => Some(b as f64), Constant::Float(ref s, _) => s.parse().ok(), - Constant::Int(i, _, Sign::Minus) => Some(-(i as f64)), - Constant::Int(i, _, Sign::Plus) => Some(i as f64), + Constant::Int(i) if i.is_negative() => Some(i.to_u64_unchecked() as i64 as f64), + Constant::Int(i) => Some(i.to_u64_unchecked() as f64), _ => None, } } @@ -92,10 +84,8 @@ impl PartialEq for Constant { match (self, other) { (&Constant::Str(ref ls, ref lsty), &Constant::Str(ref rs, ref rsty)) => ls == rs && lsty == rsty, (&Constant::Binary(ref l), &Constant::Binary(ref r)) => l == r, - (&Constant::Byte(l), &Constant::Byte(r)) => l == r, (&Constant::Char(l), &Constant::Char(r)) => l == r, - (&Constant::Int(0, _, _), &Constant::Int(0, _, _)) => true, - (&Constant::Int(lv, _, lneg), &Constant::Int(rv, _, rneg)) => lv == rv && lneg == rneg, + (&Constant::Int(l), &Constant::Int(r)) => l == r, (&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => { // we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have // `Fw32 == Fw64` so don’t compare them @@ -125,15 +115,11 @@ impl Hash for Constant { Constant::Binary(ref b) => { b.hash(state); } - Constant::Byte(u) => { - u.hash(state); - } Constant::Char(c) => { c.hash(state); } - Constant::Int(u, _, t) => { - u.hash(state); - t.hash(state); + Constant::Int(i) => { + i.hash(state); } Constant::Float(ref f, _) => { // don’t use the width here because of PartialEq implementation @@ -165,13 +151,8 @@ impl PartialOrd for Constant { None } } - (&Constant::Byte(ref l), &Constant::Byte(ref r)) => Some(l.cmp(r)), (&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)), - (&Constant::Int(0, _, _), &Constant::Int(0, _, _)) => Some(Equal), - (&Constant::Int(ref lv, _, Sign::Plus), &Constant::Int(ref rv, _, Sign::Plus)) => Some(lv.cmp(rv)), - (&Constant::Int(ref lv, _, Sign::Minus), &Constant::Int(ref rv, _, Sign::Minus)) => Some(rv.cmp(lv)), - (&Constant::Int(_, _, Sign::Minus), &Constant::Int(_, _, Sign::Plus)) => Some(Less), - (&Constant::Int(_, _, Sign::Plus), &Constant::Int(_, _, Sign::Minus)) => Some(Greater), + (&Constant::Int(l), &Constant::Int(r)) => Some(l.cmp(&r)), (&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => { match (ls.parse::(), rs.parse::()) { (Ok(ref l), Ok(ref r)) => l.partial_cmp(r), @@ -192,13 +173,24 @@ impl PartialOrd for Constant { } } +#[allow(cast_possible_wrap)] fn lit_to_constant(lit: &LitKind) -> Constant { match *lit { LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style), - LitKind::Byte(b) => Constant::Byte(b), + LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)), LitKind::ByteStr(ref s) => Constant::Binary(s.clone()), LitKind::Char(c) => Constant::Char(c), - LitKind::Int(value, ty) => Constant::Int(value, ty, Sign::Plus), + LitKind::Int(value, LitIntType::Unsuffixed) => Constant::Int(ConstInt::Infer(value)), + LitKind::Int(value, LitIntType::Unsigned(UintTy::U8)) => Constant::Int(ConstInt::U8(value as u8)), + LitKind::Int(value, LitIntType::Unsigned(UintTy::U16)) => Constant::Int(ConstInt::U16(value as u16)), + LitKind::Int(value, LitIntType::Unsigned(UintTy::U32)) => Constant::Int(ConstInt::U32(value as u32)), + LitKind::Int(value, LitIntType::Unsigned(UintTy::U64)) => Constant::Int(ConstInt::U64(value as u64)), + LitKind::Int(value, LitIntType::Unsigned(UintTy::Us)) => Constant::Int(ConstInt::Usize(ConstUsize::Us32(value as u32))), + LitKind::Int(value, LitIntType::Signed(IntTy::I8)) => Constant::Int(ConstInt::I8(value as i8)), + LitKind::Int(value, LitIntType::Signed(IntTy::I16)) => Constant::Int(ConstInt::I16(value as i16)), + LitKind::Int(value, LitIntType::Signed(IntTy::I32)) => Constant::Int(ConstInt::I32(value as i32)), + LitKind::Int(value, LitIntType::Signed(IntTy::I64)) => Constant::Int(ConstInt::I64(value as i64)), + LitKind::Int(value, LitIntType::Signed(IntTy::Is)) => Constant::Int(ConstInt::Isize(ConstIsize::Is32(value as i32))), LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()), LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any), LitKind::Bool(b) => Constant::Bool(b), @@ -209,23 +201,7 @@ fn constant_not(o: Constant) -> Option { use self::Constant::*; match o { Bool(b) => Some(Bool(!b)), - Int(value, LitIntType::Signed(ity), Sign::Plus) if value != ::std::u64::MAX => { - Some(Int(value + 1, LitIntType::Signed(ity), Sign::Minus)) - } - Int(0, LitIntType::Signed(ity), Sign::Minus) => Some(Int(1, LitIntType::Signed(ity), Sign::Minus)), - Int(value, LitIntType::Signed(ity), Sign::Minus) => Some(Int(value - 1, LitIntType::Signed(ity), Sign::Plus)), - Int(value, LitIntType::Unsigned(ity), Sign::Plus) => { - let mask = match ity { - UintTy::U8 => ::std::u8::MAX as u64, - UintTy::U16 => ::std::u16::MAX as u64, - UintTy::U32 => ::std::u32::MAX as u64, - UintTy::U64 => ::std::u64::MAX, - UintTy::Us => { - return None; - } // refuse to guess - }; - Some(Int(!value & mask, LitIntType::Unsigned(ity), Sign::Plus)) - } + Int(value) => (!value).ok().map(Int), _ => None, } } @@ -233,20 +209,12 @@ fn constant_not(o: Constant) -> Option { fn constant_negate(o: Constant) -> Option { use self::Constant::*; match o { - Int(value, LitIntType::Signed(ity), sign) => Some(Int(value, LitIntType::Signed(ity), neg_sign(sign))), - Int(value, LitIntType::Unsuffixed, sign) => Some(Int(value, LitIntType::Unsuffixed, neg_sign(sign))), + Int(value) => (-value).ok().map(Int), Float(is, ty) => Some(Float(neg_float_str(is), ty)), _ => None, } } -fn neg_sign(s: Sign) -> Sign { - match s { - Sign::Plus => Sign::Minus, - Sign::Minus => Sign::Plus, - } -} - fn neg_float_str(s: String) -> String { if s.starts_with('-') { s[1..].to_owned() @@ -255,32 +223,6 @@ fn neg_float_str(s: String) -> String { } } -fn unify_int_type(l: LitIntType, r: LitIntType) -> Option { - use syntax::ast::LitIntType::*; - match (l, r) { - (Signed(lty), Signed(rty)) => { - if lty == rty { - Some(LitIntType::Signed(lty)) - } else { - None - } - } - (Unsigned(lty), Unsigned(rty)) => { - if lty == rty { - Some(LitIntType::Unsigned(lty)) - } else { - None - } - } - (Unsuffixed, Unsuffixed) => Some(Unsuffixed), - (Signed(lty), Unsuffixed) => Some(Signed(lty)), - (Unsigned(lty), Unsuffixed) => Some(Unsigned(lty)), - (Unsuffixed, Signed(rty)) => Some(Signed(rty)), - (Unsuffixed, Unsigned(rty)) => Some(Unsigned(rty)), - _ => None, - } -} - pub fn constant(lcx: &LateContext, e: &Expr) -> Option<(Constant, bool)> { let mut cx = ConstEvalLateContext { lcx: Some(lcx), @@ -381,101 +323,36 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { } fn binop(&mut self, op: BinOp, left: &Expr, right: &Expr) -> Option { - match op.node { - BiAdd => { - self.binop_apply(left, right, |l, r| { - match (l, r) { - (Constant::Byte(l8), Constant::Byte(r8)) => l8.checked_add(r8).map(Constant::Byte), - (Constant::Int(l64, lty, lsign), Constant::Int(r64, rty, rsign)) => { - add_ints(l64, r64, lty, rty, lsign, rsign) - } - // TODO: float (would need bignum library?) - _ => None, - } - }) - } - BiSub => { - self.binop_apply(left, right, |l, r| { - match (l, r) { - (Constant::Byte(l8), Constant::Byte(r8)) => { - if r8 > l8 { - None - } else { - Some(Constant::Byte(l8 - r8)) - } - } - (Constant::Int(l64, lty, lsign), Constant::Int(r64, rty, rsign)) => { - add_ints(l64, r64, lty, rty, lsign, neg_sign(rsign)) - } - _ => None, - } - }) - } - BiMul => self.divmul(left, right, u64::checked_mul), - BiDiv => self.divmul(left, right, u64::checked_div), - // BiRem, - BiAnd => self.short_circuit(left, right, false), - BiOr => self.short_circuit(left, right, true), - BiBitXor => self.bitop(left, right, |x, y| x ^ y), - BiBitAnd => self.bitop(left, right, |x, y| x & y), - BiBitOr => self.bitop(left, right, |x, y| (x | y)), - BiShl => self.bitop(left, right, |x, y| x << y), - BiShr => self.bitop(left, right, |x, y| x >> y), - BiEq => self.binop_apply(left, right, |l, r| Some(Constant::Bool(l == r))), - BiNe => self.binop_apply(left, right, |l, r| Some(Constant::Bool(l != r))), - BiLt => self.cmp(left, right, Less, true), - BiLe => self.cmp(left, right, Greater, false), - BiGe => self.cmp(left, right, Less, false), - BiGt => self.cmp(left, right, Greater, true), + let l = if let Some(l) = self.expr(left) { l } else { return None; }; + let r = self.expr(right); + match (op.node, l, r) { + (BiAdd, Constant::Int(l), Some(Constant::Int(r))) => (l + r).ok().map(Constant::Int), + (BiSub, Constant::Int(l), Some(Constant::Int(r))) => (l - r).ok().map(Constant::Int), + (BiMul, Constant::Int(l), Some(Constant::Int(r))) => (l * r).ok().map(Constant::Int), + (BiDiv, Constant::Int(l), Some(Constant::Int(r))) => (l / r).ok().map(Constant::Int), + (BiRem, Constant::Int(l), Some(Constant::Int(r))) => (l % r).ok().map(Constant::Int), + (BiAnd, Constant::Bool(false), _) => Some(Constant::Bool(false)), + (BiAnd, Constant::Bool(true), Some(r)) => Some(r), + (BiOr, Constant::Bool(true), _) => Some(Constant::Bool(true)), + (BiOr, Constant::Bool(false), Some(r)) => Some(r), + (BiBitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)), + (BiBitXor, Constant::Int(l), Some(Constant::Int(r))) => (l ^ r).ok().map(Constant::Int), + (BiBitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)), + (BiBitAnd, Constant::Int(l), Some(Constant::Int(r))) => (l & r).ok().map(Constant::Int), + (BiBitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)), + (BiBitOr, Constant::Int(l), Some(Constant::Int(r))) => (l | r).ok().map(Constant::Int), + (BiShl, Constant::Int(l), Some(Constant::Int(r))) => (l << r).ok().map(Constant::Int), + (BiShr, Constant::Int(l), Some(Constant::Int(r))) => (l >> r).ok().map(Constant::Int), + (BiEq, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l == r)), + (BiNe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l != r)), + (BiLt, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l < r)), + (BiLe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l <= r)), + (BiGe, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l >= r)), + (BiGt, Constant::Int(l), Some(Constant::Int(r))) => Some(Constant::Bool(l > r)), _ => None, } } - fn divmul(&mut self, left: &Expr, right: &Expr, f: F) -> Option - where F: Fn(u64, u64) -> Option - { - self.binop_apply(left, right, |l, r| { - match (l, r) { - (Constant::Int(l64, lty, lsign), Constant::Int(r64, rty, rsign)) => { - f(l64, r64).and_then(|value| { - let sign = if lsign == rsign { - Sign::Plus - } else { - Sign::Minus - }; - unify_int_type(lty, rty).map(|ty| Constant::Int(value, ty, sign)) - }) - } - _ => None, - } - }) - } - - fn bitop(&mut self, left: &Expr, right: &Expr, f: F) -> Option - where F: Fn(u64, u64) -> u64 - { - self.binop_apply(left, right, |l, r| { - match (l, r) { - (Constant::Bool(l), Constant::Bool(r)) => Some(Constant::Bool(f(l as u64, r as u64) != 0)), - (Constant::Byte(l8), Constant::Byte(r8)) => Some(Constant::Byte(f(l8 as u64, r8 as u64) as u8)), - (Constant::Int(l, lty, lsign), Constant::Int(r, rty, rsign)) => { - if lsign == Sign::Plus && rsign == Sign::Plus { - unify_int_type(lty, rty).map(|ty| Constant::Int(f(l, r), ty, Sign::Plus)) - } else { - None - } - } - _ => None, - } - }) - } - - fn cmp(&mut self, left: &Expr, right: &Expr, ordering: Ordering, b: bool) -> Option { - self.binop_apply(left, - right, - |l, r| l.partial_cmp(&r).map(|o| Constant::Bool(b == (o == ordering)))) - } - fn binop_apply(&mut self, left: &Expr, right: &Expr, op: F) -> Option where F: Fn(Constant, Constant) -> Option { @@ -485,51 +362,4 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> { None } } - - fn short_circuit(&mut self, left: &Expr, right: &Expr, b: bool) -> Option { - self.expr(left).and_then(|left| { - if let Constant::Bool(lbool) = left { - if lbool == b { - Some(left) - } else { - self.expr(right).and_then(|right| { - if let Constant::Bool(_) = right { - Some(right) - } else { - None - } - }) - } - } else { - None - } - }) - } -} - -fn add_ints(l64: u64, r64: u64, lty: LitIntType, rty: LitIntType, lsign: Sign, rsign: Sign) -> Option { - let ty = if let Some(ty) = unify_int_type(lty, rty) { - ty - } else { - return None; - }; - - match (lsign, rsign) { - (Sign::Plus, Sign::Plus) => l64.checked_add(r64).map(|v| Constant::Int(v, ty, Sign::Plus)), - (Sign::Plus, Sign::Minus) => { - if r64 > l64 { - Some(Constant::Int(r64 - l64, ty, Sign::Minus)) - } else { - Some(Constant::Int(l64 - r64, ty, Sign::Plus)) - } - } - (Sign::Minus, Sign::Minus) => l64.checked_add(r64).map(|v| Constant::Int(v, ty, Sign::Minus)), - (Sign::Minus, Sign::Plus) => { - if l64 > r64 { - Some(Constant::Int(l64 - r64, ty, Sign::Minus)) - } else { - Some(Constant::Int(r64 - l64, ty, Sign::Plus)) - } - } - } } diff --git a/src/identity_op.rs b/src/identity_op.rs index 8a0da7bcd..21167ce76 100644 --- a/src/identity_op.rs +++ b/src/identity_op.rs @@ -1,8 +1,9 @@ -use consts::{constant_simple, Constant, Sign}; +use consts::{constant_simple, Constant}; use rustc::lint::*; use rustc_front::hir::*; use syntax::codemap::Span; use utils::{span_lint, snippet, in_macro}; +use rustc_const_eval::ConstInt; /// **What it does:** This lint checks for identity operations, e.g. `x + 0`. /// @@ -54,11 +55,11 @@ impl LateLintPass for IdentityOp { fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) { - if let Some(Constant::Int(v, _, sign)) = constant_simple(e) { + if let Some(Constant::Int(v)) = constant_simple(e) { if match m { - 0 => v == 0, - -1 => sign == Sign::Minus && v == 1, - 1 => sign == Sign::Plus && v == 1, + 0 => v == ConstInt::Infer(0), + -1 => v == ConstInt::InferSigned(-1), + 1 => v == ConstInt::Infer(1), _ => unreachable!(), } { span_lint(cx, diff --git a/tests/consts.rs b/tests/consts.rs index 5c6088d05..78853f7c1 100644 --- a/tests/consts.rs +++ b/tests/consts.rs @@ -5,8 +5,10 @@ extern crate clippy; extern crate syntax; extern crate rustc; extern crate rustc_front; +extern crate rustc_const_eval; use rustc_front::hir::*; +use rustc_const_eval::ConstInt; use syntax::parse::token::InternedString; use syntax::ptr::P; use syntax::codemap::{Spanned, COMMAND_LINE_SP}; @@ -15,7 +17,7 @@ use syntax::ast::LitKind; use syntax::ast::LitIntType; use syntax::ast::StrStyle; -use clippy::consts::{constant_simple, Constant, FloatWidth, Sign}; +use clippy::consts::{constant_simple, Constant, FloatWidth}; fn spanned(t: T) -> Spanned { Spanned{ node: t, span: COMMAND_LINE_SP } @@ -44,9 +46,9 @@ fn check(expect: Constant, expr: &Expr) { const TRUE : Constant = Constant::Bool(true); const FALSE : Constant = Constant::Bool(false); -const ZERO : Constant = Constant::Int(0, LitIntType::Unsuffixed, Sign::Plus); -const ONE : Constant = Constant::Int(1, LitIntType::Unsuffixed, Sign::Plus); -const TWO : Constant = Constant::Int(2, LitIntType::Unsuffixed, Sign::Plus); +const ZERO : Constant = Constant::Int(ConstInt::Infer(0)); +const ONE : Constant = Constant::Int(ConstInt::Infer(1)); +const TWO : Constant = Constant::Int(ConstInt::Infer(2)); #[test] fn test_lit() { From 64110f16dd5fc8b28bcee3f4291f8c4ffb6162aa Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 16 Mar 2016 16:28:31 +0100 Subject: [PATCH 2/2] fix `Eq`+`Hash` for `Constant` --- src/consts.rs | 5 +++-- src/identity_op.rs | 8 ++++---- tests/consts.rs | 4 ++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index 06f790d83..3e08f1b74 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -85,7 +85,7 @@ impl PartialEq for Constant { (&Constant::Str(ref ls, ref lsty), &Constant::Str(ref rs, ref rsty)) => ls == rs && lsty == rsty, (&Constant::Binary(ref l), &Constant::Binary(ref r)) => l == r, (&Constant::Char(l), &Constant::Char(r)) => l == r, - (&Constant::Int(l), &Constant::Int(r)) => l == r, + (&Constant::Int(l), &Constant::Int(r)) => l.is_negative() == r.is_negative() && l.to_u64_unchecked() == r.to_u64_unchecked(), (&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => { // we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have // `Fw32 == Fw64` so don’t compare them @@ -119,7 +119,8 @@ impl Hash for Constant { c.hash(state); } Constant::Int(i) => { - i.hash(state); + i.to_u64_unchecked().hash(state); + i.is_negative().hash(state); } Constant::Float(ref f, _) => { // don’t use the width here because of PartialEq implementation diff --git a/src/identity_op.rs b/src/identity_op.rs index 21167ce76..9ade801ab 100644 --- a/src/identity_op.rs +++ b/src/identity_op.rs @@ -55,11 +55,11 @@ impl LateLintPass for IdentityOp { fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) { - if let Some(Constant::Int(v)) = constant_simple(e) { + if let Some(v @ Constant::Int(_)) = constant_simple(e) { if match m { - 0 => v == ConstInt::Infer(0), - -1 => v == ConstInt::InferSigned(-1), - 1 => v == ConstInt::Infer(1), + 0 => v == Constant::Int(ConstInt::Infer(0)), + -1 => v == Constant::Int(ConstInt::InferSigned(-1)), + 1 => v == Constant::Int(ConstInt::Infer(1)), _ => unreachable!(), } { span_lint(cx, diff --git a/tests/consts.rs b/tests/consts.rs index 78853f7c1..3a774f674 100644 --- a/tests/consts.rs +++ b/tests/consts.rs @@ -86,4 +86,8 @@ fn test_ops() { assert_eq!(half_any, half32); assert_eq!(half_any, half64); assert_eq!(half32, half64); // for transitivity + + assert_eq!(Constant::Int(ConstInt::Infer(0)), Constant::Int(ConstInt::U8(0))); + assert_eq!(Constant::Int(ConstInt::Infer(0)), Constant::Int(ConstInt::I8(0))); + assert_eq!(Constant::Int(ConstInt::InferSigned(-1)), Constant::Int(ConstInt::I8(-1))); }