From a7590138a73e246a0b219d8ae806cf7753febf1a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 14 Apr 2026 22:02:22 +0200 Subject: [PATCH] thread through a `HirId` to emit lints in the right place Previously the lint would be reported at the right span, but could not be `allow`ed or `expect`ed in that location, because the lint was actually emitted using `CRATE_HIR_ID` --- compiler/rustc_hir_typeck/src/demand.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 2 +- compiler/rustc_hir_typeck/src/fallback.rs | 10 ++++----- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 3 ++- compiler/rustc_hir_typeck/src/pat.rs | 2 +- .../rustc_infer/src/infer/canonical/mod.rs | 5 ++++- compiler/rustc_infer/src/infer/mod.rs | 21 +++++++++++-------- .../rustc_infer/src/infer/snapshot/fudge.rs | 14 ++++++------- .../rustc_infer/src/infer/type_variable.rs | 11 ++++++++++ compiler/rustc_lint_defs/src/builtin.rs | 1 + tests/ui/float/f16-into-f32.rs | 17 +++++++++++++++ .../ui/float/f32-into-f32.next-solver.stderr | 2 +- tests/ui/float/f32-into-f32.old-solver.stderr | 2 +- tests/ui/inference/untyped-primitives.stderr | 2 +- 14 files changed, 65 insertions(+), 29 deletions(-) create mode 100644 tests/ui/float/f16-into-f32.rs diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 6316e6b9d592..29fc6729e4b3 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -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(DUMMY_SP), + ty::FloatVar(_) => self.next_float_var(DUMMY_SP, None), ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { bug!("unexpected fresh ty outside of the trait solver") } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e21cadcf3ffe..084079561a6d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -349,7 +349,7 @@ fn check_expr_kind( let tcx = self.tcx; match expr.kind { - ExprKind::Lit(ref lit) => self.check_expr_lit(lit, expected), + ExprKind::Lit(ref lit) => self.check_expr_lit(lit, expr.hir_id, expected), ExprKind::Binary(op, lhs, rhs) => self.check_expr_binop(expr, op, lhs, rhs, expected), ExprKind::Assign(lhs, rhs, span) => { self.check_expr_assign(expr, expected, lhs, rhs, span) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index cebf10e382ac..84b09d4ab477 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -161,15 +161,15 @@ fn calculate_fallback_to_f32(&self, unresolved_variables: &[Ty<'tcx>]) -> UnordS .flat_map(|ty| ty.float_vid()) .filter(|vid| roots.contains(&self.root_float_var(*vid))) .inspect(|vid| { - let span = self.float_var_origin(*vid); + let origin = 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(); + let literal = self.tcx.sess.source_map().span_to_snippet(origin.span).ok(); self.tcx.emit_node_span_lint( FLOAT_LITERAL_F32_FALLBACK, - CRATE_HIR_ID, - span, + origin.lint_id.unwrap_or(CRATE_HIR_ID), + origin.span, errors::FloatLiteralF32Fallback { - span: literal.as_ref().map(|_| span), + span: literal.as_ref().map(|_| origin.span), literal: literal.unwrap_or_default(), }, ); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 0752ecb151f6..e37cab667158 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -718,6 +718,7 @@ fn suggest_ptr_null_mut( pub(in super::super) fn check_expr_lit( &self, lit: &hir::Lit, + lint_id: HirId, expected: Expectation<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; @@ -765,7 +766,7 @@ pub(in super::super) fn check_expr_lit( ty::Float(_) => Some(ty), _ => None, }); - opt_ty.unwrap_or_else(|| self.next_float_var(lit.span)) + opt_ty.unwrap_or_else(|| self.next_float_var(lit.span, Some(lint_id))) } ast::LitKind::Bool(_) => tcx.types.bool, ast::LitKind::CStr(_, _) => Ty::new_imm_ref( diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 26f7d1ccffc9..198c177e6d44 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -917,7 +917,7 @@ fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'tcx>) -> fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> { let ty = match <.kind { rustc_hir::PatExprKind::Lit { lit, negated } => { - let ty = self.check_expr_lit(lit, Expectation::NoExpectation); + let ty = self.check_expr_lit(lit, lt.hir_id, Expectation::NoExpectation); if *negated { self.register_bound( ty, diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 321f2e43deef..55c78036c6d1 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -107,7 +107,10 @@ pub fn instantiate_canonical_var( CanonicalVarKind::Int => self.next_int_var().into(), - CanonicalVarKind::Float => self.next_float_var(span).into(), + CanonicalVarKind::Float => { + // There is no HirId available to pass as a lint_id. + self.next_float_var(span, None).into() + } CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, bound, .. }) => { let universe_mapped = universe_map(universe); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5e28fb8765cd..d27a0a77f430 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -16,8 +16,8 @@ use rustc_data_structures::undo_log::{Rollback, UndoLogs}; use rustc_data_structures::unify as ut; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, HirId}; use rustc_index::IndexVec; use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; @@ -39,6 +39,7 @@ use type_variable::TypeVariableOrigin; use crate::infer::snapshot::undo_log::UndoLog; +use crate::infer::type_variable::FloatVariableOrigin; use crate::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::traits::{ self, ObligationCause, ObligationInspector, PredicateObligation, PredicateObligations, @@ -109,9 +110,10 @@ pub struct InferCtxtInner<'tcx> { /// Map from floating variable to the kind of float it represents. float_unification_storage: ut::UnificationTableStorage, - /// 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, + /// Map from floating variable to the origin span it came from, and the HirId that should be + /// used to lint at that location. This is only used for the FCW for the fallback to `f32`, + /// so can be removed once the `f32` fallback is removed. + float_origin_origin_storage: IndexVec, /// Tracks the set of region variables and the constraints between them. /// @@ -166,7 +168,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(), + float_origin_origin_storage: Default::default(), region_constraint_storage: Some(Default::default()), region_obligations: Default::default(), region_assumptions: Default::default(), @@ -653,8 +655,8 @@ pub fn type_var_origin(&self, vid: TyVid) -> TypeVariableOrigin { /// 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] + pub fn float_var_origin(&self, vid: FloatVid) -> FloatVariableOrigin { + self.inner.borrow_mut().float_origin_origin_storage[vid] } /// Returns the origin of the const variable identified by `vid` @@ -834,10 +836,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, span: Span) -> Ty<'tcx> { + pub fn next_float_var(&self, span: Span, lint_id: Option) -> 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); + let origin = FloatVariableOrigin { span, lint_id }; + let span_index = inner.float_origin_origin_storage.push(origin); debug_assert_eq!(next_float_var_id, span_index); Ty::new_float_var(self.tcx, next_float_var_id) } diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 48af711c3177..2ce98b7541af 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -6,12 +6,11 @@ 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::type_variable::{FloatVariableOrigin, TypeVariableOrigin}; use crate::infer::unify_key::{ConstVariableValue, ConstVidKey}; use crate::infer::{ ConstVariableOrigin, InferCtxt, InferCtxtInner, RegionVariableOrigin, UnificationTable, @@ -31,9 +30,9 @@ fn vars_since_snapshot<'tcx, T>( fn float_vars_since_snapshot( inner: &mut InferCtxtInner<'_>, snapshot_var_len: usize, -) -> (Range, Vec) { +) -> (Range, Vec) { let range = vars_since_snapshot(&inner.float_unification_table(), snapshot_var_len); - (range.clone(), range.map(|index| inner.float_origin_span_storage[index]).collect()) + (range.clone(), range.map(|index| inner.float_origin_origin_storage[index]).collect()) } fn const_vars_since_snapshot<'tcx>( @@ -141,7 +140,7 @@ struct SnapshotVarData<'tcx> { region_vars: (Range, Vec>), type_vars: (Range, Vec), int_vars: Range, - float_vars: (Range, Vec), + float_vars: (Range, Vec), const_vars: (Range, Vec), } @@ -215,8 +214,9 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { ty::FloatVar(vid) => { 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) + let FloatVariableOrigin { span, lint_id } = + self.snapshot_vars.float_vars.1[idx]; + self.infcx.next_float_var(span, lint_id) } else { ty } diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 65f77fe8e25f..4c56bf0923c3 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -4,6 +4,7 @@ use rustc_data_structures::undo_log::Rollback; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; +use rustc_hir::HirId; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_middle::bug; @@ -99,6 +100,16 @@ pub struct TypeVariableOrigin { pub param_def_id: Option, } +#[derive(Copy, Clone, Debug)] +pub struct FloatVariableOrigin { + pub span: Span, + + /// `HirId` to lint at for this float variable, if any. + /// + /// This should only be used for diagnostics. + pub lint_id: Option, +} + #[derive(Clone)] pub(crate) struct TypeVariableData { origin: TypeVariableOrigin, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 65c25849c714..3462ab2ee727 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -47,6 +47,7 @@ EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, + FLOAT_LITERAL_F32_FALLBACK, FORBIDDEN_LINT_GROUPS, FUNCTION_ITEM_REFERENCES, FUZZY_PROVENANCE_CASTS, diff --git a/tests/ui/float/f16-into-f32.rs b/tests/ui/float/f16-into-f32.rs new file mode 100644 index 000000000000..ad428edec727 --- /dev/null +++ b/tests/ui/float/f16-into-f32.rs @@ -0,0 +1,17 @@ +//@ build-pass +#![feature(f16, f32_from_f16)] +#![allow(unused)] + +// Check that float conversions work, specifically a {float} literal that normally would fall back +// to an f64 but due to the Into bound here falls back to f32. Also test that the lint is emitted in +// the correct location, and can be `expect`ed or `allow`ed. +fn convert(x: impl Into) -> f32 { + x.into() +} + +pub fn main() { + let _ = convert(1.0f32); + let _ = convert(1.0f16); + #[expect(float_literal_f32_fallback)] + let _ = convert(1.0); +} diff --git a/tests/ui/float/f32-into-f32.next-solver.stderr b/tests/ui/float/f32-into-f32.next-solver.stderr index 8df6ba4ad5ee..fc88f9d2c7f3 100644 --- a/tests/ui/float/f32-into-f32.next-solver.stderr +++ b/tests/ui/float/f32-into-f32.next-solver.stderr @@ -6,7 +6,7 @@ LL | foo(1.0); | = 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 #154024 - = note: `#[warn(float_literal_f32_fallback)]` on by default + = note: `#[warn(float_literal_f32_fallback)]` (part of `#[warn(future_incompatible)]`) on by default warning: falling back to `f32` as the trait bound `f32: From` is not satisfied --> $DIR/f32-into-f32.rs:12:11 diff --git a/tests/ui/float/f32-into-f32.old-solver.stderr b/tests/ui/float/f32-into-f32.old-solver.stderr index 8df6ba4ad5ee..fc88f9d2c7f3 100644 --- a/tests/ui/float/f32-into-f32.old-solver.stderr +++ b/tests/ui/float/f32-into-f32.old-solver.stderr @@ -6,7 +6,7 @@ LL | foo(1.0); | = 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 #154024 - = note: `#[warn(float_literal_f32_fallback)]` on by default + = note: `#[warn(float_literal_f32_fallback)]` (part of `#[warn(future_incompatible)]`) on by default warning: falling back to `f32` as the trait bound `f32: From` is not satisfied --> $DIR/f32-into-f32.rs:12:11 diff --git a/tests/ui/inference/untyped-primitives.stderr b/tests/ui/inference/untyped-primitives.stderr index 3467cef75218..8b0fb15caa8f 100644 --- a/tests/ui/inference/untyped-primitives.stderr +++ b/tests/ui/inference/untyped-primitives.stderr @@ -6,7 +6,7 @@ LL | let x = f32::from(3.14); | = 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 #154024 - = note: `#[warn(float_literal_f32_fallback)]` on by default + = note: `#[warn(float_literal_f32_fallback)]` (part of `#[warn(future_incompatible)]`) on by default warning: 1 warning emitted