mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Add FCW for unsuffixed float literal f32 fallback
This commit is contained in:
committed by
Folkert de Vries
parent
f354fa86b3
commit
7aad5c0784
@@ -346,7 +346,7 @@ fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
|
||||
match infer {
|
||||
ty::TyVar(_) => self.next_ty_var(DUMMY_SP),
|
||||
ty::IntVar(_) => self.next_int_var(),
|
||||
ty::FloatVar(_) => self.next_float_var(),
|
||||
ty::FloatVar(_) => self.next_float_var(DUMMY_SP),
|
||||
ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
|
||||
bug!("unexpected fresh ty outside of the trait solver")
|
||||
}
|
||||
|
||||
@@ -1384,3 +1384,15 @@ pub(crate) struct ProjectOnNonPinProjectType {
|
||||
)]
|
||||
pub sugg_span: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied")]
|
||||
pub(crate) struct FloatLiteralF32Fallback {
|
||||
pub literal: String,
|
||||
#[suggestion(
|
||||
"explicitly specify the type as `f32`",
|
||||
code = "{literal}_f32",
|
||||
applicability = "machine-applicable"
|
||||
)]
|
||||
pub span: Option<Span>,
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
use rustc_data_structures::graph;
|
||||
use rustc_data_structures::graph::vec_graph::VecGraph;
|
||||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::attrs::DivergingFallbackBehavior;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{InferKind, Visitor};
|
||||
use rustc_hir::{self as hir, CRATE_HIR_ID, HirId};
|
||||
use rustc_lint::builtin::FLOAT_LITERAL_F32_FALLBACK;
|
||||
use rustc_middle::ty::{self, FloatVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
@@ -160,6 +160,20 @@ fn calculate_fallback_to_f32(&self, unresolved_variables: &[Ty<'tcx>]) -> UnordS
|
||||
.iter()
|
||||
.flat_map(|ty| ty.float_vid())
|
||||
.filter(|vid| roots.contains(&self.root_float_var(*vid)))
|
||||
.inspect(|vid| {
|
||||
let span = self.float_var_origin(*vid);
|
||||
// Show the entire literal in the suggestion to make it clearer.
|
||||
let literal = self.tcx.sess.source_map().span_to_snippet(span).ok();
|
||||
self.tcx.emit_node_span_lint(
|
||||
FLOAT_LITERAL_F32_FALLBACK,
|
||||
CRATE_HIR_ID,
|
||||
span,
|
||||
errors::FloatLiteralF32Fallback {
|
||||
span: literal.as_ref().map(|_| span),
|
||||
literal: literal.unwrap_or_default(),
|
||||
},
|
||||
);
|
||||
})
|
||||
.collect();
|
||||
debug!("calculate_fallback_to_f32: fallback_to_f32={:?}", fallback_to_f32);
|
||||
fallback_to_f32
|
||||
|
||||
@@ -765,7 +765,7 @@ pub(in super::super) fn check_expr_lit(
|
||||
ty::Float(_) => Some(ty),
|
||||
_ => None,
|
||||
});
|
||||
opt_ty.unwrap_or_else(|| self.next_float_var())
|
||||
opt_ty.unwrap_or_else(|| self.next_float_var(lit.span))
|
||||
}
|
||||
ast::LitKind::Bool(_) => tcx.types.bool,
|
||||
ast::LitKind::CStr(_, _) => Ty::new_imm_ref(
|
||||
|
||||
@@ -224,7 +224,7 @@ fn span(&self) -> Span {
|
||||
|
||||
fn config(&self) -> InspectConfig {
|
||||
// Avoid hang from exponentially growing proof trees (see `cycle-modulo-ambig-aliases.rs`).
|
||||
// 3 is more than enough for all occurences in practice (a.k.a. `Into`).
|
||||
// 3 is more than enough for all occurrences in practice (a.k.a. `Into`).
|
||||
InspectConfig { max_depth: 3 }
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ pub fn instantiate_canonical_var(
|
||||
|
||||
CanonicalVarKind::Int => self.next_int_var().into(),
|
||||
|
||||
CanonicalVarKind::Float => self.next_float_var().into(),
|
||||
CanonicalVarKind::Float => self.next_float_var(span).into(),
|
||||
|
||||
CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, bound, .. }) => {
|
||||
let universe_mapped = universe_map(universe);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_macros::extension;
|
||||
pub use rustc_macros::{TypeFoldable, TypeVisitable};
|
||||
use rustc_middle::bug;
|
||||
@@ -108,6 +109,10 @@ pub struct InferCtxtInner<'tcx> {
|
||||
/// Map from floating variable to the kind of float it represents.
|
||||
float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>,
|
||||
|
||||
/// Map from floating variable to the origin span it came from. This is only used for the FCW
|
||||
/// for the fallback to `f32`, so can be removed once the `f32` fallback is removed.
|
||||
float_origin_span_storage: IndexVec<FloatVid, Span>,
|
||||
|
||||
/// Tracks the set of region variables and the constraints between them.
|
||||
///
|
||||
/// This is initially `Some(_)` but when
|
||||
@@ -161,6 +166,7 @@ fn new() -> InferCtxtInner<'tcx> {
|
||||
const_unification_storage: Default::default(),
|
||||
int_unification_storage: Default::default(),
|
||||
float_unification_storage: Default::default(),
|
||||
float_origin_span_storage: Default::default(),
|
||||
region_constraint_storage: Some(Default::default()),
|
||||
region_obligations: Default::default(),
|
||||
region_assumptions: Default::default(),
|
||||
@@ -644,6 +650,13 @@ pub fn type_var_origin(&self, vid: TyVid) -> TypeVariableOrigin {
|
||||
self.inner.borrow_mut().type_variables().var_origin(vid)
|
||||
}
|
||||
|
||||
/// Returns the origin of the float type variable identified by `vid`.
|
||||
///
|
||||
/// No attempt is made to resolve `vid` to its root variable.
|
||||
pub fn float_var_origin(&self, vid: FloatVid) -> Span {
|
||||
self.inner.borrow_mut().float_origin_span_storage[vid]
|
||||
}
|
||||
|
||||
/// Returns the origin of the const variable identified by `vid`
|
||||
// FIXME: We should store origins separately from the unification table
|
||||
// so this doesn't need to be optional.
|
||||
@@ -821,9 +834,11 @@ pub fn next_int_var(&self) -> Ty<'tcx> {
|
||||
Ty::new_int_var(self.tcx, next_int_var_id)
|
||||
}
|
||||
|
||||
pub fn next_float_var(&self) -> Ty<'tcx> {
|
||||
let next_float_var_id =
|
||||
self.inner.borrow_mut().float_unification_table().new_key(ty::FloatVarValue::Unknown);
|
||||
pub fn next_float_var(&self, span: Span) -> Ty<'tcx> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let next_float_var_id = inner.float_unification_table().new_key(ty::FloatVarValue::Unknown);
|
||||
let span_index = inner.float_origin_span_storage.push(span);
|
||||
debug_assert_eq!(next_float_var_id, span_index);
|
||||
Ty::new_float_var(self.tcx, next_float_var_id)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder,
|
||||
TypeSuperFoldable, TypeVisitableExt,
|
||||
};
|
||||
use rustc_span::Span;
|
||||
use tracing::instrument;
|
||||
use ut::UnifyKey;
|
||||
|
||||
use super::VariableLengths;
|
||||
use crate::infer::type_variable::TypeVariableOrigin;
|
||||
use crate::infer::unify_key::{ConstVariableValue, ConstVidKey};
|
||||
use crate::infer::{ConstVariableOrigin, InferCtxt, RegionVariableOrigin, UnificationTable};
|
||||
use crate::infer::{
|
||||
ConstVariableOrigin, InferCtxt, InferCtxtInner, RegionVariableOrigin, UnificationTable,
|
||||
};
|
||||
|
||||
fn vars_since_snapshot<'tcx, T>(
|
||||
table: &UnificationTable<'_, 'tcx, T>,
|
||||
@@ -25,6 +28,14 @@ fn vars_since_snapshot<'tcx, T>(
|
||||
T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32)
|
||||
}
|
||||
|
||||
fn float_vars_since_snapshot(
|
||||
inner: &mut InferCtxtInner<'_>,
|
||||
snapshot_var_len: usize,
|
||||
) -> (Range<FloatVid>, Vec<Span>) {
|
||||
let range = vars_since_snapshot(&inner.float_unification_table(), snapshot_var_len);
|
||||
(range.clone(), range.map(|index| inner.float_origin_span_storage[index]).collect())
|
||||
}
|
||||
|
||||
fn const_vars_since_snapshot<'tcx>(
|
||||
table: &mut UnificationTable<'_, 'tcx, ConstVidKey<'tcx>>,
|
||||
snapshot_var_len: usize,
|
||||
@@ -130,7 +141,7 @@ struct SnapshotVarData<'tcx> {
|
||||
region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin<'tcx>>),
|
||||
type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>),
|
||||
int_vars: Range<IntVid>,
|
||||
float_vars: Range<FloatVid>,
|
||||
float_vars: (Range<FloatVid>, Vec<Span>),
|
||||
const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>),
|
||||
}
|
||||
|
||||
@@ -143,8 +154,7 @@ fn new(infcx: &InferCtxt<'tcx>, vars_pre_snapshot: VariableLengths) -> SnapshotV
|
||||
let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len);
|
||||
let int_vars =
|
||||
vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len);
|
||||
let float_vars =
|
||||
vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len);
|
||||
let float_vars = float_vars_since_snapshot(&mut inner, vars_pre_snapshot.float_var_len);
|
||||
|
||||
let const_vars = const_vars_since_snapshot(
|
||||
&mut inner.const_unification_table(),
|
||||
@@ -158,7 +168,7 @@ fn is_empty(&self) -> bool {
|
||||
region_vars.0.is_empty()
|
||||
&& type_vars.0.is_empty()
|
||||
&& int_vars.is_empty()
|
||||
&& float_vars.is_empty()
|
||||
&& float_vars.0.is_empty()
|
||||
&& const_vars.0.is_empty()
|
||||
}
|
||||
}
|
||||
@@ -203,8 +213,10 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
}
|
||||
}
|
||||
ty::FloatVar(vid) => {
|
||||
if self.snapshot_vars.float_vars.contains(&vid) {
|
||||
self.infcx.next_float_var()
|
||||
if self.snapshot_vars.float_vars.0.contains(&vid) {
|
||||
let idx = vid.as_usize() - self.snapshot_vars.float_vars.0.start.as_usize();
|
||||
let span = self.snapshot_vars.float_vars.1[idx];
|
||||
self.infcx.next_float_var(span)
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
|
||||
@@ -5643,3 +5643,52 @@
|
||||
"detects uses of deprecated LLVM intrinsics",
|
||||
@feature_gate = link_llvm_intrinsics;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `float_literal_f32_fallback` lint detects situations where the type of an unsuffixed
|
||||
/// float literal falls back to `f32` instead of `f64` to avoid a compilation error. This occurs
|
||||
/// when there is a trait bound `f32: From<T>` (or equivalent, such as `T: Into<f32>`) and the
|
||||
/// literal is inferred to have the same type as `T`.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// fn foo(x: impl Into<f32>) -> f32 {
|
||||
/// x.into()
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// dbg!(foo(2.5));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Rust allows traits that are only implemented for a single floating point type to guide type
|
||||
/// inferrence for floating point literals. This used to apply in the case of `f32: From<T>`
|
||||
/// (where `T` was inferred to be the same type as a floating point literal), as the only
|
||||
/// floating point type impl was `f32: From<f32>`. However, as Rust is in the process of adding
|
||||
/// support for `f16`, there are now two implementations for floating point types:
|
||||
/// `f32: From<f16>` and `f32: From<f32>`. This means that the trait bound `f32: From<T>` can no
|
||||
/// longer guide inference for the type of the floating point literal. The default fallback for
|
||||
/// unsuffixed floating point literals is `f64`. As `f32` does not implement `From<f64>`,
|
||||
/// falling back to `f64` would cause a compilation error; therefore, the float type fallback
|
||||
/// has been tempoarily adjusted to fallback to `f32` in this scenario.
|
||||
///
|
||||
/// The lint will automatically provide a machine-applicable suggestion to add a `_f32` suffix
|
||||
/// to the literal, which will fix the problem.
|
||||
///
|
||||
/// This is a [future-incompatible] lint to transition this to a hard error in the future. See
|
||||
/// [issue #FIXME] for more details.
|
||||
///
|
||||
/// [issue #FIXME]: https://github.com/rust-lang/rust/issues/FIXME
|
||||
pub FLOAT_LITERAL_F32_FALLBACK,
|
||||
Warn,
|
||||
"detects unsuffixed floating point literals whose type fallback to `f32`",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: fcw!(FutureReleaseError #0),
|
||||
report_in_deps: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//@ revisions: old-solver next-solver
|
||||
//@[next-solver] compile-flags: -Znext-solver
|
||||
//@ run-pass
|
||||
//@ run-rustfix
|
||||
|
||||
fn foo(_: impl Into<f32>) {}
|
||||
|
||||
fn main() {
|
||||
foo(1.0_f32);
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(-(2.5_f32));
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(1e5_f32);
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(4f32); // no warning
|
||||
let x = -4.0_f32;
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(x);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/f32-into-f32.rs:9:9
|
||||
|
|
||||
LL | foo(1.0);
|
||||
| ^^^ help: explicitly specify the type as `f32`: `1.0_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #0 <https://github.com/rust-lang/rust/issues/0>
|
||||
= note: `#[warn(float_literal_f32_fallback)]` on by default
|
||||
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/f32-into-f32.rs:12:11
|
||||
|
|
||||
LL | foo(-(2.5));
|
||||
| ^^^ help: explicitly specify the type as `f32`: `2.5_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #0 <https://github.com/rust-lang/rust/issues/0>
|
||||
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/f32-into-f32.rs:15:9
|
||||
|
|
||||
LL | foo(1e5);
|
||||
| ^^^ help: explicitly specify the type as `f32`: `1e5_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #0 <https://github.com/rust-lang/rust/issues/0>
|
||||
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/f32-into-f32.rs:19:14
|
||||
|
|
||||
LL | let x = -4.0;
|
||||
| ^^^ help: explicitly specify the type as `f32`: `4.0_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #0 <https://github.com/rust-lang/rust/issues/0>
|
||||
|
||||
warning: 4 warnings emitted
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//@ revisions: old-solver next-solver
|
||||
//@[next-solver] compile-flags: -Znext-solver
|
||||
//@ run-pass
|
||||
//@ run-rustfix
|
||||
|
||||
fn foo(_: impl Into<f32>) {}
|
||||
|
||||
fn main() {
|
||||
foo(1.0_f32);
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(-(2.5_f32));
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(1e5_f32);
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(4f32); // no warning
|
||||
let x = -4.0_f32;
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(x);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/f32-into-f32.rs:9:9
|
||||
|
|
||||
LL | foo(1.0);
|
||||
| ^^^ help: explicitly specify the type as `f32`: `1.0_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #0 <https://github.com/rust-lang/rust/issues/0>
|
||||
= note: `#[warn(float_literal_f32_fallback)]` on by default
|
||||
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/f32-into-f32.rs:12:11
|
||||
|
|
||||
LL | foo(-(2.5));
|
||||
| ^^^ help: explicitly specify the type as `f32`: `2.5_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #0 <https://github.com/rust-lang/rust/issues/0>
|
||||
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/f32-into-f32.rs:15:9
|
||||
|
|
||||
LL | foo(1e5);
|
||||
| ^^^ help: explicitly specify the type as `f32`: `1e5_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #0 <https://github.com/rust-lang/rust/issues/0>
|
||||
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/f32-into-f32.rs:19:14
|
||||
|
|
||||
LL | let x = -4.0;
|
||||
| ^^^ help: explicitly specify the type as `f32`: `4.0_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #0 <https://github.com/rust-lang/rust/issues/0>
|
||||
|
||||
warning: 4 warnings emitted
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
//@ revisions: old-solver next-solver
|
||||
//@[next-solver] compile-flags: -Znext-solver
|
||||
//@ run-pass
|
||||
//@ run-rustfix
|
||||
|
||||
fn foo(_: impl Into<f32>) {}
|
||||
|
||||
fn main() {
|
||||
foo(1.0);
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(-(2.5));
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(1e5);
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(4f32); // no warning
|
||||
let x = -4.0;
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
foo(x);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,7 @@
|
||||
|
||||
fn main() {
|
||||
let x = f32::from(3.14);
|
||||
//~^ WARN falling back to `f32`
|
||||
//~| WARN this was previously accepted
|
||||
let y = f64::from(3.14);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
warning: falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
|
||||
--> $DIR/untyped-primitives.rs:7:23
|
||||
|
|
||||
LL | let x = f32::from(3.14);
|
||||
| ^^^^ help: explicitly specify the type as `f32`: `3.14_f32`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #FIXME <https://github.com/rust-lang/rust/issues/FIXME>
|
||||
= note: `#[warn(float_literal_f32_fallback)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
Reference in New Issue
Block a user