Repository navigation
Description
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:
-
const fns with type parameters with bounds (includingwhereclauses) in scope (including from the parent e.g.impl) other than: lifetimes,Sized, or (the "un"-bound)?SizedThis restriction exists because we are not sure about our story around what bounds mean in a
const fncontext. See RFC: const bounds and methods rfcs#2237,const fnand generics const-eval#1, and https://github.com/Centril/rfc-effects/ for a discussion on this. -
const fns with argument types or return types that containfnpointers,dyn Trait, orimpl 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.
-
const fns with any operations on floating-point numbers. This is achieved by making any floating-point operation not beconstinsideconst 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.
-
using a
const fncall in a pattern, e.g.;const fn twice<T: Copy>(x: T) -> (T, T) { (x, x) } match x { twice(21) => { ... } _ => { ... } }
-
anything else that is not currently in
const_fnor constants- raw ptr to
usizecast (e.g.*const/mut T -> usize). - raw ptr deref.
if/if let/match.loop/while.letand destructuring.
- raw ptr to
-
union field access.
-
code requiring
unsafeblocks.
Exhaustive list of features supported in const fn with #![feature(min_const_fn)]:
-
type parameters where the parameters have any of the following as part of their bounds (either on
whereor directly on the parameters):- lifetimes
Sized
This means that
<T: 'a + ?Sized>and<T: 'b + Sized>+<T>are all permitted.
Note that?Sizedis the absence of a constraint when bounds have been fully elaborated
which includes adding implicitSizedbounds.
This entails that permittingSized+ lifetimes allows the above examples.This rule also applies to type parameters of items that contain
const fns. -
arithmetic operators on integers
-
boolean operators (except for
&&and||which are banned since they are short-circuiting). -
any kind of aggregate constructor (array,
struct,enum, tuple, ...) -
calls to other
const fns (methods and functions) -
index operations on arrays and slices
-
field accesses on structs and tuples
-
reading from constants (but not statics, not even taking a reference to a static)
-
&and*(only dereferencing of references, not raw pointers) -
casts except for raw pointer to
usizecasts -
const unsafe fnis 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:
- Implement the
min_const_fnfeature gate. (Implement themin_const_fnfeature gate #53604) - Ensure that the above restrictions apply.
- Adjust documentation (see instructions on forge)
- Stabilization PR (see instructions on forge)
Unresolved questions:
None.
Vocabulary:
Activity
Centril commentedon Aug 21, 2018
Also cc: @eddyb, @RalfJung, @est31, @durka, @mark-i-m, @japaric
RalfJung commentedon Aug 21, 2018
Centril commentedon Aug 21, 2018
Centril commentedon Aug 21, 2018
Some concerns noted over at #24111:
parallel-const-traitsresolved by const fn tracking issue (RFC 911) #24111 (comment)oli-obk commentedon Aug 21, 2018
does not apply here, because we neither stabilize
unionfield accesses which could be used to transmute between any pointer and an integer, nor do we stabilize casting*const Ttousize.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.
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
constkeyword to many functions.Alternate designs:
notconst(massive breaking change)#[const]attributeWrt the "everything function is getting
constattached to it" worry: The current design is entirely forward compatible with allowingconst mod,const implto 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 commentedon Aug 21, 2018
We do not? I see you sneakily added that to the first post.^^
These are already stable for
const. Why not allow them inconst fn?Centril commentedon Aug 21, 2018
@RalfJung
@oli-obk added that part, but I think about it like this: given union field accesses, you can perform
&[mut] T -> usizeand so you may violate our plans for CTFE correctness per your blog post. Now, we can say that the operation*(const/mut) T -> usizeis UB inconst fn, but I would like to wait with that because:unsafewithinconst fn.&T -> usizeand 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 fnfaster.RalfJung commentedon Aug 21, 2018
I see. I don't like these inconsistencies but oh well. Looks like
qualify_constswill 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 aconst fn, aconsthas a very simple "observable behavior" so we can actually test for these things statically) but it seems it does not complain aboutnikomatsakis commentedon Aug 21, 2018
First, a nit:
Presumably something like
T: Sizedis ok -- or, more to the point,const fn foo<T>(t: T) -> T { t }...? So is it just thatSizedtrait 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 fnand promotion, I think, since that is the main place where we can find ourselves in tricky questions (e.g., even something likeconst 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 toconst fncan 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 fncould 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 theconst fndeclaration 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 commentedon Aug 21, 2018
To expand a bit on something:
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 haveNow, if you have a
const fn foo<U: Send>(...)whereU = Foo<T>, and we accept it, we will therefore be saying thatT: Displayis 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 markedconstor some such thing, that could be problematic.Since
Sizeddoes not permit custom impls, we don't have this problem. Similarly, we could permit lifetime bounds (T: 'a).68 remaining items
RalfJung commentedon Oct 1, 2018
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 commentedon Oct 1, 2018
@RalfJung Attempting to define
with
#![feature(min_const_fn)]gives "error: function pointers in const fn are unstable".RalfJung commentedon Oct 1, 2018
If we have that in a test, I am happy :)
Centril commentedon Oct 4, 2018
@oli-obk given that we landed #53851, should we move to stabilize this?
Auto merge of #54835 - oli-obk:mögen_konstante_funktionen_doch_bitte_…
bstrie commentedon Oct 10, 2018
@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 commentedon Oct 10, 2018
Nightly on the playground gives me
https://play.rust-lang.org/?gist=8544bb6dd824c73bd0ea8466aad29ae9&version=nightly&mode=debug&edition=2015
RalfJung commentedon Oct 10, 2018
And there's tests as well: https://github.com/rust-lang/rust/blob/master/src/test/ui/consts/const-eval/ub-nonnull.rs
This can been fixed with #54762
therealprof commentedon Oct 10, 2018
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_fnavailable on beta, realistic checks are somewhat limited.oli-obk commentedon Oct 10, 2018
It will be in the next beta. Until then you could just use nightly without any feature gates
therealprof commentedon Oct 10, 2018
@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 commentedon Oct 13, 2018
Documentation was done in rust-lang/reference#440. Since everything is done, I'll close this out.
therealprof commentedon Oct 13, 2018
@oli-obk > It will be in the next beta. Until then you could just use nightly without any feature gates
crlf0710 commentedon Oct 13, 2018
@therealprof "the next beta" means the 1.31 one, which will arrive some time around the time 1.30 stable ships(Oct 25).