Skip to content

fix(given/when): implicit break and skip smartmatch on boolean exprs#589

Merged
fglock merged 1 commit intomasterfrom
fix/given-when-implicit-break
Apr 28, 2026
Merged

fix(given/when): implicit break and skip smartmatch on boolean exprs#589
fglock merged 1 commit intomasterfrom
fix/given-when-implicit-break

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Apr 28, 2026

Summary

Fix two bugs in PerlOnJava's given/when transformation that caused DateTime::Calendar::Discordian to fail 21/73 subtests in t/cardinals.t under jcpan -t (e.g. 1st came out as 1stth).

The failing test pattern:

given ($i) {
    when ($i % 10 == 1 && $i != 11) { $cardinal .= 'st' }
    when ($i % 10 == 2 && $i != 12) { $cardinal .= 'nd' }
    when ($i % 10 == 3 && $i != 13) { $cardinal .= 'rd' }
    default                          { $cardinal .= 'th' }
}

Bug 1 — missing implicit last

Per perlsyn, after a when block runs, control implicitly breaks out of the enclosing given. PerlOnJava just expanded when (COND) { BLOCK } into if ($_ ~~ COND) { BLOCK }, so every when (and the default) ran in turn, producing things like 1stth.

Fix: append a synthetic last; to every parsed when-block and mark the synthesized given BlockNode as isLoop = true so that last exits the given instead of escaping to an outer loop / the program.

Bug 2 — when always smart-matched against $_

Per perlsyn, when(EXPR) does not smart-match against $_ when EXPR is one of:

  • a comparison: ==, !=, <, >, <=, >=, <=>, eq, ne, lt, gt, le, ge, cmp
  • a boolean combinator: &&, ||, //, and, or, xor
  • a regex bind: =~, !~
  • a negation: !, not
  • defined / exists

PerlOnJava unconditionally wrapped, so e.g. when ($i % 10 == 2 && $i != 12) for $i = 2 became 2 ~~ 1 (false) instead of using the boolean 1 directly.

Fix: add whenIsBoolean(...) to detect those exceptional forms and use the expression's value directly as a boolean instead of wrapping with $_ ~~.

Test plan

  • jcpan -t DateTime::Calendar::DiscordianFiles=16, Tests=271 ... Result: PASS (was 21/73 failures in t/cardinals.t)
  • make — full unit-test suite green, no regressions
  • Spot checks confirming when semantics match perl for both smart-match and boolean cases

Generated with Devin

The given/when transformation in StatementParser had two bugs that
caused t/cardinals.t in DateTime::Calendar::Discordian to fail 21/73
subtests (e.g. "1st" coming out as "1stth"):

1. After a successful `when` match, Perl implicitly breaks out of the
   enclosing `given`. We now append `last;` to every parsed when-block
   and mark the synthesized given BlockNode as isLoop=true so that
   `last` exits the given instead of escaping to an outer loop.

2. Per perlsyn, when(EXPR) does NOT smart-match against $_ when EXPR
   is a comparison (==, !=, <, >, <=, >=, <=>, eq, ne, lt, gt, le,
   ge, cmp), boolean combinator (&&, ||, //, and, or, xor), regex
   bind (=~, !~), negation (!, not), or defined/exists. We now detect
   these forms and use the expression's value directly as a boolean
   instead of wrapping it in `$_ ~~ EXPR`.

After this fix, DateTime::Calendar::Discordian passes 271/271 tests
under `jcpan -t`.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock merged commit dfa87a6 into master Apr 28, 2026
2 checks passed
@fglock fglock deleted the fix/given-when-implicit-break branch April 28, 2026 13:52
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