Skip to content

Commit 763f923

Browse files
committed
Auto merge of #34006 - eddyb:mir-const-fixes, r=nikomatsakis
[MIR] Fix double-rounding of float constants and ignore NaN sign in tests. Fixes #32805 by handling f32 and f64 separately in rustc_const_eval. Also removes `#[rustc_no_mir]` from a couple libstd tests by ignoring NaN sign. Turns out that runtime evaluation of `0.0 / 0.0` produces a NaN with the sign bit set, whereas LLVM constant folds it to a NaN with the sign bit unset, which we were testing for.
2 parents 4a4a13a + f158a2f commit 763f923

File tree

10 files changed

+302
-107
lines changed

10 files changed

+302
-107
lines changed

src/librustc/middle/const_val.rs

+3-50
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ use syntax::parse::token::InternedString;
1212
use syntax::ast;
1313
use std::rc::Rc;
1414
use hir::def_id::DefId;
15-
use std::hash;
16-
use std::mem::transmute;
1715
use rustc_const_math::*;
1816
use self::ConstVal::*;
1917

20-
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
18+
#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
2119
pub enum ConstVal {
22-
Float(f64),
20+
Float(ConstFloat),
2321
Integral(ConstInt),
2422
Str(InternedString),
2523
ByteStr(Rc<Vec<u8>>),
@@ -36,55 +34,10 @@ pub enum ConstVal {
3634
Dummy,
3735
}
3836

39-
impl hash::Hash for ConstVal {
40-
fn hash<H: hash::Hasher>(&self, state: &mut H) {
41-
match *self {
42-
Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state),
43-
Integral(a) => a.hash(state),
44-
Str(ref a) => a.hash(state),
45-
ByteStr(ref a) => a.hash(state),
46-
Bool(a) => a.hash(state),
47-
Struct(a) => a.hash(state),
48-
Tuple(a) => a.hash(state),
49-
Function(a) => a.hash(state),
50-
Array(a, n) => { a.hash(state); n.hash(state) },
51-
Repeat(a, n) => { a.hash(state); n.hash(state) },
52-
Char(c) => c.hash(state),
53-
Dummy => ().hash(state),
54-
}
55-
}
56-
}
57-
58-
/// Note that equality for `ConstVal` means that the it is the same
59-
/// constant, not that the rust values are equal. In particular, `NaN
60-
/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
61-
/// are considering unequal).
62-
impl PartialEq for ConstVal {
63-
fn eq(&self, other: &ConstVal) -> bool {
64-
match (self, other) {
65-
(&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)},
66-
(&Integral(a), &Integral(b)) => a == b,
67-
(&Str(ref a), &Str(ref b)) => a == b,
68-
(&ByteStr(ref a), &ByteStr(ref b)) => a == b,
69-
(&Bool(a), &Bool(b)) => a == b,
70-
(&Struct(a), &Struct(b)) => a == b,
71-
(&Tuple(a), &Tuple(b)) => a == b,
72-
(&Function(a), &Function(b)) => a == b,
73-
(&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn),
74-
(&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn),
75-
(&Char(a), &Char(b)) => a == b,
76-
(&Dummy, &Dummy) => true, // FIXME: should this be false?
77-
_ => false,
78-
}
79-
}
80-
}
81-
82-
impl Eq for ConstVal { }
83-
8437
impl ConstVal {
8538
pub fn description(&self) -> &'static str {
8639
match *self {
87-
Float(_) => "float",
40+
Float(f) => f.description(),
8841
Integral(i) => i.description(),
8942
Str(_) => "string literal",
9043
ByteStr(_) => "byte string literal",

src/librustc_const_eval/eval.rs

+71-38
Original file line numberDiff line numberDiff line change
@@ -621,18 +621,19 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
621621
match (eval_const_expr_partial(tcx, &a, ty_hint, fn_args)?,
622622
eval_const_expr_partial(tcx, &b, b_ty, fn_args)?) {
623623
(Float(a), Float(b)) => {
624+
use std::cmp::Ordering::*;
624625
match op.node {
625-
hir::BiAdd => Float(a + b),
626-
hir::BiSub => Float(a - b),
627-
hir::BiMul => Float(a * b),
628-
hir::BiDiv => Float(a / b),
629-
hir::BiRem => Float(a % b),
630-
hir::BiEq => Bool(a == b),
631-
hir::BiLt => Bool(a < b),
632-
hir::BiLe => Bool(a <= b),
633-
hir::BiNe => Bool(a != b),
634-
hir::BiGe => Bool(a >= b),
635-
hir::BiGt => Bool(a > b),
626+
hir::BiAdd => Float(math!(e, a + b)),
627+
hir::BiSub => Float(math!(e, a - b)),
628+
hir::BiMul => Float(math!(e, a * b)),
629+
hir::BiDiv => Float(math!(e, a / b)),
630+
hir::BiRem => Float(math!(e, a % b)),
631+
hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal),
632+
hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less),
633+
hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater),
634+
hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
635+
hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
636+
hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
636637
_ => signal!(e, InvalidOpForFloats(op.node)),
637638
}
638639
}
@@ -1078,13 +1079,13 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::
10781079
}
10791080
},
10801081
ty::TyFloat(ast::FloatTy::F64) => match val.erase_type() {
1081-
Infer(u) => Ok(Float(u as f64)),
1082-
InferSigned(i) => Ok(Float(i as f64)),
1082+
Infer(u) => Ok(Float(F64(u as f64))),
1083+
InferSigned(i) => Ok(Float(F64(i as f64))),
10831084
_ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"),
10841085
},
10851086
ty::TyFloat(ast::FloatTy::F32) => match val.erase_type() {
1086-
Infer(u) => Ok(Float(u as f32 as f64)),
1087-
InferSigned(i) => Ok(Float(i as f32 as f64)),
1087+
Infer(u) => Ok(Float(F32(u as f32))),
1088+
InferSigned(i) => Ok(Float(F32(i as f32))),
10881089
_ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"),
10891090
},
10901091
ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
@@ -1097,13 +1098,35 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::
10971098
}
10981099
}
10991100

1100-
fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, f: f64, ty: ty::Ty) -> CastResult {
1101+
fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1102+
val: ConstFloat,
1103+
ty: ty::Ty) -> CastResult {
11011104
match ty.sty {
1102-
ty::TyInt(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty),
1103-
ty::TyInt(_) => cast_const_int(tcx, InferSigned(f as i64), ty),
1104-
ty::TyUint(_) if f >= 0.0 => cast_const_int(tcx, Infer(f as u64), ty),
1105-
ty::TyFloat(ast::FloatTy::F64) => Ok(Float(f)),
1106-
ty::TyFloat(ast::FloatTy::F32) => Ok(Float(f as f32 as f64)),
1105+
ty::TyInt(_) | ty::TyUint(_) => {
1106+
let i = match val {
1107+
F32(f) if f >= 0.0 => Infer(f as u64),
1108+
FInfer { f64: f, .. } |
1109+
F64(f) if f >= 0.0 => Infer(f as u64),
1110+
1111+
F32(f) => InferSigned(f as i64),
1112+
FInfer { f64: f, .. } |
1113+
F64(f) => InferSigned(f as i64)
1114+
};
1115+
1116+
if let (InferSigned(_), &ty::TyUint(_)) = (i, &ty.sty) {
1117+
return Err(CannotCast);
1118+
}
1119+
1120+
cast_const_int(tcx, i, ty)
1121+
}
1122+
ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val {
1123+
F32(f) => f as f64,
1124+
FInfer { f64: f, .. } | F64(f) => f
1125+
}))),
1126+
ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val {
1127+
F64(f) => f as f32,
1128+
FInfer { f32: f, .. } | F32(f) => f
1129+
}))),
11071130
_ => Err(CannotCast),
11081131
}
11091132
}
@@ -1161,33 +1184,43 @@ fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind,
11611184
infer(Infer(n), tcx, &ty::TyUint(ity)).map(Integral)
11621185
},
11631186

1164-
LitKind::Float(ref n, _) |
1187+
LitKind::Float(ref n, fty) => {
1188+
Ok(Float(parse_float(n, Some(fty), span)))
1189+
}
11651190
LitKind::FloatUnsuffixed(ref n) => {
1166-
if let Ok(x) = n.parse::<f64>() {
1167-
Ok(Float(x))
1168-
} else {
1169-
// FIXME(#31407) this is only necessary because float parsing is buggy
1170-
span_bug!(span, "could not evaluate float literal (see issue #31407)");
1171-
}
1191+
let fty_hint = match ty_hint.map(|t| &t.sty) {
1192+
Some(&ty::TyFloat(fty)) => Some(fty),
1193+
_ => None
1194+
};
1195+
Ok(Float(parse_float(n, fty_hint, span)))
11721196
}
11731197
LitKind::Bool(b) => Ok(Bool(b)),
11741198
LitKind::Char(c) => Ok(Char(c)),
11751199
}
11761200
}
11771201

1202+
fn parse_float(num: &str, fty_hint: Option<ast::FloatTy>, span: Span) -> ConstFloat {
1203+
let val = match fty_hint {
1204+
Some(ast::FloatTy::F32) => num.parse::<f32>().map(F32),
1205+
Some(ast::FloatTy::F64) => num.parse::<f64>().map(F64),
1206+
None => {
1207+
num.parse::<f32>().and_then(|f32| {
1208+
num.parse::<f64>().map(|f64| {
1209+
FInfer { f32: f32, f64: f64 }
1210+
})
1211+
})
1212+
}
1213+
};
1214+
val.unwrap_or_else(|_| {
1215+
// FIXME(#31407) this is only necessary because float parsing is buggy
1216+
span_bug!(span, "could not evaluate float literal (see issue #31407)");
1217+
})
1218+
}
1219+
11781220
pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
11791221
match (a, b) {
11801222
(&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
1181-
(&Float(a), &Float(b)) => {
1182-
// This is pretty bad but it is the existing behavior.
1183-
Some(if a == b {
1184-
Ordering::Equal
1185-
} else if a < b {
1186-
Ordering::Less
1187-
} else {
1188-
Ordering::Greater
1189-
})
1190-
}
1223+
(&Float(a), &Float(b)) => a.try_cmp(b).ok(),
11911224
(&Str(ref a), &Str(ref b)) => Some(a.cmp(b)),
11921225
(&Bool(a), &Bool(b)) => Some(a.cmp(&b)),
11931226
(&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)),

src/librustc_const_math/err.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@ impl ConstMathErr {
4545
use self::Op::*;
4646
match *self {
4747
NotInRange => "inferred value out of range",
48-
CmpBetweenUnequalTypes => "compared two integrals of different types",
49-
UnequalTypes(Add) => "tried to add two integrals of different types",
50-
UnequalTypes(Sub) => "tried to subtract two integrals of different types",
51-
UnequalTypes(Mul) => "tried to multiply two integrals of different types",
52-
UnequalTypes(Div) => "tried to divide two integrals of different types",
48+
CmpBetweenUnequalTypes => "compared two values of different types",
49+
UnequalTypes(Add) => "tried to add two values of different types",
50+
UnequalTypes(Sub) => "tried to subtract two values of different types",
51+
UnequalTypes(Mul) => "tried to multiply two values of different types",
52+
UnequalTypes(Div) => "tried to divide two values of different types",
5353
UnequalTypes(Rem) => {
54-
"tried to calculate the remainder of two integrals of different types"
54+
"tried to calculate the remainder of two values of different types"
5555
},
56-
UnequalTypes(BitAnd) => "tried to bitand two integrals of different types",
57-
UnequalTypes(BitOr) => "tried to bitor two integrals of different types",
58-
UnequalTypes(BitXor) => "tried to xor two integrals of different types",
56+
UnequalTypes(BitAnd) => "tried to bitand two values of different types",
57+
UnequalTypes(BitOr) => "tried to bitor two values of different types",
58+
UnequalTypes(BitXor) => "tried to xor two values of different types",
5959
UnequalTypes(_) => unreachable!(),
6060
Overflow(Add) => "attempted to add with overflow",
6161
Overflow(Sub) => "attempted to subtract with overflow",

0 commit comments

Comments
 (0)