Skip to content

Tracking issue for a minimal subset of RFC 911, const fn #53555

Description

@Centril

This is a tracking issue for the RFC "Const functions and inherent methods" (rust-lang/rfcs#911).

This issue only tracks a minimal subset of the proposal in 911 that we are (hopefully) comfortable with stabilizing. To opt into the minimal subset, use #![feature(min_const_fn)]. To use the more expansive feature set, you can continue using #![feature(const_fn)] and other associated feature gates.

The minimal set will not include items from the following (incomplete) list:

  1. const fns with type parameters with bounds (including where clauses) in scope (including from the parent e.g. impl) other than: lifetimes, Sized, or (the "un"-bound) ?Sized

    This restriction exists because we are not sure about our story around what bounds mean in a const fn context. See RFC: const bounds and methods rfcs#2237, const fn and generics const-eval#1, and https://github.com/Centril/rfc-effects/ for a discussion on this.

  2. const fns with argument types or return types that contain fn pointers, dyn Trait, or impl Trait.

    This is checked recursively.
    The restriction ensures that you may not reach a value of these types by any means.

    This restriction exists for the same reasons as in 1.

  3. const fns with any operations on floating-point numbers. This is achieved by making any floating-point operation not be const inside const fn.

    This restriction exists because we are not sure about the story wrt. determinism, achieving the same results on compile-time / run-time (including other machines) and floating points.

  4. using a const fn call in a pattern, e.g.;

    const fn twice<T: Copy>(x: T) -> (T, T) { (x, x) }
    match x {
        twice(21) => { ... }
        _ => { ... }
    }
  5. anything else that is not currently in const_fn or constants

    • raw ptr to usize cast (e.g. *const/mut T -> usize).
    • raw ptr deref.
    • if / if let / match.
    • loop / while.
    • let and destructuring.
  6. union field access.

  7. code requiring unsafe blocks.

Exhaustive list of features supported in const fn with #![feature(min_const_fn)]:

  1. type parameters where the parameters have any of the following as part of their bounds (either on where or directly on the parameters):

    1. lifetimes
    2. Sized

    This means that <T: 'a + ?Sized> and <T: 'b + Sized> + <T> are all permitted.
    Note that ?Sized is the absence of a constraint when bounds have been fully elaborated
    which includes adding implicit Sized bounds.
    This entails that permitting Sized + lifetimes allows the above examples.

    This rule also applies to type parameters of items that contain const fns.

  2. arithmetic operators on integers

  3. boolean operators (except for && and || which are banned since they are short-circuiting).

  4. any kind of aggregate constructor (array, struct, enum, tuple, ...)

  5. calls to other const fns (methods and functions)

  6. index operations on arrays and slices

  7. field accesses on structs and tuples

  8. reading from constants (but not statics, not even taking a reference to a static)

  9. & and * (only dereferencing of references, not raw pointers)

  10. casts except for raw pointer to usize casts

  11. const unsafe fn is allowed, but the body must consist of safe operations only

The bar for stabilizing const fns in libcore/liballoc/libstd will be that they are writable in stable user code (unless they are wrappers for intrinsics, i.e. size_of and align_of). This means that they must work with min_const_fn.

Things to be done before stabilizing:

Unresolved questions:

None.

Vocabulary:

Activity

added
B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.
T-langRelevant to the language team
B-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
on Aug 21, 2018

Centril commented on Aug 21, 2018

@Centril
ContributorAuthor

RalfJung commented on Aug 21, 2018

@RalfJung

Centril commented on Aug 21, 2018

@Centril
Author

Centril commented on Aug 21, 2018

@Centril
ContributorAuthor

oli-obk commented on Aug 21, 2018

@oli-obk
Contributor

runtime-pointer-addresses (#24111 (comment))

does not apply here, because we neither stabilize union field accesses which could be used to transmute between any pointer and an integer, nor do we stabilize casting *const T to usize.

priority (#24111 (comment))

The 2018 edition takes precedence over this feature. Since it has been minimized to a very small surface area which will be enforced by a feature whitelist, it seems plausible that we can get everyone on board to at least stabilize it shortly after the 2018 edition has been released.

design (#24111 (comment))
everything-const (#24111 (comment))

Since we are punting on (almost) all design issues by using a minimal scheme, the only design question left is whether we want to start attaching the const keyword to many functions.

Alternate designs:

  • infer the constness (deemed not feasible right now and a semver footgun)
  • inverting the system by marking functions as notconst (massive breaking change)
  • using a #[const] attribute

Wrt the "everything function is getting const attached to it" worry: The current design is entirely forward compatible with allowing const mod, const impl to allow marking entire code regions as const. Although that seems like it could benefit from the attribute, as you could write #![const] at the top of the crate root and have everything be const.

RalfJung commented on Aug 21, 2018

@RalfJung
Member

because we neither stabilize union field accesses which could be used to transmute between any pointer and an integer

We do not? I see you sneakily added that to the first post.^^

These are already stable for const. Why not allow them in const fn?

Centril commented on Aug 21, 2018

@Centril
ContributorAuthor

@RalfJung

Why not allow the in const fn?

@oli-obk added that part, but I think about it like this: given union field accesses, you can perform &[mut] T -> usize and so you may violate our plans for CTFE correctness per your blog post. Now, we can say that the operation *(const/mut) T -> usize is UB in const fn, but I would like to wait with that because:

  1. It gives us more time to develop our story around the rules of unsafe within const fn.
  2. We need to make sure that our teaching material is up to the task so that users will not do &T -> usize and think that's OK.

All in all, I think that if we are more conservative here and punt on this, we can ship a more minimal const fn faster.

RalfJung commented on Aug 21, 2018

@RalfJung
Member

I see. I don't like these inconsistencies but oh well. Looks like qualify_consts will be cleaned up... another time.^^

In terms of teaching, again we have the same problem for const... I thought the sanity check might help (unlike a const fn, a const has a very simple "observable behavior" so we can actually test for these things statically) but it seems it does not complain about

union Trans { x: &'static i32, y: usize }
const BAR : usize = unsafe { Trans { x: &0 }.y };

nikomatsakis commented on Aug 21, 2018

@nikomatsakis
Contributor

First, a nit:

const fns with type parameters with bounds (including where clauses) (lifetimes are OK) either on themselves or on the scope they are contained within (e.g. impls and such...)

Presumably something like T: Sized is ok -- or, more to the point, const fn foo<T>(t: T) -> T { t }...? So is it just that Sized trait is allowed?

But, more generally, I think that this proposal does a good job of outlining the surface area of the language to be stabilized, but I'd like to see a better outline of what the implications are regarding the "const system in general".

One key question here is the interaction of const fn and promotion, I think, since that is the main place where we can find ourselves in tricky questions (e.g., even something like const fn foo(a: u32, b: u32) -> { a + b } can be problematic if a call to it is promoted). Can someone (e.g., @oli-obk) write out the conditions in which calls to const fn can currently be promoted, if any? I don't recall. =)

I think by now I am mostly bought into the general framing from Ralf's blog post, but I'd like to also know if there are alternative approaches that we are ruling out by stabilizing const fn.

It seems like one of the key bits that I could imagine being controversial was the idea that a const fn could do things that we cannot statically guarantee are valid (e.g., in unsafe code) but that we could still promote calls to that const fn. If we wind up hitting errors at compilation time, that is then a violation of the const fn declaration analogous to hitting UB at runtime. As I said, I think I am mostly bought into this framing, but I would like to clarify if we are losing room to maneuever here in any way.

nikomatsakis commented on Aug 21, 2018

@nikomatsakis
Contributor

To expand a bit on something:

Presumably something like T: Sized is ok -- or, more to the point, const fn foo(t: T) -> T { t }...? So is it just that Sized trait is allowed?

I had initially considered suggesting that we allow auto traits as well, but I do not think this is a good idea. For one thing, unlike Sized ,auto traits may have custom impls, and that might constrain us in ways that we do not want. e.g., you can have

unsafe impl<T: Display> Send for Foo<T> { }

Now, if you have a const fn foo<U: Send>(...) where U = Foo<T>, and we accept it, we will therefore be saying that T: Display is provable in a const context. But if we wanted to change how trait resolution works in a const context to consider only impls that are marked const or some such thing, that could be problematic.

Since Sized does not permit custom impls, we don't have this problem. Similarly, we could permit lifetime bounds (T: 'a).

68 remaining items

RalfJung commented on Oct 1, 2018

@RalfJung
Member

But we could still write a function that takes two fn ptrs and compares them? It couldn't be called (currently), but we should rule out the binop as well.

cramertj commented on Oct 1, 2018

@cramertj
Member

@RalfJung Attempting to define

const fn cmp(x: fn(), y: fn()) -> bool {
    x == y
}

with #![feature(min_const_fn)] gives "error: function pointers in const fn are unstable".

RalfJung commented on Oct 1, 2018

@RalfJung
Member

If we have that in a test, I am happy :)

Centril commented on Oct 4, 2018

@Centril
ContributorAuthor

@oli-obk given that we landed #53851, should we move to stabilize this?

added a commit that references this issue on Oct 7, 2018

bstrie commented on Oct 10, 2018

@bstrie
Contributor

However, I'd like to see a test ensuring that

   const FOO: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(0) };

is caught by the sanity check. Currently nightly accepts that constant without complaining.

@RalfJung , it would appear that post-#54835 nightly still accepts this code without warning. Did you intend for this code not to work?

oli-obk commented on Oct 10, 2018

@oli-obk
Contributor

Nightly on the playground gives me

error[E0080]: this constant likely exhibits undefined behavior
 --> src/main.rs:3:1
  |
3 | const FOO: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(0) };
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0, but expected something greater or equal to 1
  |
  = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior

https://play.rust-lang.org/?gist=8544bb6dd824c73bd0ea8466aad29ae9&version=nightly&mode=debug&edition=2015

RalfJung commented on Oct 10, 2018

@RalfJung
Member

therealprof commented on Oct 10, 2018

@therealprof
Contributor

Any change we can get this into beta? I'd really love to do some 2018 edition proving and cleanup of some embedded crates but without min_const_fn available on beta, realistic checks are somewhat limited.

oli-obk commented on Oct 10, 2018

@oli-obk
Contributor

It will be in the next beta. Until then you could just use nightly without any feature gates

therealprof commented on Oct 10, 2018

@therealprof
Contributor

@oli-obk That's what I'm doing at the moment but I thought the point of the beta was to test Edition features and nightly might behave differently. I guess I'm looking forward then to trying the next beta and everything working fine just like on nighty. ;)

Centril commented on Oct 13, 2018

@Centril
ContributorAuthor

Documentation was done in rust-lang/reference#440. Since everything is done, I'll close this out.

therealprof commented on Oct 13, 2018

@therealprof
Contributor

@oli-obk > It will be in the next beta. Until then you could just use nightly without any feature gates

# rustc +beta --version
rustc 1.30.0-beta.15 (590121930 2018-10-12)
# cargo +beta build --release
...
error[E0658]: const fn is unstable (see issue #53555)
  --> /Users/egger/OSS/bare-metal/src/lib.rs:22:5
   |
22 | /     pub const unsafe fn new(address: usize) -> Self {
23 | |         Peripheral {
24 | |             address: address as *mut T,
25 | |         }
26 | |     }
   | |_____^

crlf0710 commented on Oct 13, 2018

@crlf0710
Member

@therealprof "the next beta" means the 1.31 one, which will arrive some time around the time 1.30 stable ships(Oct 25).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.B-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-langRelevant to the language teamdisposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions