Skip to content

ARRAY OF <enum> gets an inconsistent C++ element type — IEC_ENUM<>-wrapped in a STRUCT member but the raw enum in a FUNCTION_BLOCK VAR_OUTPUT — so assigning an FB output array into a bus-member array fails to compile #210

Description

@dreamclass

Problem

Environment

  • STruC++ version: 0.6.2
  • C++ compiler: g++ (MSYS2 UCRT64) 15.1.0, -std=c++17

Summary

strucpp generates code fine, but the emitted C++ does not compile whenever an
ARRAY OF <enum> value crosses a FUNCTION_BLOCK output → STRUCT/bus member
boundary. STruC++ emits the same ST array type with two different C++
element types
:

  • as a STRUCT (bus) member: Array1D<IEC_MYCOLORS, 0, 3> (the wrapped enum,
    IEC_MYCOLORS = IEC_ENUM<MYCOLORS>)
  • as a FUNCTION_BLOCK VAR_OUTPUT: Array1D<MYCOLORS, 0, 3> (the raw enum)

IEC_ARRAY_1D::operator= only accepts an argument of the exact same type, so the
plain ST assignment outBus1.a1 := i0_foo.out1; has no viable operator= and
g++ errors out. Any .st file that writes an FB-output enum array into a
STRUCT/bus member hits this.

Impact

g++ build fails

Reproduction

repro_enumarray.st (in this folder):

TYPE
    MyColors : (RED, GREEN, BLUE);
END_TYPE

TYPE
    ColorBus : STRUCT
        a1 : ARRAY[0..3] OF MyColors;
    END_STRUCT;
END_TYPE

FUNCTION_BLOCK Foo
VAR_OUTPUT
    out1 : ARRAY[0..3] OF MyColors;
END_VAR
    out1[0] := RED;
    out1[1] := GREEN;
    out1[2] := BLUE;
    out1[3] := RED;
END_FUNCTION_BLOCK

PROGRAM PLC_PRG
VAR
    i0_foo  : Foo;
    outBus1 : ColorBus;
END_VAR
    i0_foo();
    (* enum-array crossing an FB-output -> bus-member boundary *)
    outBus1.a1 := i0_foo.out1;
END_PROGRAM

Commands:

# 1) ST -> C++  (succeeds, "Compilation successful!")
strucpp repro_enumarray.st -o repro_gen.cpp --no-default-libs

# 2) compile the generated C++  (fails)
g++ -std=c++17 -I<STRUCPP_ROOT>/runtime/include -I. repro_gen.cpp -c -o /dev/null

Actual result

Codegen succeeds, but the g++ build fails:

repro_gen.cpp: In member function 'virtual void strucpp::Program_PLC_PRG::run()':
repro_gen.cpp:44:25: error: no match for 'operator=' (operand types are
  'strucpp::Array1D<strucpp::IEC_ENUM_Var<strucpp::MYCOLORS>, 0, 3>'
  and
  'strucpp::Array1D<strucpp::MYCOLORS, 0, 3>')
   44 |     OUTBUS1.A1 = I0_FOO.OUT1;
      |                         ^~~~
  note: candidate 1: 'operator=(const IEC_ARRAY_1D<IEC_ENUM_Var<MYCOLORS>,...>&)'
  note:   no known conversion for argument 1 from
          'Array1D<MYCOLORS,...>' to 'const Array1D<IEC_ENUM_Var<MYCOLORS>,...>&'
  note: candidate 2: 'operator=(IEC_ARRAY_1D<IEC_ENUM_Var<MYCOLORS>,...>&&)'
  note:   no known conversion ...

### Code Locations

_No response_

### Fix

## Root cause

The two declarations of the same ST type `ARRAY[0..3] OF MyColors` use different
C++ element types in the generated header (`repro_gen.hpp`, in this folder):

```cpp
// line 55: the enum wrapper alias
using IEC_MYCOLORS = IEC_ENUM<MYCOLORS>;

// line 57-59: STRUCT/bus member uses the WRAPPED enum
struct COLORBUS {
    Array1D<IEC_MYCOLORS, 0, 3> A1{};      // IEC_ENUM_Var<MYCOLORS> elements
};

// line 66-69: FUNCTION_BLOCK output uses the RAW enum
class FOO {
public:
    Array1D<MYCOLORS, 0, 3> OUT1;          // raw MYCOLORS elements
};

IEC_ARRAY_1D<T, Bounds>::operator= (iec_array.hpp:42) is only generated for its
own exact T, so Array1D<IEC_MYCOLORS,…> cannot be assigned from
Array1D<MYCOLORS,…>. The element types must agree for the assignment STruC++
itself emitted to type-check.

Note this is inconsistent even for scalars vs. arrays: scalar enum members are
routed through IEC_ENUM<> in both contexts; only the array element type
diverges by declaration site.

Suggested fixes (in rough order of preference)

  1. Emit one canonical element type for ARRAY OF <enum> everywhere — use the
    wrapped IEC_MYCOLORS (i.e. IEC_ENUM<MYCOLORS>) for both STRUCT members
    and FUNCTION_BLOCK outputs (and inputs/locals), matching how scalar enums are
    already handled. This is the minimal, consistent fix.
  2. Alternatively, standardize on the raw enum element type in both places
    (only if the wrapper buys nothing for enum arrays).
  3. As a runtime-side backstop, give IEC_ARRAY_1D::operator= a converting
    overload that accepts an array whose element type is convertible
    (IEC_ENUM_Var<E>E). Broader, but hides the codegen inconsistency rather
    than fixing it.

Acceptance

The generated executable builds; outBus1.a1 := i0_foo.out1; copies the enum
array element-by-element.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions