fix: properties now support the REFERENCE TO type#1701
fix: properties now support the REFERENCE TO type#1701Angus-Bethke-Bachmann wants to merge 27 commits into
Conversation
Build Artifacts🪟 Windows
From workflow run 🐧 Linux
From workflow run |
mhasel
left a comment
There was a problem hiding this comment.
This change looks like it was a headache to implement due to the participants. The reordering looks fine, but I would like a few additional tests to ensure we did not break anything, plus a mention of the reordering and why it was necesssary in the commit message in case this causes issues down the line.
Tests I'd like to see:
PROPERTY_SET REFERENCE TO coverage
chained-access tests for properties: fb.propA.propB where both propA and propB are REFERENCE TO properties - both SET and GET variants.
…o anbt/PRG-3350
ghaith
left a comment
There was a problem hiding this comment.
Note: this review was generated by Claude (via Claude Code) and posted on my behalf. Treat the suggestions as machine-generated input — verify before acting. — Ghaith
Adding to the existing review threads from @mhasel and @volsa rather than re-stating their points. A few additional concerns and explicit test gaps below; happy to discuss.
Cross-cutting concerns
-
Name-prefix heuristics in codegen are fragile.
src/codegen/generators/pou_generator.rsnow branches onparameter_name.starts_with("__get_")/"__set_"(around L627 and L806). The semantic intent is "this parameter/variable is the synthetic by-ref return slot of a property accessor", but we are inferring that from a string prefix on the variable name, not on the enclosing POU kind or a synthesized-marker on theVariableIndexEntry. Two issues:- Any user-declared variable that happens to start with
__get_/__set_will silently take the new code path (skip null-init, forcebuild_store(ptr, ptr_value)instead of by-val handling). The lexer does accept leading underscores in identifiers. - The check is duplicated in two places without a shared helper, so future changes to the synthesis convention have to keep them in lockstep.
Suggested fix: plumb an explicit flag (e.g.
is_synthetic_property_return_ref) onto theVariableIndexEntryfrom the lowerer that synthesizes it, and gate both call sites on that flag rather than on a name prefix. - Any user-declared variable that happens to start with
-
InitParticipantmove frompre_annotatetopost_annotateis a behavioral change, not just snapshot churn. As @mhasel noted, the new__FATPOINTER__ctor(reference)lines now appear in many interface-dispatch snapshots that previously had no constructor call there. Even if the body is currently a no-op, this is a live runtime change with potentially repo-wide blast radius (we are running constructors for references that previously weren't constructed). Two asks:- Confirm via an end-to-end test (lit or codegen-and-execute) that no observable behavior changes for non-property reference flows — e.g. interface dispatch with reference args still produces the same printed output it did before this PR.
- Document the new invariant near
BuildPipeline::default_participantsso the next person understands whyInitParticipanthad to move past annotation (the inline// Had to move this step post annotate to ensure that the property lowerer has a chance to runis in the participant impl, but the pipeline ordering itself has no comment).
-
pou_original_return_type_is_reference_tois now ambiguous across same-named methods on different POUs. Withshould_de_qualify = !name.contains("."), an unqualified call sitefoo()will match any POU whose suffix isfoo— including methods on unrelated FBs. This is worse than @volsa's already-flaggedinstanceB.valueregression because here the correct match cannot be recovered after the fact. At minimum, add a targeted test (see test gaps below) and consider qualifying via the current implementation's container before falling back to suffix match.
Test gaps
The added lowerer / codegen / lit coverage is a good start, but a few cases I'd want before this lands:
are_equal_typesPointer/Reference arm has no direct validation test. The new arm insrc/validation/types.rsis reachable, but there is no corresponding validation test asserting (a)REFERENCE TO X := REFERENCE TO Xis accepted, (b)REFERENCE TO X := REFERENCE TO Yis rejected, (c) the alias case @volsa flagged (MyDINTaliasingDINT). Without a test, the next refactor of this function will silently regress property-setter compatibility.- Standalone
PROPERTY_SET REFERENCE TO— the lit/codegen tests always pair GET+SET. A setter-only case (no matching getter, or a setter for a property whose getter returns a non-reference type) would prove the SET path stands on its own, and is exactly the case @mhasel asked about. PROPERTY_SETwhose RHS is a function call returningREFERENCE TO— e.g.fb.prop := bar()wherebar : REFERENCE TO X. The newvalue_is_usedflow assumes property-SET feeds a direct expression; an inlined call on the RHS exercises the pre-statement hoisting in a setter context, which the current tests do not cover.- Nested REFERENCE-TO property chains. The chained lit test (
property_with_chained_reference_to_return_type.st) chains a struct field, not a second REFERENCE-TO property. A two-deep chain (fb.outerRef.innerRef.xwhere both arePROPERTY_GET ... REFERENCE TO ...) would stressnode_is_call_statement_with_referenced_pou_that_is_reference_to_return's newReferenceExprrecursion. - Inheritance + REFERENCE-TO property override. No test covers a derived FB overriding a parent's
PROPERTY_GET ... REFERENCE TO ..., norSUPER^.propaccess to such a property. Given the super/E033 fallout already visible insuper_keyword_validation_tests.rs, this is the most likely place for a latent bug. - Property name collision with the
__get_/__set_heuristic. A property literally namedget_foo(so the lowered method is__get_get_foo) and a parameter incidentally named__get_xshould both still compile. No regression test currently anchors this. - Empty-statement substitution side-effects. When
value_is_used == false, the call replacement becomesAstStatement::EmptyStatement(reference_to_return.rs:594). Worth a test that downstream passes (debug info, statement-iteration in codegen) handle a bareEmptyStatementat statement position without panicking.
Smaller items
get_return_variable_namenow branches onname.starts_with("__"). The branch is doing two things at once (de-qualification + prefix collapsing). Consider splitting them and naming each step.- The two new helpers
get_method_name/set_method_nameare one-lineformat!s. If you keep them, fine — but they hide the fact that the same__get_/__set_constant is hardcoded inpou_generator.rstoo. Pull the prefix into a singlepub constto keep the convention discoverable. - The
printf('%d$N', ...)lit tests rely on libc and a runtime;// CHECK-NEXT: 51after an unrelated// CHECK: 51works only becauseCHECK-NEXTallows immediately-following lines. If output ordering between runs ever changes (it shouldn't, but) these tests will be hard to debug. A// CHECK-LABEL:separator per phase would make intent clearer.
Co-authored-by: Copilot <copilot@github.com>
…o anbt/PRG-3350
Changed
Testing