Skip to content

fix(compiler): infer static return type for uniform match expressions - #169

Open
prateekbhujel wants to merge 2 commits into
swoole:masterfrom
prateekbhujel:fix/match-expression-type-inference
Open

prateekbhujel wants to merge 2 commits into
swoole:masterfrom
prateekbhujel:fix/match-expression-type-inference

Conversation

@prateekbhujel

Copy link
Copy Markdown
Contributor

Summary

This PR adds static type inference for PHP match expressions when all non-throwing arms evaluate to the same uniform type (such as int, float, bool, string, etc.).

Problem

Previously, Expr\Match_ was absent from CompilerBase::detectTypeOfExpr(), defaulting all match expressions to Type::VAR (php::Var).
Even when every arm returned a known primitive (e.g. match ($x) { 1 => 10, 2 => 20, default => 30 } in a function returning : int):

  1. The emitted lambda returned php::Var, boxing every return value into a Zend Variant.
  2. A runtime return type check php::throwReturnTypeError() was emitted at the function exit.
  3. An unboxing call (php::toInt(), etc.) was required.
  4. Local variable assignments ($val = match (...)) forced the variable storage to php::Var.

Changes

  1. CompilerBase::detectTypeOfExpr():
    • Added case 'Expr_Match': aggregates the types of all non-throwing arm bodies.
    • If all non-throwing arms evaluate to a single unique type, that static type is returned.
    • Falls back to Type::VAR if types are mixed or dynamic.
  2. SelectionExpressionTrait::parseMatch():
    • Computes $returnType using detectTypeOfExpr() / getNormalAssignType().
    • Tags the AST node with ATTR_LOWERED_TYPE.
    • For non-default cases without a match, emits php::throwException("UnhandledMatchError", ...); return {}; for typed lambdas.
  3. SelectionExpressionTrait::formatMatchReturn():
    • Handles Expr\Throw_ arms without attempting to return php::throwException() as an unconvertible primitive value.
    • Allocates temporary variables matching $returnType when after-statements are present.
  4. Tests:
    • Added MatchExpressionCodegenTest and fixture match-expression-codegen.php verifying typed lambda signatures, avoidance of redundant runtime return type checks, throw arm handling, and local variable type inference.

Infer the static return type of match expressions when all non-throwing
arms evaluate to the same uniform type (e.g., int, float, bool, string).

Previously, Expr\Match_ was absent from CompilerBase::detectTypeOfExpr(),
causing all match expressions to default to Type::VAR (php::Var). This
forced redundant runtime return type checks (php::throwReturnTypeError),
unnecessary unboxing (php::toInt, etc.), and loose php::Var variable
allocation when returning or assigning match results.

This change:
1. Adds Expr_Match handling in detectTypeOfExpr() by collecting and
   unifying the types of all non-throwing arms.
2. In parseMatch(), sets the lambda return type to the inferred assign
   type and tags the expression with ATTR_LOWERED_TYPE.
3. In formatMatchReturn(), handles Expr\Throw_ arms cleanly and allocates
   temporaries matching the inferred return type.
4. Adds regression tests in MatchExpressionCodegenTest.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant