Conversation
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>
Build Artifacts🪟 Windows
From workflow run 🐧 Linux
From workflow run |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes PRG-4730. Regression from #1831.
Problem
An array whose bound names a constant of the declaring POU fails to compile, when the element
type is one that needs construction:
Moving the constant to a global makes it compile.
Cause
#1831 added a per-element construction loop to generated array constructors, and built the
loop bounds by copying the bound expression out of the declaration:
The constructor (
__mainProg_arr__ctor) is a POU of its own, sofive, a member ofmainProg, does not resolve inside it. The operand is left without a type, the resolver'sfirst branch requires both operands to be numerical, and the comparison falls through to the
compare-via-function path, which annotates a call to a
DINT_GREATERthat does not exist. Aglobal constant worked only because a global is in scope everywhere.
Only element types that need construction were affected, since a scalar array generates no
loop at all — which is why this went unnoticed.
Fix
Take the bounds from the index, which holds them already const-evaluated, and emit literals.
The generated loop is now
store i32 1/icmp sgt i32 .., 5: a native comparison, no call.An undetermined bound is rejected explicitly rather than through
as_int_value, which reportsit as the pointer size and would otherwise produce a loop silently ranging over the wrong
bounds.
Failure surface
ARRAY[1..five] OF STRING[63]ARRAY[1..five] OF <struct>ARRAY[1..five, 1..five] OF STRING[10]ARRAY[1..five] OF DINTSTRING[five]Tests
global constant, an expression over a local constant (
[two..two*3]resolves to 2..6),per-dimension bounds of a 2D array, and a guard that a scalar array gets no loop.
DINT_GREATER, whichguards the reported symptom directly.
resolved to the wrong value is caught, not only one that fails to compile.
STRING[five],WSTRING[five],STRING[five * 2 + 1],ARRAY[1..count] OF STRING[len]) against the samedeclaration written with literals. These pass without the fix as well; string sizing was
never affected. Only the global form was covered before.
The 7 tests in the first three groups were confirmed red against the pre-fix code, then green.
Note the global-constant lowering test also fails before the fix: bounds previously arrived as
identifiers even when they happened to resolve, so the fix tightens both paths.
Ran locally on Windows: 2555 + 98 lib, 170 lowering, 372 correctness, 43 integration, all
green,
fmtandclippyclean. The lit tests were not run locally (litneeds Python andthat suite runs on Linux in CI); they get their first run here.
🤖 Generated with Claude Code