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 b2fa77a

Browse files
committed
Add FCW for unsuffixed float literal f32 fallback
1 parent f5e00ec commit b2fa77a

File tree

16 files changed

+269
-15
lines changed

16 files changed

+269
-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
@@ -1035,3 +1035,11 @@ pub(crate) struct NakedFunctionsMustNakedAsm {
10351035
#[label]
10361036
pub span: Span,
10371037
}
1038+
1039+
#[derive(LintDiagnostic)]
1040+
#[diag(hir_typeck_float_literal_f32_fallback)]
1041+
pub(crate) struct FloatLiteralF32Fallback {
1042+
pub literal: String,
1043+
#[suggestion(code = "{literal}_f32", applicability = "machine-applicable")]
1044+
pub span: Option<Span>,
1045+
}

‎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::{self, FloatVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
1515
use rustc_session::lint;
1616
use rustc_span::def_id::LocalDefId;
@@ -192,6 +192,20 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
192192
.iter()
193193
.flat_map(|ty| ty.float_vid())
194194
.filter(|vid| roots.contains(&self.root_float_var(*vid)))
195+
.inspect(|vid| {
196+
let span = self.float_var_origin(*vid);
197+
// Show the entire literal in the suggestion to make it clearer.
198+
let literal = self.tcx.sess.source_map().span_to_snippet(span).ok();
199+
self.tcx.emit_node_span_lint(
200+
FLOAT_LITERAL_F32_FALLBACK,
201+
CRATE_HIR_ID,
202+
span,
203+
errors::FloatLiteralF32Fallback {
204+
span: literal.as_ref().map(|_| span),
205+
literal: literal.unwrap_or_default(),
206+
},
207+
);
208+
})
195209
.collect();
196210
debug!("calculate_fallback_to_f32: fallback_to_f32={:?}", fallback_to_f32);
197211
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
@@ -1674,7 +1674,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16741674
ty::Float(_) => Some(ty),
16751675
_ => None,
16761676
});
1677-
opt_ty.unwrap_or_else(|| self.next_float_var())
1677+
opt_ty.unwrap_or_else(|| self.next_float_var(lit.span))
16781678
}
16791679
ast::LitKind::Bool(_) => tcx.types.bool,
16801680
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.
@@ -797,9 +810,11 @@ impl<'tcx> InferCtxt<'tcx> {
797810
Ty::new_int_var(self.tcx, next_int_var_id)
798811
}
799812

800-
pub fn next_float_var(&self) -> Ty<'tcx> {
801-
let next_float_var_id =
802-
self.inner.borrow_mut().float_unification_table().new_key(ty::FloatVarValue::Unknown);
813+
pub fn next_float_var(&self, span: Span) -> Ty<'tcx> {
814+
let mut inner = self.inner.borrow_mut();
815+
let next_float_var_id = inner.float_unification_table().new_key(ty::FloatVarValue::Unknown);
816+
let span_index = inner.float_origin_span_storage.push(span);
817+
debug_assert_eq!(next_float_var_id, span_index);
803818
Ty::new_float_var(self.tcx, next_float_var_id)
804819
}
805820

‎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
@@ -5111,3 +5111,52 @@ declare_lint! {
51115111
report_in_deps: true,
51125112
};
51135113
}
5114+
5115+
declare_lint! {
5116+
/// The `float_literal_f32_fallback` lint detects situations where the type of an unsuffixed
5117+
/// float literal falls back to `f32` instead of `f64` to avoid a compilation error. This occurs
5118+
/// when there is a trait bound `f32: From<T>` (or equivalent, such as `T: Into<f32>`) and the
5119+
/// literal is inferred to have the same type as `T`.
5120+
///
5121+
/// ### Example
5122+
///
5123+
/// ```rust
5124+
/// fn foo(x: impl Into<f32>) -> f32 {
5125+
/// x.into()
5126+
/// }
5127+
///
5128+
/// fn main() {
5129+
/// dbg!(foo(2.5));
5130+
/// }
5131+
/// ```
5132+
///
5133+
/// {{produces}}
5134+
///
5135+
/// ### Explanation
5136+
///
5137+
/// Rust allows traits that are only implemented for a single floating point type to guide type
5138+
/// inferrence for floating point literals. This used to apply in the case of `f32: From<T>`
5139+
/// (where `T` was inferred to be the same type as a floating point literal), as the only
5140+
/// floating point type impl was `f32: From<f32>`. However, as Rust is in the process of adding
5141+
/// support for `f16`, there are now two implementations for floating point types:
5142+
/// `f32: From<f16>` and `f32: From<f32>`. This means that the trait bound `f32: From<T>` can no
5143+
/// longer guide inferrence for the type of the floating point literal. The default fallback for
5144+
/// unsuffixed floating point literals is `f64`. As `f32` does not implement `From<f64>`,
5145+
/// falling back to `f64` would cause a compilation error; therefore, the float type fallback
5146+
/// has been tempoarily adjusted to fallback to `f32` in this scenario.
5147+
///
5148+
/// The lint will automatically provide a machine-applicable suggestion to add a `_f32` suffix
5149+
/// to the literal, which will fix the problem.
5150+
///
5151+
/// This is a [future-incompatible] lint to transition this to a hard error in the future. See
5152+
/// [issue #FIXME] for more details.
5153+
///
5154+
/// [issue #FIXME]: https://github.com/rust-lang/rust/issues/FIXME
5155+
pub FLOAT_LITERAL_F32_FALLBACK,
5156+
Warn,
5157+
"detects unsuffixed floating point literals whose type fallback to `f32`",
5158+
@future_incompatible = FutureIncompatibleInfo {
5159+
reason: FutureIncompatibilityReason::FutureReleaseError,
5160+
reference: "issue #FIXME <https://github.com/rust-lang/rust/issues/FIXME>",
5161+
};
5162+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ revisions: old-solver next-solver
2+
//@[next-solver] compile-flags: -Znext-solver
3+
//@ run-pass
4+
//@ run-rustfix
5+
6+
fn foo(_: impl Into<f32>) {}
7+
8+
fn main() {
9+
foo(1.0_f32);
10+
//~^ WARN falling back to `f32`
11+
//~| WARN this was previously accepted
12+
foo(-(2.5_f32));
13+
//~^ WARN falling back to `f32`
14+
//~| WARN this was previously accepted
15+
foo(1e5_f32);
16+
//~^ WARN falling back to `f32`
17+
//~| WARN this was previously accepted
18+
foo(4f32); // no warning
19+
let x = -4.0_f32;
20+
//~^ WARN falling back to `f32`
21+
//~| WARN this was previously accepted
22+
foo(x);
23+
}

0 commit comments

Comments
(0)

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