Skip to content

feat: cfc#1785

Open
volsa wants to merge 29 commits into
masterfrom
cfc
Open

feat: cfc#1785
volsa wants to merge 29 commits into
masterfrom
cfc

Conversation

@volsa

@volsa volsa commented Jun 26, 2026

Copy link
Copy Markdown
Member

Introduces the new CFC implementation based on the PLCOpen XML standard (IEC61131-10, https://www.plcopen.org/standards/logic/iec-61131-10/). Consequently the old implementation was removed altogether (plc_xml) and a new plc_cfc crate has been introduced in its place. The changes are mostly self-isolated.

Recommendation for reviewing this: Start with the transpiler.rs file and some fixtures and expand from there onwards. The changes are rather small and well documented (imo), most of the additions are also just test fixtures.

volsa and others added 10 commits June 10, 2026 11:09
Introduces an initial CFC transpiler implementation with some basic
support for block execution and test coverage. However, the transpiler
is not yet wired into the compiler pipeline and as such does not have
any effects on it.
Introduces (un)conditional return statements for CFC.
Introduces a "resolver" to replace the placeholder types for the
temporary variables to their actually referenced types. For example
```
VAR_TEMP
    temp_0: __return@foo;
    temp_1: __output@bar@baz;
END_VAR
```
becomes
```
VAR_TEMP
    // Assuming function foo returns a DINT
    temp_0: DINT;

    // Assuming the output variable of function block bar is a SINT
    temp_1: SINT;
END_VAR
```
The synthetic `temp_N` variables introduced for block outputs are now named `__temp_N`, keeping them out of the user-facing identifier namespace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A CFC block whose type name is qualified (`function_block_0.myAction`) targets an action of the block's instance. It lowers to a member call on that instance — `myInstance.myAction()` — with no arguments, since actions have no inputs, outputs, in-outs, or return value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown

Build Artifacts

🐧 Linux

Artifact Link Size
deb-x86_64 Download 38.4 MB
schema Download 0.0 MB
stdlib Download 32.3 MB
plc-x86_64 Download 43.5 MB
deb-aarch64 Download 30.8 MB
plc-aarch64 Download 43.3 MB

From workflow run

🪟 Windows

Artifact Link Size
stdlib.lib Download 4.0 MB
stdlib.dll Download 0.1 MB
plc.exe Download 38.3 MB

From workflow run

volsa and others added 6 commits June 29, 2026 21:29
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@volsa volsa marked this pull request as ready for review July 1, 2026 08:53
Comment thread Makefile Outdated
Comment thread tests/lit/cfc/OPEN.md Outdated
Comment thread book/src/arch/architecture.md Outdated
Comment thread compiler/plc_ast/src/ser.rs Outdated
Comment thread compiler/plc_ast/src/ser.rs Outdated
Comment thread compiler/plc_driver/src/pipelines.rs Outdated
Comment thread compiler/plc_driver/Cargo.toml Outdated
Comment thread compiler/plc_diagnostics/src/diagnostics/error_codes/E144.md Outdated
Comment thread compiler/plc_cfc/src/validator.rs
Comment thread compiler/plc_cfc/src/validator.rs
Comment thread compiler/plc_cfc/src/resolver.rs
Comment thread compiler/plc_cfc/src/transpiler.rs
The `OutputVariable` field of `Object::BlockOutput` is only read in
tests, making `cargo clippy --workspace -- -Dwarnings` (i.e. the CI
style check, which excludes test code) fail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ghaith
ghaith previously approved these changes Jul 9, 2026

@ghaith ghaith left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review was offline, we'll get this in as is and followup with other PRs to avoid this getting stale

volsa and others added 6 commits July 10, 2026 12:15
A negated output pin now inverts the value it feeds to each of its
consumers. Also hardens two seams uncovered along the way: sinks and
returns require an `EvaluationPriority` entry at deserialize time
(a missing one silently sorted the object in front of the network,
dead-coding the POU for an unconditional return), and the transpiled
network and its temporaries are attached to the POU implementation by
name instead of position (declarations containing methods pushed the
body to the back of the implementation list).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A sink naming a compound target (`results[1]`, `myStruct.field`) now
lowers through the expression parser instead of a flat identifier
token, which previously aborted compilation with a bogus E048 and made
the validator's deferred E050 check unreachable.

Result temporaries of generic callees (e.g. the builtin `SEL`) can no
longer be typed from the index alone -- their declared return type is a
generic marker (`__SEL__U`) only a call site can specialize -- so the
placeholder resolution moved from `post_index` to `post_annotate`,
where the annotated call provides the concrete type. Non-CFC projects
skip the pass through a cheap placeholder probe.

Also renders inline `ARRAY[..] OF` declarations, range statements and
VLA bounds properly in the AST serializer (previously `ARRAY[1..5] OF
DINT` printed as `15DINT`).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A method block arrives like an action (dotted `typeName` plus
`instanceName`) but, unlike an action, carries pins. Its return pin is
named after the method alone, so return-pin detection and the result
temporary's name now use the callee's unqualified name
(`Block::callee_name()`) -- previously the pin could never match the
dotted `typeName` and was emitted as a bogus output argument with a
dot inside the variable name. The IDE does not export method blocks
yet; the fixture pins the shape anticipated by analogy with actions
and functions.

Also removes the invented `ConnectionPointOut` from the in-out pins of
four hand-written fixtures: the IDE renders in-out pins on the left
side of a block only and never exports an out-side connection point
(verified against a real export).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the missing coverage for direct self-feedback wires (design doc
FB3): `function_block_feedback` proves cycle persistence end-to-end
(y = 1, 2, 3 -- a VAR_TEMP regression would transpile identically and
only fail there) and `feedback_in_function` pins the per-invocation
counterpart (a function's feedback wire reads the type default on
every call).

Also refreshes the stale `temp_N` renderings in the fixture and lit
READMEs to the actual `__<callee>_res_<n>` names, removes the dangling
`OPEN.md` references, labels unconditional returns as "return" instead
of "conditional return", and adds the crate's fixture-first workflow
notes (AGENTS.md).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants