diff --git a/Cargo.lock b/Cargo.lock index 01300d56cff9c..89d0f9ed5cb6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4170,6 +4170,7 @@ version = "0.0.0" dependencies = [ "bitflags", "rustc_abi", + "rustc_apfloat", "rustc_ast", "rustc_ast_pretty", "rustc_attr_parsing", diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 3a50aac50cb3e..035d8b4903fa1 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" # tidy-alphabetical-start bitflags = "2.4.1" rustc_abi = { path = "../rustc_abi" } +rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index d49f599407c25..b6c67549c47e2 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -1,10 +1,12 @@ use hir::{ExprKind, Node}; use rustc_abi::{Integer, Size}; +use rustc_apfloat::Float; +use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, Semantics, SingleS}; use rustc_hir::{HirId, attrs}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use crate::LateContext; @@ -383,6 +385,13 @@ fn lint_uint_literal<'tcx>( } } +/// `None` if `v` does not parse as the float type, otherwise indicates whether a literal rounds +/// to infinity. +fn float_is_infinite(v: Symbol) -> Option { + let x: IeeeFloat = v.as_str().parse().ok()?; + Some(x.is_infinite()) +} + pub(crate) fn lint_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, @@ -405,18 +414,18 @@ pub(crate) fn lint_literal<'tcx>( lint_uint_literal(cx, hir_id, span, lit, t) } ty::Float(t) => { - let (is_infinite, sym) = match lit.node { - ast::LitKind::Float(v, _) => match t { - // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI - // issues resolved). - ty::FloatTy::F16 => (Ok(false), v), - ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v), - ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v), - ty::FloatTy::F128 => (Ok(false), v), - }, - _ => bug!(), + let ast::LitKind::Float(v, _) = lit.node else { + bug!(); }; - if is_infinite == Ok(true) { + + let is_infinite = match t { + ty::FloatTy::F16 => float_is_infinite::(v), + ty::FloatTy::F32 => float_is_infinite::(v), + ty::FloatTy::F64 => float_is_infinite::(v), + ty::FloatTy::F128 => float_is_infinite::(v), + }; + + if is_infinite == Some(true) { cx.emit_span_lint( OVERFLOWING_LITERALS, span, @@ -426,7 +435,7 @@ pub(crate) fn lint_literal<'tcx>( .sess() .source_map() .span_to_snippet(lit.span) - .unwrap_or_else(|_| sym.to_string()), + .unwrap_or_else(|_| v.to_string()), }, ); } diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 556db239f2499..59a74b7e0e5c9 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -13,8 +13,14 @@ macro_rules! impl_general_format { ($($t:ident)*) => { $(impl GeneralFormat for $t { fn already_rounded_value_should_use_exponential(&self) -> bool { + // `max_abs` rounds to infinity for `f16`. This is fine to save us from a more + // complex macro, it just means a positive-exponent `f16` will never print as + // scientific notation by default (reasonably, the max is 65504.0). + #[allow(overflowing_literals)] + let max_abs = 1e+16; + let abs = $t::abs(*self); - (abs != 0.0 && abs < 1e-4) || abs >= 1e+16 + (abs != 0.0 && abs < 1e-4) || abs >= max_abs } })* } diff --git a/tests/ui/lint/lint-type-overflow2.rs b/tests/ui/lint/lint-type-overflow2.rs index ac7420326c893..d3ff02aeb722f 100644 --- a/tests/ui/lint/lint-type-overflow2.rs +++ b/tests/ui/lint/lint-type-overflow2.rs @@ -1,13 +1,19 @@ //@ compile-flags: -O +#![feature(f16)] +#![feature(f128)] #![deny(overflowing_literals)] fn main() { let x2: i8 = --128; //~ ERROR literal out of range for `i8` //~| WARN use of a double negation + let x = -65520.0_f16; //~ ERROR literal out of range for `f16` + let x = 65520.0_f16; //~ ERROR literal out of range for `f16` let x = -3.40282357e+38_f32; //~ ERROR literal out of range for `f32` let x = 3.40282357e+38_f32; //~ ERROR literal out of range for `f32` let x = -1.7976931348623159e+308_f64; //~ ERROR literal out of range for `f64` let x = 1.7976931348623159e+308_f64; //~ ERROR literal out of range for `f64` + let x = -1.1897314953572317650857593266280075e+4932_f128; //~ ERROR literal out of range for `f128` + let x = 1.1897314953572317650857593266280075e+4932_f128; //~ ERROR literal out of range for `f128` } diff --git a/tests/ui/lint/lint-type-overflow2.stderr b/tests/ui/lint/lint-type-overflow2.stderr index 2cfb18e9fe924..c045d243753e4 100644 --- a/tests/ui/lint/lint-type-overflow2.stderr +++ b/tests/ui/lint/lint-type-overflow2.stderr @@ -1,5 +1,5 @@ warning: use of a double negation - --> $DIR/lint-type-overflow2.rs:6:18 + --> $DIR/lint-type-overflow2.rs:8:18 | LL | let x2: i8 = --128; | ^^^^^ @@ -13,7 +13,7 @@ LL | let x2: i8 = -(-128); | + + error: literal out of range for `i8` - --> $DIR/lint-type-overflow2.rs:6:20 + --> $DIR/lint-type-overflow2.rs:8:20 | LL | let x2: i8 = --128; | ^^^ @@ -21,13 +21,29 @@ LL | let x2: i8 = --128; = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` = help: consider using the type `u8` instead note: the lint level is defined here - --> $DIR/lint-type-overflow2.rs:3:9 + --> $DIR/lint-type-overflow2.rs:5:9 | LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ +error: literal out of range for `f16` + --> $DIR/lint-type-overflow2.rs:11:14 + | +LL | let x = -65520.0_f16; + | ^^^^^^^^^^^ + | + = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY` + +error: literal out of range for `f16` + --> $DIR/lint-type-overflow2.rs:12:14 + | +LL | let x = 65520.0_f16; + | ^^^^^^^^^^^ + | + = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY` + error: literal out of range for `f32` - --> $DIR/lint-type-overflow2.rs:9:14 + --> $DIR/lint-type-overflow2.rs:13:14 | LL | let x = -3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ @@ -35,7 +51,7 @@ LL | let x = -3.40282357e+38_f32; = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f32` - --> $DIR/lint-type-overflow2.rs:10:14 + --> $DIR/lint-type-overflow2.rs:14:14 | LL | let x = 3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ @@ -43,7 +59,7 @@ LL | let x = 3.40282357e+38_f32; = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f64` - --> $DIR/lint-type-overflow2.rs:11:14 + --> $DIR/lint-type-overflow2.rs:15:14 | LL | let x = -1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -51,12 +67,28 @@ LL | let x = -1.7976931348623159e+308_f64; = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` error: literal out of range for `f64` - --> $DIR/lint-type-overflow2.rs:12:14 + --> $DIR/lint-type-overflow2.rs:16:14 | LL | let x = 1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` -error: aborting due to 5 previous errors; 1 warning emitted +error: literal out of range for `f128` + --> $DIR/lint-type-overflow2.rs:17:14 + | +LL | let x = -1.1897314953572317650857593266280075e+4932_f128; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `1.1897314953572317650857593266280075e+4932_f128` does not fit into the type `f128` and will be converted to `f128::INFINITY` + +error: literal out of range for `f128` + --> $DIR/lint-type-overflow2.rs:18:14 + | +LL | let x = 1.1897314953572317650857593266280075e+4932_f128; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `1.1897314953572317650857593266280075e+4932_f128` does not fit into the type `f128` and will be converted to `f128::INFINITY` + +error: aborting due to 9 previous errors; 1 warning emitted