Skip to content

fix(cfc): generic variable type resolution - #1840

Open
volsa wants to merge 6 commits into
masterfrom
vosa/type-inference
Open

fix(cfc): generic variable type resolution#1840
volsa wants to merge 6 commits into
masterfrom
vosa/type-inference

Conversation

@volsa

@volsa volsa commented Jul 29, 2026

Copy link
Copy Markdown
Member

Problem: The CFC implementation for stateless functions creates "temporary" variables for their return and output variable values. For example bar := foo(in := localIn, acc => localOut) where foo returns a DINT value will transpile to

VAR
    bar: DINT;
    localOut: DINT;
    __out_foo_1: DINT;
    __out_acc_1: DINT;
END_VAR

__out_foo_1 := foo(in := localIn, acc => __out_acc_1);
bar := __out_foo_1;
localOut := __out_acc_1;

However, for generics this is a bit tricker because generic type resolution requires first a call site bevore its types can be derived. As such the transpiler up until now generated the following for the same foo example assuming now both the return and output values are a generic of type T: ANY_NUM:

VAR
    bar: DINT;
    localOut: DINT;
    __out_foo_1: __foo__T;
    __out_acc_1: __foo__T;
END_VAR

__out_foo_1 := foo(in := localIn, acc => __out_acc_1);
bar := __out_foo_1;
localOut := __out_acc_1;

These "generic variables" never resolved and yielded an error when compiling.

Solution: Introduce an "inference" module, which uses the existing TypeAnnotator responsible for generic type resolution. In a loop then incrementally patch these generic variables to their concrete type if the annotator has enough information to do so. Repeat until exhausted.

Note: Strictly speaking this is not type inference, rather its an extension of the generic type resolver but specific for variables for CFC's use-case. A much nicer implementation would have been type inference using something similar to the "Hindley–Milner type system" algorithm also used in e.g. rustc. Thats a bigger architectural change however.

@github-actions

github-actions Bot commented Jul 29, 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.4 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 4 commits July 30, 2026 10:49
An unresolved generic like a CFC temporary still typed `__ADD__T` is
registered with the `Any` nature, so `get_bigger_type` saw it as both
numerical and real and promoted its pairing with any integer to REAL: a
builtin ADD/MUL/SUB/DIV feedback loop over DINT silently became floating
point (plus a spurious E067 downcast warning). The generic side now always
loses to the concrete side, which alone carries a size and class.

Covered by a typesystem unit test, a `variadic_feedback` transpile
snapshot, and an `add_feedback` lit test accumulating past 2^24 where a
REAL regression fails numerically. Also locks the mixed fixed/variadic
pin emission with a `mux_call` snapshot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
derive_generic_types seeded its bigger-type fold with the nature's
smallest possible type, so a binding whose candidates were all still
unresolved generics — a CFC temporary fed only by its own feedback
loop — fell back to that seed: `T: ANY_NUM` silently became USINT and
the accumulator wrapped at 255. Unresolved generics no longer vote at
all, and with no concrete candidate the binding now stays open, so the
call keeps its generic return type and the temporary reports as E149.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The hand-made generic fixtures used `bmx:TextDeclaration` without
declaring the prefix, which the compiler's namespace-agnostic reader
accepts but a conforming XML parser — the IDE import — rejects.
Declare `xmlns:bmx` on the root element like the pristine exports do.

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.

1 participant