rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.18k stars 12.56k forks source link

Tracking issue for const generics (RFC 2000) #44580

Closed withoutboats closed 2 years ago

withoutboats commented 7 years ago

Tracking issue for rust-lang/rfcs#2000

Updates:

If you want to help out, take a look at the open const generics issues and feel free to ping @varkor, @eddyb, @yodaldevoid, @oli-obk or @lcnr for help in getting started!


Blocking stabilization:


Remaining implementation issues:

eddyb commented 6 years ago

@newpavlov I assumed constant parameters would be allowed without being used in fields. The reason type parameters must be used is because of variance, but constants don't have this issue.

withoutboats commented 6 years ago

@eddyb that's what I recall from the RFC discussion also.

Ekleog commented 6 years ago

@varkor So this means PhantomConst proposed by @cuviper cannot exist in the current state of this RFC… right? though latest comments appear to point that there is no need for PhantomConst anyway.

burdges commented 6 years ago

Do const parameters impact the variance of type parameters they involve?

eddyb commented 6 years ago

Currently I believe const generics can't have type parameters in their type.

rodrimati1992 commented 5 years ago

I am not clear on what the first working version of this will be able to do.

For example whether the code in this comment would compile: https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717

If that code compiled,it would be a way to enforce constraints for constants before getting a syntax for it.

eddyb commented 5 years ago

@rodrimati1992 You can probably just do (): IsTrue<{N < 128}> without a separate type. I guess you want to reuse the constraint and have it actually work? (because copying the N < 128 expression won't make it work, initially) You could use trait Lt128<const N: usize> = IsTrue<{N < 128}>;, I guess. There's also the option of just writing where [(); 128 - N],, I think (but I'm not sure we guarantee that will panic).

rodrimati1992 commented 5 years ago

@rodrimati1992 You can probably just do (): IsTrue<{N < 128}> without a separate type. I guess you want to reuse the constraint and have it actually work? (because copying the N < 128 expression won't make it work, initially) You could use trait Lt128<const N: usize> = IsTrue<{N < 128}>;, I guess. There's also the option of just writing where [(); 128 - N],, I think (but I'm not sure we guarantee that will panic).

So,with trait aliases I could rewrite is to this?:

trait AssertLessThan128<const N:usize>=
    Assert<{N<=128}, (
        Str<"uint cannot be constructed with a size larger than 128,the passed size is:",
        Usize<N>>
    ) >;

The idea with the Assert<cond B:bool,Msg> trait is using a type error to print an error message embedded in a type,which in the case of AssertLessThan128 is:

(Str<"uint cannot be constructed with a size larger than 128,the passed size is:",Usize<N>>)

which I expect to be more helpful than where [(); 128 - N],,since it tells you in the error message why the compile-time error happened.

eddyb commented 5 years ago

You can also do if !(N < 128) { panic!("...") } in a constant, I think?

rodrimati1992 commented 5 years ago

I thought the std::fmt architecture is not going to be there until const trait constraints (or something similar) arrive?

With this thing you can have error messages with multiple values in it (embedded in types).

Maybe it's better to wait for const generics to talk about this,since it'll be easier to show executable examples.

Nemo157 commented 5 years ago

@rodrimati1992 see https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.md, there's going to be a special case to get const panic!() before it would be possible via other features (at a guess it probably won't allow custom formatting of values at first).

eddyb commented 5 years ago

@rodrimati1992 Ahh, I see, that's indeed a clever trick!

quadrupleslap commented 5 years ago

What's the status of this?

HadrienG2 commented 5 years ago

Next step is still https://github.com/rust-lang/rust/pull/53645 AFAIK.

yodaldevoid commented 5 years ago

This is correct. To give a quick update for those not following the PR #53645, const generics have compile and work in at least one simple usecase. What remains is finishing up codegen for other usecases, including const generics in arrys, and some error output cleanup. After that, the PR should be ready merge and people can start playing with it.

jeffvandyke commented 5 years ago

Might be off-topic, but will this allow a variant or fork of Chunks and related methods to have Item be a compile-time-fixed-size array instead of a slice? This would allow a for loop to destructure/bind to an irrefutable pattern that mapped the elements in the chunk to variables, such as for (first, second) in arr.chunks(2) which currently fails, and I'm guessing (without any justification admittedly) that it would allow more optimization in certain use cases.

vadixidav commented 5 years ago

@jeffvandyke You might be thinking of ChunksExact specifically. Such a change would be API-breaking, so it can't be done. We could add new APIs that give array references in the future, but we can't break existing ones.

sdroege commented 5 years ago

@jeffvandyke You might be thinking of ChunksExact specifically. Such a change would be API-breaking, so it can't be done. We could add new APIs that give array references in the future, but we can't break existing ones.

I'm only waiting for this to be implemented and stabilized before proposing a PR that adds such API in addition to ChunksExact and its variants :)

The runtime and compile-time variants both have their use-cases, you don't always know your chunk size ahead of time. Optimization-wise, if you use ChunksExact with a constant it should be more or less the same according to my testing. The compiler can optimize away all bounds checks.

shepmaster commented 5 years ago

to be implemented and stabilized

I'd suggest not waiting for stabilization as such an API would be one more good use to help exercise this feature for stabilization.

nhynes commented 5 years ago

I'm guessing that this doesn't yet work for impl blocks? I tried

#![feature(const_generics)]

struct The<const Val: u64>();

impl<const Val: u64> The<Val> {
    fn the() {
        println!("{}", Val);
    }
}

but, as was indeed warned, the compiler crashed with

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: rustc::util::common::panic_hook
   5: std::panicking::rust_panic_with_hook
   6: std::panicking::continue_panic_fmt
   7: rust_begin_unwind
   8: core::panicking::panic_fmt
   9: core::slice::slice_index_order_fail
  10: rustc_resolve::Resolver::resolve_ident_in_lexical_scope
  11: rustc_resolve::Resolver::resolve_path
  12: rustc_resolve::Resolver::resolve_path_without_parent_scope
  13: rustc_resolve::Resolver::smart_resolve_path_fragment
  14: rustc_resolve::Resolver::smart_resolve_path
  15: <rustc_resolve::Resolver<'a> as syntax::visit::Visitor<'tcx>>::visit_ty
  16: syntax::visit::walk_generic_args
  17: syntax::visit::walk_ty
  18: rustc_resolve::Resolver::with_generic_param_rib
  19: rustc_resolve::Resolver::resolve_item
  20: rustc_resolve::Resolver::resolve_crate
  21: rustc::util::common::time
  22: rustc_interface::passes::configure_and_expand_inner
  23: rustc_interface::passes::configure_and_expand::{{closure}}
  24: <rustc_data_structures::box_region::PinnedGenerator<I, A, R>>::new
  25: rustc_interface::passes::configure_and_expand
  26: <rustc_interface::queries::Query<T>>::compute
  27: <rustc_interface::queries::Query<T>>::compute
  28: <rustc_interface::queries::Query<T>>::compute
  29: rustc_interface::queries::<impl rustc_interface::interface::Compiler>::prepare_outputs
  30: rustc_interface::interface::run_compiler_in_existing_thread_pool
  31: <std::thread::local::LocalKey<T>>::with
  32: <scoped_tls::ScopedKey<T>>::set
  33: syntax::with_globals

The error is related to the const in impl<const Val: u64> since removing that part causes other errors but doesn't crash.

but props to Rust for even considering this feature. I had no idea if it'd work but the syntax seemed natural, I went for it, and lo rustc said it existed :)

HadrienG2 commented 5 years ago

I'm not surprised that it is not working, as this hotly anticipated feature is still being plumbed through the compiler as we speak (see e.g. #59008 and #58581 and the earlier work on #53645 which was abandoned because the PR was too big, but still kept open as a tracker to announce progress).

However, I'm not sure if out-of-bounds slice accesses should be expected of the current implementation stubs. @varkor @yodaldevoid, can you have a look?

varkor commented 5 years ago

Yes, the warning is correct: const generics are not yet functional in any form. There are still a few more pull requests before they're ready to start getting played around with.

Coder-256 commented 5 years ago

Sorry if this is not the right place to ask questions but I couldn't find anywhere better. Just 2 questions:

  1. Can a single function be conditionally const? For example, a function could have 2 signatures like:

    const fn foo<A: const T>(x: T)  // `A` const implements `T`
    fn foo<A: T>(x: A)              // `A` implements `T` normally

    In this case, foo is const iff A: const T; if A does not const implement T, it still satisfies the bound but foo is no longer const. It is also important for the author to be able to specify any generic bound for more complex examples (ex. where A::Output : Bar). A great example of this is even simple arithmetic:

    // This only accepts types that const implement `T`
    const fn square_const<T: const Mul>(x: T) -> T {
      x*x
    }
    
    // This accepts types that implement `T` any way, but it is not const
    // This function behaves identically to `square_const`
    // But has a different signature and needs a different name
    fn square<T: Mul>(x: T) -> T {
      square_const(x)
    }
    
    let a: u8 = 5;
    let b: FooNumber = FooNumber::new();
    square_const(a); // `u8` const implements Mul
    square(b); // `FooNumber` implements `Mul` normally, so we need a separate function?

    I feel strongly that there should definitely be a way to do this, and I'm surprised that it is not mentioned in the RFC (unless I missed it?).

  2. [less important]: Will there be a way to detect in the body of a const function whether we are running at compile-time or run-time? I think that some macro similar to cfg!() would be a useful extension, if for no other reason than debugging. cfg!() is currently evaluated at compile-time already, so I think(/guess) that it should be able to know whether or not the function is being used as const or a regular function since that too is determined at compile-time. However this is less important than my 1st question.

rpjohnst commented 5 years ago

@Coder-256

  1. Yes, see https://github.com/rust-lang/rfcs/pull/2632.
  2. I am not sure whether that should even be possible, though given the above I'm not sure it's necessary either.
yodaldevoid commented 5 years ago

@Coder-256 Both of these questions are not related to const generics, but rather const functions. Const generics are for being generic over consts (e.g. foo<2>()) rather than functions being able to be run at compile-time. I imagine this is why you did not find the answers to your questions in RFC 2000.

Coder-256 commented 5 years ago

@rpjohnst Thank you, but I think I might have been unclear. I have already seen both rust-lang/rfcs#2632 and rust-lang/rfcs#2000, but I'm pretty sure that this is not mentioned in either. (but I could be wrong?) What I am asking about is conditionally const functions. See the examples I wrote since it is hard to describe.

@yodaldevoid Whoops you're right, where should I ask this?

As for the macro question, I agree that there isn't really much use for it now that I think about it

varkor commented 5 years ago

What I am asking about is conditionally const functions. See the examples I wrote since it is hard to describe.

Your square_const definition can be used in place of square (that is: it is coerced to a function with the equivalent signature at run-time). See https://github.com/rust-lang/rfcs/pull/2632 for discussion of this behaviour (also, that thread is the right place to ask questions about any interactions between const fn and trait bounds).

Coder-256 commented 5 years ago

@varkor I'm not convinced that that's the case (since the trait bounds change), but I'll ask in rust-lang/rfcs#2632.

Jezza commented 5 years ago

Crash Report:

Code:

#![feature(const_generics)]

use std::marker::PhantomData;

struct BoundedU32<const LOWER: u32, const UPPER: u32> {
    value: u32,
    _marker: PhantomData<(LOWER, UPPER)>,
}

Compiler:

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.35.0-nightly (719b0d984 2019-03-13) running on x86_64-unknown-linux-gnu

note: compiler flags: -C codegen-units=1 -C debuginfo=2 --crate-type lib

note: some of the compiler flags provided by cargo are hidden

error: Could not compile `playground`.

To learn more, run the command again with --verbose.
varkor commented 5 years ago

@Jezza: const generics isn't fully implemented yet and is not expected to work. We'll make an announcement when it's time to start experimenting with #![feature(const_generics)] (which you'll be notified about if you're subscribed to this issue).

eddyb commented 5 years ago

@varkor Hang on, @Jezza's example has _marker: PhantomData<(LOWER, UPPER)> - those are two const parameters used as types, why didn't rustc_resolve produce an error?

varkor commented 5 years ago

Good point: I'll investigate this. Note that this is only a problem with #![feature(const_generics)], so it's not a critical issue (using non-generic consts produces the error as expected). Maybe it never reaches rustc_resolve.

varkor commented 5 years ago

~@eddyb: this ICE is coming from resolve_ident_in_lexical_scope, so I imagine it's probably related to https://github.com/rust-lang/rust/issues/58307.~

Edit: actually, maybe not — that seems to apply just to macro_rules!.

petrochenkov commented 5 years ago

Minimized:

#![feature(const_generics)]

struct S<const C: u8>(C);

Resolve ICEs before producing the "expected type, found value" error.

The index is out-of-bounds here: https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919

newpavlov commented 5 years ago

Current nightly produces "parameter N is never used" for the following code:

struct Foo<const N: usize> { }

From the previous discussion I thought compile should accept this code. Is it simply an artifact of the unfinished implementation?

Ericson2314 commented 5 years ago

@newpavlov normal type parameters need to be used, should one be able to do PhantomData<n>?

Ekleog commented 5 years ago

I guess it's already possible to do PhantomData<[(); N]>. Not sure that's something we actually want to enforce, though, as AFAIU the point of PhantomData is to mark variance, and there's AFAIU no notion of variance wrt. a const generic parameter.

lachlansneff commented 5 years ago

And that only works when N is of type usize.

withoutboats commented 5 years ago

We did decide that it was not necessary to use const parameters during the RFC discussion and the current implementation is a bug.

yodaldevoid commented 5 years ago

@withoutboats Can you point out where in the RFC that is stated? I tried finding something to that effect and must have missed it.

withoutboats commented 5 years ago

i dont know if it was included in the RFC text

yodaldevoid commented 5 years ago

@withoutboats Would you mind pointing out where that was discussed? I trawled through the RFC PR, but it didn't jump out at me.

HadrienG2 commented 5 years ago

@yodaldevoid This comment was referred to earlier : https://github.com/rust-lang/rust/issues/44580#issuecomment-419576947 . But it was not in the RFC PR, rather on this issue.

withoutboats commented 5 years ago

I can't trawl through the comment history from several years ago now, but I can explain: using type variables is required as a blocker to make you think about variance of those parameters (IMO this is also unnecessary and we could default to covariant, but thats a separate issue). Const parameters do not have any interaction with variance, so this would have no motivation.

yodaldevoid commented 5 years ago

@HadrienG2 Thank you for finding a relevant comment.

@withoutboats I did not really expect you to go trawling through all of the years of comments for this, it was only a hope that you had it already in hand.

Thank you for the explanation. I must admit that I can never wrap my mind around variance no matter how many times I try to learn it, but even without that when thought on again not requiring the use of cost parameters makes sense. I will throw fixing this bug onto our list of FIXMEs.

varkor commented 5 years ago

Here's a summary of progress so far on const generics.


Before work on const generics could properly begin, there was some refactoring that needed to be done. @jplatte took on the task with https://github.com/rust-lang/rust/pull/45930. @jplatte then started working on the main implementation of const generics, but found they didn't have enough time to continue.

@yodaldevoid and I picked up where @jplatte left off, but quickly found that making progress was stymied by the way generic parameters were handled in general. Thus began a series of changes to revamp the way generics were handled throughout the codebase: https://github.com/rust-lang/rust/pull/48149, https://github.com/rust-lang/rust/pull/48452, https://github.com/rust-lang/rust/pull/48523, https://github.com/rust-lang/rust/pull/51880.

With that done, the implementation of const generics could begin in earnest. Since then, @yodaldevoid and I have slowly but surely been incrementally adding support for const generics: https://github.com/rust-lang/rust/pull/58191, https://github.com/rust-lang/rust/pull/58503, https://github.com/rust-lang/rust/pull/58581, https://github.com/rust-lang/rust/pull/58583, https://github.com/rust-lang/rust/pull/59170, https://github.com/rust-lang/rust/pull/59355, https://github.com/rust-lang/rust/pull/59415, https://github.com/rust-lang/rust/pull/60058, https://github.com/rust-lang/rust/pull/60280, https://github.com/rust-lang/rust/pull/60284 and most recently https://github.com/rust-lang/rust/pull/59008. (These have mostly been split out from the main const generics pull request.)


What's the status now? Some const generics tests now work 🎉However, there are still those that don't, and there are a number of FIXMEs throughout the code that we still have to address. We're getting close now, though, and we're at the stage where there's some low-hanging fruit if you want to help.

I've written an overview of some of the remaining implementation issues before const generics will be ready for proper testing, in the top post, to give a rough idea of what's left to do.

It's taken time, but we've steadily been making progress and hope to keep up the pace. It's motivating to finally see things starting to fall into place!

pvdrz commented 5 years ago

I've been expecting months for this post @varkor :) I'm no Rust wizard but I'd like to tackle one of the FIXMEs

c410-f3r commented 5 years ago

Congratulations @jplatte, @yodaldevoid and @varkor. This is a huge step to get rid of custom Array-like traits in math libs.

mark-i-m commented 5 years ago

Cross-posting...

@varkor Regarding tests, perhaps it would help to know what is expected to work. For example, I was surprised that this does not work: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=d84ffd15226fcffe02c102edb8ae5cf1

mark-i-m commented 5 years ago

Also, for reference of those interested in FIXMEs: https://oli-obk.github.io/fixmeh/