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 ab64774

Browse files
committed
Add FCW for unsuffixed float literal f32 fallback
1 parent 345c190 commit ab64774

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
@@ -971,3 +971,11 @@ pub(crate) struct RegisterTypeUnstable<'a> {
971971
pub span: Span,
972972
pub ty: Ty<'a>,
973973
}
974+
975+
#[derive(LintDiagnostic)]
976+
#[diag(hir_typeck_float_literal_f32_fallback)]
977+
pub(crate) struct FloatLiteralF32Fallback {
978+
pub literal: String,
979+
#[suggestion(code = "{literal}_f32", applicability = "machine-applicable")]
980+
pub span: Option<Span>,
981+
}

‎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
@@ -1663,7 +1663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16631663
ty::Float(_) => Some(ty),
16641664
_ => None,
16651665
});
1666-
opt_ty.unwrap_or_else(|| self.next_float_var())
1666+
opt_ty.unwrap_or_else(|| self.next_float_var(lit.span))
16671667
}
16681668
ast::LitKind::Bool(_) => tcx.types.bool,
16691669
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(),
@@ -634,6 +640,13 @@ impl<'tcx> InferCtxt<'tcx> {
634640
self.inner.borrow_mut().type_variables().var_origin(vid)
635641
}
636642

643+
/// Returns the origin of the float type variable identified by `vid`.
644+
///
645+
/// No attempt is made to resolve `vid` to its root variable.
646+
pub fn float_var_origin(&self, vid: FloatVid) -> Span {
647+
self.inner.borrow_mut().float_origin_span_storage[vid]
648+
}
649+
637650
/// Returns the origin of the const variable identified by `vid`
638651
// FIXME: We should store origins separately from the unification table
639652
// so this doesn't need to be optional.
@@ -819,9 +832,11 @@ impl<'tcx> InferCtxt<'tcx> {
819832
Ty::new_int_var(self.tcx, next_int_var_id)
820833
}
821834

822-
pub fn next_float_var(&self) -> Ty<'tcx> {
823-
let next_float_var_id =
824-
self.inner.borrow_mut().float_unification_table().new_key(ty::FloatVarValue::Unknown);
835+
pub fn next_float_var(&self, span: Span) -> Ty<'tcx> {
836+
let mut inner = self.inner.borrow_mut();
837+
let next_float_var_id = inner.float_unification_table().new_key(ty::FloatVarValue::Unknown);
838+
let span_index = inner.float_origin_span_storage.push(span);
839+
debug_assert_eq!(next_float_var_id, span_index);
825840
Ty::new_float_var(self.tcx, next_float_var_id)
826841
}
827842

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

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

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

1720
fn vars_since_snapshot<'tcx, T>(
1821
table: &UnificationTable<'_, 'tcx, T>,
@@ -25,6 +28,14 @@ where
2528
T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32)
2629
}
2730

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

@@ -142,8 +153,7 @@ impl SnapshotVarData {
142153
let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len);
143154
let int_vars =
144155
vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len);
145-
let float_vars =
146-
vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len);
156+
let float_vars = float_vars_since_snapshot(&mut inner, vars_pre_snapshot.float_var_len);
147157

148158
let const_vars = const_vars_since_snapshot(
149159
&mut inner.const_unification_table(),
@@ -157,7 +167,7 @@ impl SnapshotVarData {
157167
region_vars.0.is_empty()
158168
&& type_vars.0.is_empty()
159169
&& int_vars.is_empty()
160-
&& float_vars.is_empty()
170+
&& float_vars.0.is_empty()
161171
&& const_vars.0.is_empty()
162172
}
163173
}
@@ -202,8 +212,10 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> {
202212
}
203213
}
204214
ty::FloatVar(vid) => {
205-
if self.snapshot_vars.float_vars.contains(&vid) {
206-
self.infcx.next_float_var()
215+
if self.snapshot_vars.float_vars.0.contains(&vid) {
216+
let idx = vid.as_usize() - self.snapshot_vars.float_vars.0.start.as_usize();
217+
let span = self.snapshot_vars.float_vars.1[idx];
218+
self.infcx.next_float_var(span)
207219
} else {
208220
ty
209221
}

‎compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5093,3 +5093,52 @@ declare_lint! {
50935093
reference: "issue #138762 <https://github.com/rust-lang/rust/issues/138762>",
50945094
};
50955095
}
5096+
5097+
declare_lint! {
5098+
/// The `float_literal_f32_fallback` lint detects situations where the type of an unsuffixed
5099+
/// float literal falls back to `f32` instead of `f64` to avoid a compilation error. This occurs
5100+
/// when there is a trait bound `f32: From<T>` (or equivalent, such as `T: Into<f32>`) and the
5101+
/// literal is inferred to have the same type as `T`.
5102+
///
5103+
/// ### Example
5104+
///
5105+
/// ```rust
5106+
/// fn foo(x: impl Into<f32>) -> f32 {
5107+
/// x.into()
5108+
/// }
5109+
///
5110+
/// fn main() {
5111+
/// dbg!(foo(2.5));
5112+
/// }
5113+
/// ```
5114+
///
5115+
/// {{produces}}
5116+
///
5117+
/// ### Explanation
5118+
///
5119+
/// Rust allows traits that are only implemented for a single floating point type to guide type
5120+
/// inferrence for floating point literals. This used to apply in the case of `f32: From<T>`
5121+
/// (where `T` was inferred to be the same type as a floating point literal), as the only
5122+
/// floating point type impl was `f32: From<f32>`. However, as Rust is in the process of adding
5123+
/// support for `f16`, there are now two implementations for floating point types:
5124+
/// `f32: From<f16>` and `f32: From<f32>`. This means that the trait bound `f32: From<T>` can no
5125+
/// longer guide inferrence for the type of the floating point literal. The default fallback for
5126+
/// unsuffixed floating point literals is `f64`. As `f32` does not implement `From<f64>`,
5127+
/// falling back to `f64` would cause a compilation error; therefore, the float type fallback
5128+
/// has been tempoarily adjusted to fallback to `f32` in this scenario.
5129+
///
5130+
/// The lint will automatically provide a machine-applicable suggestion to add a `_f32` suffix
5131+
/// to the literal, which will fix the problem.
5132+
///
5133+
/// This is a [future-incompatible] lint to transition this to a hard error in the future. See
5134+
/// [issue #FIXME] for more details.
5135+
///
5136+
/// [issue #FIXME]: https://github.com/rust-lang/rust/issues/FIXME
5137+
pub FLOAT_LITERAL_F32_FALLBACK,
5138+
Warn,
5139+
"detects unsuffixed floating point literals whose type fallback to `f32`",
5140+
@future_incompatible = FutureIncompatibleInfo {
5141+
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
5142+
reference: "issue #FIXME <https://github.com/rust-lang/rust/issues/FIXME>",
5143+
};
5144+
}

‎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 によって変換されたページ (->オリジナル) /