Draft
Conversation
Add a new DataTypeInformation::Interface variant so that interfaces are tracked as first-class data types. This allows variables, inputs, arrays, and return types to reference interface types directly. Because interfaces are now indexed as data types, the dedicated validate_unique_interfaces check is removed in favor of the existing data type uniqueness validation.
…ch structure Move vtable.rs and polymorphism.rs into a new polymorphism/ module tree organized by concern (table/ for vtable generation, dispatch/ for call lowering) and by target (pou.rs for classes/function blocks, interface.rs as empty placeholders for future work). Introduce PolymorphismLowerer as a single entry point that replaces the separate VirtualTableGenerator and PolymorphicCallLowerer pipeline participants, simplifying the pipeline registration to a single participant.
…ispatch Add InterfaceTableGenerator which produces per-interface itable struct definitions (one function pointer per method, including inherited methods) and per-(interface, POU) global instances with initializers pointing to the POU's concrete method implementations. Handles deep POU inheritance chains, interface EXTENDS hierarchies, diamond patterns, method overrides at any level, and multi-unit placement (struct definitions in the interface's unit, instances in the POU's unit). Integrated into the existing TableGenerator alongside VirtualTableGenerator.
…e declarations Replace all interface-typed declarations (variables, params, arrays, return types) with __FATPOINTER, a shared struct containing data and table void pointers. The struct is injected on demand into the first compilation unit. Alias types are resolved via find_effective_type_by_name.
Replace owned Option<Index>/Option<AnnotationMapImpl> with shared references in PolymorphicCallLowerer and InterfaceDispatchLowerer. Removes unnecessary take/unwrap patterns and the unused Index return value from DispatchLowerer::lower.
Expand `ref := instance` into two fat pointer field assignments: ref.data := ADR(instance) ref.table := ADR(__itable_<interface>_<POU>_instance) Both assignments are packed into an ExpressionList node since the visitor only has access to a single node. The codegen statement generator already iterates ExpressionList children as individual statements. Global itable references are wrapped in ReferenceExpr(Member(...)) so the re-annotation pass can resolve them as global variables.
… dispatch Implements concrete POU → interface expansion for both assignments and call arguments (unnamed and named). Assignments expand into fat pointer field writes (data + table). Call arguments allocate a temporary fat pointer, initialize it, and substitute the original argument. Also serializes AllocationStatement in the AST serializer and adds nesting tests for multi-depth call argument wrapping.
…rect dispatch Interface method calls lowered to itable dispatch (e.g. `__itable_IA#(ref.table^).foo^(ref.data)`) require an LLVM function stub so that `build_indirect_call` can resolve the function type signature. Previously, interface methods were only registered as POU declarations — not as implementations — so codegen could not find them. Register an ImplementationIndexEntry for each interface method during indexing. This causes LLVM function stubs to be generated alongside concrete method stubs. Two places in pou_generator assumed every method's parent class has a struct type, which is not the case for interfaces (they are abstract): - collect_parameters_for_implementation: skip the struct-type sanity check when the parent is an interface - debug info parameter list: filter out interface types that have no debug type information
… in dispatch - Override visit_interface in InterfaceDispatchLowerer so interface method parameters with interface types get rewritten to __FATPOINTER, matching the implementing POUs (fixes signature mismatch error E112) - Wrap the self-pointer argument in a deref (reference.data^) so codegen loads the actual instance pointer instead of passing the address of the fat pointer's data slot (fixes Bus error for aggregate return types) - Add lit tests for interface polymorphism: arguments (named, positional, mixed, sequential, implicit), aggregate returns (array, string, struct), diamond/linear inheritance, and basic dispatch
…INTER - Register a forward declaration for internal (no source location) struct types in create_struct_type instead of silently returning, so they are available for debug info references - Thread index/types_index through create_function and create_subroutine_type to enable lazy debug type registration for types not yet in the map - Update debug test snapshots
7c104e0 to
5ffed38
Compare
- Add #[allow(clippy::too_many_arguments)] to create_function in debug.rs - Replace `diagnostics.len() > 0` with `!diagnostics.is_empty()` - Remove unnecessary `&` on format string arguments - Remove unnecessary trailing `return` - Simplify closure to function reference - Reorder imports alphabetically - Apply rustfmt line wrapping adjustments
…atch When a call with interface arguments is the RHS of an assignment (e.g. `result := ref.foo(instance)`), the fat pointer preamble was incorrectly nested under the assignment, producing: `result := alloca ..., tmp.data := ..., tmp.table := ..., call` instead of: `alloca ..., tmp.data := ..., tmp.table := ..., result := call` Fix by detecting the enclosing assignment context in visit_call_statement and routing the preamble through assignment_preamble so the assignment wraps only the call. Also save/restore assignment_ctx in visit_assignment to prevent inner named-parameter assignments from clobbering the outer context.
…to post_annotate Previously, AggregateTypeLowerer modified POU signatures (inserting a VAR_IN_OUT parameter for aggregate returns) in post_index. This shifted parameter positions before the annotation phase, causing the resolver to assign incorrect type hints to call arguments. In particular, when a function had both an aggregate return type (e.g. STRING) and an interface-typed parameter, the InterfaceDispatchLowerer would see the wrong type hint and fail to wrap the argument in a fat pointer. By removing the post_index hook and performing all aggregate lowering in post_annotate, the annotation phase sees the original unmodified POU signatures with correct parameter positions. The post_annotate handler now also re-indexes from the modified units (instead of reusing the stale pre-modification index) so that subsequent pipeline stages and validation see the updated signatures.
…ateTypeLowerer When AggregateTypeLowerer's map() encountered an ExpressionList produced by a previous pass (e.g. InterfaceDispatchLowerer's fat pointer preamble), it used a single shared scope for all children. This caused the aggregate alloca and extracted call to be prepended before the entire list, placing the call before the fat pointer setup it depends on. Fix by detecting ExpressionList nodes in map() and processing each element with its own scope, so generated preamble statements are placed directly before the element that produced them. Also un-ignores interface_method_call_with_aggregate_return_and_interface_argument and adds integration tests covering: - STRING return + interface argument (method dispatch) - STRUCT return + interface argument (method dispatch) - STRUCT return + mixed scalar/interface args, unnamed and reordered named - Free FUNCTION with STRING return + interface argument
Replace drain(..).collect() with std::mem::take() per clippy suggestion.
- Rename existing tests with group prefixes: - hierarchy_: single, implicit, linear, diamond - type_: array/string/struct arguments and returns - combined_: aggregate return + interface argument tests - argument_: unchanged (already well-named) - Add new tests: - hierarchy_method_name_collision: two unrelated interfaces with same-named method - dispatch_member_variable: interface ref stored as FB member field - dispatch_conditional: IF/CASE runtime dispatch - dispatch_recursive: chained and self-referential interface dispatch - type_array_of_interfaces: array of root interface with hierarchy (IA <- IB <- IC) - Remove stale Output/ directory (lit artifacts, regenerated on next run)
Fix seven discrepancies between the polymorphism design document
and the actual code:
1. VTable naming case: __vtable_fbA → __vtable_FbA throughout,
matching the code's format!("__vtable_{}", pou.name) pattern.
2. Doc comment in table/pou.rs: __vtable_instance_A →
__vtable_A_instance to match get_vtable_instance_name().
3. Initializer placement: move default initializers from the
global vtable instance onto the struct member definitions,
matching the code where members carry ADR(...) defaults and
the instance has no explicit initializer.
4. __body entry: document the __body function pointer that
function blocks get as the first vtable field, with a note
that ASCII diagrams omit it for brevity.
5. Instance argument cast: add FbA#(...) wrapping around the
instance argument in all dispatch examples, matching the
maybe_cast_instance() behavior in dispatch/pou.rs.
6. THIS calls: add THIS alongside SUPER in the list of calls
left untouched by dispatch lowering.
7. __itable_ID field order: correct bar,foo,baz,qux to
foo,bar,baz,qux matching the DFS order the code produces.
The concrete→interface mismatch detection only lived in visit_reference_expr, which never fires for call expressions. Functions returning a concrete type (e.g. FbA) assigned to an interface variable (e.g. IA) were not wrapped in a fat pointer. Extract the detection logic into a shared maybe_expand_fat_pointer method and call it from both visit_reference_expr and visit_call_statement.
Indent list continuation lines to align with the list item text, fixing clippy doc_lazy_continuation warnings.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1588 +/- ##
==========================================
- Coverage 94.95% 94.73% -0.22%
==========================================
Files 174 190 +16
Lines 56307 60323 +4016
==========================================
+ Hits 53464 57150 +3686
- Misses 2843 3173 +330 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Revert the broad forward-declaration registration for all internal struct types introduced to fix a panic when __FATPOINTER appeared as a function parameter. Instead, gracefully skip unregistered types: create_subroutine_type filters out missing parameter types, get_or_create_debug_type returns Err for unresolvable types so callers can skip them, and generate_debug_types logs and continues instead of aborting. This keeps vtables, itables, and fat pointers out of the DWARF output entirely.
Reformat closure and function call expressions in debug.rs and data_type_generator.rs to satisfy rustfmt line-length rules.
The alloca alignment for small types (i8, i16) differs between macOS and Linux CI (1/2 vs 4). Restrict these tests to macOS until platform-specific snapshots are added for Linux.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First working version of interface based polymorphism. Some things still need to be tackled in follow-up PRs, most importantly:
instanceofrefA := refB)That being said, interface based polymorphism with this PR should work for most use cases.
I've included a draft of a technical document which I want to add to our book once polymorphism is feature complete. For now it should serve as a good starting point to understand the concept and get started with reviewing this PR. In general my recommendation is: Read the draft followed by the files located in the
lowering/polymorphismfolder followed by exploring the remaining changes. Alternatively start with the draft then tests and then thelowering/polymorphism, up to you guys. Also note, the draft is a mix of human writing and LLM touch-ups, it should be correct however. Again, I will refine it once polymorphism is feature complete.