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

Tracking issue for const generics (RFC 2000) #44580

Open
withoutboats opened this Issue Sep 14, 2017 · 62 comments

Comments

@withoutboats
Contributor

withoutboats commented Sep 14, 2017

Tracking issue for rust-lang/rfcs#2000

cc @eddyb

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

This comment has been minimized.

Member

eddyb commented Sep 15, 2017

#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

This comment has been minimized.

Member

eddyb commented Sep 18, 2017

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

@eddyb eddyb added the E-mentor label Sep 18, 2017

@eddyb eddyb self-assigned this Sep 18, 2017

@jplatte

This comment has been minimized.

Contributor

jplatte commented Sep 18, 2017

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

@samsartor

This comment has been minimized.

samsartor commented Oct 26, 2017

@jplatte @eddyb Any news on this?

@jplatte

This comment has been minimized.

Contributor

jplatte commented Oct 26, 2017

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

@EdorianDark

This comment has been minimized.

Contributor

EdorianDark commented Nov 6, 2017

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

@jplatte

This comment has been minimized.

Contributor

jplatte commented Nov 6, 2017

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

@RobertWHurst

This comment has been minimized.

RobertWHurst commented Nov 6, 2017

Nice work so far @jplatte 🍻

@jplatte

This comment has been minimized.

Contributor

jplatte commented Nov 11, 2017

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

@huhlig

This comment has been minimized.

huhlig commented Nov 17, 2017

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

This comment has been minimized.

Contributor

Ixrec commented Nov 18, 2017

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?

@mark-i-m

This comment has been minimized.

Contributor

mark-i-m commented Aug 20, 2018

#51880 is done 🎉 🎉

@eddyb

This comment has been minimized.

Member

eddyb commented Aug 24, 2018

@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

This comment has been minimized.

Member

eddyb commented Aug 25, 2018

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

@mark-i-m

This comment has been minimized.

Contributor

mark-i-m commented Aug 27, 2018

#53645

mqudsi added a commit to mqudsi/rusqlite that referenced this issue Sep 5, 2018

Add impls for i128 16-byte integer db conversions
rust nightly has i128/u128 as 16-byte integer types, expected to land
into stable at some point in the (near?) future. SQLite's maximum
variable-length integer size is 8-bytes, so this needs to be serialized
into a blob for storage in the database.

Obviously endianness is a concern here; I've opted to use the platform's
native endianness rather than a hard-coded BE/LE choice. This is
contracted out to i128's `from_bytes`/`to_bytes`, which will take care
of the platform-specific endianness for us.

The usage of `unsafe` in the call to i128::from_bytes is to avoid a
potentially expensive extra allocation and looping over the contents of
the vector to copy them to a `[u8; 16]` intermediate. Once [RFC #2000](rust-lang/rfcs#2000) lands in nightly (tracked in [#44580](rust-lang/rust#44580)), [const generics](https://internals.rust-lang.org/t/lang-team-minutes-const-generics/5090) should make it possible to convert the `Vec<u8>` to a fixed-length `[u8; 16]` array in a nicer fashion.
@newpavlov

This comment has been minimized.

Contributor

newpavlov commented Sep 5, 2018

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],
}
@cuviper

This comment has been minimized.

Member

cuviper commented Sep 5, 2018

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

@Ekleog

This comment has been minimized.

Ekleog commented Sep 6, 2018

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.

@varkor

This comment has been minimized.

Member

varkor commented Sep 6, 2018

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

@Zauberklavier

This comment has been minimized.

Zauberklavier commented Sep 7, 2018

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

@GabrielMajeri

This comment has been minimized.

Contributor

GabrielMajeri commented Sep 7, 2018

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

There's a long way to go

@eddyb

This comment has been minimized.

Member

eddyb commented Sep 7, 2018

@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

This comment has been minimized.

Contributor

withoutboats commented Sep 7, 2018

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

@Ekleog

This comment has been minimized.

Ekleog commented Sep 9, 2018

@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

This comment has been minimized.

burdges commented Sep 21, 2018

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

@eddyb

This comment has been minimized.

Member

eddyb commented Sep 22, 2018

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

@rodrimati1992

This comment has been minimized.

rodrimati1992 commented Nov 2, 2018

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

This comment has been minimized.

Member

eddyb commented Nov 5, 2018

@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

This comment has been minimized.

rodrimati1992 commented Nov 5, 2018

@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

This comment has been minimized.

Member

eddyb commented Nov 5, 2018

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

@rodrimati1992

This comment has been minimized.

rodrimati1992 commented Nov 5, 2018

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

This comment has been minimized.

Contributor

Nemo157 commented Nov 5, 2018

@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

This comment has been minimized.

Member

eddyb commented Nov 5, 2018

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

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