Skip to content

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

Closed

Description

@rodrimati1992

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.

Activity

  1. changed the title [-]Compiler can't infer size const parameter from array infallible pattern[/-] [+]Compiler can't infer const parameter from infallible array pattern[/+] on Mar 29, 2020
  2. Centril commented on Mar 29, 2020

    @Centril
    Contributor

    Simplified:

    #![feature(const_generics)]
    
    fn as_chunks<const N: usize>() -> [u8; N] {
        loop {}
    }
    
    fn main() {
        let [x, y] = as_chunks();
    }
  3. added
    C-feature-requestCategory: A feature request, i.e: not implemented / a PR.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    T-langRelevant to the language team
    and removed
    C-bugCategory: This is a bug.
    on Mar 29, 2020
  4. Centril commented on Mar 29, 2020

    @Centril
    Contributor

    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();
      }

  5. Centril commented on Mar 29, 2020

    @Centril
    Contributor

    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

  6. eddyb commented on Mar 29, 2020

    @eddyb
    Contributor

    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.

  7. Centril commented on Mar 29, 2020

    @Centril
    Contributor

    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.

  8. lcnr commented on Mar 30, 2020

    @lcnr
    Contributor

    @rustbot claim

  9. self-assigned this
    on Mar 30, 2020
  10. added 3 commits that reference this issue on Mar 31, 2020
    981118e
    48ce7c3
    3ef70fe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-const-genericsArea: const generics (parameters and arguments)A-type-systemArea: Type systemC-feature-requestCategory: A feature request, i.e: not implemented / a PR.F-const_generics`#![feature(const_generics)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions