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 cfde659

Browse files
committed
Fallback {float} to f32 when f32: From<{float}>
1 parent c6a9554 commit cfde659

File tree

9 files changed

+200
-4
lines changed

9 files changed

+200
-4
lines changed

‎compiler/rustc_hir/src/lang_items.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,9 @@ language_item_table! {
436436
DefaultTrait1, sym::default_trait1, default_trait1_trait, Target::Trait, GenericRequirement::None;
437437

438438
ContractCheckEnsures, sym::contract_check_ensures, contract_check_ensures_fn, Target::Fn, GenericRequirement::None;
439+
440+
// Used to fallback `{float}` to `f32` when `f32: From<{float}>`
441+
From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1);
439442
}
440443

441444
/// The requirement imposed on the generics of a lang item

‎compiler/rustc_hir_typeck/src/fallback.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_hir::HirId;
1111
use rustc_hir::def::{DefKind, Res};
1212
use rustc_hir::def_id::DefId;
1313
use rustc_hir::intravisit::{InferKind, Visitor};
14-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
14+
use rustc_middle::ty::{self, FloatVid,Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
1515
use rustc_session::lint;
1616
use rustc_span::def_id::LocalDefId;
1717
use rustc_span::{DUMMY_SP, Span};
@@ -92,14 +92,16 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
9292

9393
let diverging_fallback = self
9494
.calculate_diverging_fallback(&unresolved_variables, self.diverging_fallback_behavior);
95+
let fallback_to_f32 = self.calculate_fallback_to_f32(&unresolved_variables);
9596

9697
// We do fallback in two passes, to try to generate
9798
// better error messages.
9899
// The first time, we do *not* replace opaque types.
99100
let mut fallback_occurred = false;
100101
for ty in unresolved_variables {
101102
debug!("unsolved_variable = {:?}", ty);
102-
fallback_occurred |= self.fallback_if_possible(ty, &diverging_fallback);
103+
fallback_occurred |=
104+
self.fallback_if_possible(ty, &diverging_fallback, &fallback_to_f32);
103105
}
104106

105107
fallback_occurred
@@ -109,7 +111,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
109111
//
110112
// - Unconstrained ints are replaced with `i32`.
111113
//
112-
// - Unconstrained floats are replaced with `f64`.
114+
// - Unconstrained floats are replaced with `f64`, except when there is a trait predicate
115+
// `f32: From<{float}>`, in which case `f32` is used as the fallback instead.
113116
//
114117
// - Non-numerics may get replaced with `()` or `!`, depending on
115118
// how they were categorized by `calculate_diverging_fallback`
@@ -124,6 +127,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
124127
&self,
125128
ty: Ty<'tcx>,
126129
diverging_fallback: &UnordMap<Ty<'tcx>, Ty<'tcx>>,
130+
fallback_to_f32: &UnordSet<FloatVid>,
127131
) -> bool {
128132
// Careful: we do NOT shallow-resolve `ty`. We know that `ty`
129133
// is an unsolved variable, and we determine its fallback
@@ -146,6 +150,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
146150
let fallback = match ty.kind() {
147151
_ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx, e),
148152
ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
153+
ty::Infer(ty::FloatVar(vid)) if fallback_to_f32.contains(vid) => self.tcx.types.f32,
149154
ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
150155
_ => match diverging_fallback.get(&ty) {
151156
Some(&fallback_ty) => fallback_ty,
@@ -160,6 +165,38 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
160165
true
161166
}
162167

168+
/// Existing code relies on `f32: From<T>` (usually written as `T: Into<f32>`) resolving `T` to
169+
/// `f32` when the type of `T` is inferred from an unsuffixed float literal. Using the default
170+
/// fallback of `f64`, this would break when adding `impl From<f16> for f32`, as there are now
171+
/// two float type which could be `T`, meaning that the fallback of `f64` would be used and
172+
/// compilation error would occur as `f32` does not implement `From<f64>`. To avoid breaking
173+
/// existing code, we instead fallback `T` to `f32` when there is a trait predicate
174+
/// `f32: From<T>`. This means code like the following will continue to compile:
175+
///
176+
/// ```rust
177+
/// fn foo<T: Into<f32>>(_: T) {}
178+
///
179+
/// foo(1.0);
180+
/// ```
181+
fn calculate_fallback_to_f32(&self, unresolved_variables: &[Ty<'tcx>]) -> UnordSet<FloatVid> {
182+
let roots: UnordSet<ty::FloatVid> = self.from_float_for_f32_root_vids();
183+
if roots.is_empty() {
184+
// Most functions have no `f32: From<{float}>` predicates, so short-circuit and return
185+
// an empty set when this is the case.
186+
return UnordSet::new();
187+
}
188+
// Calculate all the unresolved variables that need to fallback to `f32` here. This ensures
189+
// we don't need to find root variables in `fallback_if_possible`: see the comment at the
190+
// top of that function for details.
191+
let fallback_to_f32 = unresolved_variables
192+
.iter()
193+
.flat_map(|ty| ty.float_vid())
194+
.filter(|vid| roots.contains(&self.root_float_var(*vid)))
195+
.collect();
196+
debug!("calculate_fallback_to_f32: fallback_to_f32={:?}", fallback_to_f32);
197+
fallback_to_f32
198+
}
199+
163200
/// The "diverging fallback" system is rather complicated. This is
164201
/// a result of our need to balance 'do the right thing' with
165202
/// backwards compatibility.
@@ -565,6 +602,11 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
565602
Some(self.root_var(self.shallow_resolve(ty).ty_vid()?))
566603
}
567604

605+
/// If `ty` is an unresolved float type variable, returns its root vid.
606+
pub(crate) fn root_float_vid(&self, ty: Ty<'tcx>) -> Option<ty::FloatVid> {
607+
Some(self.root_float_var(self.shallow_resolve(ty).float_vid()?))
608+
}
609+
568610
/// Given a set of diverging vids and coercions, walk the HIR to gather a
569611
/// set of suggestions which can be applied to preserve fallback to unit.
570612
fn try_to_suggest_annotations(

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

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! A utility module to inspect currently ambiguous obligations in the current context.
22
3+
use rustc_data_structures::unord::UnordSet;
4+
use rustc_hir::def_id::DefId;
35
use rustc_infer::traits::{self, ObligationCause, PredicateObligations};
46
use rustc_middle::traits::solve::GoalSource;
57
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
@@ -96,6 +98,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9698
});
9799
obligations_for_self_ty
98100
}
101+
102+
/// Only needed for the `From<{float}>` for `f32` type fallback.
103+
#[instrument(skip(self), level = "debug")]
104+
pub(crate) fn from_float_for_f32_root_vids(&self) -> UnordSet<ty::FloatVid> {
105+
if self.next_trait_solver() {
106+
self.from_float_for_f32_root_vids_next()
107+
} else {
108+
let Some(from_trait) = self.tcx.lang_items().from_trait() else {
109+
return UnordSet::new();
110+
};
111+
self.fulfillment_cx
112+
.borrow_mut()
113+
.pending_obligations()
114+
.into_iter()
115+
.filter_map(|obligation| {
116+
self.predicate_from_float_for_f32_root_vid(from_trait, obligation.predicate)
117+
})
118+
.collect()
119+
}
120+
}
121+
122+
fn predicate_from_float_for_f32_root_vid(
123+
&self,
124+
from_trait: DefId,
125+
predicate: ty::Predicate<'tcx>,
126+
) -> Option<ty::FloatVid> {
127+
// The predicates we are looking for look like
128+
// `TraitPredicate(<f32 as std::convert::From<{float}>>, polarity:Positive)`.
129+
// They will have no bound variables.
130+
match predicate.kind().no_bound_vars() {
131+
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate {
132+
polarity: ty::PredicatePolarity::Positive,
133+
trait_ref,
134+
}))) if trait_ref.def_id == from_trait
135+
&& self.shallow_resolve(trait_ref.self_ty()).kind()
136+
== &ty::Float(ty::FloatTy::F32) =>
137+
{
138+
self.root_float_vid(trait_ref.args.type_at(1))
139+
}
140+
_ => None,
141+
}
142+
}
143+
144+
fn from_float_for_f32_root_vids_next(&self) -> UnordSet<ty::FloatVid> {
145+
let Some(from_trait) = self.tcx.lang_items().from_trait() else {
146+
return UnordSet::new();
147+
};
148+
let obligations = self.fulfillment_cx.borrow().pending_obligations();
149+
debug!(?obligations);
150+
let mut vids = UnordSet::new();
151+
for obligation in obligations {
152+
let mut visitor = FindFromFloatForF32RootVids {
153+
fcx: self,
154+
from_trait,
155+
vids: &mut vids,
156+
span: obligation.cause.span,
157+
};
158+
159+
let goal = obligation.as_goal();
160+
self.visit_proof_tree(goal, &mut visitor);
161+
}
162+
vids
163+
}
99164
}
100165

101166
struct NestedObligationsForSelfTy<'a, 'tcx> {
@@ -105,7 +170,7 @@ struct NestedObligationsForSelfTy<'a, 'tcx> {
105170
obligations_for_self_ty: &'a mut PredicateObligations<'tcx>,
106171
}
107172

108-
impl<'a,'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
173+
impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> {
109174
fn span(&self) -> Span {
110175
self.root_cause.span
111176
}
@@ -166,3 +231,34 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
166231
}
167232
}
168233
}
234+
235+
struct FindFromFloatForF32RootVids<'a, 'tcx> {
236+
fcx: &'a FnCtxt<'a, 'tcx>,
237+
from_trait: DefId,
238+
vids: &'a mut UnordSet<ty::FloatVid>,
239+
span: Span,
240+
}
241+
242+
impl<'tcx> ProofTreeVisitor<'tcx> for FindFromFloatForF32RootVids<'_, 'tcx> {
243+
fn span(&self) -> Span {
244+
self.span
245+
}
246+
247+
fn config(&self) -> InspectConfig {
248+
// See comment in `NestedObligationsForSelfTy`.
249+
InspectConfig { max_depth: 5 }
250+
}
251+
252+
fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) {
253+
self.vids.extend(
254+
self.fcx.predicate_from_float_for_f32_root_vid(
255+
self.from_trait,
256+
inspect_goal.goal().predicate,
257+
),
258+
);
259+
260+
if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
261+
candidate.visit_nested_no_probe(self)
262+
}
263+
}
264+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,10 @@ impl<'tcx> InferCtxt<'tcx> {
10501050
self.inner.borrow_mut().type_variables().root_var(var)
10511051
}
10521052

1053+
pub fn root_float_var(&self, var: ty::FloatVid) -> ty::FloatVid {
1054+
self.inner.borrow_mut().float_unification_table().find(var)
1055+
}
1056+
10531057
pub fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid {
10541058
self.inner.borrow_mut().const_unification_table().find(var).vid
10551059
}

‎compiler/rustc_middle/src/ty/sty.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,14 @@ impl<'tcx> Ty<'tcx> {
11261126
}
11271127
}
11281128

1129+
#[inline]
1130+
pub fn float_vid(self) -> Option<ty::FloatVid> {
1131+
match self.kind() {
1132+
&Infer(FloatVar(vid)) => Some(vid),
1133+
_ => None,
1134+
}
1135+
}
1136+
11291137
#[inline]
11301138
pub fn is_ty_or_numeric_infer(self) -> bool {
11311139
matches!(self.kind(), Infer(_))

‎library/core/src/convert/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ pub trait Into<T>: Sized {
573573
/// [`from`]: From::from
574574
/// [book]: ../../book/ch09-00-error-handling.html
575575
#[rustc_diagnostic_item = "From"]
576+
#[lang = "From"]
576577
#[stable(feature = "rust1", since = "1.0.0")]
577578
#[rustc_on_unimplemented(on(
578579
all(Self = "&str", T = "alloc::string::String"),

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ revisions: old-solver next-solver
2+
//@[next-solver] compile-flags: -Znext-solver
3+
//@ run-pass
4+
5+
fn foo(_: impl Into<f32>) {}
6+
7+
fn main() {
8+
foo(1.0);
9+
}

‎tests/ui/float/trait-f16-or-f32.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ check-fail
2+
3+
#![feature(f16)]
4+
5+
trait Trait {}
6+
impl Trait for f16 {}
7+
impl Trait for f32 {}
8+
9+
fn foo(_: impl Trait) {}
10+
11+
fn main() {
12+
foo(1.0); //~ ERROR the trait bound `f64: Trait` is not satisfied
13+
}

‎tests/ui/float/trait-f16-or-f32.stderr

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0277]: the trait bound `f64: Trait` is not satisfied
2+
--> $DIR/trait-f16-or-f32.rs:12:9
3+
|
4+
LL | foo(1.0);
5+
| --- ^^^ the trait `Trait` is not implemented for `f64`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the following other types implement trait `Trait`:
10+
f16
11+
f32
12+
note: required by a bound in `foo`
13+
--> $DIR/trait-f16-or-f32.rs:9:16
14+
|
15+
LL | fn foo(_: impl Trait) {}
16+
| ^^^^^ required by this bound in `foo`
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
(0)

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