Skip to content

Validate binary operators against each type in a union operand - #1768

Merged
markwpearce merged 4 commits into
v1from
fix-union-operator-validation
Jul 30, 2026
Merged

Validate binary operators against each type in a union operand#1768
markwpearce merged 4 commits into
v1from
fix-union-operator-validation

Conversation

@markwpearce

Copy link
Copy Markdown
Collaborator

Summary

  • validateBinaryExpression skipped validation entirely whenever either operand of a binary op was a union type, per a TODO saying it should check each inner-type combination instead. This meant something like x as string or integer used in x + "world" never got flagged, even though integer + string is invalid.
  • Implements that: decomposes each union operand into its member types and runs binaryOperatorResultType over every combination, flagging operator-type-mismatch if any combination is invalid.
  • Also narrows the void/uninitialized short-circuit in binaryOperatorResultType so =/<> comparisons of a possibly-void value against invalid/dynamic remain allowed (e.g. channel = noReturn(); if channel <> invalid) — real per-union-member checking now exercises this path for types like void or roAssociativeArray, which previously never reached this code due to the union bypass.

Fixes #1749

Test plan

  • Added a regression test reproducing the issue's exact scenario (x as string or integer then x + "world")
  • Full test suite passes (npm run test:nocover) - 4262 passing, 0 failing
  • npm run lint passes

🤖 Generated with Claude Code

validateBinaryExpression skipped validation entirely whenever either
operand was a union type, with a TODO to check each inner-type
combination instead. Implement that: decompose each union and run
binaryOperatorResultType over every combination, flagging an error if
any combination is invalid (e.g. `x as string or integer` used in
`x + "world"`).

Also narrow the void/uninitialized short-circuit in
binaryOperatorResultType so `=`/`<>` comparisons of a possibly-void
value against invalid/dynamic remain allowed, since real per-union-member
checking now exercises that path for values like `void or roAssociativeArray`.

Fixes #1749

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@markwpearce markwpearce added this to the v1.0.0 milestone Jul 30, 2026
markwpearce and others added 3 commits July 30, 2026 11:23
Covers the narrowed void short-circuit added in the previous commit:
= and <> against invalid/dynamic stay allowed for void, other operators
and other rhs types on void remain disallowed, and uninitialized still
always rejects comparisons against invalid.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Verified against brs-engine that a void call result compared with =/<>
against a dynamic value or invalid, and a "void or <type>" union compared
against invalid, are all valid, runnable BrightScript patterns - these
should not be flagged by the type checker.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@markwpearce
markwpearce merged commit 50f5b21 into v1 Jul 30, 2026
10 of 11 checks passed
@markwpearce
markwpearce deleted the fix-union-operator-validation branch July 30, 2026 15:02
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