Skip to content

Commit 9d7166c

Browse files
authored
Rollup merge of #93827 - eholk:stabilize-const_fn-features, r=wesleywiser
Stabilize const_fn_fn_ptr_basics, const_fn_trait_bound, and const_impl_trait # Stabilization Report This PR serves as a request for stabilization for three const evaluation features: 1. `const_fn_fn_ptr_basics` 2. `const_fn_trait_bound` 3. `const_impl_trait` These are being stabilized together because they are relatively minor and related updates to existing functionality. ## `const_fn_fn_ptr_basics` Allows creating, passing, and casting function pointers in a `const fn`. The following is an example of what is now allowed: ```rust const fn get_function() -> fn() { fn foo() { println!("Hello, World!"); } foo } ``` Casts between function pointer types are allowed, as well as transmuting from integers: ```rust const fn get_function() -> fn() { unsafe { std::mem::transmute(0x1234usize) } } ``` However, casting from a function pointer to an integer is not allowed: ```rust const fn fn_to_usize(f: fn()) -> usize { f as usize //~ pointers cannot be cast to integers during const eval } ``` Calling function pointers is also not allowed. ```rust const fn call_fn_ptr(f: fn()) { f() //~ function pointers are not allowed in const fn } ``` ### Test Coverage The following tests include code that exercises this feature: - `src/test/ui/consts/issue-37550.rs` - `src/test/ui/consts/issue-46553.rs` - `src/test/ui/consts/issue-56164.rs` - `src/test/ui/consts/min_const_fn/allow_const_fn_ptr_run_pass.rs` - `src/test/ui/consts/min_const_fn/cast_fn.rs` - `src/test/ui/consts/min_const_fn/cmp_fn_pointers.rs` ## `const_fn_trait_bound` Allows trait bounds in `const fn`. Additionally, this feature allows creating and passing `dyn Trait` objects. Examples such as the following are allowed by this feature: ```rust const fn do_thing<T: Foo>(_x: &T) { // ... } ``` Previously only `Sized` was allowed as a trait bound. There is no way to call methods from the trait because trait methods cannot currently be marked as const. Allowing trait bounds in const functions does allow the const function to use the trait's associated types and constants. This feature also allowes `dyn Trait` types. These work equivalently to non-const code. Similar to other pointers in const code, the value of a `dyn Trait` pointer cannot be observed. Note that due to #90912, it was already possible to do the example above as follows: ```rust const fn do_thing<T>(_x: &T) where (T,): Foo { // ... } ``` ### Test Coverage The following tests include code that exercises `const_fn_trait_bound`: - `src/test/ui/consts/const-fn.rs` - `src/test/ui/consts/issue-88071.rs` - `src/test/ui/consts/min_const_fn/min_const_fn.rs` - `src/test/ui/consts/min_const_fn/min_const_fn_dyn.rs` - `src/test/ui/nll/issue-55825-const-fn.rs` - Many of the tests in `src/test/ui/rfc-2632-const-trait-impl/` also exercise this feature. ## `const_impl_trait` Allows argument and return position `impl Trait` in a `const fn`, such as in the following example: ```rust const fn do_thing(x: impl Foo) -> impl Foo { x } ``` Similar to generic parameters and function pointers, this allows the creation of such opaque types, but not doing anything with them beyond accessing associated types and constants. ### Test Coverage The following tests exercise this feature: - `src/test/ui/type-alias-impl-trait/issue-53096.rs` - `src/test/ui/type-alias-impl-trait/issue-53678-generator-and-const-fn.rs` ## Documentation These features are documented along with the other const evaluation features in the Rust Reference at https://doc.rust-lang.org/stable/reference/const_eval.html. There is a PR that updates this documentation to reflect the capabilities enabled by these features at rust-lang/reference#1166. Tracking issues: #57563, #63997, #93706
2 parents 1ca8d0b + bb6bcaa commit 9d7166c

File tree

104 files changed

+155
-1001
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+155
-1001
lines changed

compiler/rustc_const_eval/src/transform/check_consts/check.rs

+3-75
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
230230
}
231231
}
232232

233-
self.check_item_predicates();
234-
235233
for (idx, local) in body.local_decls.iter_enumerated() {
236234
// Handle the return place below.
237235
if idx == RETURN_PLACE || local.internal {
@@ -358,83 +356,11 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
358356

359357
match *ty.kind() {
360358
ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef(kind)),
361-
ty::Opaque(..) => self.check_op(ops::ty::ImplTrait),
362-
ty::FnPtr(..) => self.check_op(ops::ty::FnPtr(kind)),
363-
364-
ty::Dynamic(preds, _) => {
365-
for pred in preds.iter() {
366-
match pred.skip_binder() {
367-
ty::ExistentialPredicate::AutoTrait(_)
368-
| ty::ExistentialPredicate::Projection(_) => {
369-
self.check_op(ops::ty::DynTrait(kind))
370-
}
371-
ty::ExistentialPredicate::Trait(trait_ref) => {
372-
if Some(trait_ref.def_id) != self.tcx.lang_items().sized_trait() {
373-
self.check_op(ops::ty::DynTrait(kind))
374-
}
375-
}
376-
}
377-
}
378-
}
379359
_ => {}
380360
}
381361
}
382362
}
383363

384-
fn check_item_predicates(&mut self) {
385-
let ConstCx { tcx, .. } = *self.ccx;
386-
387-
let mut current = self.def_id().to_def_id();
388-
loop {
389-
let predicates = tcx.predicates_of(current);
390-
for (predicate, _) in predicates.predicates {
391-
match predicate.kind().skip_binder() {
392-
ty::PredicateKind::RegionOutlives(_)
393-
| ty::PredicateKind::TypeOutlives(_)
394-
| ty::PredicateKind::WellFormed(_)
395-
| ty::PredicateKind::Projection(_)
396-
| ty::PredicateKind::ConstEvaluatable(..)
397-
| ty::PredicateKind::ConstEquate(..)
398-
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
399-
ty::PredicateKind::ObjectSafe(_) => {
400-
bug!("object safe predicate on function: {:#?}", predicate)
401-
}
402-
ty::PredicateKind::ClosureKind(..) => {
403-
bug!("closure kind predicate on function: {:#?}", predicate)
404-
}
405-
ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) => {
406-
bug!("subtype/coerce predicate on function: {:#?}", predicate)
407-
}
408-
ty::PredicateKind::Trait(pred) => {
409-
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
410-
continue;
411-
}
412-
match pred.self_ty().kind() {
413-
ty::Param(p) => {
414-
let generics = tcx.generics_of(current);
415-
let def = generics.type_param(p, tcx);
416-
let span = tcx.def_span(def.def_id);
417-
418-
// These are part of the function signature, so treat them like
419-
// arguments when determining importance.
420-
let kind = LocalKind::Arg;
421-
422-
self.check_op_spanned(ops::ty::TraitBound(kind), span);
423-
}
424-
// other kinds of bounds are either tautologies
425-
// or cause errors in other passes
426-
_ => continue,
427-
}
428-
}
429-
}
430-
}
431-
match predicates.parent {
432-
Some(parent) => current = parent,
433-
None => break,
434-
}
435-
}
436-
}
437-
438364
fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
439365
match self.const_kind() {
440366
// In a const fn all borrows are transient or point to the places given via
@@ -613,7 +539,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
613539
),
614540
_,
615541
_,
616-
) => self.check_op(ops::FnPtrCast),
542+
) => {
543+
// Nothing to do here. Function pointer casts are allowed now.
544+
}
617545

618546
Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => {
619547
// Nothing to check here (`check_local_or_return_ty` ensures no trait objects occur

compiler/rustc_const_eval/src/transform/check_consts/ops.rs

-188
Original file line numberDiff line numberDiff line change
@@ -355,31 +355,6 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
355355
}
356356
}
357357

358-
#[derive(Debug)]
359-
pub struct FnPtrCast;
360-
impl<'tcx> NonConstOp<'tcx> for FnPtrCast {
361-
fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
362-
if ccx.const_kind() != hir::ConstContext::ConstFn {
363-
Status::Allowed
364-
} else {
365-
Status::Unstable(sym::const_fn_fn_ptr_basics)
366-
}
367-
}
368-
369-
fn build_error(
370-
&self,
371-
ccx: &ConstCx<'_, 'tcx>,
372-
span: Span,
373-
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
374-
feature_err(
375-
&ccx.tcx.sess.parse_sess,
376-
sym::const_fn_fn_ptr_basics,
377-
span,
378-
&format!("function pointer casts are not allowed in {}s", ccx.const_kind()),
379-
)
380-
}
381-
}
382-
383358
#[derive(Debug)]
384359
pub struct Generator(pub hir::GeneratorKind);
385360
impl<'tcx> NonConstOp<'tcx> for Generator {
@@ -820,167 +795,4 @@ pub mod ty {
820795
)
821796
}
822797
}
823-
824-
#[derive(Debug)]
825-
pub struct FnPtr(pub mir::LocalKind);
826-
impl<'tcx> NonConstOp<'tcx> for FnPtr {
827-
fn importance(&self) -> DiagnosticImportance {
828-
match self.0 {
829-
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
830-
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
831-
DiagnosticImportance::Primary
832-
}
833-
}
834-
}
835-
836-
fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
837-
if ccx.const_kind() != hir::ConstContext::ConstFn {
838-
Status::Allowed
839-
} else {
840-
Status::Unstable(sym::const_fn_fn_ptr_basics)
841-
}
842-
}
843-
844-
fn build_error(
845-
&self,
846-
ccx: &ConstCx<'_, 'tcx>,
847-
span: Span,
848-
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
849-
feature_err(
850-
&ccx.tcx.sess.parse_sess,
851-
sym::const_fn_fn_ptr_basics,
852-
span,
853-
&format!("function pointers cannot appear in {}s", ccx.const_kind()),
854-
)
855-
}
856-
}
857-
858-
#[derive(Debug)]
859-
pub struct ImplTrait;
860-
impl<'tcx> NonConstOp<'tcx> for ImplTrait {
861-
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
862-
Status::Unstable(sym::const_impl_trait)
863-
}
864-
865-
fn build_error(
866-
&self,
867-
ccx: &ConstCx<'_, 'tcx>,
868-
span: Span,
869-
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
870-
feature_err(
871-
&ccx.tcx.sess.parse_sess,
872-
sym::const_impl_trait,
873-
span,
874-
&format!("`impl Trait` is not allowed in {}s", ccx.const_kind()),
875-
)
876-
}
877-
}
878-
879-
#[derive(Debug)]
880-
pub struct TraitBound(pub mir::LocalKind);
881-
impl<'tcx> NonConstOp<'tcx> for TraitBound {
882-
fn importance(&self) -> DiagnosticImportance {
883-
match self.0 {
884-
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
885-
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
886-
DiagnosticImportance::Primary
887-
}
888-
}
889-
}
890-
891-
fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
892-
if ccx.const_kind() != hir::ConstContext::ConstFn {
893-
Status::Allowed
894-
} else {
895-
Status::Unstable(sym::const_fn_trait_bound)
896-
}
897-
}
898-
899-
fn build_error(
900-
&self,
901-
ccx: &ConstCx<'_, 'tcx>,
902-
span: Span,
903-
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
904-
let mut err = feature_err(
905-
&ccx.tcx.sess.parse_sess,
906-
sym::const_fn_trait_bound,
907-
span,
908-
"trait bounds other than `Sized` on const fn parameters are unstable",
909-
);
910-
911-
match ccx.fn_sig() {
912-
Some(fn_sig) if !fn_sig.span.contains(span) => {
913-
err.span_label(fn_sig.span, "function declared as const here");
914-
}
915-
_ => {}
916-
}
917-
918-
err
919-
}
920-
}
921-
922-
#[derive(Debug)]
923-
pub struct DynTrait(pub mir::LocalKind);
924-
impl<'tcx> NonConstOp<'tcx> for DynTrait {
925-
fn importance(&self) -> DiagnosticImportance {
926-
match self.0 {
927-
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
928-
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
929-
DiagnosticImportance::Primary
930-
}
931-
}
932-
}
933-
934-
fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
935-
if ccx.const_kind() != hir::ConstContext::ConstFn {
936-
Status::Allowed
937-
} else {
938-
Status::Unstable(sym::const_fn_trait_bound)
939-
}
940-
}
941-
942-
fn build_error(
943-
&self,
944-
ccx: &ConstCx<'_, 'tcx>,
945-
span: Span,
946-
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
947-
let mut err = feature_err(
948-
&ccx.tcx.sess.parse_sess,
949-
sym::const_fn_trait_bound,
950-
span,
951-
"trait objects in const fn are unstable",
952-
);
953-
954-
match ccx.fn_sig() {
955-
Some(fn_sig) if !fn_sig.span.contains(span) => {
956-
err.span_label(fn_sig.span, "function declared as const here");
957-
}
958-
_ => {}
959-
}
960-
961-
err
962-
}
963-
}
964-
965-
/// A trait bound with the `?const Trait` opt-out
966-
#[derive(Debug)]
967-
pub struct TraitBoundNotConst;
968-
impl<'tcx> NonConstOp<'tcx> for TraitBoundNotConst {
969-
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
970-
Status::Unstable(sym::const_trait_bound_opt_out)
971-
}
972-
973-
fn build_error(
974-
&self,
975-
ccx: &ConstCx<'_, 'tcx>,
976-
span: Span,
977-
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
978-
feature_err(
979-
&ccx.tcx.sess.parse_sess,
980-
sym::const_trait_bound_opt_out,
981-
span,
982-
"`?const Trait` syntax is unstable",
983-
)
984-
}
985-
}
986798
}

compiler/rustc_feature/src/accepted.rs

+6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ declare_features! (
8686
(accepted, conservative_impl_trait, "1.26.0", Some(34511), None),
8787
/// Allows calling constructor functions in `const fn`.
8888
(accepted, const_constructor, "1.40.0", Some(61456), None),
89+
/// Allows using and casting function pointers in a `const fn`.
90+
(accepted, const_fn_fn_ptr_basics, "1.61.0", Some(57563), None),
91+
/// Allows trait bounds in `const fn`.
92+
(accepted, const_fn_trait_bound, "1.61.0", Some(93706), None),
8993
/// Allows calling `transmute` in const fn
9094
(accepted, const_fn_transmute, "1.56.0", Some(53605), None),
9195
/// Allows accessing fields of unions inside `const` functions.
@@ -96,6 +100,8 @@ declare_features! (
96100
(accepted, const_generics_defaults, "1.59.0", Some(44580), None),
97101
/// Allows the use of `if` and `match` in constants.
98102
(accepted, const_if_match, "1.46.0", Some(49146), None),
103+
/// Allows argument and return position `impl Trait` in a `const fn`.
104+
(accepted, const_impl_trait, "1.61.0", Some(77463), None),
99105
/// Allows indexing into constant arrays.
100106
(accepted, const_indexing, "1.26.0", Some(29947), None),
101107
/// Allows let bindings, assignments and destructuring in `const` functions and constants.

compiler/rustc_feature/src/active.rs

-6
Original file line numberDiff line numberDiff line change
@@ -338,14 +338,8 @@ declare_features! (
338338
(active, const_extern_fn, "1.40.0", Some(64926), None),
339339
/// Allows basic arithmetic on floating point types in a `const fn`.
340340
(active, const_fn_floating_point_arithmetic, "1.48.0", Some(57241), None),
341-
/// Allows using and casting function pointers in a `const fn`.
342-
(active, const_fn_fn_ptr_basics, "1.48.0", Some(57563), None),
343-
/// Allows trait bounds in `const fn`.
344-
(active, const_fn_trait_bound, "1.53.0", Some(93706), None),
345341
/// Allows `for _ in _` loops in const contexts.
346342
(active, const_for, "1.56.0", Some(87575), None),
347-
/// Allows argument and return position `impl Trait` in a `const fn`.
348-
(active, const_impl_trait, "1.48.0", Some(77463), None),
349343
/// Allows using `&mut` in constant functions.
350344
(active, const_mut_refs, "1.41.0", Some(57349), None),
351345
/// Be more precise when looking for live drops in a const context.

library/alloc/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
#![feature(box_syntax)]
141141
#![feature(cfg_sanitize)]
142142
#![feature(const_deref)]
143-
#![feature(const_fn_trait_bound)]
143+
#![cfg_attr(bootstrap, feature(const_fn_trait_bound))]
144144
#![feature(const_mut_refs)]
145145
#![feature(const_ptr_write)]
146146
#![feature(const_precise_live_drops)]

library/core/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,9 @@
158158
#![feature(cfg_target_has_atomic)]
159159
#![feature(cfg_target_has_atomic_equal_alignment)]
160160
#![feature(const_fn_floating_point_arithmetic)]
161-
#![feature(const_fn_fn_ptr_basics)]
162-
#![feature(const_fn_trait_bound)]
163-
#![feature(const_impl_trait)]
161+
#![cfg_attr(bootstrap, feature(const_fn_fn_ptr_basics))]
162+
#![cfg_attr(bootstrap, feature(const_fn_trait_bound))]
163+
#![cfg_attr(bootstrap, feature(const_impl_trait))]
164164
#![feature(const_mut_refs)]
165165
#![feature(const_precise_live_drops)]
166166
#![feature(const_refs_to_cell)]

library/proc_macro/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
#![feature(rustc_allow_const_fn_unstable)]
2121
#![feature(nll)]
2222
#![feature(staged_api)]
23-
#![feature(const_fn_trait_bound)]
24-
#![feature(const_fn_fn_ptr_basics)]
23+
#![cfg_attr(bootstrap, feature(const_fn_trait_bound))]
24+
#![cfg_attr(bootstrap, feature(const_fn_fn_ptr_basics))]
2525
#![feature(allow_internal_unstable)]
2626
#![feature(decl_macro)]
2727
#![feature(extern_types)]

library/std/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,8 @@
242242
#![feature(char_internals)]
243243
#![feature(concat_bytes)]
244244
#![feature(concat_idents)]
245-
#![feature(const_fn_fn_ptr_basics)]
246-
#![feature(const_fn_trait_bound)]
245+
#![cfg_attr(bootstrap, feature(const_fn_fn_ptr_basics))]
246+
#![cfg_attr(bootstrap, feature(const_fn_trait_bound))]
247247
#![feature(const_format_args)]
248248
#![feature(const_io_structs)]
249249
#![feature(const_ip)]

src/test/ui/borrowck/issue-88434-minimal-example.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![feature(const_fn_trait_bound)]
21
// Regression test related to issue 88434
32

43
const _CONST: &() = &f(&|_| {});

0 commit comments

Comments
 (0)