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

Make <[T]>::array_* methods fail to compile on 0 len arrays #99471

Conversation

AngelicosPhosphoros
Copy link
Contributor

@AngelicosPhosphoros AngelicosPhosphoros commented Jul 19, 2022

Methods updated:

  • array_windows
  • array_chunks
  • array_chunks_mut
  • as_chunks
  • as_chunks_mut
  • as_chunks_unchecked
  • as_rchunks
  • as_rchunks_mut
  • as_chunks_unchecked_mut

I implemented this using compile time assertions.

Example compilation error:

> rustc +stage1 .\improper_array_windows.rs --crate-type=rlib
error[E0080]: evaluation of `core::slice::<impl [u32]>::array_windows::<0>::{constant#0}` failed
    --> J:\rust_lang\library\core\src\slice\mod.rs:1381:13
     |
1381 |             assert!(N != 0, "window size must be non-zero");
     |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'window size must be non-zero', J:\rust_lang\library\core\src\slice\mod.rs:1381:13
     |
     = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)

note: the above error was encountered while instantiating `fn core::slice::<impl [u32]>::array_windows::<0>`
 --> .\improper_array_windows.rs:4:14
  |
4 |     for _ in s.array_windows::<0>(){
  |              ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

I also added doctests and adjusted documentation.

Relates:
#74985
#75027

@rustbot rustbot added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Jul 19, 2022
@rustbot
Copy link
Collaborator

rustbot commented Jul 19, 2022

Hey! It looks like you've submitted a new PR for the library teams!

If this PR contains changes to any rust-lang/rust public library APIs then please comment with @rustbot label +T-libs-api -T-libs to tag it appropriately. If this PR contains changes to any unstable APIs please edit the PR description to add a link to the relevant API Change Proposal or create one if you haven't already. If you're unsure where your change falls no worries, just leave it as is and the reviewer will take a look and make a decision to forward on if necessary.

Examples of T-libs-api changes:

  • Stabilizing library features
  • Introducing insta-stable changes such as new implementations of existing stable traits on existing stable types
  • Introducing new or changing existing unstable library APIs (excluding permanently unstable features / features without a tracking issue)
  • Changing public documentation in ways that create new stability guarantees
  • Changing observable runtime behavior of library APIs

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 19, 2022
@AngelicosPhosphoros
Copy link
Contributor Author

There are also few other methods, would change them too.

@lcnr
Copy link
Contributor

lcnr commented Jul 19, 2022

while it results in a better user experience than runtime panic, this solution has the issue that it only panics during monomorphization, so check builds compile successfully, even for N == 0.

This also means that fn foo<const N: usize>() { let _ = [1, 2, 3, 4].array_chunks::<N>(); } compiles. If we then later change the N == 0 bound to an actual where-clause on array_chunks, foo will stop compiling.

So this doesn't allow us to stabilize these methods as is and add a 0 check as a where clause later.

@rustbot label +T-libs-api -T-libs

@rustbot rustbot added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jul 19, 2022
@lcnr
Copy link
Contributor

lcnr commented Jul 19, 2022

r? rust-lang/libs

@rust-highfive rust-highfive assigned joshtriplett and unassigned lcnr Jul 19, 2022
@AngelicosPhosphoros AngelicosPhosphoros force-pushed the make_compile_error_on_array_chunks_and_windows branch from 1561b70 to 26e0a2d Compare July 19, 2022 15:40
@AngelicosPhosphoros
Copy link
Contributor Author

AngelicosPhosphoros commented Jul 19, 2022

while it results in a better user experience than runtime panic, this solution has the issue that it only panics during monomorphization, so check builds compile successfully, even for N == 0.

Ooops, really rustc +stage1 .\improper_array_windows.rs --crate-type=rlib --emit=metadata -Z no-codegen doesn't complain.
However, this would prevent nightly users from writing some code that we would broke later by adding bound.

@AngelicosPhosphoros
Copy link
Contributor Author

Added this check to more methods.

@AngelicosPhosphoros
Copy link
Contributor Author

Also relevant: #99682

@AngelicosPhosphoros
Copy link
Contributor Author

@joshtriplett

Hi! Do I need to do something with this PR?

@bors
Copy link
Contributor

bors commented Sep 5, 2022

☔ The latest upstream changes (presumably #100759) made this pull request unmergeable. Please resolve the merge conflicts.

@nbdd0121
Copy link
Contributor

nbdd0121 commented Sep 9, 2022

Might just do const { assert!(N != 0); }?

@the8472
Copy link
Member

the8472 commented Mar 20, 2023

As other reviewers already mentioned, asserts in inline const blocks work now and should be used.

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 20, 2023
@AngelicosPhosphoros AngelicosPhosphoros force-pushed the make_compile_error_on_array_chunks_and_windows branch 3 times, most recently from 1c393d3 to 40a75ef Compare May 2, 2023 15:08
@AngelicosPhosphoros
Copy link
Contributor Author

@joshtriplett @the8472
Sorry for delay, I was forced to leave my country and had been some time without suitable equipment to work.

I switched to inline const blocks as requested.

@AngelicosPhosphoros
Copy link
Contributor Author

@rustbot label -S-waiting-on-author +S-waiting-on-review

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 2, 2023
@AngelicosPhosphoros
Copy link
Contributor Author

@scottmcm Old docs for the methods mentioned that in future any instantiations with N == 0 would stop compile. My assertions ensure that we wouldn't broke someones code later when we add premonomorphisation check.

Also, we can always remove checks later if we wish because it would not be breaking changes.

@nbdd0121
Copy link
Contributor

Ideally this would be a where N > 0 bound. But the compile time assert is not a proper substitute for that because bound can be infectious while the assert only fires for concrete instantiations.

Where bound would stop you writing if N != 0 { foo.as_chunks::<N>() } else { ... } at all.

@the8472
Copy link
Member

the8472 commented May 12, 2023

Ideally this would be a where N > 0 bound. But the compile time assert is not a proper substitute for that because bound can be infectious while the assert only fires for concrete instantiations.

Where bound would stop you writing if N != 0 { foo.as_chunks::<N>() } else { ... } at all.

You can then have a separate implementation for N = 0. Assuming the trait solver can then figure out that for each N there exists exactly one implementation.

@CAD97
Copy link
Contributor

CAD97 commented May 13, 2023

The full-fat solution to working with generic instantiation errors is some sort of static if (C++ calls it "constexpr if"), e.g.

static if N != 0 {
    foo.as_chunks::<N>() // ...
} else {
    // ...
}

where each arm of the static if is only instantiated if the controlling condition (which is constant evaluated) says to take that arm.

With arbitrary requirements and conditions, we probably can't prevent instantiation errors (post-mono errors), but we could probably utilize pattern restricted types to effect here (i.e. instead of N: usize, N: usize is 1..). (Though that might have the additional effect of delaying these APIs by perhaps a decade or more...)

@bors
Copy link
Contributor

bors commented May 20, 2023

☔ The latest upstream changes (presumably #111453) made this pull request unmergeable. Please resolve the merge conflicts.

Methods updated:
* `array_windows`
* `array_chunks`
* `array_chunks_mut`
* `as_chunks`
* `as_chunks_mut`
* `as_chunks_unchecked`
* `as_rchunks`
* `as_rchunks_mut`
* `as_chunks_unchecked_mut`

I implemented this using compile time assertions.

Example compilation error:

```
> rustc +stage1 .\improper_array_windows.rs --crate-type=rlib
error[E0080]: evaluation of `core::slice::<impl [u32]>::array_windows::<0>::{constant#0}` failed
    --> J:\rust_lang\library\core\src\slice\mod.rs:1381:13
     |
1381 |             assert!(N != 0, "window size must be non-zero");
     |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'window size must be non-zero', J:\rust_lang\library\core\src\slice\mod.rs:1381:13
     |
     = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)

note: the above error was encountered while instantiating `fn core::slice::<impl [u32]>::array_windows::<0>`
 --> .\improper_array_windows.rs:4:14
  |
4 |     for _ in s.array_windows::<0>(){
  |              ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error
```

I also added doctests and adjusted documentation.

Relates:
rust-lang#74985
rust-lang#75027
@AngelicosPhosphoros AngelicosPhosphoros force-pushed the make_compile_error_on_array_chunks_and_windows branch from 40a75ef to 7bb2c00 Compare May 20, 2023 11:43
@AngelicosPhosphoros
Copy link
Contributor Author

I would like to merge this PR already because it just gather merge conflicts as it stays.

So I like to address all concerns in a single place:

  1. Compilation error happens only during monomorphization (e.g. cargo build vs cargo check). True, but compile error during monomorphization is still better than runtime panic.
  2. If we decide later to support case which @scottmcm brought up (if N != 0 { my_slice.array_xxx::<N>() } else { do something else }), we can always relax this check later. Removing const from assertions will not break code with N != 0. And const assert added now would prevent breaking users code later if we decide that we don't support this usecase.
  3. About allowing N==0 and making infinite loop from it. I am against this proposition for 2 reasons:
    • It is just confusing. Infinite loops are uncommon thing so they should be made only using loop {}. Making iteration over chunks infinite loop would be surprising for programmer almost always.
    • It contradicts existing behaviour of windows(n) and chunks(n) methods so should not be allowed for sake of consistency.

TLDR: We cannot broke any valid code by adding this, we can always remove this later and it would prevent breaking more code if we keep this behaviour.

@joshtriplett
Copy link
Member

I don't feel comfortable unilaterally approving this.

cc @rust-lang/libs-api

@dtolnay dtolnay assigned dtolnay and unassigned joshtriplett May 20, 2023
@BurntSushi
Copy link
Member

Let's FCP this perhaps?

@rfcbot fcp merge

@rfcbot
Copy link

rfcbot commented May 20, 2023

Team member @BurntSushi has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels May 20, 2023
@BurntSushi
Copy link
Member

BurntSushi commented May 20, 2023

@rfcbot concern monomorphization errors

True, but compile error during monomorphization is still better than runtime panic.

I don't agree generally.

I don't think we should be litigating whether we want to start introducing more monomorphization errors in this PR. It should probably be done as a separate decision, albeit I'm not sure by what process and by what team.

@dtolnay
Copy link
Member

dtolnay commented May 20, 2023

I agree with #99471 (comment) and I would strongly prefer not to accept this PR. The following is fine and correct code; we do not need to make it fail to compile.

fn f<const N: usize>() {
    if N == 0 {
        ...
    } else {
        foo.as_chunks::<N>()...
    }
}

fn main() {
    f::<0>();
}

@BurntSushi
Copy link
Member

Given @dtolnay's comment above, the FCP seems unnecessary.

@rfcbot fcp cancel

@rfcbot
Copy link

rfcbot commented May 20, 2023

@BurntSushi proposal cancelled.

@rfcbot rfcbot removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels May 20, 2023
@the8472
Copy link
Member

the8472 commented May 20, 2023

I'd like fn as_chunks::<N>(&self) where N > 0. But this would require constructs such as the static if mentioned in this comment. As long as we don't have the tools to make that usable compile-time enforcement is more of a hindrance.

@AngelicosPhosphoros
Copy link
Contributor Author

True, but compile error during monomorphization is still better than runtime panic.

I don't agree generally.

Maybe allow such compile errors for unstable functions/traits/structs until we implement bounds on constant generics?

@dtolnay dtolnay closed this May 20, 2023
Copy link
Member

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks anyway for the PR! This was worth exploring.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.