Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking Issue for const_fn_trait_bound #93706

Closed
3 tasks done
PatchMixolydic opened this issue Feb 6, 2022 · 14 comments
Closed
3 tasks done

Tracking Issue for const_fn_trait_bound #93706

PatchMixolydic opened this issue Feb 6, 2022 · 14 comments
Labels
A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-trait-system Area: Trait system C-tracking-issue Category: A tracking issue for an RFC or an unstable feature.

Comments

@PatchMixolydic
Copy link
Contributor

PatchMixolydic commented Feb 6, 2022

This is a tracking issue for trait bounds on const fns. The feature gate for the issue is #![feature(const_fn_trait_bound)]. This is not to be confused with the "trait impl must be const in const contexts" syntax, ~const Trait, which is a part of const_trait_impl and is tracked in #67792.

About tracking issues

Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

Steps

Unresolved Questions

Implementation history

@rustbot modify labels +A-const-fn +A-traits

@PatchMixolydic PatchMixolydic added the C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. label Feb 6, 2022
@rustbot rustbot added A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-trait-system Area: Trait system labels Feb 6, 2022
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Feb 6, 2022
…s-const_fn_trait_bound, r=oli-obk

Update tracking issue for `const_fn_trait_bound`

It previously pointed to rust-lang#57563, the conglomerate issue for `const fn` (presumably under the feature gate `const_fn`). This tracking issue doesn't mention anything about `const_fn_trait_bound`(the only occurrence of "trait bound" is for the now-removed `?const Trait` syntax), which can be confusing to people who want to find out more about trait bounds on `const fn`s. This pull request changes the tracking issue to one meant specifically for `const_fn_trait_bound`, rust-lang#93706, which can help collect information on this feature's stabilization and point users towards `const_trait_impl` if they're looking for const-in-const-contexts trait bounds.

Fixes rust-lang#93679.

`@rustbot` modify labels +A-const-fn +F-const_trait_impl
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Feb 7, 2022
…s-const_fn_trait_bound, r=oli-obk

Update tracking issue for `const_fn_trait_bound`

It previously pointed to rust-lang#57563, the conglomerate issue for `const fn` (presumably under the feature gate `const_fn`). This tracking issue doesn't mention anything about `const_fn_trait_bound`(the only occurrence of "trait bound" is for the now-removed `?const Trait` syntax), which can be confusing to people who want to find out more about trait bounds on `const fn`s. This pull request changes the tracking issue to one meant specifically for `const_fn_trait_bound`, rust-lang#93706, which can help collect information on this feature's stabilization and point users towards `const_trait_impl` if they're looking for const-in-const-contexts trait bounds.

Fixes rust-lang#93679.

``@rustbot`` modify labels +A-const-fn +F-const_trait_impl
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Feb 7, 2022
…s-const_fn_trait_bound, r=oli-obk

Update tracking issue for `const_fn_trait_bound`

It previously pointed to rust-lang#57563, the conglomerate issue for `const fn` (presumably under the feature gate `const_fn`). This tracking issue doesn't mention anything about `const_fn_trait_bound`(the only occurrence of "trait bound" is for the now-removed `?const Trait` syntax), which can be confusing to people who want to find out more about trait bounds on `const fn`s. This pull request changes the tracking issue to one meant specifically for `const_fn_trait_bound`, rust-lang#93706, which can help collect information on this feature's stabilization and point users towards `const_trait_impl` if they're looking for const-in-const-contexts trait bounds.

Fixes rust-lang#93679.

```@rustbot``` modify labels +A-const-fn +F-const_trait_impl
m-ou-se added a commit to m-ou-se/rust that referenced this issue Feb 7, 2022
…s-const_fn_trait_bound, r=oli-obk

Update tracking issue for `const_fn_trait_bound`

It previously pointed to rust-lang#57563, the conglomerate issue for `const fn` (presumably under the feature gate `const_fn`). This tracking issue doesn't mention anything about `const_fn_trait_bound`(the only occurrence of "trait bound" is for the now-removed `?const Trait` syntax), which can be confusing to people who want to find out more about trait bounds on `const fn`s. This pull request changes the tracking issue to one meant specifically for `const_fn_trait_bound`, rust-lang#93706, which can help collect information on this feature's stabilization and point users towards `const_trait_impl` if they're looking for const-in-const-contexts trait bounds.

Fixes rust-lang#93679.

````@rustbot```` modify labels +A-const-fn +F-const_trait_impl
m-ou-se added a commit to m-ou-se/rust that referenced this issue Feb 7, 2022
…s-const_fn_trait_bound, r=oli-obk

Update tracking issue for `const_fn_trait_bound`

It previously pointed to rust-lang#57563, the conglomerate issue for `const fn` (presumably under the feature gate `const_fn`). This tracking issue doesn't mention anything about `const_fn_trait_bound`(the only occurrence of "trait bound" is for the now-removed `?const Trait` syntax), which can be confusing to people who want to find out more about trait bounds on `const fn`s. This pull request changes the tracking issue to one meant specifically for `const_fn_trait_bound`, rust-lang#93706, which can help collect information on this feature's stabilization and point users towards `const_trait_impl` if they're looking for const-in-const-contexts trait bounds.

Fixes rust-lang#93679.

`````@rustbot````` modify labels +A-const-fn +F-const_trait_impl
@fee1-dead
Copy link
Member

cc @rust-lang/wg-const-eval: Should we remove this feature because of #90912?

@oli-obk
Copy link
Contributor

oli-obk commented Feb 7, 2022

Yes, together with function pointers and trait objects. We just need a stabilization PR and a summary for the lang team

@eholk
Copy link
Contributor

eholk commented Feb 11, 2022

I have a stabilization PR at #93827 and a documentation PR now at rust-lang/reference#1166.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Mar 7, 2022
…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
@jplatte
Copy link
Contributor

jplatte commented Mar 22, 2022

This has been stabilized in #93827, I'd really prefer if we could take a step back, revert that stabilization and consider an edition-based flow for getting ?const instead of ~const afterall:

The few currently allowed bounds in const fn could be transitioned to ?const via deprecation (plus more ?const bounds allowed) in this edition, then the next edition can change the meaning of trait bounds without ?const in const fn and we can have the thing we originally wanted.

I've voiced this idea on the tracking issue for the impl const Trait RFC earlier: #67792 (comment)

@jhpratt
Copy link
Member

jhpratt commented Mar 22, 2022

What's wrong with having the stabilization that's already landed and switching to ?const in a future edition?

@RalfJung
Copy link
Member

It's not just the bounds, it's also function pointers and trait objects that already made me advocate for this change long before the bounds stabilization hole was discovered.

A T: Trait bound should be symmetric with &dyn Trait, so if the former implicitly gains constness so should the latter. However, this is stable since forever:

trait NotConstTrait {
    fn action(&self);
}

impl NotConstTrait for i32 {
    fn action(&self) {
        println!("{}", self);
    }
}

const X: &dyn NotConstTrait = &13;

@jplatte
Copy link
Contributor

jplatte commented Mar 22, 2022

What's wrong with having the stabilization that's already landed and switching to ?const in a future edition?

Then you'd be stabilizing something just to deprecate it short after. But I guess it would be fine.

It's not just the bounds, it's also function pointers and trait objects that already made me advocate for this change long before the bounds stabilization hole was discovered.

I think I've seen this argument before but forgot about it. To me personally, with how different and magic dyn already is, I'm not convinced by this argument but I can see where you're coming from.

@RalfJung
Copy link
Member

RalfJung commented Mar 22, 2022

To add to the argument, consider the syntactic parallel between impl and dyn:

const fn foo(x: &dyn Trait);
const fn foo(x: impl Trait);
const fn foo<T: Trait>(x: T);

The last two are the same by definition; the first two IMO should be the same as everything else would be really confusing.

If you consider dyn too much magic, let me note that this also affects const fn foo(x: fn()), but the syntactic symmetry is more striking with dyn. These (traits bounds, dyn, fn) are all just different ways to "abstract over code", and IMO their interaction with const should be consistent across the board.

@fee1-dead
Copy link
Member

consider this:

const fn foo(x: &dyn Trait);
const fn foo(x: impl Trait);
const fn foo<T: Trait>(x: T);
const fn foo(x: fn());

If we go with the ~const approach, all of these will not impose additional requirements for the callers.

If we do ?const, some of them will be inconsistent, even if we made it consistent for the next edition it is going to be a pain to migrate, because all types (including fn pointers) now needs ?const to have the same behavior as before.

When you are arguing for ?const you are arguing for the fact that all of const trait bounds, const dyn traits, const impl traits, const fn pointers are used more frequently than their non-const versions in const contexts.

@RalfJung
Copy link
Member

If we do ?const, some of them will be inconsistent,

Well, we could make all of them require ?const, but that is super backwards incompatible.
(In const fn, fn and dyn are unstable -- or just have recently been stabilized together with the trait bounds. However, in const/static they are stable and having the same type mean something different in const vs const fn sounds like a Bad Idea.)

@jplatte
Copy link
Contributor

jplatte commented Mar 29, 2022

To add to the argument, consider the syntactic parallel between impl and dyn:

const fn foo(x: &dyn Trait);
const fn foo(x: impl Trait);
const fn foo<T: Trait>(x: T);

The last two are the same by definition; the first two IMO should be the same as everything else would be really confusing.

I thought about this for a while, and I am not convinced for two reasons:

First, I don't see why you would ever use dyn Trait in a const fn but second and more importantly, I can now articulate this "dyn Trait is just inherently different" feeling I had before. Take the case of auto traits:

// Return type is !Send !Sync even if the type underpinning `x` is
const fn foo(x: &dyn Trait) -> &dyn Trait { x }
// Return type Send and/or Sync if the type of `x` is
const fn foo(x: impl Trait) -> impl Trait { x }

It's not something people think about a lot, but once you do it's actually intuitive (IMHO): Because of the type erasure, you can't propagate auto traits.

Additionally, and this has been stated before but I think it's important to bring it up again: Requiring a const impl in a const context is simply the way more useful default, and it's a backwards-compatible change to add ?const. People don't need to learn about all of this concept of "needs to be const impl if called in const context" until they actually need itm or come across an API that uses ?const.

Finally, and this isn't really a strong argument for either side I think, but I feel like I should point out that there are conceptually three cases for trait bounds in generic const fn. This could probably be seen two ways, let me try to explain with a table:

"simpler syntax: less constraints" "simpler syntax: less weird"
requires const impl T: const Trait (?) T: const Trait (?)
requires const impl in const context T: ~const Trait T: Trait
requires a regular impl T: Trait T: ?const Trait

@RalfJung
Copy link
Member

RalfJung commented Mar 30, 2022

I don't see why you would ever use dyn Trait in a const fn

For the same reasons you would use dyn Trait in a regular fn.

Return type Send and/or Sync if the type of x is

Yeah, return-type impl Trait is semi-transparent, and in particular is transparent for auto traits. That's an odd quirk, but nothing fundamental and not conceptually very relevant in my eyes.

Requiring a const impl in a const context is simply the way more useful default,

I agree! IMO that also applies to dyn Trait and fn types.

But I don't agree strong enough to be willing to sacrifice consistency for this.

there are conceptually three cases for trait bounds in generic const fn

Yes, I agree.
The "less weird" column however is inconsistent with how ? syntax is used elsewhere -- with ?Sized, we have that writing nothing is equivalent to having a Sized bound. So I would expect that with ?const, this indicates opting out of a default const, so explicitly writing const should be the same as writing nothing.

What this goes to show is that the situation is fundamentally different from ?Sized.

@jplatte
Copy link
Contributor

jplatte commented Mar 30, 2022

Requiring a const impl in a const context is simply the way more useful default,

I agree! IMO that also applies to dyn Trait and fn types.

Oh, I think I see. Theoretically we could have this behavior even for non-generic const fn, there's no technical reasons preventing it. I didn't really consider that.

with ?Sized, we have that writing nothing is equivalent to having a Sized bound. So I would expect that with ?const, this indicates opting out of a default const

Heh, so I guess this gives rise to yet another possible (but to me very unlikely) option:

semantics syntax
requires const impl T: const Trait (?)
requires const impl in const context T: Trait or T: ~const Trait
requires a regular impl T: ?~const Trait

@oli-obk
Copy link
Contributor

oli-obk commented May 13, 2022

This has been stabilized in #93827

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-trait-system Area: Trait system C-tracking-issue Category: A tracking issue for an RFC or an unstable feature.
Projects
None yet
Development

No branches or pull requests

8 participants