Skip to content

Nested coercion in array results in type mismatch, inconsistent with if and match. #145048

@adwinwhite

Description

@adwinwhite

I tried this code:

fn foo() {}
fn bar() {}
fn main() {
    let _ =  [foo, if false { bar } else { foo }]; 
}

I expected to see this happen: It compiles, like in if or match.

fn foo() {}
fn bar() {}
fn main() {
    let _ = if false {
        foo
    } else {
        if false {
            bar
        } else {
            foo
        }
    };
    let _ = match 1 {
        2 => foo,
        _ => if false {
            bar
        } else {
            foo
        },
    };
}

Instead, this happened: type mismatch when trying to coerce bar into foo.

The cause: check_expr_array uses a type variable as the expectation during element typecking but the type variable is unified with the first element's type during coercion.

let coerce_to = expected
.to_option(self)
.and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index())
.unwrap_or_else(|| self.next_ty_var(expr.span));
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
assert_eq!(self.diverges.get(), Diverges::Maybe);
for e in args {
let e_ty = self.check_expr_with_hint(e, coerce_to);
let cause = self.misc(e.span);
coerce.coerce(self, &cause, e, e_ty);
}

It should use try_structurally_resolve_and_adjust_for_branches on the element expectation as if and match do, which turns the expectation into NoExpectation if the expected_ty is a true type variable.
However, this simple fix can break some other code.
Recursive tuple coercion can fix this case. But it's complicated.

Meta

rustc --version --verbose:

1.91.0-nightly
(2025-08-06 7d82b83ed57d188ab3f2)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-coercionsArea: implicit and explicit `expr as Type` coercionsC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions