Skip to content

Inheritance/interface override of a REFERENCE TO property is rejected #1750

Description

@Angus-Bethke-Bachmann

Describe the bug
Inheritance/interface override of a REFERENCE TO property is rejected. Both an override in a derived FB and an FB implementing an interface that declares a REFERENCE TO property fail at the signature-compatibility check, because the synthesized return-val parameter is named after the enclosing POU rather than after the property.

To Reproduce

// A derived FB overrides a parent's PROPERTY_GET ... REFERENCE TO ...
// We then access through both the parent ref and the child ref.

TYPE Pair :
    STRUCT
        a : INT;
        b : INT;
    END_STRUCT
END_TYPE

FUNCTION_BLOCK parent
    VAR
        _p_parent : Pair;
    END_VAR

    PROPERTY_GET p : REFERENCE TO Pair
        p REF= _p_parent;
    END_PROPERTY
END_FUNCTION_BLOCK

FUNCTION_BLOCK child EXTENDS parent
    VAR
        _p_child : Pair;
    END_VAR

    // Override the GET: route to the child's own pair instead.
    PROPERTY_GET p : REFERENCE TO Pair
        p REF= _p_child;
    END_PROPERTY
END_FUNCTION_BLOCK

FUNCTION main : INT
    VAR
        c : child;
        v_parent_field : INT;
        v_child_field : INT;
    END_VAR
    c._p_parent.a := 1;     // mutate parent's struct directly
    c._p_child.a  := 2;     // mutate child's struct directly

    // Read through the overridden child property — should yield 2
    v_child_field := c.p.a;

    main := v_child_field;
END_FUNCTION

/*
  error[E112]: Derived methods with conflicting signatures, parameters do not match:
  note[E118]: Expected parameter `__parent___get_p_return_val`
              but got        `__child___get_p_return_val`
*/
// REFERENCE TO property declared in an INTERFACE,
// accessed through an interface-typed variable.
// Stresses the polymorphism+REFERENCE-TO interaction (the new fat-pointer
// constructor calls in interface dispatch snapshots came from this PR).

TYPE Pair :
    STRUCT
        a : INT;
        b : INT;
    END_STRUCT
END_TYPE

INTERFACE IPair
    PROPERTY_GET p : REFERENCE TO Pair END_PROPERTY
END_INTERFACE

FUNCTION_BLOCK fb IMPLEMENTS IPair
    VAR
        _p : Pair;
    END_VAR
    PROPERTY_GET p : REFERENCE TO Pair
        p REF= _p;
    END_PROPERTY
END_FUNCTION_BLOCK

FUNCTION main : INT
    VAR
        x      : fb;
        iface  : IPair;
        v      : INT;
    END_VAR
    iface := x;
    x._p.a := 13;
    v := iface.p.a;
    main := v;       // expect 13
END_FUNCTION

/*
  error[E112]: Derived methods with conflicting signatures, parameters do not match:
  note[E118]: Expected parameter `__IPair___get_p_return_val`
              but got        `__fb___get_p_return_val`
*/

Expected behavior
No error should occur.

Suggested Fix
Keep the per-POU parameter naming (which is genuinely needed to disambiguate same-named properties across unrelated POUs) but teach the signature-compat validator to ignore synthesized parameters. The override check should compare only the user-written property types — i.e. the original REFERENCE TO Pair declaration on both sides — not the synthetic return-val slot that the lowerer is about to add.

Concretely, this probably wants:

  • An explicit marker on VariableIndexEntry (e.g. is_synthetic_property_return_ref: bool) set by ReferenceToReturnLowerer::visit_pou when it inserts the new VAR_INPUT variable.
  • The override-compatibility checker filters out parameters carrying that flag before comparing parameter lists / return types.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions