mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
Auto merge of #139087 - beetrees:impl-from-f16-for-f32, r=jackh726
Fallback `{float}` to `f32` when `f32: From<{float}>` and add `impl From<f16> for f32`
Currently, the following code compiles:
```rust
fn foo<T: Into<f32>>(_: T) {}
fn main() {
foo(1.0);
}
```
This is because the only `From<{float}>` impl for `f32` is currently `From<f32>`. However, once `impl From<f16> for f32` is added this is no longer the case. This would cause the float literal to fallback to `f64`, subsequently causing a type error as `f32` does not implement `From<f64>`. While this kind of change to type inference isn't technically a breaking change according to Rust's breaking change policy, the previous attempt to add `impl From<f16> for f32` was removed rust-lang/rust#123830 due to the large number of crates affected (by my count, there were root regressions in 42 crates and 52 GitHub repos, not including duplicates). This PR solves this problem by using `f32` as the fallback type for `{float}` when there is a trait predicate of `f32: From<{float}>`. This allows adding `impl From<f16> for f32` without affecting the code that currently compiles (such as the example above; this PR shouldn't affect what is possible on stable).
This PR also allows adding a future-incompatibility warning for the fallback to `f32` (currently implemented in the third commit) if the lang team wants one (allowing the `f32` fallback to be removed in the future); alternatively this could be expanded in the future into something more general like @tgross35 suggested in https://github.com/rust-lang/rust/issues/123831#issuecomment-2064728053. I think it would be also possible to disallow the `f32` fallback in a future edition.
As expected, a crater check showed [no non-spurious regressions](https://github.com/rust-lang/rust/pull/139087#issuecomment-2764732580).
For reference, I've based the implementation loosely on the existing `calculate_diverging_fallback`. This first commit adds the `f32` fallback, the second adds `impl From<f16> for f32`, and the third adds a FCW lint for the `f32` fallback. I think this falls under the types team, so
r? types
Fixes: rust-lang/rust#123831
Tracking issue: rust-lang/rust#116909
@rustbot label +T-lang +T-types +T-libs-api +F-f16_and_f128
To decide on whether a future-incompatibility warning is desired or otherwise (see above):
@rustbot label +I-lang-nominated
cc https://github.com/rust-lang/rust/issues/154024 https://github.com/rust-lang/rust/issues/154005
This commit is contained in:
@@ -443,6 +443,9 @@ fn hash_stable(&self, _: &mut Hcx, hasher: &mut StableHasher) {
|
||||
FieldBase, sym::field_base, field_base, Target::AssocTy, GenericRequirement::Exact(0);
|
||||
FieldType, sym::field_type, field_type, Target::AssocTy, GenericRequirement::Exact(0);
|
||||
FieldOffset, sym::field_offset, field_offset, Target::AssocConst, GenericRequirement::Exact(0);
|
||||
|
||||
// Used to fallback `{float}` to `f32` when `f32: From<{float}>`
|
||||
From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
}
|
||||
|
||||
/// The requirement imposed on the generics of a lang item
|
||||
|
||||
@@ -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, None),
|
||||
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>,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
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_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
|
||||
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;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
@@ -55,6 +55,7 @@ fn fallback_types(&self) -> bool {
|
||||
|
||||
let (diverging_fallback, diverging_fallback_ty) =
|
||||
self.calculate_diverging_fallback(&unresolved_variables);
|
||||
let fallback_to_f32 = self.calculate_fallback_to_f32(&unresolved_variables);
|
||||
|
||||
// We do fallback in two passes, to try to generate
|
||||
// better error messages.
|
||||
@@ -62,8 +63,12 @@ fn fallback_types(&self) -> bool {
|
||||
let mut fallback_occurred = false;
|
||||
for ty in unresolved_variables {
|
||||
debug!("unsolved_variable = {:?}", ty);
|
||||
fallback_occurred |=
|
||||
self.fallback_if_possible(ty, &diverging_fallback, diverging_fallback_ty);
|
||||
fallback_occurred |= self.fallback_if_possible(
|
||||
ty,
|
||||
&diverging_fallback,
|
||||
diverging_fallback_ty,
|
||||
&fallback_to_f32,
|
||||
);
|
||||
}
|
||||
|
||||
fallback_occurred
|
||||
@@ -73,7 +78,8 @@ fn fallback_types(&self) -> bool {
|
||||
///
|
||||
/// - Unconstrained ints are replaced with `i32`.
|
||||
///
|
||||
/// - Unconstrained floats are replaced with `f64`.
|
||||
/// - Unconstrained floats are replaced with `f64`, except when there is a trait predicate
|
||||
/// `f32: From<{float}>`, in which case `f32` is used as the fallback instead.
|
||||
///
|
||||
/// - Non-numerics may get replaced with `()` or `!`, depending on how they
|
||||
/// were categorized by [`Self::calculate_diverging_fallback`], crate's
|
||||
@@ -89,6 +95,7 @@ fn fallback_if_possible(
|
||||
ty: Ty<'tcx>,
|
||||
diverging_fallback: &UnordSet<Ty<'tcx>>,
|
||||
diverging_fallback_ty: Ty<'tcx>,
|
||||
fallback_to_f32: &UnordSet<FloatVid>,
|
||||
) -> bool {
|
||||
// Careful: we do NOT shallow-resolve `ty`. We know that `ty`
|
||||
// is an unsolved variable, and we determine its fallback
|
||||
@@ -111,6 +118,7 @@ fn fallback_if_possible(
|
||||
let fallback = match ty.kind() {
|
||||
_ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e),
|
||||
ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
|
||||
ty::Infer(ty::FloatVar(vid)) if fallback_to_f32.contains(vid) => self.tcx.types.f32,
|
||||
ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
|
||||
_ if diverging_fallback.contains(&ty) => {
|
||||
self.diverging_fallback_has_occurred.set(true);
|
||||
@@ -125,6 +133,52 @@ fn fallback_if_possible(
|
||||
true
|
||||
}
|
||||
|
||||
/// Existing code relies on `f32: From<T>` (usually written as `T: Into<f32>`) resolving `T` to
|
||||
/// `f32` when the type of `T` is inferred from an unsuffixed float literal. Using the default
|
||||
/// fallback of `f64`, this would break when adding `impl From<f16> for f32`, as there are now
|
||||
/// two float type which could be `T`, meaning that the fallback of `f64` would be used and
|
||||
/// compilation error would occur as `f32` does not implement `From<f64>`. To avoid breaking
|
||||
/// existing code, we instead fallback `T` to `f32` when there is a trait predicate
|
||||
/// `f32: From<T>`. This means code like the following will continue to compile:
|
||||
///
|
||||
/// ```rust
|
||||
/// fn foo<T: Into<f32>>(_: T) {}
|
||||
///
|
||||
/// foo(1.0);
|
||||
/// ```
|
||||
fn calculate_fallback_to_f32(&self, unresolved_variables: &[Ty<'tcx>]) -> UnordSet<FloatVid> {
|
||||
let roots: UnordSet<ty::FloatVid> = self.from_float_for_f32_root_vids();
|
||||
if roots.is_empty() {
|
||||
// Most functions have no `f32: From<{float}>` predicates, so short-circuit and return
|
||||
// an empty set when this is the case.
|
||||
return UnordSet::new();
|
||||
}
|
||||
// Calculate all the unresolved variables that need to fallback to `f32` here. This ensures
|
||||
// we don't need to find root variables in `fallback_if_possible`: see the comment at the
|
||||
// top of that function for details.
|
||||
let fallback_to_f32 = unresolved_variables
|
||||
.iter()
|
||||
.flat_map(|ty| ty.float_vid())
|
||||
.filter(|vid| roots.contains(&self.root_float_var(*vid)))
|
||||
.inspect(|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(origin.span).ok();
|
||||
self.tcx.emit_node_span_lint(
|
||||
FLOAT_LITERAL_F32_FALLBACK,
|
||||
origin.lint_id.unwrap_or(CRATE_HIR_ID),
|
||||
origin.span,
|
||||
errors::FloatLiteralF32Fallback {
|
||||
span: literal.as_ref().map(|_| origin.span),
|
||||
literal: literal.unwrap_or_default(),
|
||||
},
|
||||
);
|
||||
})
|
||||
.collect();
|
||||
debug!("calculate_fallback_to_f32: fallback_to_f32={:?}", fallback_to_f32);
|
||||
fallback_to_f32
|
||||
}
|
||||
|
||||
fn calculate_diverging_fallback(
|
||||
&self,
|
||||
unresolved_variables: &[Ty<'tcx>],
|
||||
@@ -362,6 +416,11 @@ fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
|
||||
Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
|
||||
}
|
||||
|
||||
/// If `ty` is an unresolved float type variable, returns its root vid.
|
||||
pub(crate) fn root_float_vid(&self, ty: Ty<'tcx>) -> Option<ty::FloatVid> {
|
||||
Some(self.root_float_var(self.shallow_resolve(ty).float_vid()?))
|
||||
}
|
||||
|
||||
/// Given a set of diverging vids and coercions, walk the HIR to gather a
|
||||
/// set of suggestions which can be applied to preserve fallback to unit.
|
||||
fn try_to_suggest_annotations(
|
||||
|
||||
@@ -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())
|
||||
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(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! A utility module to inspect currently ambiguous obligations in the current context.
|
||||
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::traits::{self, ObligationCause, PredicateObligations};
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_span::Span;
|
||||
@@ -96,6 +98,69 @@ pub(crate) fn obligations_for_self_ty_next(
|
||||
});
|
||||
obligations_for_self_ty
|
||||
}
|
||||
|
||||
/// Only needed for the `From<{float}>` for `f32` type fallback.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
pub(crate) fn from_float_for_f32_root_vids(&self) -> UnordSet<ty::FloatVid> {
|
||||
if self.next_trait_solver() {
|
||||
self.from_float_for_f32_root_vids_next()
|
||||
} else {
|
||||
let Some(from_trait) = self.tcx.lang_items().from_trait() else {
|
||||
return UnordSet::new();
|
||||
};
|
||||
self.fulfillment_cx
|
||||
.borrow_mut()
|
||||
.pending_obligations()
|
||||
.into_iter()
|
||||
.filter_map(|obligation| {
|
||||
self.predicate_from_float_for_f32_root_vid(from_trait, obligation.predicate)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn predicate_from_float_for_f32_root_vid(
|
||||
&self,
|
||||
from_trait: DefId,
|
||||
predicate: ty::Predicate<'tcx>,
|
||||
) -> Option<ty::FloatVid> {
|
||||
// The predicates we are looking for look like
|
||||
// `TraitPredicate(<f32 as std::convert::From<{float}>>, polarity:Positive)`.
|
||||
// They will have no bound variables.
|
||||
match predicate.kind().no_bound_vars() {
|
||||
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate {
|
||||
polarity: ty::PredicatePolarity::Positive,
|
||||
trait_ref,
|
||||
}))) if trait_ref.def_id == from_trait
|
||||
&& self.shallow_resolve(trait_ref.self_ty()).kind()
|
||||
== &ty::Float(ty::FloatTy::F32) =>
|
||||
{
|
||||
self.root_float_vid(trait_ref.args.type_at(1))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_float_for_f32_root_vids_next(&self) -> UnordSet<ty::FloatVid> {
|
||||
let Some(from_trait) = self.tcx.lang_items().from_trait() else {
|
||||
return UnordSet::new();
|
||||
};
|
||||
let obligations = self.fulfillment_cx.borrow().pending_obligations();
|
||||
debug!(?obligations);
|
||||
let mut vids = UnordSet::new();
|
||||
for obligation in obligations {
|
||||
let mut visitor = FindFromFloatForF32RootVids {
|
||||
fcx: self,
|
||||
from_trait,
|
||||
vids: &mut vids,
|
||||
span: obligation.cause.span,
|
||||
};
|
||||
|
||||
let goal = obligation.as_goal();
|
||||
self.visit_proof_tree(goal, &mut visitor);
|
||||
}
|
||||
vids
|
||||
}
|
||||
}
|
||||
|
||||
struct NestedObligationsForSelfTy<'a, 'tcx> {
|
||||
@@ -105,7 +170,7 @@ struct NestedObligationsForSelfTy<'a, 'tcx> {
|
||||
obligations_for_self_ty: &'a mut PredicateObligations<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
|
||||
impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> {
|
||||
fn span(&self) -> Span {
|
||||
self.root_cause.span
|
||||
}
|
||||
@@ -144,3 +209,37 @@ fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FindFromFloatForF32RootVids<'a, 'tcx> {
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
from_trait: DefId,
|
||||
vids: &'a mut UnordSet<ty::FloatVid>,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> ProofTreeVisitor<'tcx> for FindFromFloatForF32RootVids<'_, 'tcx> {
|
||||
fn span(&self) -> 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 occurrences in practice (a.k.a. `Into`).
|
||||
InspectConfig { max_depth: 3 }
|
||||
}
|
||||
|
||||
fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) {
|
||||
if let Some(vid) = self
|
||||
.fcx
|
||||
.predicate_from_float_for_f32_root_vid(self.from_trait, inspect_goal.goal().predicate)
|
||||
{
|
||||
self.vids.insert(vid);
|
||||
} else if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
|
||||
let start_len = self.vids.len();
|
||||
let _ = candidate.goal().infcx().commit_if_ok(|_| {
|
||||
candidate.visit_nested_no_probe(self);
|
||||
if self.vids.len() > start_len { Ok(()) } else { Err(()) }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -107,7 +107,10 @@ pub fn instantiate_canonical_var(
|
||||
|
||||
CanonicalVarKind::Int => self.next_int_var().into(),
|
||||
|
||||
CanonicalVarKind::Float => self.next_float_var().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);
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
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};
|
||||
use rustc_middle::bug;
|
||||
@@ -38,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,
|
||||
@@ -108,6 +110,11 @@ 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, 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<FloatVid, FloatVariableOrigin>,
|
||||
|
||||
/// Tracks the set of region variables and the constraints between them.
|
||||
///
|
||||
/// This is initially `Some(_)` but when
|
||||
@@ -161,6 +168,7 @@ fn new() -> InferCtxtInner<'tcx> {
|
||||
const_unification_storage: Default::default(),
|
||||
int_unification_storage: Default::default(),
|
||||
float_unification_storage: Default::default(),
|
||||
float_origin_origin_storage: Default::default(),
|
||||
region_constraint_storage: Some(Default::default()),
|
||||
region_obligations: Default::default(),
|
||||
region_assumptions: Default::default(),
|
||||
@@ -644,6 +652,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) -> FloatVariableOrigin {
|
||||
self.inner.borrow_mut().float_origin_origin_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 +836,12 @@ 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, lint_id: Option<HirId>) -> Ty<'tcx> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let next_float_var_id = inner.float_unification_table().new_key(ty::FloatVarValue::Unknown);
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1166,6 +1184,10 @@ pub fn sub_unification_table_root_var(&self, var: ty::TyVid) -> ty::TyVid {
|
||||
self.inner.borrow_mut().type_variables().sub_unification_table_root_var(var)
|
||||
}
|
||||
|
||||
pub fn root_float_var(&self, var: ty::FloatVid) -> ty::FloatVid {
|
||||
self.inner.borrow_mut().float_unification_table().find(var)
|
||||
}
|
||||
|
||||
pub fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid {
|
||||
self.inner.borrow_mut().const_unification_table().find(var).vid
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
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, RegionVariableOrigin, UnificationTable};
|
||||
use crate::infer::{
|
||||
ConstVariableOrigin, InferCtxt, InferCtxtInner, RegionVariableOrigin, UnificationTable,
|
||||
};
|
||||
|
||||
fn vars_since_snapshot<'tcx, T>(
|
||||
table: &UnificationTable<'_, 'tcx, T>,
|
||||
@@ -25,6 +27,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<FloatVariableOrigin>) {
|
||||
let range = vars_since_snapshot(&inner.float_unification_table(), snapshot_var_len);
|
||||
(range.clone(), range.map(|index| inner.float_origin_origin_storage[index]).collect())
|
||||
}
|
||||
|
||||
fn const_vars_since_snapshot<'tcx>(
|
||||
table: &mut UnificationTable<'_, 'tcx, ConstVidKey<'tcx>>,
|
||||
snapshot_var_len: usize,
|
||||
@@ -130,7 +140,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<FloatVariableOrigin>),
|
||||
const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>),
|
||||
}
|
||||
|
||||
@@ -143,8 +153,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 +167,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 +212,11 @@ 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 FloatVariableOrigin { span, lint_id } =
|
||||
self.snapshot_vars.float_vars.1[idx];
|
||||
self.infcx.next_float_var(span, lint_id)
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
|
||||
@@ -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<DefId>,
|
||||
}
|
||||
|
||||
#[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<HirId>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct TypeVariableData {
|
||||
origin: TypeVariableOrigin,
|
||||
|
||||
@@ -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,
|
||||
@@ -5643,3 +5644,53 @@
|
||||
"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
|
||||
/// inference 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 #154024] for more details.
|
||||
///
|
||||
/// [issue #154024]: https://github.com/rust-lang/rust/issues/154024
|
||||
/// [future-incompatible]: ../index.md#future-incompatible-lints
|
||||
pub FLOAT_LITERAL_F32_FALLBACK,
|
||||
Warn,
|
||||
"detects unsuffixed floating point literals whose type fallback to `f32`",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: fcw!(FutureReleaseError #154024),
|
||||
report_in_deps: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1180,6 +1180,14 @@ pub fn ty_vid(self) -> Option<ty::TyVid> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn float_vid(self) -> Option<ty::FloatVid> {
|
||||
match self.kind() {
|
||||
&Infer(FloatVar(vid)) => Some(vid),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_ty_or_numeric_infer(self) -> bool {
|
||||
matches!(self.kind(), Infer(_))
|
||||
|
||||
@@ -577,6 +577,7 @@ pub const trait Into<T>: Sized {
|
||||
/// [`from`]: From::from
|
||||
/// [book]: ../../book/ch09-00-error-handling.html
|
||||
#[rustc_diagnostic_item = "From"]
|
||||
#[lang = "From"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_on_unimplemented(on(
|
||||
all(Self = "&str", T = "alloc::string::String"),
|
||||
|
||||
@@ -172,8 +172,14 @@ fn from(small: $small) -> Self {
|
||||
impl_from!(u64 => f128, #[unstable(feature = "f128", issue = "116909")], #[unstable_feature_bound(f128)]);
|
||||
|
||||
// float -> float
|
||||
// FIXME(f16,f128): adding additional `From<{float}>` impls to `f32` breaks inference. See
|
||||
// <https://github.com/rust-lang/rust/issues/123831>
|
||||
|
||||
// FIXME(f16): adding the additional `From<{float}>` impl to `f32` would break inference in cases
|
||||
// like `f32::from(1.0)`. The type checker has a custom workaround to keep that and similar code
|
||||
// compiling even with the second `From<16> for f32` instance. We keep this instance unstable for
|
||||
// now so that we can later remove the workaround.
|
||||
//
|
||||
// See also <https://github.com/rust-lang/rust/issues/123831>.
|
||||
impl_from!(f16 => f32, #[unstable(feature = "f32_from_f16", issue = "154005")], #[unstable_feature_bound(f32_from_f16)]);
|
||||
impl_from!(f16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
|
||||
impl_from!(f16 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
|
||||
impl_from!(f32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]);
|
||||
|
||||
@@ -19,7 +19,7 @@ LL | let a: f16 = 100.0;
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: the type `f16` is unstable
|
||||
--> $DIR/feature-gate-f16.rs:16:11
|
||||
--> $DIR/feature-gate-f16.rs:19:11
|
||||
|
|
||||
LL | fn foo(a: f16) {}
|
||||
| ^^^
|
||||
@@ -29,7 +29,7 @@ LL | fn foo(a: f16) {}
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: the type `f16` is unstable
|
||||
--> $DIR/feature-gate-f16.rs:19:8
|
||||
--> $DIR/feature-gate-f16.rs:22:8
|
||||
|
|
||||
LL | a: f16,
|
||||
| ^^^
|
||||
@@ -58,6 +58,27 @@ LL | let c = 0f16;
|
||||
= help: add `#![feature(f16)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error[E0658]: the type `f16` is unstable
|
||||
--> $DIR/feature-gate-f16.rs:13:18
|
||||
|
|
||||
LL | let d: f32 = 1.0f16.into();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: see issue #116909 <https://github.com/rust-lang/rust/issues/116909> for more information
|
||||
= help: add `#![feature(f16)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: use of unstable library feature `f32_from_f16`
|
||||
--> $DIR/feature-gate-f16.rs:13:25
|
||||
|
|
||||
LL | let d: f32 = 1.0f16.into();
|
||||
| ^^^^
|
||||
|
|
||||
= help: add `#![feature(f32_from_f16)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
= note: required for `f32` to implement `From<f16>`
|
||||
= note: required for `f16` to implement `Into<f32>`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
||||
@@ -19,7 +19,7 @@ LL | let a: f16 = 100.0;
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: the type `f16` is unstable
|
||||
--> $DIR/feature-gate-f16.rs:16:11
|
||||
--> $DIR/feature-gate-f16.rs:19:11
|
||||
|
|
||||
LL | fn foo(a: f16) {}
|
||||
| ^^^
|
||||
@@ -29,7 +29,7 @@ LL | fn foo(a: f16) {}
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: the type `f16` is unstable
|
||||
--> $DIR/feature-gate-f16.rs:19:8
|
||||
--> $DIR/feature-gate-f16.rs:22:8
|
||||
|
|
||||
LL | a: f16,
|
||||
| ^^^
|
||||
@@ -58,6 +58,27 @@ LL | let c = 0f16;
|
||||
= help: add `#![feature(f16)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error[E0658]: the type `f16` is unstable
|
||||
--> $DIR/feature-gate-f16.rs:13:18
|
||||
|
|
||||
LL | let d: f32 = 1.0f16.into();
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: see issue #116909 <https://github.com/rust-lang/rust/issues/116909> for more information
|
||||
= help: add `#![feature(f16)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error[E0658]: use of unstable library feature `f32_from_f16`
|
||||
--> $DIR/feature-gate-f16.rs:13:25
|
||||
|
|
||||
LL | let d: f32 = 1.0f16.into();
|
||||
| ^^^^
|
||||
|
|
||||
= help: add `#![feature(f32_from_f16)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
= note: required for `f32` to implement `From<f16>`
|
||||
= note: required for `f16` to implement `Into<f32>`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
||||
@@ -10,6 +10,9 @@ pub fn main() {
|
||||
let a: f16 = 100.0; //~ ERROR the type `f16` is unstable
|
||||
let b = 0.0f16; //~ ERROR the type `f16` is unstable
|
||||
let c = 0f16; //~ ERROR the type `f16` is unstable
|
||||
let d: f32 = 1.0f16.into();
|
||||
//~^ ERROR the type `f16` is unstable
|
||||
//~| ERROR use of unstable library feature `f32_from_f16`
|
||||
foo(1.23);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#![feature(f16)]
|
||||
#![allow(unused)]
|
||||
|
||||
pub fn main() {
|
||||
let _: f32 = 1.0f16.into();
|
||||
//~^ ERROR use of unstable library feature `f32_from_f16`
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
error[E0658]: use of unstable library feature `f32_from_f16`
|
||||
--> $DIR/feature-gate-f32_from_f16.rs:5:25
|
||||
|
|
||||
LL | let _: f32 = 1.0f16.into();
|
||||
| ^^^^
|
||||
|
|
||||
= help: add `#![feature(f32_from_f16)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
= note: required for `f32` to implement `From<f16>`
|
||||
= note: required for `f16` to implement `Into<f32>`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
@@ -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>) -> f32 {
|
||||
x.into()
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let _ = convert(1.0f32);
|
||||
let _ = convert(1.0f16);
|
||||
#[expect(float_literal_f32_fallback)]
|
||||
let _ = convert(1.0);
|
||||
}
|
||||
@@ -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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
= 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<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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
|
||||
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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
|
||||
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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
|
||||
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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
= 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<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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
|
||||
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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
|
||||
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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
|
||||
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);
|
||||
//~^ 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);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//@ check-fail
|
||||
|
||||
#![feature(f16)]
|
||||
|
||||
trait Trait {}
|
||||
impl Trait for f16 {}
|
||||
impl Trait for f32 {}
|
||||
|
||||
fn foo(_: impl Trait) {}
|
||||
|
||||
fn main() {
|
||||
foo(1.0); //~ ERROR the trait bound `f64: Trait` is not satisfied
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
error[E0277]: the trait bound `f64: Trait` is not satisfied
|
||||
--> $DIR/trait-f16-or-f32.rs:12:9
|
||||
|
|
||||
LL | foo(1.0);
|
||||
| --- ^^^ the trait `Trait` is not implemented for `f64`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
help: the following other types implement trait `Trait`
|
||||
--> $DIR/trait-f16-or-f32.rs:6:1
|
||||
|
|
||||
LL | impl Trait for f16 {}
|
||||
| ^^^^^^^^^^^^^^^^^^ `f16`
|
||||
LL | impl Trait for f32 {}
|
||||
| ^^^^^^^^^^^^^^^^^^ `f32`
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/trait-f16-or-f32.rs:9:16
|
||||
|
|
||||
LL | fn foo(_: impl Trait) {}
|
||||
| ^^^^^ required by this bound in `foo`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
@@ -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 #154024 <https://github.com/rust-lang/rust/issues/154024>
|
||||
= note: `#[warn(float_literal_f32_fallback)]` (part of `#[warn(future_incompatible)]`) on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
Reference in New Issue
Block a user