Skip to content

Codegen: external access to inherited PROPERTY on a derived FB instance emits raw field access (invalid C++) #182

Description

@strwthvn

Summary

Accessing a PROPERTY declared on a base FB through an instance of a derived FB generates raw field access (inst.PROP) instead of the accessor call (inst.get_PROP() / inst.set_PROP(...)). The emitted C++ does not compile. The compiler reports "Compilation successful!" — the error only surfaces later at g++.

Inherited methods, own properties of the derived FB, properties accessed on the base type directly, and composition (a.b.Prop) all work — only derived instance + external access + inherited property is broken.

Minimal repro

FUNCTION_BLOCK Base
  VAR _v : INT; END_VAR
  PROPERTY Val : INT
    GET Val := _v; END_GET
    SET _v := Val; END_SET
  END_PROPERTY
END_FUNCTION_BLOCK

FUNCTION_BLOCK Derived EXTENDS Base
END_FUNCTION_BLOCK

PROGRAM Main
  VAR b : Base; d : Derived; x : INT; END_VAR
  b.Val := 1;  x := b.Val;   (* OK   -> set_VAL / get_VAL *)
  d.Val := 2;  x := d.Val;   (* BUG  -> raw D.VAL         *)
END_PROGRAM

strucpp repro.st -o out/repro.cpp → "Compilation successful!". Generated body:

B.set_VAL(1); X = B.get_VAL();   // correct
D.VAL = 2;    X = D.VAL;         // wrong — DERIVED has no member VAL (the field is _V)

g++ then fails:

error: 'class strucpp::DERIVED' has no member named 'VAL'
   32 |     D.VAL = 2;

Root cause

In src/backend/codegen.ts:

  • propertyNameMap is populated with key <FB>.<prop> for each FB's own properties only (~L950–955).
  • resolvePropertyName(typeName, field) looks up the exact typeName and does not walk fb.extends, so DERIVED.VAL misses and returns undefined.
  • Member-access codegen (~L3689 and L3752) then falls through from the property branch to raw .field access.

Methods are unaffected because a method call is emitted as .NAME() (valid via C++ public inheritance) regardless of the map lookup, whereas a property requires the get_/set_ rewrite — which is gated on the lookup.

Expected

Walk the EXTENDS chain when resolving properties:

d.Val := 2;   // -> D.set_VAL(2)
x := d.Val;   // -> X = D.get_VAL()

(e.g. register inherited properties under the derived FB's keys, or have resolvePropertyName follow fb.extends.)

Notes

  • docs/IEC_COMPLIANCE.md lists Properties (GET/SET) and Inheritance (EXTENDS) as Supported, so this looks like a bug rather than an unimplemented feature.
  • No tests/backend/codegen-oop.test.ts case currently exercises external access to an inherited property via a derived instance — a coverage gap.

Environment

  • STruC++ 0.5.7, branch development, commit 72edddd (v0.4.20-66-g72edddd)
  • Node v26, g++ 16.1.1, Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions