rust-lang/rust

Tracking issue for const generics (RFC 2000)

withoutboats opened this issue Β· 206 comments

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:

  • Design:
    • Resolving ordering of const and type parameters, with default parameters
    • Decide what the best UX / implementation cost balance is for unifying abstract const expressions.
    • How we determine well formedness of const expressions.
  • Implementation
  • Documentation
    • rustc guide

Remaining implementation issues:

  • Resolve various FIXME(const_generics) comments.
  • Resolve concerns with canonicalisation / lazy normalisation.
  • Investigate handling of const parameters in patterns.
  • Add more tests.
  • Implement defaults for const parameters (FIXME(const_generics_defaults)).
  • Fix other A-const-generics issues.
  • Audit uses of has_infer_types.
  • Forbid complex expressions for const arguments involving parameters (for now), e.g. {X * 2}.
  • Audit diagnostics (e.g. #76401 (comment)).
eddyb commented

#44275 added a ConstEvaluatable predicate, and WF([T; expr]) now requires ConstEvaluatable(expr), as expr is lazily evaluated (even if it's just an integer literal). Fulfilling this predicate requires the expression to evaluate successfully, while normalization ignores the error and simply leaves the Unevaluated expression it found, untouched, which is more or less what happens with associated type projections. I expect the same system to scale to const generics.

@EpicatSupercell has expressed interest in working on this, I'll mentor them through the initial implementation. However, we can't go too far because of the limitations described in #44275.

That is, we need @nikomatsakis' lazy normalization to allow constant expressions embedded in types to observe the bounds in scope (from the function / type definition / impl / etc. item they're in), without producing cyclic dependencies half the time.

eddyb commented

Implementation waypoints (for more direct mentoring, seek @eddyb on Gitter or eddyb on IRC):

  • Declaration / Syntax:
  • Declaration / Semantics
    • Data structures: ty::Generics - add const parameters alongside type ones
    • Conversion from HIR: generics_of - create ty::ConstParameterDef from hir::ConstParam
  • Use / Name Resolution
    • Data structures: Def - add a variant for const parameters
    • Name resolution pass: with_type_parameter_rib - support both type and const generics
  • Use / Semantics
    • ConstVal - add Param variant akin to ty::TyParam
    • Conversion from HIR: TyArray with a length that's an ExprPath that resolved to Def::ConstParam should use ConstVal::Param instead of ConstVal::Unevaluated - in a similar fashion to how Def::TyParam turns into ty::TyParam
    • subst::Kind - support &ty::Const and check as_const as well in where places where as_type and as_region are checked
  • Inference

Note that all of this should allow impl<T, const N: usize> Trait for [T; N] {...}, but not actually passing a constant expression to a type/function, e.g. ArrayVec<T, 3>.

I'd like to take a stab at this :)

@jplatte @eddyb Any news on this?

@samsartor there was a refactoring that had to be done before the main implementation work. That is now almost done (I'm waiting for feedback currently). I don't actually know how much work there is after that. Parsing const params was what I started with initially, before the refactoring. It's not done but I should be able to get that done relatively soon.

@jplatte It would be nice, if you could link the pull request. I was not able to find it.

There is no PR yet. All my work can be found here.

Nice work so far @jplatte 🍻

There is now a PR for the groundwork (Generics refactoring): #45930

I think this is already included but it would be good to handle the C++ style folding for handling linear algebra style n dimensional arrays with varying lengths, I.E. a 4x4 [[f64;4];4]or a 4x3x5x6 dimensional array [[[[f64;6];5];3];4] and be able to properly wrap and generate specialized methods for both it AND proper trait implementations for scalars properly dimensioned vectors, etc. See https://gist.github.com/huhlig/8b21850b54a75254be4b093551f8c2cb for a rudamentary example.

Ixrec commented

I don't recall anyone proposing fold expressions for Rust before, much less as part of this RFC. But since this is Rust, is there any reason we couldn't implement fold expressions as an ordinary macro, rather than new dedicated syntax?

What are the next steps now that #45930 is merged? @jplatte

@kjaleshire The next step is to implement parsing of const generics (see @eddyb's comment further up). I've already started work on this before it became clear that the refactoring in the refactoring would be necessary. My existing work on that can be found here; it hasn't yet been rebased since the refactoring PR was merged though.

@jplatte I believe you meant to mention @kjetilkjeka.

Thanks for the update! I'm sure I'm far from the only one looking forward to this feature. Keep up the good work!

est31 commented

@jplatte not wanting to be impatient, but has there been any work on this? Do you need help? Should someone help out?

@est31 Sorry. Haven't had time to work on this in a while, and I'm not entirely sure when I will have the time. Maybe it's best for someone else to continue where I left off. I think the parsing part is mostly done. There are two places in the code where I wasn't sure what to do in the case of a generic param being a const param:

There also aren't any tests for the parsing of const generics yet (and for the pretty-printing code that I updated at the same time). Not sure what other information I can provide that would be needed / helpful for someone else to continue working on this, but feel free to ping me if something is unclear about my code.

eddyb commented

@jplatte ~None in the first and nothing in the second~~ (cc @pnkfelix @nikomatsakis)

EDIT: nothing to do in either case.

@eddyb For the first linked snippet are you sure that None should be returned? I may not understand what is going on here, but it seems to me that nothing should be done. If None is returned other params that might be unsafe will be skipped.

eddyb commented

@yodaldevoid Sorry, you're right, I didn't realize this was on Generics.

I would like to pick this up where @jplatte left off. I've been working away at this for a couple days now, going through @eddyb's mentoring instructions as I've had time just to see if I could get anywhere. I've gotten down to the "Use / Semantics" section at this point. I'll probably swing by one of the chats soon enough to ask questions.

@yodaldevoid Great to hear. I don't know about the Gitter, but you'll definitely get plenty of guidance on #rustc and/or #rust-internals on IRC!

@yodaldevoid: would you be up for some collaboration? I've also been doing some investigation into this issue (πŸ˜…) β€” perhaps we could fill in the gaps in each other's work. (You can see what I've done so far here.) Maybe we could chat on IRC (#rustc or #rust-internals) about it?

@varkor It looks like you are further ahead than I got. I would certainly be willing to collaborate. I'll try to grab you on IRC at some point and in the mean time see if I've gotten anything done that you haven't already.

Is there any progress in this?
I'm writing code for embedded, and I really need const generics.

@qwerty19106
Progress is being made on this, though slowly. @varkor and I have been working on this off and on as we had time. I made some progress this week and we are seeing the light at the end of the tunnel for basic usage.

Beyond just implementing const generics, we (varkor) have done some cleanup to make this all possible (see #48149 and #48523). I believe the current plan is to wait for those two pull requests to go through before pulling in const generics, but varkor can speak more to that.

I really understand your need for const generics for embedded work. I started on this because I too really want const generics so I can clean up and make type safe large swathes of embedded code.

TL;DR: Progress is going, but this is complex. I feel you on the embedded front.

For the "documentation" checkbox, it would be great to get something in the rustc guide.

Thanks @yodaldevoid for your reply. I will look forward for the end of your work.

Update for those curious (since I was also curious). Re: the two PRs mentioned above: #48523 has merged and #48149 is steadily making progress.

@mark-i-m Good stuff! Nice work by @varkor. When's ETA roughly, do you know? :-)

Cheers, @flip111.

nikic commented

Looks like the second big refactoring PR #48149 got merged :)

/cc me

est31 commented

Next @varkor PR #51880

Just wanted to give a quick update, as I know some people have been asking about the progress with const generics.

To give some context, in March , @yodaldevoid and I had an initial implementation that was almost working (the bulk of the implementation seemed to be done, and we were just cleaning up some remaining crashes). However, the branch we were working on was pre-miri. When miri was merged (and in a number of subsequent PRs), the code for dealing with constant evaluation changed significantly, meaning a lot of what we had done became outdated.

On top of that, it was decided that the generic parameters code needed to be cleaned up in general before tacking const generics on, both to improve readability, but also to make mistakes where we forgot to deal with const generics in a certain case harder to make. This was done in #48523, #48149 and will be completed in #51880. These were a little more involved than I initially expected, and they've taken a little longer to push through than estimated.

In the meantime, @yodaldevoid and I have been working on making our original implementation compatible with all the subsequent changes in rustc. It's taken a while, but we're getting there (though there's the perennial problem of never having as much time as you expected). I hope we'll have some good news soon on that front. (Meanwhile, https://github.com/rust-lang-nursery/chalk has been making good progress, which should address some of the difficulties @eddyb originally described.)


Some people have asked how they can help: I think at this stage, it's going to be easier for us to try to finish off the initial implementation, and then see which parts need attention. Once that's ready, we're going to need a lot of tests, utilising const generics in different places (including invalid ones, for error messages), so that's definitely somewhere we could do with a lot of help! We'll let you know when that happens!

Sorry if it's not the appropriate place for that, but I have a suggestion regarding equality of abstract const expressions. In general it directly reduces to full dependent typing and undecidable territory. However, in rust everything is eventually instantiated with concrete values/types, so we can assert some equalities and postpone their checking to monomorphic instances.

What I mean is that a relatively simple way of checking the equality of abstract const expressions is the following:

  • automatically deal with syntactic equality (ie. N+1 == N+1 should work out of the box)
  • allow the user, at definition time, to add equations such as N+M == M+N (maybe in the where clause?). These equations can be used by the equality check (using some form of congruence closure). A definition that doesn't type check using these provided equations is rejected.
  • at a monomorphic expansion point, all the equations can be checked by actually computing the const exprs, which are not abstract anymore. Here there's a design choice: if an equation that reduces to false, either there could be a compilation error ("equations are axioms") or the trait cannot be instantiated ("equations are constraints")
  • at a parametrized expansion point, these equations are carried over: if you have a function f parametrized by N where N+0==N, this equation must not be lost in a caller g since it'll need to be checked at each monomorphic place where g is called.

The pros of this method is that there isn't a need for a theorem prover, SMT solver, or arithmetic rewriter in rustc itself. "Only" a syntactic equality check of types, modulo aliases and modulo a set of equations provided by the user.
The cons is that it's more manual for the user since seemingly obvious equalities like M+N=N+M have to be added explicitly.

Example:

/// this works directly because of syntactic checks
fn add_end<T:Copy, const N: usize>(a: &[T;N], b: T) -> [T;N+1] {
  let x : [T;N+1] = [b;N+1];
  x[0..N] = a;
  x
}

/// this should work already
fn append<T:Copy, const M:usize, const N:usize>(a: &[T;M], b: &[T;N])
  -> [T;{M+N}]
{ … }

/// Here the equation M+M==N must be carried over or checked whenever this function is used
fn sum_pairwise_append<const M: usize, const N: usize>(a: &[i32, M],b: &[i32;N])-> [T;N]
  where M+M == N {
  let mut res : [i32; N] = append(a,a);
  for i in 0 .. N { res[i] += b[i] };
  res
} 


fn main() {
  let a: [i32; 2] = [1;2];
  let b: [i32; 4] = [2;4];
  let _ = sum_pairwise_append(a, b);  // need to check 2+2=4 
}

Equations as axioms

This means that e1 == e2 should always be true, so it's not really a where constraint but more like a assert_eq!(e1,e2) that can be used by the type checker. Here the implementor makes a promise that this is always true, and exposes her users to compilation errors if they provide parameter that refute the equation.

Equations as constraints

Here a clause where e1 == e2 is a constraint that must be satisfied for the successful use of this parametrized trait/function. That means that e1 == e2 doesn't have to always hold, which may be interesting for equations only true over a domain, such as (x*y) / z == (y*x) / z which would fail to instantiate for z==0.

eddyb commented

@c-cube We already have a system for "implicit where clauses", at least for functions, derived from "WF (well-formedness) rules", i.e. if you define a fn foo<'a, 'b>(x: &'a &'b i32) {} then it requires callers to satisfy 'b: 'a (as a result of needing WF(&'a &'b i32) -> &'b i32: 'a -> 'b: 'a).

We can reuse that system (in fact this is already implemented) to push constraints given by the signature (of the form "this constant expression evaluates successfully") up to the callers, whereas anything used only inside the body would still need where clauses.

Most of everything else you're describing seems close to what's planned (including a form of "syntactic equality"), but we don't want "monomorphization-time" checks, we can instead have ways (either implicit or through where clauses) to "propagate constraints to instantiators", and then we produce errors where constraints are "still too generic" (so unknown if they hold), or otherwise if we can evaluate them and they don't happen to hold.

Most of everything else you're describing seems close to what's planned

@eddyb do you know any reference posts that discusses these plans?

eddyb commented

@flip111 Probably some of the RFCs. Maybe @withoutboats knows better.

#51880 is done πŸŽ‰ πŸŽ‰

eddyb commented

@withoutboats @oli-obk What do you think about blocking proper unification of expressions like N + 1 (from e.g. [T; N + 1]) on getting some basic form of symbolic evaluation in miri?

I think we'd already have a mechanism to encode it, when @varkor adds ConstValue::{Infer,Param}, we can use that to track "(shallowly) valid but unknown" (symbolic) values, and then also include value (mainly integers) operations and calls on top of that.

Any non-assert control-flow decisions would still require known values, but assert itself could perhaps be reified as AssertThen(condition, assert message, success value).
(Both the condition and the success value could be symbolic!)

Being able to export the symbolic values into ty::Const means we can implement some of the unification outside miri, treating (most?) operations as opaque.

We have to be careful not to assume anything of inference variables, but for e.g. N + 1 I think we can support unifying two Add(Param(N), Bits(1)) together.

eddyb commented

@varkor A test-case I think should work with just lazy normalization, no unification:

/*const*/ fn append<const N: usize, T>(xs: [T; N - 1], x: T) -> [T; N] {
    let new = MaybeUninit::<[T; N]>::uninitialized();
    unsafe {
        let p = new.as_mut_ptr() as *mut T;
        (p as *mut _).write(xs);
        p.add(N - 1).write(x);
    }
    new.into_inner()
}

That would only work because:

  • N - 1 shows up at the type level exactly once
    • everything can refer to that expression, opaquely
    • (potential workaround: type ArrayWithoutLast<T, const N: usize> = [T; N - 1];)
  • it's in an argument position within the signature
    • (can't remember if return position would also work. @nikomatsakis?)
    • callers get to prove it's WF, by providing concrete values for N
    • "bubbling up" the WF requirement needs unification / ArrayWithoutLast
    • other uses would need "embedding in a where clause" e.g. [T; N - 1]: Sized
      • can even abuse where [T; N - 1]: (does that get ignored today? ouch!)
      • again bypass unification with where ArrayWithoutLast<T, N>: ...

So overall, we can probably rely on WF rules to force "validation" of const expressions onto the users, but we'd still want unification for other things (including not needing hacks like ArrayWithoutLast).

Small question which originates from discussion of Units of Measure. What should we do if type generic over constant does not directly depend on it? For example:

struct Foo<T, const N: usize> {
    a: T,
    // Will such array be "the way" to handle this problem?
    // Or do we want some kind of `std:marker::PhantomConstData`? (which can be a simple
    // type alias to the same array)
    // Or maybe make `PhantomData` to accept constants?
    _phantom: [(), N],
}

_phantom: [(), N],

I assume you meant [(); N], but this will only work for usize.

I think it would make sense to have a special marker::PhantomConst allowing any const type.

Hmm… Re-reading the RFC with the latest comments in mind, I'm wondering: would something like this be allowed?

struct Foo<T, const V: T> {
    _phantom: PhantomConst<T, V>,
}

I can't see it being banned anywhere, but would have thought it'd deserve at least an example.

@Ekleog: as far as I'm aware, const parameter types may not depend on type parameters initially (though it would definitely make sense for an extension). (The implementation is trickier if we allow this, so it makes sense to start with the simplest version.)

How is the progress on this? Do we know an approximate time when this should hit nightly?

@Zauberklavier As soon as this pull request is done. To quote from it:

There's a long way to go

eddyb commented

@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.

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

@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.

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

eddyb commented

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

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:
rust-lang/rfcs#2581 (comment)

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

eddyb commented

@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 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

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

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.

@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

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

What's the status of this?

Next step is still #53645 AFAIK.

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.

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.

@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.

@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.

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.

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 :)

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?

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.

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.

@Coder-256

  1. Yes, see rust-lang/rfcs#2632.
  2. I am not sure whether that should even be possible, though given the above I'm not sure it's necessary either.

@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.

@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

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 rust-lang/rfcs#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).

@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

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.

@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

@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?

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.

@eddyb: this ICE is coming from resolve_ident_in_lexical_scope, so I imagine it's probably related to #58307.

Edit: actually, maybe not β€” that seems to apply just to macro_rules!.

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

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?

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

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.

And that only works when N is of type usize.

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

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

i dont know if it was included in the RFC text

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

@yodaldevoid This comment was referred to earlier : #44580 (comment) . But it was not in the RFC PR, rather on this issue.

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.

@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.

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 #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: #48149, #48452, #48523, #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: #58191, #58503, #58581, #58583, #59170, #59355, #59415, #60058, #60280, #60284 and most recently #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.

  • First, we need more tests. There are only a handful of const generics tests so far, but we'd like many more. At the moment, because we're aware of a number of issues, we don't need bug reports, but we do need passing tests. If you come up with a test case that works (and doesn't look too similar to an existing test), feel free to open a pull request to add it.
  • Secondly, as mentioned, there are a number of FIXME(const_generics) scattered throughout the code. We're planning on working our way through them, but @yodaldevoid and I only have so much time, so if you think you can tackle one, go ahead (though you might like to leave a comment saying as much, so we don't duplicate efforts).

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

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

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

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

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