@@ -5,8 +5,8 @@ use crate::{
5
5
use initializers:: { get_user_init_fn_name, Init , InitAssignments , Initializers , GLOBAL_SCOPE } ;
6
6
use plc_ast:: {
7
7
ast:: {
8
- AstFactory , AstNode , AstStatement , CallStatement , CompilationUnit , ConfigVariable , DataType ,
9
- LinkageType , PouType ,
8
+ Assignment , AstFactory , AstNode , AstStatement , CallStatement , CompilationUnit , ConfigVariable ,
9
+ DataType , LinkageType , PouType , ReferenceExpr ,
10
10
} ,
11
11
mut_visitor:: { AstVisitorMut , WalkerMut } ,
12
12
provider:: IdProvider ,
@@ -36,7 +36,7 @@ impl InitVisitor {
36
36
) -> Vec < CompilationUnit > {
37
37
let mut visitor = Self :: new ( index, unresolvables, id_provider) ;
38
38
// before visiting, we need to collect all candidates for user-defined init functions
39
- units. iter ( ) . for_each ( |unit| {
39
+ units. iter_mut ( ) . for_each ( |unit| {
40
40
visitor. collect_user_init_candidates ( unit) ;
41
41
} ) ;
42
42
// visit all units
@@ -70,7 +70,7 @@ impl InitVisitor {
70
70
self . ctxt . scope ( old) ;
71
71
}
72
72
73
- fn collect_user_init_candidates ( & mut self , unit : & CompilationUnit ) {
73
+ fn collect_user_init_candidates ( & mut self , unit : & mut CompilationUnit ) {
74
74
// collect all candidates for user-defined init functions
75
75
for pou in unit. pous . iter ( ) . filter ( |it| matches ! ( it. kind, PouType :: FunctionBlock | PouType :: Program ) )
76
76
{
@@ -80,7 +80,7 @@ impl InitVisitor {
80
80
}
81
81
82
82
for user_type in
83
- unit. user_types . iter ( ) . filter ( |it| matches ! ( it. data_type, DataType :: StructType { .. } ) )
83
+ unit. user_types . iter_mut ( ) . filter ( |it| matches ! ( it. data_type, DataType :: StructType { .. } ) )
84
84
{
85
85
// add the struct to potential `STRUCT_INIT` candidates
86
86
if let Some ( name) = user_type. data_type . get_name ( ) {
@@ -284,30 +284,31 @@ impl InitVisitor {
284
284
fn update_struct_initializers ( & mut self , user_type : & mut plc_ast:: ast:: UserTypeDeclaration ) {
285
285
let effective_type =
286
286
user_type. data_type . get_name ( ) . and_then ( |it| self . index . find_effective_type_by_name ( it) ) ;
287
- if let DataType :: StructType { .. } = & user_type. data_type {
287
+ if let DataType :: StructType { ref mut variables , .. } = & mut user_type. data_type {
288
288
let Some ( ty) = effective_type else {
289
289
return user_type. walk ( self ) ;
290
290
} ;
291
291
let name = ty. get_name ( ) ;
292
292
293
- let member_inits = self
294
- . index
295
- . get_container_members ( name)
296
- . iter ( )
297
- . filter_map ( |var| {
298
- // struct member initializers don't have a qualifier/scope while evaluated in `const_evaluator.rs` and are registered as globals under their data-type name ;
299
- // look for member initializers for this struct in the global initializers, remove them and add new entries with the correct qualifier and left-hand-side
300
- self . unresolved_initializers
301
- . get_mut ( GLOBAL_SCOPE )
302
- . and_then ( |it| it. swap_remove ( var . get_type_name ( ) ) )
303
- . map ( |node| ( var . get_name ( ) , node ) )
304
- } )
305
- . collect :: < Vec < _ > > ( ) ;
306
-
307
- for ( lhs , init ) in member_inits {
308
- // update struct member initializers
309
- self . unresolved_initializers . maybe_insert_initializer ( name , Some ( lhs ) , & init ) ;
293
+ for variable in variables {
294
+ self . unresolved_initializers . maybe_insert_initializer (
295
+ name,
296
+ Some ( & variable . name ) ,
297
+ & variable . initializer ,
298
+ ) ;
299
+
300
+ // XXX: Very duct-tapey but essentially we now have two initializers, one in the struct datatype
301
+ // definition itself (`DataType::StructType { initializer: Some(<arena id>), ... }`) and one in the
302
+ // `__init_*` function now. The former is unresolvable because it has the raw initializer, e.g.
303
+ // `foo := (a := (b := (c := REF(...))))` whereas the latter is resolvable because it yields something
304
+ // like `foo.a.b.c := REF(...)`. Thus we remove the initializer from the struct datatype definition
305
+ // as the codegen would otherwise fail at generating them and result in a `Cannot generate values for..`
306
+ // Literals and references are ignored however, since they are resolvable and / or constant.
307
+ if variable . initializer . as_ref ( ) . is_some_and ( |opt| !opt . is_literal ( ) && !opt . is_reference ( ) ) {
308
+ variable . initializer = None ;
309
+ }
310
310
}
311
+
311
312
// add container to keys if not already present
312
313
self . unresolved_initializers . maybe_insert_initializer ( name, None , & user_type. initializer ) ;
313
314
}
@@ -429,18 +430,76 @@ fn create_member_reference(ident: &str, id_provider: IdProvider, base: Option<As
429
430
create_member_reference_with_location ( ident, id_provider, base, SourceLocation :: internal ( ) )
430
431
}
431
432
432
- fn create_assignment_if_necessary (
433
- lhs_ident : & str ,
434
- base_ident : Option < & str > ,
433
+ /// Takes some expression such as `bar := (baz := (qux := ADR(val)), baz2 := (qux := ADR(val)))` returning all final
434
+ /// assignment paths such as [`bar.baz.qux := ADR(val)`, `bar.baz2.qux := ADR(val)`].
435
+ fn create_assignment_paths ( node : & AstNode , id_provider : IdProvider ) -> Vec < Vec < AstNode > > {
436
+ match node. get_stmt ( ) {
437
+ AstStatement :: Assignment ( Assignment { left, right } ) => {
438
+ let mut result = create_assignment_paths ( right, id_provider. clone ( ) ) ;
439
+ for inner in result. iter_mut ( ) {
440
+ inner. insert ( 0 , left. as_ref ( ) . clone ( ) ) ;
441
+ }
442
+ result
443
+ }
444
+ AstStatement :: ExpressionList ( nodes) => {
445
+ let mut result = vec ! [ ] ;
446
+ for node in nodes {
447
+ let inner = create_assignment_paths ( node, id_provider. clone ( ) ) ;
448
+ result. extend ( inner) ;
449
+ }
450
+ result
451
+ }
452
+ AstStatement :: ParenExpression ( node) => create_assignment_paths ( node, id_provider) ,
453
+ _ => vec ! [ vec![ node. clone( ) ] ] ,
454
+ }
455
+ }
456
+
457
+ /// Takes some expression such as `foo : FooStruct := (bar := (baz := (qux := ADR(val)), baz2 := (qux := ADR(val))));`
458
+ /// and returns assignments of form [`foo.bar.baz.qux := ADR(val)`, `foo.bar.baz2.qux := ADR(val)`].
459
+ fn create_assignments_from_initializer (
460
+ var_ident : & str ,
461
+ self_ident : Option < & str > ,
435
462
rhs : & Option < AstNode > ,
436
463
mut id_provider : IdProvider ,
437
- ) -> Option < AstNode > {
438
- let lhs = create_member_reference (
439
- lhs_ident,
440
- id_provider. clone ( ) ,
441
- base_ident. map ( |id| create_member_reference ( id, id_provider. clone ( ) , None ) ) ,
442
- ) ;
443
- rhs. as_ref ( ) . map ( |node| AstFactory :: create_assignment ( lhs, node. to_owned ( ) , id_provider. next_id ( ) ) )
464
+ ) -> Vec < AstNode > {
465
+ let Some ( initializer) = rhs else {
466
+ return Vec :: new ( ) ;
467
+ } ;
468
+
469
+ let mut result = vec ! [ ] ;
470
+ for mut path in create_assignment_paths ( initializer, id_provider. clone ( ) ) {
471
+ path. insert ( 0 , create_member_reference ( var_ident, id_provider. clone ( ) , None ) ) ;
472
+ if self_ident. is_some ( ) {
473
+ path. insert ( 0 , create_member_reference ( "self" , id_provider. clone ( ) , None ) ) ;
474
+ }
475
+
476
+ let right = path. pop ( ) . expect ( "must have at least one node in the path" ) ;
477
+ let mut left = path. pop ( ) . expect ( "must have at least one node in the path" ) ;
478
+
479
+ for node in path. into_iter ( ) . rev ( ) {
480
+ insert_base_node ( & mut left, node) ;
481
+ }
482
+
483
+ result. push ( AstFactory :: create_assignment ( left, right, id_provider. next_id ( ) ) ) ;
484
+ }
485
+
486
+ result
487
+ }
488
+
489
+ /// Inserts a new base node into the member reference chain. For example a call such as `insert_base_node("b.c", a")`
490
+ /// will yield `a.b.c`.
491
+ fn insert_base_node ( member : & mut AstNode , new_base : AstNode ) {
492
+ match & mut member. stmt {
493
+ AstStatement :: ReferenceExpr ( ReferenceExpr { base, .. } ) => match base {
494
+ Some ( inner) => insert_base_node ( inner, new_base) ,
495
+ None => {
496
+ // We hit the end of the chain, simply replace the base (which must be None) with the new one
497
+ base. replace ( Box :: new ( new_base) ) ;
498
+ }
499
+ } ,
500
+
501
+ _ => panic ! ( "invalid function call, expected a member reference" ) ,
502
+ }
444
503
}
445
504
446
505
fn create_ref_assignment (
0 commit comments