Skip to content
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

generic_const_exprs does not work properly with const/type param defaults #106994

Open
10 tasks
BoxyUwU opened this issue Jan 17, 2023 · 5 comments
Open
10 tasks
Labels
A-const-generics Area: const generics (parameters and arguments) C-bug Category: This is a bug. F-generic_const_exprs `#![feature(generic_const_exprs)]` I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ requires-incomplete-features requires-nightly This issue requires a nightly compiler in some way. S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@BoxyUwU
Copy link
Member

BoxyUwU commented Jan 17, 2023

In order to make feature(generic_const_exprs) not completely broken when used with feature(const_generics_defaults), #86580 was landed. From the description of the PR:

This PR doesn't handle the predicates of the const so

trait Foo<const N: usize> { const Assoc: usize; }
pub struct Bar<const N: usize = { <()>::Assoc }> where (): Foo<N>;

Resolves to <() as Foo<N>>::Assoc which can allow for using fwd declared params indirectly.

trait Foo<const N: usize> {}
struct Bar<const N: usize = { 2 + 3 }> where (): Foo<N>;

This code also ICEs under this PR because instantiating the default's predicates causes an ICE as predicates_of contains predicates with fwd declared params

because of this these two features are still pretty incompatible with eachother. This issue tracks this as there have been a large volume of issues that are all about which makes looking through F-generic_const-exprs issues harder than it needs to be.

It was previously attempted to fix this in #106847 but that was closed:

I don't feel super comfortable merging more hacks to make generic_const_exprs + const_generics_defaults work in the presence of each other. #86580 alone was already rather hacky and I think this PR just makes it way too much to be reasonable. It's also unclear to me whether this is going to interfere with fixing some of the other issues we have with const generics and I don't want to make that any harder than necessary.

feature(generic_const_exprs) is an incomplete feature (both literally and according to the incomplete_features lint), having it in a broken state because we are not currently in a good position to properly fix this seems fine to me. Thanks for making the PR anyway but I expect this issue will stay open until we make a lot more progress on generic_const_exprs 😅

duplicate issues

When this is fixed the following should be revisited and checked to make sure everything works as intended:

@BoxyUwU BoxyUwU added S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. A-const-generics Area: const generics (parameters and arguments) F-generic_const_exprs `#![feature(generic_const_exprs)]` labels Jan 17, 2023
@BoxyUwU BoxyUwU added I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. C-bug Category: This is a bug. labels Jan 17, 2023
@peter-kehl
Copy link
Contributor

peter-kehl commented Jan 17, 2023

Thank you for identifying this. And for any progress (hopefully).
Weeks of my proof-of-concept work on co-operative allocation in library/alloc depend on this. (I've referenced the old/duplicate of this issue in my commits; I'll reference this issue from new commits and rebase.)

Minimizing this even more - even without any declared trait:

#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

struct V<const U: usize = {1+2}>
where
    [(); U]:;

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=971190f4162846600db09b2f4282f754

Update for ordinary Rust developers:
The above (ICE-triggering) example doesn't need feature generic_const_exprs - and without it, it does compile:

struct V<const U: usize = {1+2}>
where
    [(); U]:;

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=73bf77d07827c62dfe19ac9486e22983

So, if you need expressions in const generic defaults, but not in where bounds, there's a workaround:
Do not use generic_const_exprs. If you do need generic_const_exprs for other types, separate them into a separate crate, and make that generic_const_exprs-friendly crate consume/depend on the non-generic_const_exprs crate.

@peter-kehl
Copy link
Contributor

peter-kehl commented Feb 5, 2023

Hoping to help narrow this closer: This fails even if the const expression has no arithmetics, just type casting, and that type casting is in the struct's generic parameters' default value expression. The casting itself may not even be needed (in this example, casting usize to usize):

#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

struct FailingToCompile<const U: usize = 0usize as usize>
where
    [(); U]:;

Interestingly, if the const expression would normally require braces {...} (for example, because of invoking a macro), this ICE is triggered before checking for missing braces:

#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

macro_rules! bound_size {
    () => {1usize}
}

struct BracesAreNOTcheckedBeforeICE<const U: usize = bound_size!() as usize>
where [(); U]:;

But, if there's no casting/transformation in the const generic param's default value (expression), then there's no ICE, and the checks for braces do get run:

#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

macro_rules! bound_size {
    () => {1usize}
}

struct BracesAREcheckedIfNoICE<const U: usize = bound_size!()>
where [(); U]:;

If the same value is NOT cast in the generic params' list from a default value (or if it has no default value), but it's cast when instantiating the generic type, it DOES compile:

#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

struct Compiles<const U: usize = 0>
where [(); U]:;

type T = Compiles<{1i32 as usize}>;

fn main() {
    let _t: T = T {};
}

However, if the struct's generic parameter list does have a default value BUT that expression does NOT involve casting/arithmetics (or any other transformations?), but the (generic param value) is cast in the where bound itself instead, it DOES compile:

#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

struct Compiles<const I: i32 = 0>
where [(); I as usize]:;
{}

The result type of the const generic parameter's default value (expression) is NOT checked (to be the same as that const generic parameter's defined type) before the ICE happens:

#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

struct IncorrectTypeOfDefaultValueIsNOTcheckedBeforeICE<const U: usize = {1u8 + 0i32 as usize}>
where
    [(); U]:;

But, when there is no where bound, such an incorrectly typed of the const generic parameter's default value IS checked:

struct TypeOfDefaultValueIScheckedIfNObounds<const U: usize = {1u8 + 0i32 as usize}>;

@peter-kehl
Copy link
Contributor

peter-kehl commented Feb 5, 2023

What other combinations are worth checking (for someone not knowing deep rustc internals)? So that I can help this forward, please?

@BoxyUwU
Copy link
Member Author

BoxyUwU commented Feb 6, 2023

I do not believe there is anything worth checking, this issue is relatively well understood already. The problem is how to solve it, not figuring out what the cause is

@peter-kehl
Copy link
Contributor

Still, while I'm narrowing this down from the "consumer's" point of view, and searching for workarounds, here are 2 examples.

  1. ICE, but ONLY if this generic type is actually used (in a function signature, an expression...). Otherwise the type definition itself does compile.
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]

trait Tr {
    const C: usize = 0;
}

struct Str {}
impl Tr for Str {}

// ICE, but ONLY if this generic type is used (in a function signature, an expression...).
struct ICE<T: Tr = Str, const C: usize = {T::C}>
where [(); C]:,
{
    _t: core::marker::PhantomData<T>,
    _arr: [(); C],
}

// Using the above type in a function signature, or in an expression causes an ICE.

fn _type_in_fn_sig_return_causes_ice() -> ICE {
    loop{}
}
// and/or:

fn _type_in_fn_sig_param_causes_ice(_: &ICE) {
    loop{}
}
// and/or:

fn _instantiate() {
    ICE::<Str> {
        _t: core::marker::PhantomData {},
        _arr: []
    };
}
  1. ICE caused by mere presence of #![feature(generic_const_exprs)] itself. This code doesn't actually need generic_const_exprs. But, if the feature is enabled, then this code has an ICE.
#![allow(incomplete_features)]
// `struct ICE` below compiles well WITHOUT generic_const_exprs. But it does have an ICE with
// generic_const_exprs!
//
// The ICE is triggered even if the const generic-based struct itself (`ICE` below) is not used at
// all (not in any function signature, expression...). (And regardless of whether the type is
// public.)
#![feature(generic_const_exprs)]

trait Tr {
    const C: usize = 0;
}

struct Str {}
impl Tr for Str {}

struct ICE<const C: usize={Str::C}>
where [(); C]:,
{
    _arr: [(); C],
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-generics Area: const generics (parameters and arguments) C-bug Category: This is a bug. F-generic_const_exprs `#![feature(generic_const_exprs)]` I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ requires-incomplete-features requires-nightly This issue requires a nightly compiler in some way. S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
3 participants