Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6e6d23c

Browse files
committed
Add FCW for unsuffixed float literal f32 fallback
1 parent 17abf3a commit 6e6d23c

File tree

14 files changed

+205
-15
lines changed

14 files changed

+205
-15
lines changed

‎compiler/rustc_hir_typeck/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ hir_typeck_field_multiply_specified_in_initializer =
9292
.label = used more than once
9393
.previous_use_label = first use of `{$ident}`
9494
95+
hir_typeck_float_literal_f32_fallback =
96+
falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
97+
.suggestion = explicitly specify the type as `f32`
98+
9599
hir_typeck_fn_item_to_variadic_function = can't pass a function item to a variadic function
96100
.suggestion = use a function pointer instead
97101
.help = a function item is zero-sized and needs to be cast into a function pointer to be used in FFI

‎compiler/rustc_hir_typeck/src/demand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
342342
match infer {
343343
ty::TyVar(_) => self.next_ty_var(DUMMY_SP),
344344
ty::IntVar(_) => self.next_int_var(),
345-
ty::FloatVar(_) => self.next_float_var(),
345+
ty::FloatVar(_) => self.next_float_var(DUMMY_SP),
346346
ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {
347347
bug!("unexpected fresh ty outside of the trait solver")
348348
}

‎compiler/rustc_hir_typeck/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,3 +983,11 @@ pub(crate) struct RegisterTypeUnstable<'a> {
983983
pub span: Span,
984984
pub ty: Ty<'a>,
985985
}
986+
987+
#[derive(LintDiagnostic)]
988+
#[diag(hir_typeck_float_literal_f32_fallback)]
989+
pub(crate) struct FloatLiteralF32Fallback {
990+
pub literal: String,
991+
#[suggestion(code = "{literal}_f32", applicability = "machine-applicable")]
992+
pub span: Option<Span>,
993+
}

‎compiler/rustc_hir_typeck/src/fallback.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ use rustc_data_structures::graph::iterate::DepthFirstSearch;
66
use rustc_data_structures::graph::vec_graph::VecGraph;
77
use rustc_data_structures::graph::{self};
88
use rustc_data_structures::unord::{UnordBag, UnordMap, UnordSet};
9-
use rustc_hir as hir;
10-
use rustc_hir::HirId;
119
use rustc_hir::def::{DefKind, Res};
1210
use rustc_hir::def_id::DefId;
1311
use rustc_hir::intravisit::{InferKind, Visitor};
12+
use rustc_hir::{self as hir, CRATE_HIR_ID, HirId};
13+
use rustc_lint::builtin::FLOAT_LITERAL_F32_FALLBACK;
1414
use rustc_middle::ty::{
1515
self, ClauseKind, FloatVid, PredicatePolarity, TraitPredicate, Ty, TyCtxt, TypeSuperVisitable,
1616
TypeVisitable,
@@ -221,6 +221,20 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
221221
.iter()
222222
.flat_map(|ty| ty.float_vid())
223223
.filter(|vid| roots.contains(&self.root_float_var(*vid)))
224+
.inspect(|vid| {
225+
let span = self.float_var_origin(*vid);
226+
// Show the entire literal in the suggestion to make it clearer.
227+
let literal = self.tcx.sess.source_map().span_to_snippet(span).ok();
228+
self.tcx.emit_node_span_lint(
229+
FLOAT_LITERAL_F32_FALLBACK,
230+
CRATE_HIR_ID,
231+
span,
232+
errors::FloatLiteralF32Fallback {
233+
span: literal.as_ref().map(|_| span),
234+
literal: literal.unwrap_or_default(),
235+
},
236+
);
237+
})
224238
.collect();
225239
debug!("calculate_fallback_to_f32: fallback_to_f32={:?}", fallback_to_f32);
226240
fallback_to_f32

‎compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1607,7 +1607,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16071607
ty::Float(_) => Some(ty),
16081608
_ => None,
16091609
});
1610-
opt_ty.unwrap_or_else(|| self.next_float_var())
1610+
opt_ty.unwrap_or_else(|| self.next_float_var(lit.span))
16111611
}
16121612
ast::LitKind::Bool(_) => tcx.types.bool,
16131613
ast::LitKind::CStr(_, _) => Ty::new_imm_ref(

‎compiler/rustc_infer/src/infer/canonical/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl<'tcx> InferCtxt<'tcx> {
116116

117117
CanonicalTyVarKind::Int => self.next_int_var(),
118118

119-
CanonicalTyVarKind::Float => self.next_float_var(),
119+
CanonicalTyVarKind::Float => self.next_float_var(span),
120120
};
121121
ty.into()
122122
}

‎compiler/rustc_infer/src/infer/mod.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_data_structures::unify as ut;
2121
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
2222
use rustc_hir as hir;
2323
use rustc_hir::def_id::{DefId, LocalDefId};
24+
use rustc_index::IndexVec;
2425
use rustc_macros::extension;
2526
pub use rustc_macros::{TypeFoldable, TypeVisitable};
2627
use rustc_middle::bug;
@@ -110,6 +111,10 @@ pub struct InferCtxtInner<'tcx> {
110111
/// Map from floating variable to the kind of float it represents.
111112
float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>,
112113

114+
/// Map from floating variable to the origin span it came from. This is only used for the FCW
115+
/// for the fallback to `f32`, so can be removed once the `f32` fallback is removed.
116+
float_origin_span_storage: IndexVec<FloatVid, Span>,
117+
113118
/// Tracks the set of region variables and the constraints between them.
114119
///
115120
/// This is initially `Some(_)` but when
@@ -166,6 +171,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
166171
const_unification_storage: Default::default(),
167172
int_unification_storage: Default::default(),
168173
float_unification_storage: Default::default(),
174+
float_origin_span_storage: Default::default(),
169175
region_constraint_storage: Some(Default::default()),
170176
region_obligations: vec![],
171177
opaque_type_storage: Default::default(),
@@ -625,6 +631,13 @@ impl<'tcx> InferCtxt<'tcx> {
625631
self.inner.borrow_mut().type_variables().var_origin(vid)
626632
}
627633

634+
/// Returns the origin of the float type variable identified by `vid`.
635+
///
636+
/// No attempt is made to resolve `vid` to its root variable.
637+
pub fn float_var_origin(&self, vid: FloatVid) -> Span {
638+
self.inner.borrow_mut().float_origin_span_storage[vid]
639+
}
640+
628641
/// Returns the origin of the const variable identified by `vid`
629642
// FIXME: We should store origins separately from the unification table
630643
// so this doesn't need to be optional.
@@ -810,9 +823,11 @@ impl<'tcx> InferCtxt<'tcx> {
810823
Ty::new_int_var(self.tcx, next_int_var_id)
811824
}
812825

813-
pub fn next_float_var(&self) -> Ty<'tcx> {
814-
let next_float_var_id =
815-
self.inner.borrow_mut().float_unification_table().new_key(ty::FloatVarValue::Unknown);
826+
pub fn next_float_var(&self, span: Span) -> Ty<'tcx> {
827+
let mut inner = self.inner.borrow_mut();
828+
let next_float_var_id = inner.float_unification_table().new_key(ty::FloatVarValue::Unknown);
829+
let span_index = inner.float_origin_span_storage.push(span);
830+
debug_assert_eq!(next_float_var_id, span_index);
816831
Ty::new_float_var(self.tcx, next_float_var_id)
817832
}
818833

‎compiler/rustc_infer/src/infer/snapshot/fudge.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ use rustc_middle::ty::{
55
self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder,
66
TypeSuperFoldable, TypeVisitableExt,
77
};
8+
use rustc_span::Span;
89
use tracing::instrument;
910
use ut::UnifyKey;
1011

1112
use super::VariableLengths;
1213
use crate::infer::type_variable::TypeVariableOrigin;
1314
use crate::infer::unify_key::{ConstVariableValue, ConstVidKey};
14-
use crate::infer::{ConstVariableOrigin, InferCtxt, RegionVariableOrigin, UnificationTable};
15+
use crate::infer::{
16+
ConstVariableOrigin, InferCtxt, InferCtxtInner, RegionVariableOrigin, UnificationTable,
17+
};
1518

1619
fn vars_since_snapshot<'tcx, T>(
1720
table: &UnificationTable<'_, 'tcx, T>,
@@ -24,6 +27,14 @@ where
2427
T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32)
2528
}
2629

30+
fn float_vars_since_snapshot(
31+
inner: &mut InferCtxtInner<'_>,
32+
snapshot_var_len: usize,
33+
) -> (Range<FloatVid>, Vec<Span>) {
34+
let range = vars_since_snapshot(&inner.float_unification_table(), snapshot_var_len);
35+
(range.clone(), range.map(|index| inner.float_origin_span_storage[index]).collect())
36+
}
37+
2738
fn const_vars_since_snapshot<'tcx>(
2839
table: &mut UnificationTable<'_, 'tcx, ConstVidKey<'tcx>>,
2940
snapshot_var_len: usize,
@@ -128,7 +139,7 @@ struct SnapshotVarData {
128139
region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>),
129140
type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>),
130141
int_vars: Range<IntVid>,
131-
float_vars: Range<FloatVid>,
142+
float_vars: (Range<FloatVid>,Vec<Span>),
132143
const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>),
133144
}
134145

@@ -141,8 +152,7 @@ impl SnapshotVarData {
141152
let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len);
142153
let int_vars =
143154
vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len);
144-
let float_vars =
145-
vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len);
155+
let float_vars = float_vars_since_snapshot(&mut inner, vars_pre_snapshot.float_var_len);
146156

147157
let const_vars = const_vars_since_snapshot(
148158
&mut inner.const_unification_table(),
@@ -156,7 +166,7 @@ impl SnapshotVarData {
156166
region_vars.0.is_empty()
157167
&& type_vars.0.is_empty()
158168
&& int_vars.is_empty()
159-
&& float_vars.is_empty()
169+
&& float_vars.0.is_empty()
160170
&& const_vars.0.is_empty()
161171
}
162172
}
@@ -201,8 +211,10 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> {
201211
}
202212
}
203213
ty::FloatVar(vid) => {
204-
if self.snapshot_vars.float_vars.contains(&vid) {
205-
self.infcx.next_float_var()
214+
if self.snapshot_vars.float_vars.0.contains(&vid) {
215+
let idx = vid.as_usize() - self.snapshot_vars.float_vars.0.start.as_usize();
216+
let span = self.snapshot_vars.float_vars.1[idx];
217+
self.infcx.next_float_var(span)
206218
} else {
207219
ty
208220
}

‎compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5067,3 +5067,52 @@ declare_lint! {
50675067
reference: "issue #138762 <https://github.com/rust-lang/rust/issues/138762>",
50685068
};
50695069
}
5070+
5071+
declare_lint! {
5072+
/// The `float_literal_f32_fallback` lint detects situations where the type of an unsuffixed
5073+
/// float literal falls back to `f32` instead of `f64` to avoid a compilation error. This occurs
5074+
/// when there is a trait bound `f32: From<T>` (or equivalent, such as `T: Into<f32>`) and the
5075+
/// literal is inferred to have the same type as `T`.
5076+
///
5077+
/// ### Example
5078+
///
5079+
/// ```rust
5080+
/// fn foo(x: impl Into<f32>) -> f32 {
5081+
/// x.into()
5082+
/// }
5083+
///
5084+
/// fn main() {
5085+
/// dbg!(foo(2.5));
5086+
/// }
5087+
/// ```
5088+
///
5089+
/// {{produces}}
5090+
///
5091+
/// ### Explanation
5092+
///
5093+
/// Rust allows traits that are only implemented for a single floating point type to guide type
5094+
/// inferrence for floating point literals. This used to apply in the case of `f32: From<T>`
5095+
/// (where `T` was inferred to be the same type as a floating point literal), as the only
5096+
/// floating point type impl was `f32: From<f32>`. However, as Rust is in the process of adding
5097+
/// support for `f16`, there are now two implementations for floating point types:
5098+
/// `f32: From<f16>` and `f32: From<f32>`. This means that the trait bound `f32: From<T>` can no
5099+
/// longer guide inferrence for the type of the floating point literal. The default fallback for
5100+
/// unsuffixed floating point literals is `f64`. As `f32` does not implement `From<f64>`,
5101+
/// falling back to `f64` would cause a compilation error; therefore, the float type fallback
5102+
/// has been tempoarily adjusted to fallback to `f32` in this scenario.
5103+
///
5104+
/// The lint will automatically provide a machine-applicable suggestion to add a `_f32` suffix
5105+
/// to the literal, which will fix the problem.
5106+
///
5107+
/// This is a [future-incompatible] lint to transition this to a hard error in the future. See
5108+
/// [issue #FIXME] for more details.
5109+
///
5110+
/// [issue #FIXME]: https://github.com/rust-lang/rust/issues/FIXME
5111+
pub FLOAT_LITERAL_F32_FALLBACK,
5112+
Warn,
5113+
"detects unsuffixed floating point literals whose type fallback to `f32`",
5114+
@future_incompatible = FutureIncompatibleInfo {
5115+
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
5116+
reference: "issue #FIXME <https://github.com/rust-lang/rust/issues/FIXME>",
5117+
};
5118+
}

‎tests/ui/float/f32-into-f32.fixed

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ run-pass
2+
//@ run-rustfix
3+
4+
fn foo(_: impl Into<f32>) {}
5+
6+
fn main() {
7+
foo(1.0_f32);
8+
//~^ WARN falling back to `f32`
9+
//~| WARN this was previously accepted
10+
foo(-(2.5_f32));
11+
//~^ WARN falling back to `f32`
12+
//~| WARN this was previously accepted
13+
foo(1e5_f32);
14+
//~^ WARN falling back to `f32`
15+
//~| WARN this was previously accepted
16+
foo(4f32); // no warning
17+
let x = -4.0_f32;
18+
//~^ WARN falling back to `f32`
19+
//~| WARN this was previously accepted
20+
foo(x);
21+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /