From 983606d3676c967e79a94b74b890bfb02367b608 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 28 Dec 2022 08:55:01 +0000 Subject: [PATCH] Allow trait method paths to satisfy const Fn bounds --- .../src/traits/select/confirmation.rs | 26 ++++++++++++++----- .../src/traits/select/mod.rs | 8 ++++++ library/core/src/cmp.rs | 18 ++++++++----- .../const-closure-trait-method-fail.rs | 19 ++++++++++++++ .../const-closure-trait-method-fail.stderr | 23 ++++++++++++++++ .../const-closure-trait-method.rs | 19 ++++++++++++++ 6 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method.rs diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index f6fe71fbd4f88..466641ea6df3b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -12,8 +12,8 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_middle::ty::{ - self, GenericArg, GenericArgKind, GenericParamDefKind, InternalSubsts, SubstsRef, - ToPolyTraitRef, ToPredicate, Ty, TyCtxt, + self, Binder, GenericArg, GenericArgKind, GenericParamDefKind, InternalSubsts, SubstsRef, + ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, }; use rustc_span::def_id::DefId; @@ -98,8 +98,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Future(vtable_future) } - FnPointerCandidate { .. } => { - let data = self.confirm_fn_pointer_candidate(obligation)?; + FnPointerCandidate { is_const } => { + let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; ImplSource::FnPointer(data) } @@ -597,17 +597,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_fn_pointer_candidate( &mut self, obligation: &TraitObligation<'tcx>, + is_const: bool, ) -> Result>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); + let tcx = self.tcx(); let self_ty = self .infcx .shallow_resolve(obligation.self_ty().no_bound_vars()) .expect("fn pointer should not capture bound vars from predicate"); - let sig = self_ty.fn_sig(self.tcx()); + let sig = self_ty.fn_sig(tcx); let trait_ref = closure_trait_ref_and_return_type( - self.tcx(), + tcx, obligation.predicate.def_id(), self_ty, sig, @@ -616,9 +618,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_bound(|(trait_ref, _)| trait_ref); let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let cause = obligation.derived_cause(BuiltinDerivedObligation); + + if obligation.is_const() && !is_const { + // function is a trait method + if let ty::FnDef(def_id, substs) = self_ty.kind() && let Some(trait_id) = tcx.trait_of_item(*def_id) { + let trait_ref = TraitRef::from_method(tcx, trait_id, *substs); + let poly_trait_pred = Binder::dummy(trait_ref).with_constness(ty::BoundConstness::ConstIfConst); + let obligation = Obligation::new(tcx, cause.clone(), obligation.param_env, poly_trait_pred); + nested.push(obligation); + } + } // Confirm the `type Output: Sized;` bound that is present on `FnOnce` - let cause = obligation.derived_cause(BuiltinDerivedObligation); let output_ty = self.infcx.replace_bound_vars_with_placeholders(sig.output()); let output_ty = normalize_with_depth_to( self, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 81e8f9e914c23..760b4585f4e19 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1374,6 +1374,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { FutureCandidate => {} // FnDef where the function is const FnPointerCandidate { is_const: true } => {} + FnPointerCandidate { is_const: false } => { + if let ty::FnDef(def_id, _) = obligation.self_ty().skip_binder().kind() && tcx.trait_of_item(*def_id).is_some() { + // Trait methods are not seen as const unless the trait is implemented as const. + // We do not filter that out in here, but nested obligations will be needed to confirm this. + } else { + continue + } + } ConstDestructCandidate(_) => {} _ => { // reject all other types of candidates diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 949896e574806..74fb1aadaf9b9 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -800,9 +800,12 @@ pub trait Ord: Eq + PartialOrd { Self: Sized, Self: ~const Destruct, { - // HACK(fee1-dead): go back to using `self.max_by(other, Ord::cmp)` - // when trait methods are allowed to be used when a const closure is - // expected. + #[cfg(not(bootstrap))] + { + max_by(self, other, Ord::cmp) + } + + #[cfg(bootstrap)] match self.cmp(&other) { Ordering::Less | Ordering::Equal => other, Ordering::Greater => self, @@ -827,9 +830,12 @@ pub trait Ord: Eq + PartialOrd { Self: Sized, Self: ~const Destruct, { - // HACK(fee1-dead): go back to using `self.min_by(other, Ord::cmp)` - // when trait methods are allowed to be used when a const closure is - // expected. + #[cfg(not(bootstrap))] + { + min_by(self, other, Ord::cmp) + } + + #[cfg(bootstrap)] match self.cmp(&other) { Ordering::Less | Ordering::Equal => self, Ordering::Greater => other, diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs b/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs new file mode 100644 index 0000000000000..b5f19e40c0334 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs @@ -0,0 +1,19 @@ +#![feature(const_trait_impl)] + +#[const_trait] +trait Tr { + fn a(self) -> i32; +} + +impl Tr for () { + fn a(self) -> i32 { 42 } +} + +const fn need_const_closure i32>(x: T) -> i32 { + x(()) +} + +const _: () = assert!(need_const_closure(Tr::a) == 42); +//~^ ERROR: the trait bound + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr new file mode 100644 index 0000000000000..4470e287cc36d --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `(): ~const Tr` is not satisfied in `fn(()) -> i32 {<() as Tr>::a}` + --> $DIR/const-closure-trait-method-fail.rs:16:42 + | +LL | const _: () = assert!(need_const_closure(Tr::a) == 42); + | ------------------ ^^^^^ within `fn(()) -> i32 {<() as Tr>::a}`, the trait `~const Tr` is not implemented for `()` + | | + | required by a bound introduced by this call + | +note: the trait `Tr` is implemented for `()`, but that implementation is not `const` + --> $DIR/const-closure-trait-method-fail.rs:16:42 + | +LL | const _: () = assert!(need_const_closure(Tr::a) == 42); + | ^^^^^ + = note: required because it appears within the type `fn(()) -> i32 {<() as Tr>::a}` +note: required by a bound in `need_const_closure` + --> $DIR/const-closure-trait-method-fail.rs:12:32 + | +LL | const fn need_const_closure i32>(x: T) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `need_const_closure` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method.rs b/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method.rs new file mode 100644 index 0000000000000..3e6d1908848e6 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-closure-trait-method.rs @@ -0,0 +1,19 @@ +// check-pass +#![feature(const_trait_impl)] + +#[const_trait] +trait Tr { + fn a(self) -> i32; +} + +impl const Tr for () { + fn a(self) -> i32 { 42 } +} + +const fn need_const_closure i32>(x: T) -> i32 { + x(()) +} + +const _: () = assert!(need_const_closure(Tr::a) == 42); + +fn main() {}