Skip to content

Commit 973a4db

Browse files
committed
Auto merge of #106210 - fee1-dead-contrib:const-closure-trait-method, r=compiler-errors
Allow trait method paths to satisfy const Fn bounds r? `@oli-obk`
2 parents 2c7536e + 983606d commit 973a4db

File tree

6 files changed

+100
-13
lines changed

6 files changed

+100
-13
lines changed

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+19-7
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use rustc_index::bit_set::GrowableBitSet;
1212
use rustc_infer::infer::InferOk;
1313
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
1414
use rustc_middle::ty::{
15-
self, GenericArg, GenericArgKind, GenericParamDefKind, InternalSubsts, SubstsRef,
16-
ToPolyTraitRef, ToPredicate, Ty, TyCtxt,
15+
self, Binder, GenericArg, GenericArgKind, GenericParamDefKind, InternalSubsts, SubstsRef,
16+
ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt,
1717
};
1818
use rustc_span::def_id::DefId;
1919

@@ -98,8 +98,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
9898
ImplSource::Future(vtable_future)
9999
}
100100

101-
FnPointerCandidate { .. } => {
102-
let data = self.confirm_fn_pointer_candidate(obligation)?;
101+
FnPointerCandidate { is_const } => {
102+
let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
103103
ImplSource::FnPointer(data)
104104
}
105105

@@ -597,17 +597,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
597597
fn confirm_fn_pointer_candidate(
598598
&mut self,
599599
obligation: &TraitObligation<'tcx>,
600+
is_const: bool,
600601
) -> Result<ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
601602
{
602603
debug!(?obligation, "confirm_fn_pointer_candidate");
603604

605+
let tcx = self.tcx();
604606
let self_ty = self
605607
.infcx
606608
.shallow_resolve(obligation.self_ty().no_bound_vars())
607609
.expect("fn pointer should not capture bound vars from predicate");
608-
let sig = self_ty.fn_sig(self.tcx());
610+
let sig = self_ty.fn_sig(tcx);
609611
let trait_ref = closure_trait_ref_and_return_type(
610-
self.tcx(),
612+
tcx,
611613
obligation.predicate.def_id(),
612614
self_ty,
613615
sig,
@@ -616,9 +618,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
616618
.map_bound(|(trait_ref, _)| trait_ref);
617619

618620
let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
621+
let cause = obligation.derived_cause(BuiltinDerivedObligation);
622+
623+
if obligation.is_const() && !is_const {
624+
// function is a trait method
625+
if let ty::FnDef(def_id, substs) = self_ty.kind() && let Some(trait_id) = tcx.trait_of_item(*def_id) {
626+
let trait_ref = TraitRef::from_method(tcx, trait_id, *substs);
627+
let poly_trait_pred = Binder::dummy(trait_ref).with_constness(ty::BoundConstness::ConstIfConst);
628+
let obligation = Obligation::new(tcx, cause.clone(), obligation.param_env, poly_trait_pred);
629+
nested.push(obligation);
630+
}
631+
}
619632

620633
// Confirm the `type Output: Sized;` bound that is present on `FnOnce`
621-
let cause = obligation.derived_cause(BuiltinDerivedObligation);
622634
let output_ty = self.infcx.replace_bound_vars_with_placeholders(sig.output());
623635
let output_ty = normalize_with_depth_to(
624636
self,

compiler/rustc_trait_selection/src/traits/select/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
13741374
FutureCandidate => {}
13751375
// FnDef where the function is const
13761376
FnPointerCandidate { is_const: true } => {}
1377+
FnPointerCandidate { is_const: false } => {
1378+
if let ty::FnDef(def_id, _) = obligation.self_ty().skip_binder().kind() && tcx.trait_of_item(*def_id).is_some() {
1379+
// Trait methods are not seen as const unless the trait is implemented as const.
1380+
// We do not filter that out in here, but nested obligations will be needed to confirm this.
1381+
} else {
1382+
continue
1383+
}
1384+
}
13771385
ConstDestructCandidate(_) => {}
13781386
_ => {
13791387
// reject all other types of candidates

library/core/src/cmp.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -798,9 +798,12 @@ pub trait Ord: Eq + PartialOrd<Self> {
798798
Self: Sized,
799799
Self: ~const Destruct,
800800
{
801-
// HACK(fee1-dead): go back to using `self.max_by(other, Ord::cmp)`
802-
// when trait methods are allowed to be used when a const closure is
803-
// expected.
801+
#[cfg(not(bootstrap))]
802+
{
803+
max_by(self, other, Ord::cmp)
804+
}
805+
806+
#[cfg(bootstrap)]
804807
match self.cmp(&other) {
805808
Ordering::Less | Ordering::Equal => other,
806809
Ordering::Greater => self,
@@ -825,9 +828,12 @@ pub trait Ord: Eq + PartialOrd<Self> {
825828
Self: Sized,
826829
Self: ~const Destruct,
827830
{
828-
// HACK(fee1-dead): go back to using `self.min_by(other, Ord::cmp)`
829-
// when trait methods are allowed to be used when a const closure is
830-
// expected.
831+
#[cfg(not(bootstrap))]
832+
{
833+
min_by(self, other, Ord::cmp)
834+
}
835+
836+
#[cfg(bootstrap)]
831837
match self.cmp(&other) {
832838
Ordering::Less | Ordering::Equal => self,
833839
Ordering::Greater => other,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#![feature(const_trait_impl)]
2+
3+
#[const_trait]
4+
trait Tr {
5+
fn a(self) -> i32;
6+
}
7+
8+
impl Tr for () {
9+
fn a(self) -> i32 { 42 }
10+
}
11+
12+
const fn need_const_closure<T: ~const FnOnce(()) -> i32>(x: T) -> i32 {
13+
x(())
14+
}
15+
16+
const _: () = assert!(need_const_closure(Tr::a) == 42);
17+
//~^ ERROR: the trait bound
18+
19+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0277]: the trait bound `(): ~const Tr` is not satisfied in `fn(()) -> i32 {<() as Tr>::a}`
2+
--> $DIR/const-closure-trait-method-fail.rs:16:42
3+
|
4+
LL | const _: () = assert!(need_const_closure(Tr::a) == 42);
5+
| ------------------ ^^^^^ within `fn(()) -> i32 {<() as Tr>::a}`, the trait `~const Tr` is not implemented for `()`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: the trait `Tr` is implemented for `()`, but that implementation is not `const`
10+
--> $DIR/const-closure-trait-method-fail.rs:16:42
11+
|
12+
LL | const _: () = assert!(need_const_closure(Tr::a) == 42);
13+
| ^^^^^
14+
= note: required because it appears within the type `fn(()) -> i32 {<() as Tr>::a}`
15+
note: required by a bound in `need_const_closure`
16+
--> $DIR/const-closure-trait-method-fail.rs:12:32
17+
|
18+
LL | const fn need_const_closure<T: ~const FnOnce(()) -> i32>(x: T) -> i32 {
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `need_const_closure`
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// check-pass
2+
#![feature(const_trait_impl)]
3+
4+
#[const_trait]
5+
trait Tr {
6+
fn a(self) -> i32;
7+
}
8+
9+
impl const Tr for () {
10+
fn a(self) -> i32 { 42 }
11+
}
12+
13+
const fn need_const_closure<T: ~const FnOnce(()) -> i32>(x: T) -> i32 {
14+
x(())
15+
}
16+
17+
const _: () = assert!(need_const_closure(Tr::a) == 42);
18+
19+
fn main() {}

0 commit comments

Comments
 (0)