Skip to content

Commit

Permalink
Rollup merge of rust-lang#93827 - eholk:stabilize-const_fn-features, …
Browse files Browse the repository at this point in the history
…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 rust-lang#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: rust-lang#57563, rust-lang#63997, rust-lang#93706
  • Loading branch information
matthiaskrgr authored Mar 7, 2022
2 parents 1ca8d0b + bb6bcaa commit 9d7166c
Show file tree
Hide file tree
Showing 104 changed files with 155 additions and 1,001 deletions.
78 changes: 3 additions & 75 deletions compiler/rustc_const_eval/src/transform/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
}
}

self.check_item_predicates();

for (idx, local) in body.local_decls.iter_enumerated() {
// Handle the return place below.
if idx == RETURN_PLACE || local.internal {
Expand Down Expand Up @@ -358,83 +356,11 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {

match *ty.kind() {
ty::Ref(_, _, hir::Mutability::Mut) => self.check_op(ops::ty::MutRef(kind)),
ty::Opaque(..) => self.check_op(ops::ty::ImplTrait),
ty::FnPtr(..) => self.check_op(ops::ty::FnPtr(kind)),

ty::Dynamic(preds, _) => {
for pred in preds.iter() {
match pred.skip_binder() {
ty::ExistentialPredicate::AutoTrait(_)
| ty::ExistentialPredicate::Projection(_) => {
self.check_op(ops::ty::DynTrait(kind))
}
ty::ExistentialPredicate::Trait(trait_ref) => {
if Some(trait_ref.def_id) != self.tcx.lang_items().sized_trait() {
self.check_op(ops::ty::DynTrait(kind))
}
}
}
}
}
_ => {}
}
}
}

fn check_item_predicates(&mut self) {
let ConstCx { tcx, .. } = *self.ccx;

let mut current = self.def_id().to_def_id();
loop {
let predicates = tcx.predicates_of(current);
for (predicate, _) in predicates.predicates {
match predicate.kind().skip_binder() {
ty::PredicateKind::RegionOutlives(_)
| ty::PredicateKind::TypeOutlives(_)
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::Projection(_)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
ty::PredicateKind::ObjectSafe(_) => {
bug!("object safe predicate on function: {:#?}", predicate)
}
ty::PredicateKind::ClosureKind(..) => {
bug!("closure kind predicate on function: {:#?}", predicate)
}
ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) => {
bug!("subtype/coerce predicate on function: {:#?}", predicate)
}
ty::PredicateKind::Trait(pred) => {
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
continue;
}
match pred.self_ty().kind() {
ty::Param(p) => {
let generics = tcx.generics_of(current);
let def = generics.type_param(p, tcx);
let span = tcx.def_span(def.def_id);

// These are part of the function signature, so treat them like
// arguments when determining importance.
let kind = LocalKind::Arg;

self.check_op_spanned(ops::ty::TraitBound(kind), span);
}
// other kinds of bounds are either tautologies
// or cause errors in other passes
_ => continue,
}
}
}
}
match predicates.parent {
Some(parent) => current = parent,
None => break,
}
}
}

fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
match self.const_kind() {
// In a const fn all borrows are transient or point to the places given via
Expand Down Expand Up @@ -613,7 +539,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
),
_,
_,
) => self.check_op(ops::FnPtrCast),
) => {
// Nothing to do here. Function pointer casts are allowed now.
}

Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => {
// Nothing to check here (`check_local_or_return_ty` ensures no trait objects occur
Expand Down
188 changes: 0 additions & 188 deletions compiler/rustc_const_eval/src/transform/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,31 +355,6 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
}
}

#[derive(Debug)]
pub struct FnPtrCast;
impl<'tcx> NonConstOp<'tcx> for FnPtrCast {
fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
Status::Unstable(sym::const_fn_fn_ptr_basics)
}
}

fn build_error(
&self,
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_fn_ptr_basics,
span,
&format!("function pointer casts are not allowed in {}s", ccx.const_kind()),
)
}
}

#[derive(Debug)]
pub struct Generator(pub hir::GeneratorKind);
impl<'tcx> NonConstOp<'tcx> for Generator {
Expand Down Expand Up @@ -820,167 +795,4 @@ pub mod ty {
)
}
}

#[derive(Debug)]
pub struct FnPtr(pub mir::LocalKind);
impl<'tcx> NonConstOp<'tcx> for FnPtr {
fn importance(&self) -> DiagnosticImportance {
match self.0 {
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
DiagnosticImportance::Primary
}
}
}

fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
Status::Unstable(sym::const_fn_fn_ptr_basics)
}
}

fn build_error(
&self,
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_fn_ptr_basics,
span,
&format!("function pointers cannot appear in {}s", ccx.const_kind()),
)
}
}

#[derive(Debug)]
pub struct ImplTrait;
impl<'tcx> NonConstOp<'tcx> for ImplTrait {
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_impl_trait)
}

fn build_error(
&self,
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_impl_trait,
span,
&format!("`impl Trait` is not allowed in {}s", ccx.const_kind()),
)
}
}

#[derive(Debug)]
pub struct TraitBound(pub mir::LocalKind);
impl<'tcx> NonConstOp<'tcx> for TraitBound {
fn importance(&self) -> DiagnosticImportance {
match self.0 {
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
DiagnosticImportance::Primary
}
}
}

fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
Status::Unstable(sym::const_fn_trait_bound)
}
}

fn build_error(
&self,
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let mut err = feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_trait_bound,
span,
"trait bounds other than `Sized` on const fn parameters are unstable",
);

match ccx.fn_sig() {
Some(fn_sig) if !fn_sig.span.contains(span) => {
err.span_label(fn_sig.span, "function declared as const here");
}
_ => {}
}

err
}
}

#[derive(Debug)]
pub struct DynTrait(pub mir::LocalKind);
impl<'tcx> NonConstOp<'tcx> for DynTrait {
fn importance(&self) -> DiagnosticImportance {
match self.0 {
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
mir::LocalKind::ReturnPointer | mir::LocalKind::Arg => {
DiagnosticImportance::Primary
}
}
}

fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
Status::Unstable(sym::const_fn_trait_bound)
}
}

fn build_error(
&self,
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let mut err = feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_trait_bound,
span,
"trait objects in const fn are unstable",
);

match ccx.fn_sig() {
Some(fn_sig) if !fn_sig.span.contains(span) => {
err.span_label(fn_sig.span, "function declared as const here");
}
_ => {}
}

err
}
}

/// A trait bound with the `?const Trait` opt-out
#[derive(Debug)]
pub struct TraitBoundNotConst;
impl<'tcx> NonConstOp<'tcx> for TraitBoundNotConst {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_trait_bound_opt_out)
}

fn build_error(
&self,
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_trait_bound_opt_out,
span,
"`?const Trait` syntax is unstable",
)
}
}
}
6 changes: 6 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ declare_features! (
(accepted, conservative_impl_trait, "1.26.0", Some(34511), None),
/// Allows calling constructor functions in `const fn`.
(accepted, const_constructor, "1.40.0", Some(61456), None),
/// Allows using and casting function pointers in a `const fn`.
(accepted, const_fn_fn_ptr_basics, "1.61.0", Some(57563), None),
/// Allows trait bounds in `const fn`.
(accepted, const_fn_trait_bound, "1.61.0", Some(93706), None),
/// Allows calling `transmute` in const fn
(accepted, const_fn_transmute, "1.56.0", Some(53605), None),
/// Allows accessing fields of unions inside `const` functions.
Expand All @@ -96,6 +100,8 @@ declare_features! (
(accepted, const_generics_defaults, "1.59.0", Some(44580), None),
/// Allows the use of `if` and `match` in constants.
(accepted, const_if_match, "1.46.0", Some(49146), None),
/// Allows argument and return position `impl Trait` in a `const fn`.
(accepted, const_impl_trait, "1.61.0", Some(77463), None),
/// Allows indexing into constant arrays.
(accepted, const_indexing, "1.26.0", Some(29947), None),
/// Allows let bindings, assignments and destructuring in `const` functions and constants.
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,8 @@ declare_features! (
(active, const_extern_fn, "1.40.0", Some(64926), None),
/// Allows basic arithmetic on floating point types in a `const fn`.
(active, const_fn_floating_point_arithmetic, "1.48.0", Some(57241), None),
/// Allows using and casting function pointers in a `const fn`.
(active, const_fn_fn_ptr_basics, "1.48.0", Some(57563), None),
/// Allows trait bounds in `const fn`.
(active, const_fn_trait_bound, "1.53.0", Some(93706), None),
/// Allows `for _ in _` loops in const contexts.
(active, const_for, "1.56.0", Some(87575), None),
/// Allows argument and return position `impl Trait` in a `const fn`.
(active, const_impl_trait, "1.48.0", Some(77463), None),
/// Allows using `&mut` in constant functions.
(active, const_mut_refs, "1.41.0", Some(57349), None),
/// Be more precise when looking for live drops in a const context.
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
#![feature(box_syntax)]
#![feature(cfg_sanitize)]
#![feature(const_deref)]
#![feature(const_fn_trait_bound)]
#![cfg_attr(bootstrap, feature(const_fn_trait_bound))]
#![feature(const_mut_refs)]
#![feature(const_ptr_write)]
#![feature(const_precise_live_drops)]
Expand Down
6 changes: 3 additions & 3 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@
#![feature(cfg_target_has_atomic)]
#![feature(cfg_target_has_atomic_equal_alignment)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_fn_fn_ptr_basics)]
#![feature(const_fn_trait_bound)]
#![feature(const_impl_trait)]
#![cfg_attr(bootstrap, feature(const_fn_fn_ptr_basics))]
#![cfg_attr(bootstrap, feature(const_fn_trait_bound))]
#![cfg_attr(bootstrap, feature(const_impl_trait))]
#![feature(const_mut_refs)]
#![feature(const_precise_live_drops)]
#![feature(const_refs_to_cell)]
Expand Down
4 changes: 2 additions & 2 deletions library/proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
#![feature(rustc_allow_const_fn_unstable)]
#![feature(nll)]
#![feature(staged_api)]
#![feature(const_fn_trait_bound)]
#![feature(const_fn_fn_ptr_basics)]
#![cfg_attr(bootstrap, feature(const_fn_trait_bound))]
#![cfg_attr(bootstrap, feature(const_fn_fn_ptr_basics))]
#![feature(allow_internal_unstable)]
#![feature(decl_macro)]
#![feature(extern_types)]
Expand Down
4 changes: 2 additions & 2 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@
#![feature(char_internals)]
#![feature(concat_bytes)]
#![feature(concat_idents)]
#![feature(const_fn_fn_ptr_basics)]
#![feature(const_fn_trait_bound)]
#![cfg_attr(bootstrap, feature(const_fn_fn_ptr_basics))]
#![cfg_attr(bootstrap, feature(const_fn_trait_bound))]
#![feature(const_format_args)]
#![feature(const_io_structs)]
#![feature(const_ip)]
Expand Down
1 change: 0 additions & 1 deletion src/test/ui/borrowck/issue-88434-minimal-example.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#![feature(const_fn_trait_bound)]
// Regression test related to issue 88434

const _CONST: &() = &f(&|_| {});
Expand Down
Loading

0 comments on commit 9d7166c

Please sign in to comment.