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

Compiler can't infer const parameter from infallible array pattern #70529

Closed
rodrimati1992 opened this issue Mar 29, 2020 · 6 comments · Fixed by #70562
Closed

Compiler can't infer const parameter from infallible array pattern #70529

rodrimati1992 opened this issue Mar 29, 2020 · 6 comments · Fixed by #70562
Assignees
Labels
A-const-generics Area: const generics (parameters and arguments) A-typesystem Area: The type system C-feature-request Category: A feature request, i.e: not implemented / a PR. F-const_generics `#![feature(const_generics)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@rodrimati1992
Copy link
Contributor

rodrimati1992 commented Mar 29, 2020

The compiler can't infer a const parameter based on the expected array type in an infallible pattern.

I tried this code:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4673d663e2f78cdc15395c1cc3701d67

#![feature(const_generics)]

struct ArrayChunks<'a,T,const N:usize>{
    rem: &'a [T],
}

impl<'a,T,const N:usize> Iterator for ArrayChunks<'a,T,N>{
    type Item=&'a [T;N];
    
    fn next(&mut self)->Option<&'a [T;N]>{
        if self.rem.len() < N {
            return None;
        }
        
        let (ret,rem)=self.rem.split_at(N);
        self.rem=rem;
        let ret=unsafe{ std::mem::transmute::<*const T,&[T;N]>(ret.as_ptr()) };
        Some(ret)
    }
}

fn as_chunks<T,const N:usize>(slice:&[T])-> ArrayChunks<'_,T,N> {
    ArrayChunks{rem:slice}
}

fn main(){
    for &[x,y] in as_chunks(&[0,1,2,3,4]) {
        println!("x:{:?} y:{:?}",x,y)
    }
}

I expected the code to compile and run without errors.

Instead, I got this compiler error:

error[E0730]: cannot pattern-match on an array without a fixed length
  --> src/main.rs:27:10
   |
27 |     for &[x,y] in as_chunks(&[0,1,2,3,4]) {
   |          ^^^^^

Meta

The compiler was 1.44.0-nightly (2020-03-28 7762131)

This issue has been assigned to @lcnr via this comment.

@rodrimati1992 rodrimati1992 added the C-bug Category: This is a bug. label Mar 29, 2020
@rodrimati1992 rodrimati1992 changed the title Compiler can't infer size const parameter from array infallible pattern Compiler can't infer const parameter from infallible array pattern Mar 29, 2020
@Centril
Copy link
Contributor

Centril commented Mar 29, 2020

Simplified:

#![feature(const_generics)]

fn as_chunks<const N: usize>() -> [u8; N] {
    loop {}
}

fn main() {
    let [x, y] = as_chunks();
}

@Centril Centril added A-typesystem Area: The type system C-feature-request Category: A feature request, i.e: not implemented / a PR. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. and removed C-bug Category: This is a bug. labels Mar 29, 2020
@Centril
Copy link
Contributor

Centril commented Mar 29, 2020

The relevant code is defined here:

  • ty::Array(inner_ty, len) => {
    let min = before.len() as u64 + after.len() as u64;
    let slice_ty = self
    .check_array_pat_len(span, slice, len, min)
    .map_or(err, |len| self.tcx.mk_array(inner_ty, len));
    (inner_ty, slice_ty, expected)
    }

  • fn check_array_pat_len(
    &self,
    span: Span,
    slice: Option<&'tcx Pat<'tcx>>,
    len: &ty::Const<'tcx>,
    min_len: u64,
    ) -> Option<u64> {
    if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) {
    // Now we know the length...
    if slice.is_none() {
    // ...and since there is no variable-length pattern,
    // we require an exact match between the number of elements
    // in the array pattern and as provided by the matched type.
    if min_len != len {
    self.error_scrutinee_inconsistent_length(span, min_len, len);
    }
    } else if let r @ Some(_) = len.checked_sub(min_len) {
    // The variable-length pattern was there,
    // so it has an array type with the remaining elements left as its size...
    return r;
    } else {
    // ...however, in this case, there were no remaining elements.
    // That is, the slice pattern requires more than the array type offers.
    self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len);
    }
    } else {
    // No idea what the length is, which happens if we have e.g.,
    // `let [a, b] = arr` where `arr: [T; N]` where `const N: usize`.
    self.error_scrutinee_unfixed_length(span);
    }
    None
    }

  • fn error_scrutinee_unfixed_length(&self, span: Span) {
    struct_span_err!(
    self.tcx.sess,
    span,
    E0730,
    "cannot pattern-match on an array without a fixed length",
    )
    .emit();
    }

@Centril Centril added A-const-generics Area: const generics (parameters and arguments) F-const_generics `#![feature(const_generics)]` labels Mar 29, 2020
@Centril
Copy link
Contributor

Centril commented Mar 29, 2020

To solve this, I suspect we'd need to register an obligation CheckArrayPatLen { has_slice: bool, min_len: u64 } which is the information required to check what check_array_pat_len does and then connect this to a const inference variable. Later on, we'll do the checks which were in check_array_pat_len when dealing with the obligation.

cc @varkor @eddyb

@eddyb
Copy link
Member

eddyb commented Mar 29, 2020

What if we just handle the case in which they have to be equal, for now? Because that needs no obligation, you can just unify the constant with the length.

And you'd get a proper mismatch error if the length is not an inference variable.

@Centril
Copy link
Contributor

Centril commented Mar 29, 2020

Yeah, scratch what I said. We can tweak the branch here:

        } else {
            // No idea what the length is, which happens if we have e.g.,
            // `let [a, b] = arr` where `arr: [T; N]` where `const N: usize`.
            self.error_scrutinee_unfixed_length(span);
        }

and as you say, unify the constant with the known length.

@lcnr
Copy link
Contributor

lcnr commented Mar 30, 2020

@rustbot claim

@rustbot rustbot self-assigned this Mar 30, 2020
Centril added a commit to Centril/rust that referenced this issue Mar 31, 2020
infer array len from pattern

closes rust-lang#70529

This still errors in the following case

```rust
#![feature(const_generics)]
fn arr<const N: usize>() -> [u8; N] {
    todo!()
}

fn main() {
    match arr() {
        [5, ..] => (),
        //~^ ERROR cannot pattern-match on an array without a fixed length
        [_, _] => (),
    }
}
```
Considering that this should be rare and is harder to implement I would merge this PR without *fixing* the above.
Centril added a commit to Centril/rust that referenced this issue Mar 31, 2020
infer array len from pattern

closes rust-lang#70529

This still errors in the following case

```rust
#![feature(const_generics)]
fn arr<const N: usize>() -> [u8; N] {
    todo!()
}

fn main() {
    match arr() {
        [5, ..] => (),
        //~^ ERROR cannot pattern-match on an array without a fixed length
        [_, _] => (),
    }
}
```
Considering that this should be rare and is harder to implement I would merge this PR without *fixing* the above.
@bors bors closed this as completed in 3ef70fe Mar 31, 2020
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) A-typesystem Area: The type system C-feature-request Category: A feature request, i.e: not implemented / a PR. F-const_generics `#![feature(const_generics)]` T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants