-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Inconsistent bound requirements on arrays with generic sizes #79778
Comments
Yes, this is working as intended |
Is this something likely to ever change, or is the behavior expected to stabilize like this? I'll have to discuss with the rest of the portable SIMD project group but this may preclude a const generic implementation. |
I'm still very much in the court of requiring bounds just like we require trait bounds on types before being able to use them beyond moving or otherwise directly forwarding them. This is definitely open for discussion, just like anything, but I'd really prefer it if we stabilized the current version first and thought about convenience extensions second. Ideally any convenience features would also apply to types. Note that you can create a const fn that does your math, thus requiring you to only bubble up |
Oh, also note that callers who supply concrete integers will not have to do anything, the bound will be evaluated once at the call site and either error or just work if the expression evaluates successfully. |
That's a good point about the
I do understand that it's a bound, but it's unfortunate because I do think this is a common pattern (I'm basically just trying to replicate C++'s
|
you do not actually need the
I think you would probably name your |
Okay, I think I misunderstood what that bound was representing. Perhaps it could be be possible to indicate that a bound is always valid? In my example above, |
I guess some kind of opt out of the system could be reasonable. I'm trying to come up with an example in the type system and trait bounds that have a similar situation. I guess it's when you do |
@oli-obk fn foo<const N: usize>(val: Val) -> [u8; bar(N)] where [u8; bar(N)]: Sized { .. } A trait analog of this function is: fn foo<N: Unsigned>(val: Val) -> Bar<N> { .. } We don't need the sized bound here because, contrary to the But if I am not mistaken, strictly speaking it's not guaranteed that So the only difference between traits and const expressions used in type construction is potential panics. In other words, I think that the mandatory sized bound effectively ensure that const computations used in type construction will not panic for every possible input. And as I've argued in the other discussion, I don't think it's worth to introduce mandatory manual bounds just for that. |
we do not care about these for constants either.
Ah, I do see how my post was actively misleading. I am not trying to have termination guarantees or similar here, but had something like bound checks failing in mind. Example: struct Bar<T: Cake>(T);
fn foo<N: Unsigned>(val: Val) -> Bar<N> { .. } Here we will fail to compile So if we go back to const generics, I think the equivalent example is const fn bar(n: usize) -> usize {
assert!(cake(n));
n
}
fn foo<const N: usize>(val: Val) -> [u8; bar(N)] where [u8; bar(N)]: Sized { .. } now, we don't have a way to specify that fn foo<const N: usize>() {
let x: [u8; N / 8] = ...;
} because that has no possible failure condition, but we still expect you to prove that In the type system the equivalent is fn foo<T: Mep>(t: T) {
let x: &Trait = &t;
} which is obviously ok if trait Trait {}
impl<T: Mep> Trait for T {} So... I think everyone is on board with
|
I understand and fully support the desire to promote approaches which result in a more reliable code. But we should carefully weight ergonomic costs and improvements which will be achieved in practice. I do not oppose the cautious approach of introducing a more conservative system first and then gradually relax it. I just argue about a general direction. I simply do not see a principal difference between const expressions used in constants and type: trait Foo<const N: usize> {
const FOO: usize = bar(N);
fn foo() -> [u8; bar(N)] where [u8; bar(N)]: Sized;
} The associated constant does not require the bound, even though it will cause the same compilation error in the case of a bad
Since we apparently agree that the main problem lies in possibility of panics in const expressions, I think a really great solution would have been a For such // `foo` will not panic for any `n`, for which `cake(n)` is true.
// In runtime code compiler will insert asserts before calling this function.
const nopanic fn foo(n: usize) where cake(n) { .. } But even without such system, it would allow to cover most simple cases. |
I'm working on integrating const generics into
stdsimd
. We want to support two types of bit masks. The first has an entire mask per lane, e.g.:The other type of mask only uses a single bit per lane, e.g.:
This doesn't work, however, and errors with
error: unconstrained generic constant
. After finding #68436, I was able to get it working with:This seems inconsistent since the two types are negligibly different. Also, since this type will be public, the bound bleeds into the interface which is undesirable.
The text was updated successfully, but these errors were encountered: