Skip to content

Commit a230a3d

Browse files
committed
feat!: rework initializers
1 parent fb001ba commit a230a3d

File tree

69 files changed

+2722
-2581
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+2722
-2581
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

TODO.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Initializer Implementation Status
2+
3+
## Overview
4+
5+
The goal is for each datatype/POU to get a constructor (`<TypeName>_ctor`), and then a global constructor is created with calls to all constructors in the unit.
6+
7+
## Current Implementation
8+
9+
### What's Already Created (in `plc_lowering/src/initializer.rs`)
10+
11+
#### 1. Per-Type Constructors (`<TypeName>_ctor`)
12+
- [x] Collects constructor body statements for each POU and struct
13+
- [x] Tracks `Body::Internal`, `Body::External`, or `Body::None` per type
14+
- [x] Generates assignment statements (`self.field := value`) for variables with initializers
15+
- [x] Handles nested struct initializers
16+
- [x] Generates call to user-defined `FB_INIT` method if present
17+
- [x] Creates the constructor POU and Implementation AST nodes via `new_constructor()`
18+
- [x] Adds constructors to the CompilationUnit
19+
20+
#### 2. Stack Constructors (for temp/local variables in stateless POUs)
21+
- [x] Tracks per-POU (`stack_constructor` map)
22+
- [x] Applied to function bodies
23+
24+
#### 3. Global Constructor Statements
25+
- [x] Collects statements for global variables into `global_constructor` Vec
26+
- [x] Generated statements for global initializers
27+
28+
---
29+
30+
## What's Missing
31+
32+
### 4. Features from Old Implementation Not Yet Ported
33+
34+
Comparing old `InitVisitor` (in `src/lowering/init_visitor.rs` + `src/lowering/initializers.rs`) vs new `Initializer`:
35+
36+
| Feature | Old (`InitVisitor`) | New (`Initializer`) |
37+
|---------|---------------------|---------------------|
38+
| Type constructors (`__init_<type>`) | Done | Done (`<type>_ctor`) |
39+
| User init functions (`__user_init_<type>`) | Done | Missing |
40+
| Global wrapper (`__init___<project>`) | Done | Missing |
41+
| VAR_CONFIG init | Done | Missing |
42+
| VTable init in constructor | Done | Missing |
43+
| Stack var init in functions | Done | Collected but not applied |
44+
| External linkage handling | Done | Done |
45+
46+
---
47+
48+
## Implementation Plan
49+
50+
### Step 1: Generate Global Constructor
51+
52+
In `apply_initialization()`, after adding all type constructors:
53+
54+
```rust
55+
// Create __global_ctor function
56+
if !self.global_constructor.is_empty() {
57+
let mut global_ctor_body = vec![];
58+
59+
// Add calls to constructors for global struct instances
60+
for (var_name, var_type) in global_struct_instances {
61+
if self.constructors.contains_key(&var_type) {
62+
let call = create_call_statement(
63+
&format!("{}_ctor", var_type),
64+
var_name,
65+
None,
66+
self.id_provider.clone(),
67+
&SourceLocation::internal(),
68+
);
69+
global_ctor_body.push(call);
70+
}
71+
}
72+
73+
// Add collected assignment statements
74+
global_ctor_body.extend(self.global_constructor);
75+
76+
// Create the POU and implementation
77+
let (pou, impl) = new_global_constructor("__global_ctor", global_ctor_body, ...);
78+
unit.pous.push(pou);
79+
unit.implementations.push(impl);
80+
}
81+
```
82+
83+
### Step 2: Apply Stack Constructors
84+
85+
Modify function implementations to include stack initialization:
86+
87+
```rust
88+
// For each implementation in unit.implementations
89+
for impl in &mut unit.implementations {
90+
if let Some(Body::Internal(stmts)) = self.stack_constructor.get(&impl.name) {
91+
// Prepend stack constructor statements to the implementation body
92+
let mut new_body = stmts.clone();
93+
new_body.extend(impl.statements.drain(..));
94+
impl.statements = new_body;
95+
}
96+
}
97+
```
98+
99+
### Step 3: Ensure Constructor Call Chain
100+
101+
In `visit_variable()`, verify that for struct-typed variables:
102+
1. First, call the struct's constructor: `<StructType>_ctor(self.var_name)`
103+
2. Then, apply any field overrides from the initializer
104+
105+
### Step 4: Port Remaining Features
106+
107+
1. **VTable initialization:** Add `self.__vtable := ADR(__vtable_<type>_instance)` to constructor body for classes/FBs
108+
2. **User init functions:** Generate `__user_init_<type>` that calls `FB_INIT` if present
109+
3. **VAR_CONFIG init:** Handle VAR_CONFIG initialization in global constructor
110+
111+
---
112+
113+
## Files to Modify
114+
115+
- `compiler/plc_lowering/src/initializer.rs` - Main implementation
116+
- `src/lowering/helper.rs` - May need additional helper functions
117+
- `compiler/plc_driver/src/pipelines/participant.rs` - InitParticipant integration
118+
119+
## Testing
120+
121+
Existing tests in `compiler/plc_lowering/src/initializer.rs` cover:
122+
- Struct constructors
123+
- Nested structs
124+
- Pointer initializers
125+
- Enum defaults
126+
- Global constructor collection
127+
- Function/program constructors
128+
- External types
129+
- FB_INIT calls
130+
- Inheritance chains
131+
132+
Additional tests needed:
133+
- [ ] Global constructor function generation
134+
- [ ] Stack constructor application to function bodies
135+
- [ ] Constructor call chain for nested structs
136+
- [ ] VTable initialization
137+
- [ ] Integration with codegen (actual LLVM output)

compiler/plc_ast/src/ast.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,26 @@ impl Pou {
296296
pub fn is_generic(&self) -> bool {
297297
!self.generics.is_empty()
298298
}
299+
300+
pub fn is_stateful(&self) -> bool {
301+
matches!(self.kind, PouType::Program | PouType::FunctionBlock | PouType::Action | PouType::Class)
302+
}
303+
304+
pub fn is_external(&self) -> bool {
305+
matches!(self.linkage, LinkageType::External)
306+
}
307+
308+
pub fn is_built_in(&self) -> bool {
309+
matches!(self.linkage, LinkageType::BuiltIn)
310+
}
311+
312+
pub fn is_function_block(&self) -> bool {
313+
matches!(self.kind, PouType::FunctionBlock)
314+
}
315+
316+
pub fn is_class(&self) -> bool {
317+
matches!(self.kind, PouType::Class)
318+
}
299319
}
300320

301321
#[derive(Debug, PartialEq)]
@@ -313,11 +333,19 @@ pub struct Implementation {
313333
pub access: Option<AccessModifier>,
314334
}
315335

336+
/// Marks declaration and linking requirements for an ast member
316337
#[derive(Debug, Copy, PartialEq, Eq, Clone, Hash)]
317338
pub enum LinkageType {
339+
/// The element is declared in the project currently being complied
318340
Internal,
341+
/// The element is declared externally and being used by the project
319342
External,
343+
/// This indicates an element that should not have any declarations within the compiled project
344+
/// For example a built in function is implied to exist but not declared
320345
BuiltIn,
346+
// TODO: A private linkage indicates an internal element that should not be visible externally
347+
// This is for example a static constructor that should not leak outside its module
348+
// Private,
321349
}
322350

323351
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -480,6 +508,19 @@ pub enum VariableBlockType {
480508
InOut,
481509
External,
482510
}
511+
impl VariableBlockType {
512+
pub fn is_temp(&self) -> bool {
513+
matches!(self, VariableBlockType::Temp)
514+
}
515+
516+
pub fn is_local(&self) -> bool {
517+
matches!(self, VariableBlockType::Local)
518+
}
519+
520+
pub fn is_global(&self) -> bool {
521+
matches!(self, VariableBlockType::Global)
522+
}
523+
}
483524

484525
impl Display for VariableBlockType {
485526
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
@@ -517,6 +558,11 @@ impl VariableBlock {
517558
VariableBlock::default().with_block_type(VariableBlockType::Global)
518559
}
519560

561+
pub fn with_linkage(mut self, linkage: LinkageType) -> Self {
562+
self.linkage = linkage;
563+
self
564+
}
565+
520566
pub fn with_block_type(mut self, block_type: VariableBlockType) -> Self {
521567
self.kind = block_type;
522568
self
@@ -526,6 +572,14 @@ impl VariableBlock {
526572
self.variables = variables;
527573
self
528574
}
575+
576+
pub fn is_local(&self) -> bool {
577+
matches!(self.kind, VariableBlockType::Local)
578+
}
579+
580+
pub fn is_temp(&self) -> bool {
581+
matches!(self.kind, VariableBlockType::Temp)
582+
}
529583
}
530584

531585
impl Default for VariableBlock {
@@ -640,9 +694,9 @@ impl DataTypeDeclaration {
640694
}
641695
}
642696

643-
pub fn get_referenced_type(&self) -> Option<String> {
697+
pub fn get_referenced_type(&self) -> Option<&str> {
644698
let DataTypeDeclaration::Reference { referenced_type, .. } = self else { return None };
645-
Some(referenced_type.to_owned())
699+
Some(referenced_type.as_str())
646700
}
647701

648702
pub fn get_inner_pointer_ty(&self) -> Option<DataTypeDeclaration> {
@@ -679,6 +733,7 @@ pub struct UserTypeDeclaration {
679733
pub location: SourceLocation,
680734
/// stores the original scope for compiler-generated types
681735
pub scope: Option<String>,
736+
pub linkage: LinkageType,
682737
}
683738

684739
impl Debug for UserTypeDeclaration {
@@ -804,6 +859,10 @@ impl DataType {
804859
pub fn is_type_safe_pointer(&self) -> bool {
805860
matches!(self, DataType::PointerType { type_safe: true, .. })
806861
}
862+
863+
pub fn is_generic(&self) -> bool {
864+
matches!(self, DataType::GenericType { .. })
865+
}
807866
}
808867

809868
fn replace_reference(

compiler/plc_ast/src/pre_processor.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,13 @@ pub fn pre_process(unit: &mut CompilationUnit, mut id_provider: IdProvider) {
6161
if let DataTypeDeclaration::Definition { mut data_type, location, scope } = *datatype {
6262
data_type.set_name(type_name);
6363
add_nested_datatypes(name, &mut data_type, &mut new_types, &location);
64-
let data_type =
65-
UserTypeDeclaration { data_type: *data_type, initializer: None, location, scope };
64+
let data_type = UserTypeDeclaration {
65+
data_type: *data_type,
66+
initializer: None,
67+
location,
68+
scope,
69+
linkage: crate::ast::LinkageType::Internal,
70+
};
6671
new_types.push(data_type);
6772
}
6873
}
@@ -289,6 +294,7 @@ fn preprocess_generic_structs(pou: &mut Pou) -> Vec<UserTypeDeclaration> {
289294
initializer: None,
290295
scope: Some(pou.name.clone()),
291296
location: pou.location.clone(),
297+
linkage: crate::ast::LinkageType::Internal,
292298
};
293299
types.push(data_type);
294300
generic_types.insert(binding.name.clone(), new_name);
@@ -314,8 +320,13 @@ fn preprocess_return_type(pou: &mut Pou, types: &mut Vec<UserTypeDeclaration>) {
314320
if let Some(DataTypeDeclaration::Definition { mut data_type, location, scope }) = datatype {
315321
data_type.set_name(type_name);
316322
add_nested_datatypes(pou.name.as_str(), &mut data_type, types, &location);
317-
let data_type =
318-
UserTypeDeclaration { data_type: *data_type, initializer: None, location, scope };
323+
let data_type = UserTypeDeclaration {
324+
data_type: *data_type,
325+
initializer: None,
326+
location,
327+
scope,
328+
linkage: crate::ast::LinkageType::Internal,
329+
};
319330
types.push(data_type);
320331
}
321332
}
@@ -350,7 +361,13 @@ fn pre_process_variable_data_type(
350361
// create index entry
351362
add_nested_datatypes(new_type_name.as_str(), &mut data_type, types, &location);
352363
data_type.set_name(new_type_name);
353-
types.push(UserTypeDeclaration { data_type: *data_type, initializer: None, location, scope });
364+
types.push(UserTypeDeclaration {
365+
data_type: *data_type,
366+
initializer: None,
367+
location,
368+
scope,
369+
linkage: crate::ast::LinkageType::Internal,
370+
});
354371
}
355372
//make sure it gets generated
356373
}
@@ -379,6 +396,7 @@ fn add_nested_datatypes(
379396
initializer: None,
380397
location: location.clone(),
381398
scope,
399+
linkage: crate::ast::LinkageType::Internal,
382400
});
383401
}
384402
}

compiler/plc_diagnostics/src/diagnostics.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::{
2+
backtrace,
23
fmt::Display,
34
ops::{Deref, DerefMut},
45
};
@@ -276,6 +277,7 @@ impl Diagnostic {
276277
where
277278
T: Into<SourceLocation>,
278279
{
280+
panic!("Unresolved reference to {}", reference);
279281
Diagnostic::new(format!("Could not resolve reference to {reference:}"))
280282
.with_error_code("E048")
281283
.with_location(location)
@@ -303,6 +305,7 @@ impl Diagnostic {
303305

304306
pub fn cannot_generate_call_statement(operator: &AstNode) -> Diagnostic {
305307
//TODO: We could probably get a better slice here
308+
panic!("Cannot generate call statement for {:?}", operator);
306309
Diagnostic::codegen_error(format!("cannot generate call statement for {:?}", operator), operator)
307310
}
308311

0 commit comments

Comments
 (0)