rust-lang / rust-lang/rust-analyzer
`fn f((arg: (usize, bool)) {}` triggers `Only tuple has tuple field` error
Nobody has claimed this yet.
- 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)) {}tofn 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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