rust-lang / rust-lang/rust-analyzer

`fn f((arg: (usize, bool)) {}` triggers `Only tuple has tuple field` error

Open
#18,339 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-mir A-ty C-bug
Dominant language
Rust
Stars
16.9k
Forks
2.2k
Avg merge
1d 12h
Merged PRs (30d)
72

Description

I've found a more minimal reproduction. Going from fn f(arg: (usize, bool)) {} to fn f((arg: (usize, bool)) {} is enough to crash rust-analyzer with "internal error: entered unreachable code: Only tuple has tuple field".

Originally posted by @davidbarsky in #15090

The parse tree for fn f((arg: (usize, bool)) {} is

FN@0..28
  FN_KW@0..2 "fn"
  WHITESPACE@2..3 " "
  NAME@3..4
    IDENT@3..4 "f"
  PARAM_LIST@4..25
    L_PAREN@4..5 "("
    PARAM@5..25
      TUPLE_PAT@5..25
        L_PAREN@5..6 "("
        IDENT_PAT@6..9
          NAME@6..9
            IDENT@6..9 "arg"
        ERROR@9..10
          COLON@9..10 ":"
        WHITESPACE@10..11 " "
        TUPLE_PAT@11..24
          L_PAREN@11..12 "("
          IDENT_PAT@12..17
            NAME@12..17
              IDENT@12..17 "usize"
          COMMA@17..18 ","
          WHITESPACE@18..19 " "
          IDENT_PAT@19..23
            NAME@19..23
              IDENT@19..23 "bool"
          R_PAREN@23..24 ")"
        R_PAREN@24..25 ")"
  WHITESPACE@25..26 " "
  BLOCK_EXPR@26..28
    STMT_LIST@26..28
      L_CURLY@26..27 "{"
      R_CURLY@27..28 "}"

so its basically parses as a function with a tuple pattern containing an ident pattern and another tuple pattern, missing the closing paren for the parameter list.

The HIR is

fn f((arg, (usize, bool)): {unknown}) -> () {}

which is expected.
The mir is

fn f() {
    let _0: ();
    let _1: {unknown};
    let arg_2: {unknown};
    let usize_3: {unknown};
    let bool_4: {unknown};
    
    
    'bb0: {
        StorageLive(arg_2)
        arg_2 = _1.0;
        StorageLive(usize_3)
        usize_3 = _1.1.0;
        StorageLive(bool_4)
        bool_4 = _1.1.1;
        Terminator { span: ExprId(Idx::<Expr>(0)), kind: Drop { place: Place { local: Idx::<Local>(4), projection: ProjectionId(0) }, target: Idx::<BasicBlock>(1), unwind: None } };
    }
    
    'bb1: {
        StorageDead(bool_4)
        Terminator { span: ExprId(Idx::<Expr>(0)), kind: Drop { place: Place { local: Idx::<Local>(3), projection: ProjectionId(0) }, target: Idx::<BasicBlock>(2), unwind: None } };
    }
    
    'bb2: {
        StorageDead(usize_3)
        Terminator { span: ExprId(Idx::<Expr>(0)), kind: Drop { place: Place { local: Idx::<Local>(2), projection: ProjectionId(0) }, target: Idx::<BasicBlock>(3), unwind: None } };
    }
    
    'bb3: {
        StorageDead(arg_2)
        Terminator { span: ExprId(Idx::<Expr>(0)), kind: Drop { place: Place { local: Idx::<Local>(1), projection: ProjectionId(0) }, target: Idx::<BasicBlock>(4), unwind: None } };
    }
    
    'bb4: {
        StorageDead(_1)
        Terminator { span: ExprId(Idx::<Expr>(0)), kind: Return };
    }
}

which is obviously bad, we shouldn't have a mir with unknowns in it in the first place!

And hence, as it turns out, just a fn f((a,)) {} also reproduces this (likely any non ident pattern in a param with a missing type. We probably just forget to check the function param pattern types if they contain unknowns and marking the type check result as tainted appropriately.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the minimal reproducer fn f((a,)) {} and compare its parser tree, HIR, and MIR with a valid parameter. Trace function-parameter pattern type checking and unknown-type propagation, focusing on the suspected missing taint handling. Done means rust-analyzer no longer panics and does not produce MIR containing unknown types; add a regression test for the reproducer.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.