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

const fn tracking issue (RFC 911) #24111

Open
nikomatsakis opened this Issue Apr 6, 2015 · 244 comments

Comments

Projects
None yet
@nikomatsakis
Contributor

nikomatsakis commented Apr 6, 2015

Tracking issue for rust-lang/rfcs#911.

Things to be done before stabilizing:

CTFE = https://en.wikipedia.org/wiki/Compile_time_function_execution

rfcbot comment: #24111 (comment)

@Munksgaard

This comment has been minimized.

Show comment
Hide comment
@Munksgaard

Munksgaard Jun 20, 2015

Contributor

Is this closed by #25609?

Contributor

Munksgaard commented Jun 20, 2015

Is this closed by #25609?

@abonander

This comment has been minimized.

Show comment
Hide comment
@abonander

abonander Jul 2, 2015

Contributor

@Munksgaard That just adds support to the compiler AFAIK. There's a lot of functions in the stdlib that need to be changed to const fn and tested for breakage. I don't know what the progress is on that.

Contributor

abonander commented Jul 2, 2015

@Munksgaard That just adds support to the compiler AFAIK. There's a lot of functions in the stdlib that need to be changed to const fn and tested for breakage. I don't know what the progress is on that.

@nodakai

This comment has been minimized.

Show comment
Hide comment
@nodakai

nodakai Aug 2, 2015

Contributor

I'm hoping this to be implemented on std::ptr::null() and null_mut() so that we can use them to initialize static mut *MyTypeWithDrop without resorting to 0usize as *mut _

Contributor

nodakai commented Aug 2, 2015

I'm hoping this to be implemented on std::ptr::null() and null_mut() so that we can use them to initialize static mut *MyTypeWithDrop without resorting to 0usize as *mut _

@alexcrichton alexcrichton added the T-lang label Aug 11, 2015

@rohel01

This comment was marked as off-topic.

Show comment
Hide comment
@rohel01

rohel01 Aug 14, 2015

EDIT: Removed since it was out of subject

rohel01 commented Aug 14, 2015

EDIT: Removed since it was out of subject

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Aug 15, 2015

Contributor

To be clear, the question here is not primarily about the usefulness of the feature but rather regarding the best way to formulate it (or the best framework to formulate it in). See the RFC discussion.

Contributor

glaebhoerl commented Aug 15, 2015

To be clear, the question here is not primarily about the usefulness of the feature but rather regarding the best way to formulate it (or the best framework to formulate it in). See the RFC discussion.

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Nov 5, 2015

Member

This is now the tracking issue for eventual stabilization.

Member

aturon commented Nov 5, 2015

This is now the tracking issue for eventual stabilization.

@briansmith

This comment has been minimized.

Show comment
Hide comment
@briansmith

briansmith Jan 12, 2016

#29107 has been closed.

I disagree that "Integration with patterns", or any changes to the standard library should block this. This is very useful even without those changes, and those changes can be done later. In particular, I would like to start using const fn in my own code soon.

Accordingly, could the stabilization status of this be re-evaluated?

briansmith commented Jan 12, 2016

#29107 has been closed.

I disagree that "Integration with patterns", or any changes to the standard library should block this. This is very useful even without those changes, and those changes can be done later. In particular, I would like to start using const fn in my own code soon.

Accordingly, could the stabilization status of this be re-evaluated?

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Jan 15, 2016

Contributor

I don't doubt that const fn even in its current limited form would be useful functionality to have, but what I would really like, ideally before going further along this path, would be for those in favor of "the const fn approach" to think about and articulate their preferred endgame. If we just keep on incrementally adding useful-seeming functionality in the most obvious way, it seems very likely to me that we'll eventually end up copying more or less the entirety of C++'s constexpr design. Is that something we are comfortable with? Even if we say yes, I would much rather that we choose that path in a clear-eyed way, instead of backing into it with small steps over time, as the path of least resistance, until it has become inevitable.

(Given that the semantics of safe Rust code should be fully definable, it seems likely that eventually at least every function which doesn't (transitively) depend on unsafe should be able to be marked as const. And given that unsafe is supposed to be an implementation detail, I bet people will push for somehow loosening that restriction as well. I would much rather we looked abroad and tried to find a more cohesive, capable, and well-integrated story for staging and type-level computation.)

Contributor

glaebhoerl commented Jan 15, 2016

I don't doubt that const fn even in its current limited form would be useful functionality to have, but what I would really like, ideally before going further along this path, would be for those in favor of "the const fn approach" to think about and articulate their preferred endgame. If we just keep on incrementally adding useful-seeming functionality in the most obvious way, it seems very likely to me that we'll eventually end up copying more or less the entirety of C++'s constexpr design. Is that something we are comfortable with? Even if we say yes, I would much rather that we choose that path in a clear-eyed way, instead of backing into it with small steps over time, as the path of least resistance, until it has become inevitable.

(Given that the semantics of safe Rust code should be fully definable, it seems likely that eventually at least every function which doesn't (transitively) depend on unsafe should be able to be marked as const. And given that unsafe is supposed to be an implementation detail, I bet people will push for somehow loosening that restriction as well. I would much rather we looked abroad and tried to find a more cohesive, capable, and well-integrated story for staging and type-level computation.)

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Jan 16, 2016

Contributor

@glaebhoerl

I don't doubt that const fn even in its current limited form would be useful functionality to have, but what I would really like, ideally before going further along this path, would be for those in favor of "the const fn approach" to think about and articulate their preferred endgame...it seems very likely to me that we'll eventually end up copying more or less the entirety of C++'s constexpr design.

What I would personally like, even more than that, is that we have a fairly clear view on how we're going to implement it, and what portion of the language we are going to cover. That said, this is very closely related to support for associated constants or generics over integers in my mind.

@eddyb and I did some sketching recently on a scheme which could enable constant evaluation of a very broad swath of code. Basically lowering all constants to MIR and intrepreting it (in some cases,
abstract interpreting, if there are generics you cannot yet evaluate, which is where things get most interesting to me).

However, while it seems like it would be fairly easy to support a very large fraction of the "builtin language", real code in practice hits up against the need to do memory allocation very quickly. In other words, you want to use Vec or some other container. And that's where this whole interpreting scheme starts to get more complicated to my mind.

That said, @glaebhoerl, I'd also love to hear you articulate your preferred alternative endgame. I think you sketched out some such thoughts in the const fn RFC, but I think it'd be good to hear it again, and in this context. :)

Contributor

nikomatsakis commented Jan 16, 2016

@glaebhoerl

I don't doubt that const fn even in its current limited form would be useful functionality to have, but what I would really like, ideally before going further along this path, would be for those in favor of "the const fn approach" to think about and articulate their preferred endgame...it seems very likely to me that we'll eventually end up copying more or less the entirety of C++'s constexpr design.

What I would personally like, even more than that, is that we have a fairly clear view on how we're going to implement it, and what portion of the language we are going to cover. That said, this is very closely related to support for associated constants or generics over integers in my mind.

@eddyb and I did some sketching recently on a scheme which could enable constant evaluation of a very broad swath of code. Basically lowering all constants to MIR and intrepreting it (in some cases,
abstract interpreting, if there are generics you cannot yet evaluate, which is where things get most interesting to me).

However, while it seems like it would be fairly easy to support a very large fraction of the "builtin language", real code in practice hits up against the need to do memory allocation very quickly. In other words, you want to use Vec or some other container. And that's where this whole interpreting scheme starts to get more complicated to my mind.

That said, @glaebhoerl, I'd also love to hear you articulate your preferred alternative endgame. I think you sketched out some such thoughts in the const fn RFC, but I think it'd be good to hear it again, and in this context. :)

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Jan 16, 2016

Member

The problem with allocation is having it escape into run-time.
If we can somehow disallow crossing that compile-time/run-time barrier, then I believe we could have a working liballoc with const fn.
It would be no harder to manage those kinds of allocations than would be to deal with byte-addressable values on an interpreted stack.

Alternatively, we could generate runtime code to allocate and fill in the values every time that barrier has to be passed, although I'm not sure what kind of usecases that has.

Keep in mind that even with full-fledged constexpr-like evaluation, const fn would still be pure: running it twice on 'static data would result in the exact same result and no side-effects.

Member

eddyb commented Jan 16, 2016

The problem with allocation is having it escape into run-time.
If we can somehow disallow crossing that compile-time/run-time barrier, then I believe we could have a working liballoc with const fn.
It would be no harder to manage those kinds of allocations than would be to deal with byte-addressable values on an interpreted stack.

Alternatively, we could generate runtime code to allocate and fill in the values every time that barrier has to be passed, although I'm not sure what kind of usecases that has.

Keep in mind that even with full-fledged constexpr-like evaluation, const fn would still be pure: running it twice on 'static data would result in the exact same result and no side-effects.

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Jan 16, 2016

Contributor

@nikomatsakis If I had one I would have mentioned it. :) I mainly just see known unknowns. The whole thing with consts as part of the generics system was of course part of what I understood as being the C++ design. As far as having associated consts and const generic parameters, considering that we already have fixed-size arrays with consts as part of their type and would like to abstract over them, I would be surprised if there were a much better -- as opposed to merely more general -- way of doing it. The const fn part of things feels more separable and variable. It's easy to imagine taking things further and having things like const impls and const Trait bounds in generics, but I'm sure there is prior art for this sort of general thing which has already figured things out and we should try to find it.

Of the main use cases for the Rust language, the ones that primarily need low-level control, like kernels, seem reasonably well-served already, but another area where Rust could have lots of potential is things that primarily need high performance, and in that space powerful support (in some form) for staged computation (which const fn is already a very limited instance of) seems like it could be a game-changer. (Just in the last few weeks I came across two separate tweets by people who decided to switch from Rust to a language with better staging capabilities.) I'm not sure if any of the existing solutions in languages "close to us" -- C++'s constexpr, D's ad-hoc CTFE, our procedural macros -- really feel inspiring and powerful/complete enough for this sort of thing. (Procedural macros seem like a good thing to have, but more for abstraction and DSLs, not as much for performance-oriented code generation.)

As for what would be inspiring and good enough... I haven't seen it yet, and I'm not familiar enough with the whole space to know, precisely, where to look. Of course, per the above, we might want to at least glance at Julia and Terra, even if they seem like quite different languages from Rust in many ways. I know Oleg Kiselyov has done a lot of interesting work in this area. Tiark Rompf's work on Lancet and Lightweight Modular Staging for Scala seems definitely worth looking at. I recall seeing a presentation by @kmcallister at some point about what a dependently typed Rust might look like (which might at least be more general than sticking const everywhere), and I also recall seeing something from Oleg to the effect that types themselves are a form of staging (which feels natural considering the phase separation between compile- and runtime is a lot like stages)... lots of exciting potential connections in many different directions, which is why it'd feel like a missed opportunity if we were to just commit to the first solution which occurs to us. :)

(This was just a braindump and I've almost surely imperfectly characterized many things.)

Contributor

glaebhoerl commented Jan 16, 2016

@nikomatsakis If I had one I would have mentioned it. :) I mainly just see known unknowns. The whole thing with consts as part of the generics system was of course part of what I understood as being the C++ design. As far as having associated consts and const generic parameters, considering that we already have fixed-size arrays with consts as part of their type and would like to abstract over them, I would be surprised if there were a much better -- as opposed to merely more general -- way of doing it. The const fn part of things feels more separable and variable. It's easy to imagine taking things further and having things like const impls and const Trait bounds in generics, but I'm sure there is prior art for this sort of general thing which has already figured things out and we should try to find it.

Of the main use cases for the Rust language, the ones that primarily need low-level control, like kernels, seem reasonably well-served already, but another area where Rust could have lots of potential is things that primarily need high performance, and in that space powerful support (in some form) for staged computation (which const fn is already a very limited instance of) seems like it could be a game-changer. (Just in the last few weeks I came across two separate tweets by people who decided to switch from Rust to a language with better staging capabilities.) I'm not sure if any of the existing solutions in languages "close to us" -- C++'s constexpr, D's ad-hoc CTFE, our procedural macros -- really feel inspiring and powerful/complete enough for this sort of thing. (Procedural macros seem like a good thing to have, but more for abstraction and DSLs, not as much for performance-oriented code generation.)

As for what would be inspiring and good enough... I haven't seen it yet, and I'm not familiar enough with the whole space to know, precisely, where to look. Of course, per the above, we might want to at least glance at Julia and Terra, even if they seem like quite different languages from Rust in many ways. I know Oleg Kiselyov has done a lot of interesting work in this area. Tiark Rompf's work on Lancet and Lightweight Modular Staging for Scala seems definitely worth looking at. I recall seeing a presentation by @kmcallister at some point about what a dependently typed Rust might look like (which might at least be more general than sticking const everywhere), and I also recall seeing something from Oleg to the effect that types themselves are a form of staging (which feels natural considering the phase separation between compile- and runtime is a lot like stages)... lots of exciting potential connections in many different directions, which is why it'd feel like a missed opportunity if we were to just commit to the first solution which occurs to us. :)

(This was just a braindump and I've almost surely imperfectly characterized many things.)

@briansmith

This comment has been minimized.

Show comment
Hide comment
@briansmith

briansmith Jan 16, 2016

However, while it seems like it would be fairly easy to support a very large fraction of the "builtin language", real code in practice hits up against the need to do memory allocation very quickly. In other words, you want to use Vec or some other container. And that's where this whole interpreting scheme starts to get more complicated to my mind.

I disagree with that characterization of "real code in practice". I think there is big interest in Rust because it helps reduce the need for heap memory allocation. My code, in particular, makes a concrete effort to avoid heap allocation whenever possible.

Being able to do more than that would be nice but being able to construct static instances of non-trivial types with compiler-enforced invariants is essential. The C++ constexpr approach is extremely limiting, but it is more than what I need for my use cases: I need to provide a function that can construct an instance of type T with parameters x, y, and z such that the function guarantees that x, y, and z are valid (e.g., x < y && z > 0), such that the result can be a static variable, without the use of initialization code that runs at startup.

briansmith commented Jan 16, 2016

However, while it seems like it would be fairly easy to support a very large fraction of the "builtin language", real code in practice hits up against the need to do memory allocation very quickly. In other words, you want to use Vec or some other container. And that's where this whole interpreting scheme starts to get more complicated to my mind.

I disagree with that characterization of "real code in practice". I think there is big interest in Rust because it helps reduce the need for heap memory allocation. My code, in particular, makes a concrete effort to avoid heap allocation whenever possible.

Being able to do more than that would be nice but being able to construct static instances of non-trivial types with compiler-enforced invariants is essential. The C++ constexpr approach is extremely limiting, but it is more than what I need for my use cases: I need to provide a function that can construct an instance of type T with parameters x, y, and z such that the function guarantees that x, y, and z are valid (e.g., x < y && z > 0), such that the result can be a static variable, without the use of initialization code that runs at startup.

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Jan 16, 2016

Contributor

@briansmith FWIW another approach which has a chance of solving the same use cases would be if macros had privacy hygiene, which I believe (hope) we're planning to make them have.

Contributor

glaebhoerl commented Jan 16, 2016

@briansmith FWIW another approach which has a chance of solving the same use cases would be if macros had privacy hygiene, which I believe (hope) we're planning to make them have.

@briansmith

This comment has been minimized.

Show comment
Hide comment
@briansmith

briansmith Jan 16, 2016

@briansmith FWIW another approach which has a chance of solving the same use cases would be if macros had privacy hygiene, which I believe (hope) we're planning to make them have.

I guess if you use the procedural macros then you can evaluate x < y && z > 0 at compile-time. But, it seems like it would be many, many months before procedural macros could be used in stable Rust, if they ever are. const fn is interesting because it can be enabled for stable Rust now, as far as I understand the state of things.

briansmith commented Jan 16, 2016

@briansmith FWIW another approach which has a chance of solving the same use cases would be if macros had privacy hygiene, which I believe (hope) we're planning to make them have.

I guess if you use the procedural macros then you can evaluate x < y && z > 0 at compile-time. But, it seems like it would be many, many months before procedural macros could be used in stable Rust, if they ever are. const fn is interesting because it can be enabled for stable Rust now, as far as I understand the state of things.

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Jan 16, 2016

Member

@glaebhoerl I wouldn't hold my breath for strict hygiene, it's quite possible we're going to have an escape mechanism (just like real LISPs), so you may not want it for any kind of safety purposes.

Member

eddyb commented Jan 16, 2016

@glaebhoerl I wouldn't hold my breath for strict hygiene, it's quite possible we're going to have an escape mechanism (just like real LISPs), so you may not want it for any kind of safety purposes.

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Jan 17, 2016

Contributor

@glaebhoerl there is also https://anydsl.github.io/, which even uses
Rust-like syntax ;) they are basically targeting staged computation and in
particular partial evaluation.

On Sat, Jan 16, 2016 at 6:29 PM, Eduard-Mihai Burtescu <
notifications@github.com> wrote:

@glaebhoerl https://github.com/glaebhoerl I wouldn't hold my breath for
strict hygiene, it's quite possible we're going to have an escape mechanism
(just like real LISPs), so you may not want it for any kind of safety
purposes.


Reply to this email directly or view it on GitHub
#24111 (comment).

Contributor

nikomatsakis commented Jan 17, 2016

@glaebhoerl there is also https://anydsl.github.io/, which even uses
Rust-like syntax ;) they are basically targeting staged computation and in
particular partial evaluation.

On Sat, Jan 16, 2016 at 6:29 PM, Eduard-Mihai Burtescu <
notifications@github.com> wrote:

@glaebhoerl https://github.com/glaebhoerl I wouldn't hold my breath for
strict hygiene, it's quite possible we're going to have an escape mechanism
(just like real LISPs), so you may not want it for any kind of safety
purposes.


Reply to this email directly or view it on GitHub
#24111 (comment).

@jethrogb

This comment has been minimized.

Show comment
Hide comment
@jethrogb

jethrogb Apr 2, 2016

Contributor

#29525 should be fixed before stabilization

Contributor

jethrogb commented Apr 2, 2016

#29525 should be fixed before stabilization

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Apr 3, 2016

Contributor

Given that the semantics of safe Rust code should be fully definable, it seems likely that eventually at least every function which doesn't (transitively) depend on unsafe should be able to be marked as const. And given that unsafe is supposed to be an implementation detail, I bet people will push for somehow loosening that restriction as well.

Just a thought: if we ever formally define Rust's memory model, then even unsafe code could potentially be safely and sensibly evaluated at compile-time by interpreting it abstractly/symbolically -- that is, use of raw pointers wouldn't turn into direct memory accesses like at runtime, but rather something (just as an example for illustration) like a lookup into a hashmap of allocated addresses, together with their types and values, or similar, with every step checked for validity -- so that any execution whose behavior is undefined would be strictly a compiler-reported error, instead of a security vulnerability in rustc. (This might also be connected to the situation w.r.t. handling isize and usize at compile-time symbolically or in a platform-dependent way.)

I'm not sure where that leaves us with respect to const fn. On the one hand, that would likely open up much more useful code to be available at compile time -- Box, Vec, Rc, anything using unsafe for performance optimization -- which is good. One arbitrary restriction fewer. On the other hand, the boundary for "what can possibly be a const fn" would now essentially be moved outwards to anything that doesn't involve the FFI. So what we'd actually be tracking in the type system, under the guise of constness, is what things (transitively) rely on the FFI and what things don't. And whether or not something uses the FFI is still something that people rightfully consider to be an internal implementation detail, and this restriction (unlike unsafe) really doesn't seem feasible to lift. And under this scenario you'd probably have far more fns being eligible for constness than ones which wouldn't.

So you'd still have constness revolving around an arbitrary, implementation-exposing restriction, and you'd also end up having to write const almost everywhere. That doesn't sound too appealing either...

Contributor

glaebhoerl commented Apr 3, 2016

Given that the semantics of safe Rust code should be fully definable, it seems likely that eventually at least every function which doesn't (transitively) depend on unsafe should be able to be marked as const. And given that unsafe is supposed to be an implementation detail, I bet people will push for somehow loosening that restriction as well.

Just a thought: if we ever formally define Rust's memory model, then even unsafe code could potentially be safely and sensibly evaluated at compile-time by interpreting it abstractly/symbolically -- that is, use of raw pointers wouldn't turn into direct memory accesses like at runtime, but rather something (just as an example for illustration) like a lookup into a hashmap of allocated addresses, together with their types and values, or similar, with every step checked for validity -- so that any execution whose behavior is undefined would be strictly a compiler-reported error, instead of a security vulnerability in rustc. (This might also be connected to the situation w.r.t. handling isize and usize at compile-time symbolically or in a platform-dependent way.)

I'm not sure where that leaves us with respect to const fn. On the one hand, that would likely open up much more useful code to be available at compile time -- Box, Vec, Rc, anything using unsafe for performance optimization -- which is good. One arbitrary restriction fewer. On the other hand, the boundary for "what can possibly be a const fn" would now essentially be moved outwards to anything that doesn't involve the FFI. So what we'd actually be tracking in the type system, under the guise of constness, is what things (transitively) rely on the FFI and what things don't. And whether or not something uses the FFI is still something that people rightfully consider to be an internal implementation detail, and this restriction (unlike unsafe) really doesn't seem feasible to lift. And under this scenario you'd probably have far more fns being eligible for constness than ones which wouldn't.

So you'd still have constness revolving around an arbitrary, implementation-exposing restriction, and you'd also end up having to write const almost everywhere. That doesn't sound too appealing either...

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Apr 3, 2016

Member

that is, use of raw pointers wouldn't turn into direct memory accesses like at runtime, but rather something ... like a lookup into a hashmap of allocated addresses,

@glaebhoerl Well, that is pretty much the model I described and which @tsion's miri is implementing.

I think the FFI distinction is very important because of purity, which is required for coherence.
You couldn't even use GHC for Rust const fns because it has unsafePerformIO.

I don't like the const keyword too much myself which is why I am okay with const fn foo<T: Trait> instead of const fn foo<T: const Trait> (for requiring a const impl Trait for T).

Just like Sized, we probably have the wrong defaults, but I haven't seen any other proposals that can realistically work.

Member

eddyb commented Apr 3, 2016

that is, use of raw pointers wouldn't turn into direct memory accesses like at runtime, but rather something ... like a lookup into a hashmap of allocated addresses,

@glaebhoerl Well, that is pretty much the model I described and which @tsion's miri is implementing.

I think the FFI distinction is very important because of purity, which is required for coherence.
You couldn't even use GHC for Rust const fns because it has unsafePerformIO.

I don't like the const keyword too much myself which is why I am okay with const fn foo<T: Trait> instead of const fn foo<T: const Trait> (for requiring a const impl Trait for T).

Just like Sized, we probably have the wrong defaults, but I haven't seen any other proposals that can realistically work.

@solson

This comment has been minimized.

Show comment
Hide comment
@solson

solson Apr 3, 2016

Member

@eddyb I think you meant to link to https://internals.rust-lang.org/t/mir-constant-evaluation/3143/31 (comment 31, not 11).

Member

solson commented Apr 3, 2016

@eddyb I think you meant to link to https://internals.rust-lang.org/t/mir-constant-evaluation/3143/31 (comment 31, not 11).

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Apr 3, 2016

Member

@tsion Fixed, thanks!

Member

eddyb commented Apr 3, 2016

@tsion Fixed, thanks!

@MaikKlein

This comment has been minimized.

Show comment
Hide comment
@MaikKlein

MaikKlein Jun 22, 2016

Contributor

Please ignore this if I am completely off point.

The problem I see with this RFC is that as a user, you have to mark as many function const fn as possible because that will probably be the best practice. The same thing is happening currently in C++ with contexpr. I think this is just unnecessary verbosity.

D doesn't have const fn but it allows any function to be called at compile time ( with some exceptions ).

for example

// Standalone example.
struct Point { x: i32, y: i32 }

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x: x, y: y }
    }

    fn add(self, other: Point) -> Point {
        Point::new(self.x + other.x, self.y + other.y)
    }
}

const ORIGIN: Point = Point::new(0, 0); // works because 0, 0 are both known at compile time 
const ORIGIN2: Point = Point::new(0, 0); // ditto

const ANOTHER: Point = ORIGIN.add(ORIGIN2); // works because ORIGIN and ORIGIN2 are both const.
{
    let x: i32 = 42;
    let y: i32 = 24;
    const SOME_POINT: Point = Point::new(x, y); // Error: x and y are not known at compile time
}
{
    const x: i32 = 42;
    const y: i32 = 24;
    const SOME_POINT: Point = Point::new(x, y); // Works x and y are both known at compile time.
}

Note, I am not really a Rust user and I have only read the RFC a few minutes ago, so it is possible that I might have misunderstood something.

Contributor

MaikKlein commented Jun 22, 2016

Please ignore this if I am completely off point.

The problem I see with this RFC is that as a user, you have to mark as many function const fn as possible because that will probably be the best practice. The same thing is happening currently in C++ with contexpr. I think this is just unnecessary verbosity.

D doesn't have const fn but it allows any function to be called at compile time ( with some exceptions ).

for example

// Standalone example.
struct Point { x: i32, y: i32 }

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x: x, y: y }
    }

    fn add(self, other: Point) -> Point {
        Point::new(self.x + other.x, self.y + other.y)
    }
}

const ORIGIN: Point = Point::new(0, 0); // works because 0, 0 are both known at compile time 
const ORIGIN2: Point = Point::new(0, 0); // ditto

const ANOTHER: Point = ORIGIN.add(ORIGIN2); // works because ORIGIN and ORIGIN2 are both const.
{
    let x: i32 = 42;
    let y: i32 = 24;
    const SOME_POINT: Point = Point::new(x, y); // Error: x and y are not known at compile time
}
{
    const x: i32 = 42;
    const y: i32 = 24;
    const SOME_POINT: Point = Point::new(x, y); // Works x and y are both known at compile time.
}

Note, I am not really a Rust user and I have only read the RFC a few minutes ago, so it is possible that I might have misunderstood something.

@jethrogb

This comment has been minimized.

Show comment
Hide comment
@jethrogb

jethrogb Jun 22, 2016

Contributor

@MaikKlein there was a lot of discussion on CTFE in the RFC discussion

Contributor

jethrogb commented Jun 22, 2016

@MaikKlein there was a lot of discussion on CTFE in the RFC discussion

@brson

This comment has been minimized.

Show comment
Hide comment
@brson

brson Jan 11, 2017

Contributor

I don't see any recent comments explaining the blockers here, and the op isn't very illuminating. What's the status. How can we move this across the finish line?

Contributor

brson commented Jan 11, 2017

I don't see any recent comments explaining the blockers here, and the op isn't very illuminating. What's the status. How can we move this across the finish line?

@brson brson added the I-nominated label Jan 11, 2017

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril May 8, 2018

Contributor

@RalfJung I already revealed the secret at rust-lang/rfcs#2237 last year but I'll have to rewrite it ;)
Pretty much public domain now ^,-

Contributor

Centril commented May 8, 2018

@RalfJung I already revealed the secret at rust-lang/rfcs#2237 last year but I'll have to rewrite it ;)
Pretty much public domain now ^,-

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung May 8, 2018

Member

@SoniEx2

yes, a pointer one past the end of an array element would point to the next array element, probably. so what? it's not really nondeterministic? deterministic at compile-time is all that matters anyway. as far as I care, runtime evaluation could segfault for stack exhaustion.

The problem is in situations like the following (in C++):

int x[2];
int y; // let's assume y is put right after the end of x in the stack frame
if (&x[0] + 2 == &y) {
  // ...
}

C compilers want to (and do!) optimize that comparison to false. After all, one pointer points into x and one into y, so it is not possible for them to ever be equal.
Except, of course, that the addresses are equal on the machine because one pointer points right at the end of x, which is the same address as (the beginning of) y! So, if you obscure the code enough such that the compiler does not see any more where the addresses come from, you can tell that the comparison evaluates to true. The C++ standard hence allows both results to occur nondeterministically, justifying both the optimization (which says false) and the compilation to assembly (which says true). The C standard does not allow this, making LLVM (and GCC) non-conforming compilers as both will perform these kinds of optimizations.

Member

RalfJung commented May 8, 2018

@SoniEx2

yes, a pointer one past the end of an array element would point to the next array element, probably. so what? it's not really nondeterministic? deterministic at compile-time is all that matters anyway. as far as I care, runtime evaluation could segfault for stack exhaustion.

The problem is in situations like the following (in C++):

int x[2];
int y; // let's assume y is put right after the end of x in the stack frame
if (&x[0] + 2 == &y) {
  // ...
}

C compilers want to (and do!) optimize that comparison to false. After all, one pointer points into x and one into y, so it is not possible for them to ever be equal.
Except, of course, that the addresses are equal on the machine because one pointer points right at the end of x, which is the same address as (the beginning of) y! So, if you obscure the code enough such that the compiler does not see any more where the addresses come from, you can tell that the comparison evaluates to true. The C++ standard hence allows both results to occur nondeterministically, justifying both the optimization (which says false) and the compilation to assembly (which says true). The C standard does not allow this, making LLVM (and GCC) non-conforming compilers as both will perform these kinds of optimizations.

bors bot added a commit to japaric/heapless that referenced this issue Jul 13, 2018

Merge #43
43: Put all const functions behind a const-fn feature r=japaric a=XOSplicer

## Purpose
This PR introduces the `const-fn` feature gate, which when enabled makes most `new` methods `const` 
and therefore usable for initializing `static` variables.
The idea was introduced in #40 with the purpose to come closer to targeting stable rust.
`const` functions are currently only available on rust nightly (tracking issue: [const fn tracking issue (RFC 911)](rust-lang/rust#24111)).
In order to target stable rust this feature is made opt-in.

This feature is a **breaking change** as users of the library now need to explicitly enable it by changing their `Cargo.toml`:
```
...
[dependencies]
heapless = { version = "0.4.0", features = ["const-fn"] }
...
```



## Approach
The implementation of the feature mainly consist of the `const_fn!` macro, which takes a function with `const` modifier
and removes the modifier if the feature gate is not activated.
For the `const` functions a test is intoduced that checks if `static` variables can be initialized.
These tests are only active if the feature is active.
I have not found a way to make doc-test depend on a feature. Therefore some doc-tests are adapted, so that no static initialization is necessary.
The `ci/script.sh` is adapted to also tests with the `--all-feature` flag

## Future
When in the future the `const_fn` rust feature becomes stable, this feature gate **might become active by default**.


Closes #41 .

Co-authored-by: Felix <stegmaier.felix@gmail.com>
Co-authored-by: Felix Stegmaier <stegmaier.felix@gmail.com>
Co-authored-by: Felix Stegmaier <felix.stegmaier@hpe.com>
@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung Jul 19, 2018

Member

I wrote a summary of my ideas of const safety, const soundness etc. that have come up here in this thread and/or in related discussion on IRC: https://www.ralfj.de/blog/2018/07/19/const.html

Member

RalfJung commented Jul 19, 2018

I wrote a summary of my ideas of const safety, const soundness etc. that have come up here in this thread and/or in related discussion on IRC: https://www.ralfj.de/blog/2018/07/19/const.html

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung Jul 21, 2018

Member

This issue here has become somewhat hard to disentangle because so many things have been discussed. @oli-obk helpfully created a repo for const-eval concerns, so a good place to discuss specific sub-issues is probably the issue tracker of https://github.com/rust-rfcs/const-eval.

Member

RalfJung commented Jul 21, 2018

This issue here has become somewhat hard to disentangle because so many things have been discussed. @oli-obk helpfully created a repo for const-eval concerns, so a good place to discuss specific sub-issues is probably the issue tracker of https://github.com/rust-rfcs/const-eval.

@oli-obk

This comment has been minimized.

Show comment
Hide comment
@oli-obk

oli-obk Aug 20, 2018

Contributor

@Centril suggested to stabilize a minimal version that is forward compatible to any future extensions:

  • no generic arguments with trait bounds
  • no arguments or return types of fn pointer or dyn Trait type
    • checked recursively on the argument type so fields of arguments may also not be of these types
  • no unsafe code (because we don't know whether there's stuff there that's problematic)
    • I personally think that is fine except for unions which are already behind an extra feature gate, and raw pointer derefs (generally forbidden in any constant right now). Any other unsafe code needs to go through other unsafe const fns or const intrinsics, which require their own discussion wrt stabilization.
Contributor

oli-obk commented Aug 20, 2018

@Centril suggested to stabilize a minimal version that is forward compatible to any future extensions:

  • no generic arguments with trait bounds
  • no arguments or return types of fn pointer or dyn Trait type
    • checked recursively on the argument type so fields of arguments may also not be of these types
  • no unsafe code (because we don't know whether there's stuff there that's problematic)
    • I personally think that is fine except for unions which are already behind an extra feature gate, and raw pointer derefs (generally forbidden in any constant right now). Any other unsafe code needs to go through other unsafe const fns or const intrinsics, which require their own discussion wrt stabilization.
@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Aug 20, 2018

Contributor

(nit: my suggestion also included a recursive check for fn pointers or dyn Trait on the return type of const fns)

Contributor

Centril commented Aug 20, 2018

(nit: my suggestion also included a recursive check for fn pointers or dyn Trait on the return type of const fns)

@japaric

This comment has been minimized.

Show comment
Hide comment
@japaric

japaric Aug 20, 2018

Member

no generic arguments with trait bounds

To clarify, would something like this be accepted or not?

struct Mutex<T> where T: Send { /* .. */ }

impl<T> Mutex<T> where T: Send {
    pub const fn new(val: T) -> Self { /* .. */ }
}

The bound is not part of the const fn itself.

Member

japaric commented Aug 20, 2018

no generic arguments with trait bounds

To clarify, would something like this be accepted or not?

struct Mutex<T> where T: Send { /* .. */ }

impl<T> Mutex<T> where T: Send {
    pub const fn new(val: T) -> Self { /* .. */ }
}

The bound is not part of the const fn itself.

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Aug 20, 2018

Contributor

Also to clarify: is the proposal to stabilize those things and leave the rest behind the feature gate OR to stabilize those and make the rest an error altogether?

Contributor

mark-i-m commented Aug 20, 2018

Also to clarify: is the proposal to stabilize those things and leave the rest behind the feature gate OR to stabilize those and make the rest an error altogether?

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung Aug 20, 2018

Member

@mark-i-m the rest would stay behind a feature gate.

@oli-obk what is the problem with unsafe code? We do allow unsafe in const X : Ty = ..., which has all the same problems. I think const fn bodies should be checked exactly like const bodies.

I think we do want to remain conservative around "unconst" operations -- operations that are safe but not const-safe (basically anything on raw pointers) -- but those are already disallowed entirely in const context, right?

Member

RalfJung commented Aug 20, 2018

@mark-i-m the rest would stay behind a feature gate.

@oli-obk what is the problem with unsafe code? We do allow unsafe in const X : Ty = ..., which has all the same problems. I think const fn bodies should be checked exactly like const bodies.

I think we do want to remain conservative around "unconst" operations -- operations that are safe but not const-safe (basically anything on raw pointers) -- but those are already disallowed entirely in const context, right?

@oli-obk

This comment has been minimized.

Show comment
Hide comment
@oli-obk

oli-obk Aug 20, 2018

Contributor

The bound is not part of the const fn itself.

No, bounds on the impl block would also not be allowed under that proposal

what is the problem with unsafe code?

I don't see any problems as noted in my comment. Every const unsafe feature/function needs to go through their own stabilization anyway.

Contributor

oli-obk commented Aug 20, 2018

The bound is not part of the const fn itself.

No, bounds on the impl block would also not be allowed under that proposal

what is the problem with unsafe code?

I don't see any problems as noted in my comment. Every const unsafe feature/function needs to go through their own stabilization anyway.

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Aug 20, 2018

Contributor

@RalfJung I think the problem is "@Centril is nervous that we missed something wrt. those are already disallowed entirely in const context". ;) But we have to stabilize unsafe { .. } in const fn at some point so if you are sure there are no problems and that we caught all unconst operations then let's do it?

Contributor

Centril commented Aug 20, 2018

@RalfJung I think the problem is "@Centril is nervous that we missed something wrt. those are already disallowed entirely in const context". ;) But we have to stabilize unsafe { .. } in const fn at some point so if you are sure there are no problems and that we caught all unconst operations then let's do it?

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung Aug 20, 2018

Member

Moreover, if we missed something, we're already kind of screwed as people can use it on const.

I still plan to write a PR filling in the const safety / promotion parts of the const fn RFC repo; that would be the time to check carefully if we covered everything (and have testcases).

Member

RalfJung commented Aug 20, 2018

Moreover, if we missed something, we're already kind of screwed as people can use it on const.

I still plan to write a PR filling in the const safety / promotion parts of the const fn RFC repo; that would be the time to check carefully if we covered everything (and have testcases).

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung Aug 20, 2018

Member

Another thing that came up on Discord are FP operations: We cannot currently guarantee that they will match real hardware. CTFE will follow IEEE exactly, but LLVM/hardware might not.

This also applies to const items, but those will never be runtime-executed -- whereas const fn might be. So it seems prudent to not stabilize FP operations in const fn.

OTOH, we already promote results of FP operations? So there we already have that run-time/compile-time mismatch observable on stable. Would it be worth a crater run to see if we can undo that?

Member

RalfJung commented Aug 20, 2018

Another thing that came up on Discord are FP operations: We cannot currently guarantee that they will match real hardware. CTFE will follow IEEE exactly, but LLVM/hardware might not.

This also applies to const items, but those will never be runtime-executed -- whereas const fn might be. So it seems prudent to not stabilize FP operations in const fn.

OTOH, we already promote results of FP operations? So there we already have that run-time/compile-time mismatch observable on stable. Would it be worth a crater run to see if we can undo that?

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Aug 20, 2018

Contributor

For future reference, the following article is relevant with respect to floating points and determinism:

Contributor

Centril commented Aug 20, 2018

For future reference, the following article is relevant with respect to floating points and determinism:

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Aug 20, 2018

Contributor

@RalfJung

Would it be worth a crater run to see if we can undo that?

I would be surprised if we could do this, but it is worth a try at least. :)

Contributor

Centril commented Aug 20, 2018

@RalfJung

Would it be worth a crater run to see if we can undo that?

I would be surprised if we could do this, but it is worth a try at least. :)

@est31

This comment has been minimized.

Show comment
Hide comment
@est31

est31 Aug 20, 2018

Contributor

@RalfJung there might be interesting input further up in this thread #24111 (comment)

Contributor

est31 commented Aug 20, 2018

@RalfJung there might be interesting input further up in this thread #24111 (comment)

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Aug 20, 2018

Member

Assuming we want to keep the keyword form const fn, I think stabilizing something now, that's limited enough, is a pretty decent solution (how did I not see it before?!)

Member

eddyb commented Aug 20, 2018

Assuming we want to keep the keyword form const fn, I think stabilizing something now, that's limited enough, is a pretty decent solution (how did I not see it before?!)

@durka

This comment has been minimized.

Show comment
Hide comment
@durka

durka Aug 20, 2018

Contributor
Contributor

durka commented Aug 20, 2018

@RalfJung

This comment has been minimized.

Show comment
Hide comment
@RalfJung

RalfJung Aug 20, 2018

Member

@est31 As @rkruppe already wrote, those fuses would be illegal to perform without -ffast-math -- and I think LLVM handles that correctly.

From what I recall, basic arithmetic is not even all that problematic (except on 32bit x86 because x87...), but transcendental functions are. And those are not const fn, right? So my hope would be that in the end we can have parity between const items, promotion and const fn in this regard as well.

Member

RalfJung commented Aug 20, 2018

@est31 As @rkruppe already wrote, those fuses would be illegal to perform without -ffast-math -- and I think LLVM handles that correctly.

From what I recall, basic arithmetic is not even all that problematic (except on 32bit x86 because x87...), but transcendental functions are. And those are not const fn, right? So my hope would be that in the end we can have parity between const items, promotion and const fn in this regard as well.

@est31

This comment has been minimized.

Show comment
Hide comment
@est31

est31 Aug 20, 2018

Contributor

@RalfJung I'm still not convinced that e.g. spilling and then loading it back into the FPU registers in between some operations gives the same results as the same computation without that spilling.

From what I recall, basic arithmetic is not even all that problematic (except on 32bit x86 because x87...), but transcendental functions are.

How would transcendental functions mean a problem?

IIRC, while some transcendental functions are supported by common x86 processors, those functions are slow and eschewed, and only included for completeness and compatibility with existing implementations. Thus, almost everywhere, transcendental functions are expressed in terms of combinations of basic arithmetic functions. This means that there's no difference in their referential transparency to that of arithmetic functions. If basic functions are "safe" then anything built on them is, including transcendental functions. The only source of "referential intransparency" here might be different approximations (implementations) of those transcendental functions in terms of those basic arithmetic functions. Is that the source of the problem?

Contributor

est31 commented Aug 20, 2018

@RalfJung I'm still not convinced that e.g. spilling and then loading it back into the FPU registers in between some operations gives the same results as the same computation without that spilling.

From what I recall, basic arithmetic is not even all that problematic (except on 32bit x86 because x87...), but transcendental functions are.

How would transcendental functions mean a problem?

IIRC, while some transcendental functions are supported by common x86 processors, those functions are slow and eschewed, and only included for completeness and compatibility with existing implementations. Thus, almost everywhere, transcendental functions are expressed in terms of combinations of basic arithmetic functions. This means that there's no difference in their referential transparency to that of arithmetic functions. If basic functions are "safe" then anything built on them is, including transcendental functions. The only source of "referential intransparency" here might be different approximations (implementations) of those transcendental functions in terms of those basic arithmetic functions. Is that the source of the problem?

@rkruppe

This comment has been minimized.

Show comment
Hide comment
@rkruppe

rkruppe Aug 21, 2018

Contributor

@est31 While most transcendental functions are ultimately just library code composed of primitive integer and float operations, these implementations are not standardized and in practice a Rust program can interact with maybe three different implementations throughout its lifetime, some of which also vary by host or target platform:

  • There's the runtime implementation the program uses on the target (e.g. the target platform libm, or hardware instructions in some circumstances)
  • There's the constant folder LLVM uses (apparently this is just the host platform C libm)
  • There's whatever MIRI would do with these operations (e.g., something in rustc_apfloat, or interpreting a Rust implementation such as https://github.com/japaric/libm/)

If any of these disagree with each other, you can get different results depending on when an expression is evaluated.

Contributor

rkruppe commented Aug 21, 2018

@est31 While most transcendental functions are ultimately just library code composed of primitive integer and float operations, these implementations are not standardized and in practice a Rust program can interact with maybe three different implementations throughout its lifetime, some of which also vary by host or target platform:

  • There's the runtime implementation the program uses on the target (e.g. the target platform libm, or hardware instructions in some circumstances)
  • There's the constant folder LLVM uses (apparently this is just the host platform C libm)
  • There's whatever MIRI would do with these operations (e.g., something in rustc_apfloat, or interpreting a Rust implementation such as https://github.com/japaric/libm/)

If any of these disagree with each other, you can get different results depending on when an expression is evaluated.

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Aug 21, 2018

Contributor

@rfcbot cancel

We are unlikely to stabilize the full monty in the near future.
Instead, I'd like to develop consensus for a more minimal subset (as roughly outlined in #24111 (comment)) that we hopefully can stabilize in the near term.
This subset is tracked in #53555. Further description is available there.

Contributor

Centril commented Aug 21, 2018

@rfcbot cancel

We are unlikely to stabilize the full monty in the near future.
Instead, I'd like to develop consensus for a more minimal subset (as roughly outlined in #24111 (comment)) that we hopefully can stabilize in the near term.
This subset is tracked in #53555. Further description is available there.

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Aug 21, 2018

@Centril proposal cancelled.

rfcbot commented Aug 21, 2018

@Centril proposal cancelled.

@est31

This comment has been minimized.

Show comment
Hide comment
@est31

est31 Aug 21, 2018

Contributor

@rkruppe is there a reason for lowering the transcendental functions to llvm intrinsics? Can't we just avoid the entire problem by lowering them to well known, rust-only implementations that we control and that are the same on all platforms?

Contributor

est31 commented Aug 21, 2018

@rkruppe is there a reason for lowering the transcendental functions to llvm intrinsics? Can't we just avoid the entire problem by lowering them to well known, rust-only implementations that we control and that are the same on all platforms?

@rkruppe

This comment has been minimized.

Show comment
Hide comment
@rkruppe

rkruppe Aug 21, 2018

Contributor

is there a reason for lowering the transcendental functions to llvm intrinsics?

Besides the simplicity of not implementing a whole cross platform libm, the intrinsics have advantage in LLVM's optimizer and codegen that an ordinary library function won't get. Obviously constant folding (& related things like value range analysis) is a problem in this context but it's quite useful otherwise. There are also algebraic identities (applied by the SimplifyLibCalls pass). Finally, a few functions (mostly sqrt and its reciprocal, which are not transcendental but whatever) have special code generation support to e.g. generate sqrtss on x86 with SSE.

Can't we just avoid the entire problem by lowering them to well known, rust-only implementations that we control and that are the same on all platforms?

Even ignoring all of the above, this is not a great option IMO. If the target platform has a C libm, it should be possible to use it, either because it's more optimized or to avoid bloat.

Contributor

rkruppe commented Aug 21, 2018

is there a reason for lowering the transcendental functions to llvm intrinsics?

Besides the simplicity of not implementing a whole cross platform libm, the intrinsics have advantage in LLVM's optimizer and codegen that an ordinary library function won't get. Obviously constant folding (& related things like value range analysis) is a problem in this context but it's quite useful otherwise. There are also algebraic identities (applied by the SimplifyLibCalls pass). Finally, a few functions (mostly sqrt and its reciprocal, which are not transcendental but whatever) have special code generation support to e.g. generate sqrtss on x86 with SSE.

Can't we just avoid the entire problem by lowering them to well known, rust-only implementations that we control and that are the same on all platforms?

Even ignoring all of the above, this is not a great option IMO. If the target platform has a C libm, it should be possible to use it, either because it's more optimized or to avoid bloat.

@est31

This comment has been minimized.

Show comment
Hide comment
@est31

est31 Aug 21, 2018

Contributor

the intrinsics have advantage in LLVM's optimizer and codegen that an ordinary library function won't get.

I've thought that such optimizations are only being enabled if fast math is turned on?

If the target platform has a C libm, it should be possible to use it, either because it's more optimized or to avoid bloat.

Sure. There should always be an option to trade this rather academic property -- referential transparency -- in favour of things that matter more like improved speed of the compiled binary or smaller binaries. E.g. by using platform libm or by turning on fast math mode. Or do you suggest that we should disallow fast math mode in all eternity?

IMO we shouldn't disallow f32::sin() in const contexts, at least not if we are allowing +, - etc.. Such a ban will force people to create and use crates that provide const-compatible implementations.

Contributor

est31 commented Aug 21, 2018

the intrinsics have advantage in LLVM's optimizer and codegen that an ordinary library function won't get.

I've thought that such optimizations are only being enabled if fast math is turned on?

If the target platform has a C libm, it should be possible to use it, either because it's more optimized or to avoid bloat.

Sure. There should always be an option to trade this rather academic property -- referential transparency -- in favour of things that matter more like improved speed of the compiled binary or smaller binaries. E.g. by using platform libm or by turning on fast math mode. Or do you suggest that we should disallow fast math mode in all eternity?

IMO we shouldn't disallow f32::sin() in const contexts, at least not if we are allowing +, - etc.. Such a ban will force people to create and use crates that provide const-compatible implementations.

@rkruppe

This comment has been minimized.

Show comment
Hide comment
@rkruppe

rkruppe Aug 21, 2018

Contributor

I've thought that such optimizations are only being enabled if fast math is turned on?

Constant evaluation of these functions and emission of sqrtss is easy to justify without -ffast-math, since it can be made correctly rounded.

Or do you suggest that we should disallow fast math mode in all eternity?

I am not suggesting anything, nor do I have an opinion (atm) on whether such a property should be guaranteed. I am simply reporting constraints.

Contributor

rkruppe commented Aug 21, 2018

I've thought that such optimizations are only being enabled if fast math is turned on?

Constant evaluation of these functions and emission of sqrtss is easy to justify without -ffast-math, since it can be made correctly rounded.

Or do you suggest that we should disallow fast math mode in all eternity?

I am not suggesting anything, nor do I have an opinion (atm) on whether such a property should be guaranteed. I am simply reporting constraints.

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