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

RFC: Constant generics (restricted Π types) for Rust, core RFC (v2) #1931

Closed
wants to merge 39 commits into
base: master
from

Conversation

@ticki
Contributor

ticki commented Feb 26, 2017

This is one of 3 RFCs. The collection of the pi type RFCs is tracked by issue #1930.

Rendered.

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Mar 12, 2017

Member

@kryptan Ah yes that is a problem. #1229 may be relevant.

Member

eddyb commented Mar 12, 2017

@kryptan Ah yes that is a problem. #1229 may be relevant.

@kryptan

This comment has been minimized.

Show comment
Hide comment
@kryptan

kryptan Mar 12, 2017

const fn can fail to execute due to a panic; this will cause a compile-time error at monomorphisation-time.

Maybe we shouldn't actually care about monomorphisation-time errors and just check types for equality at monomorphisation-time? Otherwise it seems that under current proposal types [T; m + n] and [T; n + m] would be treated as different because of structural equality.

EDIT: @eddyb comment above refers to the first sentence of this comment.

kryptan commented Mar 12, 2017

const fn can fail to execute due to a panic; this will cause a compile-time error at monomorphisation-time.

Maybe we shouldn't actually care about monomorphisation-time errors and just check types for equality at monomorphisation-time? Otherwise it seems that under current proposal types [T; m + n] and [T; n + m] would be treated as different because of structural equality.

EDIT: @eddyb comment above refers to the first sentence of this comment.

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Mar 12, 2017

Member

@kryptan We can't check types for equality that late, even if we wanted to, it'd make the whole trait system less useful. What we can do, however, is codegen unconditional panics inside functions (and warn), while outside functions we should be able to treat evaluation of constants in type as if it "propagates" like WF.

That said, I'd need to talk to @arielb1 or @nikomatsakis to understand how well the WF analogy works.

Member

eddyb commented Mar 12, 2017

@kryptan We can't check types for equality that late, even if we wanted to, it'd make the whole trait system less useful. What we can do, however, is codegen unconditional panics inside functions (and warn), while outside functions we should be able to treat evaluation of constants in type as if it "propagates" like WF.

That said, I'd need to talk to @arielb1 or @nikomatsakis to understand how well the WF analogy works.

@kennytm

This comment has been minimized.

Show comment
Hide comment
@kennytm

kennytm Mar 13, 2017

Member

@kryptan @withoutboats I think m + n == n + m is treated in #1932 (which is going to be postponed) using the AdditiveCancelationLR rule. If we merge #1931 and postpone all of #1932 then we can only allow m + n ≟ n + m.

Member

kennytm commented Mar 13, 2017

@kryptan @withoutboats I think m + n == n + m is treated in #1932 (which is going to be postponed) using the AdditiveCancelationLR rule. If we merge #1931 and postpone all of #1932 then we can only allow m + n ≟ n + m.

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Mar 13, 2017

Member

@kennytm is tricky because here it's not that we "know them to be unequal", but rather that we "don't know them to be equal", in fact we don't really know anything at all about any expressions.

Member

eddyb commented Mar 13, 2017

@kennytm is tricky because here it's not that we "know them to be unequal", but rather that we "don't know them to be equal", in fact we don't really know anything at all about any expressions.

@kennytm

This comment has been minimized.

Show comment
Hide comment
@kennytm

kennytm Mar 13, 2017

Member

@eddyb Sorry I do mean "the equality is unknown". Switched to a better symbol.

Member

kennytm commented Mar 13, 2017

@eddyb Sorry I do mean "the equality is unknown". Switched to a better symbol.

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Mar 13, 2017

Contributor

@eddyb

Treating non-panicness of consts as WF would not allow you to do things such as

fn bar<n: const usize>(...) {}

fn foo<n: const usize>() {
    bar::<n+1>();
}

Because we can't prove that constant functions won't panic.

Contributor

arielb1 commented Mar 13, 2017

@eddyb

Treating non-panicness of consts as WF would not allow you to do things such as

fn bar<n: const usize>(...) {}

fn foo<n: const usize>() {
    bar::<n+1>();
}

Because we can't prove that constant functions won't panic.

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Mar 13, 2017

Member

@arielb1 Right, but I did mention a different strategy in that case (as opposed to using bar in a signature):

What we can do, however, is codegen unconditional panics inside functions (and warn)

Member

eddyb commented Mar 13, 2017

@arielb1 Right, but I did mention a different strategy in that case (as opposed to using bar in a signature):

What we can do, however, is codegen unconditional panics inside functions (and warn)

@arielb1

This comment has been minimized.

Show comment
Hide comment
@arielb1

arielb1 Mar 14, 2017

Contributor

@eddyb

What will we do with local variables? Will we panic when the local is defined? When the local is used?

fn main() {
    let x;
    // ...
    x = [0isize; !0 + 1];
}
Contributor

arielb1 commented Mar 14, 2017

@eddyb

What will we do with local variables? Will we panic when the local is defined? When the local is used?

fn main() {
    let x;
    // ...
    x = [0isize; !0 + 1];
}
@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Mar 15, 2017

Contributor

If we want to keep consistency with what the compiler already does, we would panic at the assignment:

error[E0080]: constant evaluation error
 --> <anon>:4:13
  |
4 |     x = [0; !0 + 1];
  |             ^^^^^^ attempt to add with overflow

error: aborting due to previous error
Contributor

mark-i-m commented Mar 15, 2017

If we want to keep consistency with what the compiler already does, we would panic at the assignment:

error[E0080]: constant evaluation error
 --> <anon>:4:13
  |
4 |     x = [0; !0 + 1];
  |             ^^^^^^ attempt to add with overflow

error: aborting due to previous error
@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Mar 16, 2017

Member

@arielb1 I think the best we can do is when evaluating the array repeat, i.e. as if it were unsized.
Although that example is not really relevant, as there are no const parameters.

Member

eddyb commented Mar 16, 2017

@arielb1 I think the best we can do is when evaluating the array repeat, i.e. as if it were unsized.
Although that example is not really relevant, as there are no const parameters.

@clarcharr

This comment has been minimized.

Show comment
Hide comment
@clarcharr

clarcharr Mar 16, 2017

Contributor

I want to add to @Rufflewind's comment that such provability only comes when you have a recursive expression; non-recursive expressions can be safely evaluated.

I don't mind adding that restriction TBH. const fns are also currently unstable for a reason and stuff like that is probably why.

Contributor

clarcharr commented Mar 16, 2017

I want to add to @Rufflewind's comment that such provability only comes when you have a recursive expression; non-recursive expressions can be safely evaluated.

I don't mind adding that restriction TBH. const fns are also currently unstable for a reason and stuff like that is probably why.

@ticki

This comment has been minimized.

Show comment
Hide comment
@ticki

ticki Mar 19, 2017

Contributor

My plans were to get back too this in this weekend, but I was busy. I apologize. I hope that I can do so this week instead.

Contributor

ticki commented Mar 19, 2017

My plans were to get back too this in this weekend, but I was busy. I apologize. I hope that I can do so this week instead.

@withoutboats

This comment has been minimized.

Show comment
Hide comment
@withoutboats

withoutboats Mar 28, 2017

Contributor

We talked about this in IRC a bit today & I had a few thoughts.

First, the unification issues seem to come from bracketed expressions. Can we accept a version of this RFC that accepts only literals and variables, and not expressions like {n + 1}? Does that make the implementation issues more resolvable (eg by avoiding the new const expr system)? @eddyb @nikomatsakis

Second, the sort of "design" issues (e.g. do we want this in the language? what is the best syntax? how will we teach this?) come mainly from the expression of const parameterization to arbitrary types. We already have one type which is parameterized by a const [T; n]. I feel like we would be able to move faster toward stabilization if we only supported things like impl<const n: usize> PartialEq<[i32; n] for [i32; n] as a first pass.

Contributor

withoutboats commented Mar 28, 2017

We talked about this in IRC a bit today & I had a few thoughts.

First, the unification issues seem to come from bracketed expressions. Can we accept a version of this RFC that accepts only literals and variables, and not expressions like {n + 1}? Does that make the implementation issues more resolvable (eg by avoiding the new const expr system)? @eddyb @nikomatsakis

Second, the sort of "design" issues (e.g. do we want this in the language? what is the best syntax? how will we teach this?) come mainly from the expression of const parameterization to arbitrary types. We already have one type which is parameterized by a const [T; n]. I feel like we would be able to move faster toward stabilization if we only supported things like impl<const n: usize> PartialEq<[i32; n] for [i32; n] as a first pass.

@est31

This comment has been minimized.

Show comment
Hide comment
@est31

est31 Mar 29, 2017

Contributor

I too think that we should do a minimal version of this RFC as possible, so that we can merge it as fast as possible.

For example, we should disallow any calculation, like {n+1} style expressions.

I have heard @eddyb say though that impl args and struct args are handled the same way in the compiler, so disallowing const generics as generic struct params would involve more work than allowing them.

Alternatively, we can do it like with the ? operator, where there has been similarly large demand from the community for the feature: accept the RFC as a base, and then implement & stabilize one feature of it.

Contributor

est31 commented Mar 29, 2017

I too think that we should do a minimal version of this RFC as possible, so that we can merge it as fast as possible.

For example, we should disallow any calculation, like {n+1} style expressions.

I have heard @eddyb say though that impl args and struct args are handled the same way in the compiler, so disallowing const generics as generic struct params would involve more work than allowing them.

Alternatively, we can do it like with the ? operator, where there has been similarly large demand from the community for the feature: accept the RFC as a base, and then implement & stabilize one feature of it.

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Mar 29, 2017

Contributor

I do think there should at least be a consensus about the syntax for using expressions, even if they aren't in the base RFC.

Contributor

mark-i-m commented Mar 29, 2017

I do think there should at least be a consensus about the syntax for using expressions, even if they aren't in the base RFC.

@dylanede

This comment has been minimized.

Show comment
Hide comment
@dylanede

dylanede Mar 30, 2017

@withoutboats, would that subset of features support doing impls for types like (for example) MyType<[i32; n]>? Because if it does then that is pretty much functionally equivalent (in terms of things you can use this to implement) to being able to write impls for MyType<n>. Point being that if this ends up enabling a lot of things anyway it may end up being just as easy to add support for MyType<n> impls at the same time.

dylanede commented Mar 30, 2017

@withoutboats, would that subset of features support doing impls for types like (for example) MyType<[i32; n]>? Because if it does then that is pretty much functionally equivalent (in terms of things you can use this to implement) to being able to write impls for MyType<n>. Point being that if this ends up enabling a lot of things anyway it may end up being just as easy to add support for MyType<n> impls at the same time.

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Apr 3, 2017

Contributor

I am also curious what @ticki thinks...

Contributor

mark-i-m commented Apr 3, 2017

I am also curious what @ticki thinks...

```rust
// [T; N] is a constructor, T → usize → 𝓤 (parameterize over T and you get A → 𝓤).
fn foo<const n: usize, const l: [u32; n]>() -> [u32; n] {

This comment has been minimized.

@ejmahler

ejmahler Apr 12, 2017

In my browser, the letter lowercase L (l) and the number 1 are almost identical. For a little bit I actually thought it was the number 1 and I was really confused. Something like arr instead of l would eliminate any possible confusion

@ejmahler

ejmahler Apr 12, 2017

In my browser, the letter lowercase L (l) and the number 1 are almost identical. For a little bit I actually thought it was the number 1 and I was really confused. Something like arr instead of l would eliminate any possible confusion

This comment has been minimized.

@eddyb

eddyb Apr 12, 2017

Member

We use UPPER_SNAKE_CASE for const globals and I still would like that somewhat - unless, of course, there's a good argument for keeping it lowercase.

@eddyb

eddyb Apr 12, 2017

Member

We use UPPER_SNAKE_CASE for const globals and I still would like that somewhat - unless, of course, there's a good argument for keeping it lowercase.

This comment has been minimized.

@ejmahler

ejmahler Apr 12, 2017

You mean SCREAMING_SNAKE_CASE?

@ejmahler

ejmahler Apr 12, 2017

You mean SCREAMING_SNAKE_CASE?

@aturon aturon referenced this pull request Apr 13, 2017

Open

Lang team roadmap #18

@jethrogb

This comment has been minimized.

Show comment
Hide comment
Contributor

jethrogb commented Apr 15, 2017

@withoutboats

This comment has been minimized.

Show comment
Hide comment
@withoutboats

withoutboats May 25, 2017

Contributor

@rfcbot fcp close

In favor of #2000.

Thanks for this RFC @ticki, and the previous one as well. This trilogy as a whole has been more ambitious than we feel comfortable committing to right now, but it really highlighted how valuable const generics would be to many users in the community. I'm excited to see us move toward a more expressive const system, if in more incremental steps.

Contributor

withoutboats commented May 25, 2017

@rfcbot fcp close

In favor of #2000.

Thanks for this RFC @ticki, and the previous one as well. This trilogy as a whole has been more ambitious than we feel comfortable committing to right now, but it really highlighted how valuable const generics would be to many users in the community. I'm excited to see us move toward a more expressive const system, if in more incremental steps.

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot May 25, 2017

Team member @withoutboats has proposed to close this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

rfcbot commented May 25, 2017

Team member @withoutboats has proposed to close this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Jun 6, 2017

Member

I'm going to go ahead and close this for the time being; I don't think there's much point in waiting for the final checkbox and FCP here. Discussion has clearly moved to the new RFC. Thanks all!

Member

aturon commented Jun 6, 2017

I'm going to go ahead and close this for the time being; I don't think there's much point in waiting for the final checkbox and FCP here. Discussion has clearly moved to the new RFC. Thanks all!

@aturon aturon closed this Jun 6, 2017

boundary between value and type. Unfortunately, as cool as it sounds, it has
some severe disadvantages: most importantly, the type checking becomes
undecidable. Often you would need some form of theorem prover to type check
the program, and those have their limitations too.

This comment has been minimized.

@freebroccolo

freebroccolo Oct 31, 2017

Contributor

I just want to say that the part about undecidability here is very misleading.

To be clear, type checking in Agda and Coq is decidable in general. Undecidability potentially comes into play from non-termination of some program fragment during evaluation that occurs while type checking. But by default, both Agda and Coq require that programs pass the termination check before they can be used. So that source of undecidability cannot arise unless termination checking is explicitly disabled.

The case may be different for Idris since it does not enforce termination by default as far as I know.

However, even in the case where there is undecidability, it's also misleading to bring it up as a serious disadvantage in the context of Rust. The fact of the matter is that type checking in Rust is already undecidable due to the expressive power of associated types. For instance, you can already encode a full lambda calculus interpreter in the type system.

@freebroccolo

freebroccolo Oct 31, 2017

Contributor

I just want to say that the part about undecidability here is very misleading.

To be clear, type checking in Agda and Coq is decidable in general. Undecidability potentially comes into play from non-termination of some program fragment during evaluation that occurs while type checking. But by default, both Agda and Coq require that programs pass the termination check before they can be used. So that source of undecidability cannot arise unless termination checking is explicitly disabled.

The case may be different for Idris since it does not enforce termination by default as far as I know.

However, even in the case where there is undecidability, it's also misleading to bring it up as a serious disadvantage in the context of Rust. The fact of the matter is that type checking in Rust is already undecidable due to the expressive power of associated types. For instance, you can already encode a full lambda calculus interpreter in the type system.

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