Skip to content

fix(lowering): resolve array ctor bounds through the index (#1897) - #1898

Open
ghaith wants to merge 3 commits into
release/1.0.xfrom
backport/1.0.x/array-ctor-bounds
Open

fix(lowering): resolve array ctor bounds through the index (#1897)#1898
ghaith wants to merge 3 commits into
release/1.0.xfrom
backport/1.0.x/array-ctor-bounds

Conversation

@ghaith

@ghaith ghaith commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Attribution: this branch, its tests and this description were produced by Claude
(Claude Code) working in my repository, not written directly by me. I have reviewed the
change, but please review it as machine-authored work. The commits carry a
Co-Authored-By: Claude trailer.

Backport of #1897 to release/1.0.x. Fixes PRG-4730 on the v1 line.

Why it is wanted on v1

The bug is present on this branch. An array whose bound names a constant of the declaring POU
fails to compile when the element type needs construction:

PROGRAM mainProg
VAR CONSTANT five : DINT := 5; END_VAR
VAR arr : ARRAY[1..five] OF STRING[63]; END_VAR
END_PROGRAM
error: cannot generate call statement for ReferenceExpr {
    kind: Member(Identifier { name: "DINT_GREATER" }), base: None }

Reproduced on release/1.0.x before porting. Note that #1831, which introduced the loop on
master, is not on this branch; an equivalent element construction loop is, so the branch is
affected independently rather than through that commit.

See #1897 for the full cause and reasoning. In short: the loop bounds were copied out of the
declaration into the generated constructor, which is a POU of its own where a member of the
declaring POU does not resolve. The bounds now come from the index, already const-evaluated,
and are emitted as literals.

Port risk

Low. Both commits cherry-picked with no conflicts.

Verified on this branch on Windows: the repro compiles, and 2582 + 91 + 50 lib, 170 lowering,
352 correctness tests pass, with fmt clean. The lit tests were not run locally (lit needs
Python and that suite runs on Linux in CI).

What reviewers should weigh

  • The change only affects the bounds of the generated element construction loop; a bound that
    was already a literal produces the same loop as before.
  • Arrays of scalar types are unaffected, since they generate no element construction loop.

🤖 Generated with Claude Code

ghaith and others added 2 commits August 27, 2026 17:12
Problem: An array whose bound names a constant of the declaring POU
(`ARRAY[1..five]`) failed to compile when its element type needs construction,
a sized string or a struct for example:

    error: cannot generate call statement for ReferenceExpr {
        kind: Member(Identifier { name: "DINT_GREATER" }), base: None }
    error occurred while generating initialization code for type '__mainProg_arr'

The generated element construction loop copied the bound expression out of the
declaration. That constructor is a POU of its own, so a name that belongs to the
declaring POU does not resolve inside it, the operand was left without a type,
and the comparison was then annotated as a call to a `DINT_GREATER` function
that does not exist. The same bound worked as a global constant, because a global
is in scope everywhere.

Solution: Take the bounds from the index, which holds them already
const-evaluated, and emit them as literals. An undetermined bound is rejected
explicitly rather than through `as_int_value`, which reports it as the pointer
size and would otherwise produce a loop over the wrong range.

Regression from #1831, which introduced the element construction loop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A constant declared in a POU sizes a string exactly like the literal it stands
for, on its own (`STRING[five]`, `WSTRING[five]`), inside an expression
(`STRING[five * 2 + 1]`), and as both the bound and the element length of an
array (`ARRAY[1..count] OF STRING[len]`).

The tests compare the generated field layout against the same declaration
written with literals, rather than snapshotting IR, so they assert the sizes
themselves. Only the global form was covered before, by
`variable_length_strings_using_constants_can_be_created`.

These pass without the array constructor fix as well: string sizing was never
affected by it. They are here because the local and the global form turned out
to be handled differently in a neighbouring position, and this pins the one that
was already correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Build Artifacts

🪟 Windows

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

From workflow run

🐧 Linux

Artifact Link Size
deb-x86_64 Download 10.9 MB
schema Download 0.0 MB
stdlib Download 33.5 MB
plc-x86_64 Download 43.5 MB
deb-aarch64 Download 5.4 MB
plc-aarch64 Download 43.3 MB

From workflow run

`AstStatement` was only needed for the `RangeStatement` destructuring of the
declaration bounds, which the previous commit replaced with the const-evaluated
bounds from the index.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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