Skip to content

fix(validation): type-check variable initializers - #1924

Open
ghaith wants to merge 6 commits into
masterfrom
fix/PRG-4728
Open

ghaith wants to merge 6 commits into
masterfrom
fix/PRG-4728

Conversation

@ghaith

@ghaith ghaith commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Problem: An initializer whose type cannot be assigned to the declared variable type, such as x : STRING := 3 or a : DINT := TRUE, passes --check and aborts later in code generation or silently stores a converted value. Only body assignments ran the assignment type check; VAR, VAR_GLOBAL, function block members and struct members were never validated, while VAR_TEMP was caught by accident through its lowered copy and reported without a source location. BOOL was treated as a numeric value everywhere, so DINT := TRUE and DINT#TRUE were accepted in bodies too, and array literal elements were never checked against the element type, so ARRAY[0..1] OF REAL := [TRUE, FALSE] panicked in code generation.

Solution: Run the same assignment validation on every variable initializer and on every element of an array literal, so declarations and body assignments report identical diagnostics (E037 mismatches, E067 downcasts). A BOOL value assigned to another numeric type is now an invalid assignment, as is an integer literal other than 0 or 1, or a real literal, assigned to a BOOL; BOOL := 1 stays valid. A BOOL literal behind a numeric type prefix (DINT#TRUE) is an incompatible literal cast like DINT#1.5, and BOOL#TRUE now evaluates as a constant initializer. The standard library's BOOL_TO_<integer> functions use the explicit IF form already used for the bit conversions, and the CFC storage test on a numeric sink now asserts the rejection. The lowered copies of initializers are skipped so each mismatch is reported once, at its declaration. Pointer-typed declarations accept pointer and integer initializers for now; any other value, such as a string or a real, is rejected like in a body, and a REF_TO initializer whose target type differs reports the same E090 warning as the body form instead of an error. Inline pointer types no longer include their initializer in the location used for type names. Numeric array literal elements are cast to the array's element type in code generation, so accepted conversions such as ARRAY OF INT := [1.5, 2] no longer panic.

Refs: PRG-4728

🤖 Generated with Claude Code

Problem: An initializer whose type cannot be assigned to the declared
variable type, such as `x : STRING := 3`, passes `--check` and aborts
later in code generation. Only body assignments ran the assignment type
check; declarations did not.

Solution: Run the same assignment validation on every variable
initializer. `REFERENCE TO` and alias declarations bind a reference and
keep their own check, and the lowered copies of initializers are skipped
so each mismatch is reported once at its declaration. Inline pointer
types no longer include their initializer in the location used for type
names in diagnostics.

Refs: PRG-4728

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown

Build Artifacts

🐧 Linux

Artifact Link Size
deb-x86_64 Download 38.4 MB
stdlib Download 32.4 MB
plc-x86_64 Download 43.5 MB
schema Download 0.0 MB
plc-aarch64 Download 43.4 MB
deb-aarch64 Download 30.8 MB

From workflow run

🪟 Windows

Artifact Link Size
stdlib.lib Download 4.0 MB
stdlib.dll Download 0.1 MB
plc.exe Download 38.4 MB

From workflow run

@github-actions

Copy link
Copy Markdown

0 findings in 11m 3s for $0.41 between e72bfbe (master) and c64eead (fix/PRG-4728)

volsa
volsa previously approved these changes Sep 17, 2026
Problem: A BOOL or REAL literal inside an array initializer, such as
`ARRAY[0..1] OF REAL := [TRUE, FALSE]`, passes `--check` and then either
panics in code generation or produces an ill-typed constant that breaks
the link step. Scalar initializers already convert such literals.

Solution: Cast every numeric element of an array literal to the array's
element type before the constant array is built, in declarations and in
body assignments alike.

Refs: PRG-4728

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

0 findings in 4m 10s for $0.16 between e72bfbe (master) and 671a247 (fix/PRG-4728)

Problem: Running the assignment validation on pointer-typed declarations
turns previously accepted initializers such as `POINTER TO INT := 'foo'`
into errors, and `REF_TO STRING := REF(a)` with a different target type
reports an E037 error where the body form only warns.

Solution: Skip the new initializer check for pointer-typed declarations
and validate address initializers with the same check the body uses, so
a `REF_TO` target type mismatch reports the E090 warning and nothing
else. The two pointer initializer tests keep documenting the gap.

Refs: PRG-4728

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

0 findings in 1m 59s for $0.14 between e72bfbe (master) and c0535b6 (fix/PRG-4728)

ghaith and others added 2 commits September 17, 2026 10:27
Problem: A string or real value stored into a pointer declaration, such
as `p : POINTER TO INT := 'foo'`, is accepted, while the same assignment
in a body is an error. The value is never a valid address.

Solution: Pointer declarations only skip the assignment check for
integer initializers; every other non-pointer value is reported like in
a body. Address initializers keep going through the address branch.

Refs: PRG-4728

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Problem: `a : DINT := TRUE` is accepted although BOOL is not a numeric
value, `b : BOOL := 5` is accepted although only 0 and 1 are BOOL
literals, and the elements of an array literal are never checked against
the element type, so `ARRAY[0..1] OF DINT := ['a', 'b']` passes `--check`
and fails in code generation.

Solution: A BOOL value assigned to another numeric type is an invalid
assignment, as is an integer literal other than 0 or 1, or a real
literal, assigned to a BOOL. Every element of an array literal is
validated like an assignment to the element type, including nested
literals; spliced array references and flat initializers of nested
arrays are left to the existing length checks. The standard library's
BOOL_TO_<integer> functions use the explicit IF form already used for
the bit types, and the CFC storage test on a numeric sink now asserts
the rejection.

Refs: PRG-4728

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

0 findings in 3m 54s for $0.26 between e72bfbe (master) and ea94468 (fix/PRG-4728)

Problem: `DINT#TRUE` and `REAL#TRUE` pass validation although a BOOL
literal is no numeric value, while `x : BOOL := BOOL#TRUE` fails with an
unresolved constant because the constant evaluator expects an integer
literal behind every integer type prefix.

Solution: A BOOL literal behind a non-BOOL type prefix is an incompatible
literal cast (E054), like `DINT#1.5` already is. The constant evaluator
keeps a BOOL literal behind a BOOL prefix as it is. The equal-operation
test no longer stores a BOOL comparison into a DINT variable.

Refs: PRG-4728

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

0 findings in 1m 56s for $0.14 between e72bfbe (master) and 7584dbc (fix/PRG-4728)

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.

2 participants