@@ -127,15 +127,15 @@ impl InitVisitor {
127127
128128fn create_var_config_init ( statements : Vec < AstNode > , mut id_provider : IdProvider ) -> CompilationUnit {
129129 let loc = SourceLocation :: internal_in_unit ( Some ( INIT_COMPILATION_UNIT ) ) ;
130- let pou = new_pou ( VAR_CONFIG_INIT , id_provider. next_id ( ) , vec ! [ ] , PouType :: Init , & loc) ; // this can probably just be internal
131- let implementation = new_implementation ( VAR_CONFIG_INIT , statements, PouType :: Init , loc) ;
130+ let pou = new_pou ( VAR_CONFIG_INIT , id_provider. next_id ( ) , vec ! [ ] , PouType :: Init , LinkageType :: Internal , & loc) ; // this can probably just be internal
131+ let implementation = new_implementation ( VAR_CONFIG_INIT , statements, PouType :: Init , LinkageType :: Internal , loc) ;
132132 new_unit ( pou, implementation, INIT_COMPILATION_UNIT )
133133}
134134
135135fn create_init_units ( lowerer : & InitVisitor ) -> Vec < CompilationUnit > {
136136 let lookup = lowerer. unresolved_initializers . keys ( ) . map ( |it| it. as_str ( ) ) . collect :: < FxIndexSet < _ > > ( ) ;
137137 lowerer
138- . unresolved_initializers
138+ . unresolved_initializers
139139 . iter ( )
140140 . filter_map ( |( container, init) | {
141141 // globals will be initialized in the `__init` body
@@ -158,11 +158,11 @@ fn create_init_unit(
158158 let mut id_provider = lowerer. ctxt . id_provider . clone ( ) ;
159159 let init_fn_name = get_init_fn_name ( container_name) ;
160160 log:: trace!( "creating {init_fn_name}" ) ;
161- let ( is_stateless, location) = lowerer
161+ let ( is_stateless, location, linkage ) = lowerer
162162 . index
163163 . find_pou ( container_name)
164- . map ( |it| ( it. is_function ( ) || it. is_method ( ) , it. get_location ( ) ) )
165- . unwrap_or_else ( || ( false , & lowerer. index . get_type_or_panic ( container_name) . location ) ) ;
164+ . map ( |it| ( it. is_function ( ) || it. is_method ( ) , it. get_location ( ) , * it . get_linkage ( ) ) )
165+ . unwrap_or_else ( || ( false , & lowerer. index . get_type_or_panic ( container_name) . location , LinkageType :: Internal ) ) ;
166166
167167 if is_stateless {
168168 // functions do not get their own init-functions -
@@ -188,7 +188,7 @@ fn create_init_unit(
188188 "self" . to_string ( ) ,
189189 ) ;
190190
191- let init_pou = new_pou ( & init_fn_name, id_provider. next_id ( ) , self_param, PouType :: Init , & location) ;
191+ let init_pou = new_pou ( & init_fn_name, id_provider. next_id ( ) , self_param, PouType :: Init , linkage , & location) ;
192192
193193 let mut statements = Vec :: new ( ) ;
194194
@@ -236,7 +236,7 @@ fn create_init_unit(
236236 . collect :: < Vec < _ > > ( ) ;
237237
238238 let statements = [ member_init_calls, statements] . concat ( ) ;
239- let implementation = new_implementation ( & init_fn_name, statements, PouType :: Init , location) ;
239+ let implementation = new_implementation ( & init_fn_name, statements, PouType :: Init , linkage , location) ;
240240
241241 Some ( new_unit ( init_pou, implementation, INIT_COMPILATION_UNIT ) )
242242}
@@ -261,8 +261,10 @@ fn create_user_init_units(lowerer: &InitVisitor) -> Vec<CompilationUnit> {
261261 location: location. clone( ) ,
262262 } ] ) ] ;
263263
264+
265+ let linkage = lowerer. index . find_pou ( container_name) . map ( |it|* it. get_linkage ( ) ) . unwrap_or ( LinkageType :: Internal ) ;
264266 let fn_name = get_user_init_fn_name ( container_name) ;
265- let init_pou = new_pou ( & fn_name, id_provider. next_id ( ) , param, PouType :: Init , & location) ;
267+ let init_pou = new_pou ( & fn_name, id_provider. next_id ( ) , param, PouType :: Init , linkage , & location) ;
266268
267269 let mut statements = lowerer
268270 . index
@@ -297,7 +299,7 @@ fn create_user_init_units(lowerer: &InitVisitor) -> Vec<CompilationUnit> {
297299 AstFactory :: create_call_statement ( op, None , id_provider. next_id ( ) , location. clone ( ) ) ;
298300 statements. push ( call_statement) ;
299301 }
300- let implementation = new_implementation ( & fn_name, statements, PouType :: Init , location) ;
302+ let implementation = new_implementation ( & fn_name, statements, PouType :: Init , linkage , location) ;
301303
302304 new_unit ( init_pou, implementation, INIT_COMPILATION_UNIT )
303305 } )
@@ -319,6 +321,7 @@ fn create_init_wrapper_function(
319321 id_provider. next_id ( ) ,
320322 vec ! [ ] ,
321323 PouType :: ProjectInit ,
324+ LinkageType :: Internal ,
322325 & SourceLocation :: internal ( ) ,
323326 ) ;
324327
@@ -387,7 +390,7 @@ fn create_init_wrapper_function(
387390 let user_init_calls = get_global_user_init_statements ( lowerer) ;
388391 let statements = [ calls, statements, user_init_calls] . concat ( ) ;
389392 let implementation =
390- new_implementation ( init_symbol_name, statements, PouType :: ProjectInit , SourceLocation :: internal ( ) ) ;
393+ new_implementation ( init_symbol_name, statements, PouType :: ProjectInit , LinkageType :: Internal , SourceLocation :: internal ( ) ) ;
391394 let mut global_init = new_unit ( init_pou, implementation, init_symbol_name) ;
392395
393396 if skip_var_config {
@@ -449,6 +452,7 @@ fn new_pou(
449452 id : AstId ,
450453 variable_blocks : Vec < VariableBlock > ,
451454 kind : PouType ,
455+ linkage : LinkageType ,
452456 location : & SourceLocation ,
453457) -> Pou {
454458 Pou {
@@ -473,6 +477,7 @@ fn new_implementation(
473477 name : & str ,
474478 statements : Vec < AstNode > ,
475479 pou_type : PouType ,
480+ linkage : LinkageType ,
476481 location : SourceLocation ,
477482) -> Implementation {
478483 Implementation {
@@ -555,8 +560,13 @@ fn create_vtable_initializer(lowerer: &InitVisitor, ids: &mut IdProvider, pou_na
555560
556561#[ cfg( test) ]
557562mod tests {
563+ use inkwell:: module:: Linkage ;
564+ use insta:: assert_debug_snapshot;
565+ use plc_ast:: ast:: LinkageType ;
558566 use test_utils:: parse_and_validate_buffered_ast;
559567
568+ use crate :: lowering:: vtable;
569+
560570 #[ test]
561571 fn usertype_todo_better_name_00 ( ) {
562572 let src = r#"
@@ -1638,4 +1648,90 @@ mod tests {
16381648 ]
16391649 "# ) ;
16401650 }
1651+
1652+ #[ test]
1653+ fn function_block_initializer_with_base_call ( ) {
1654+ let src = r#"
1655+ FUNCTION_BLOCK FB_Base
1656+ VAR
1657+ baseValue: DINT := 5;
1658+ END_VAR
1659+ END_FUNCTION_BLOCK
1660+
1661+ FUNCTION_BLOCK FB_Derived EXTENDS FB_Base
1662+ VAR
1663+ derivedValue: DINT := 10;
1664+ END_VAR
1665+ END_FUNCTION_BLOCK
1666+ "# ;
1667+
1668+ let units = parse_and_validate_buffered_ast ( src) ;
1669+ // Check that a base initializer call is generated
1670+ let init_unit = & units
1671+ . iter ( )
1672+ . find ( |unit| unit. file . get_name ( ) . is_some_and ( |name| name == "__initializers" ) )
1673+ . unwrap ( ) ;
1674+ let init_base = init_unit. implementations . iter ( ) . find ( |it| it. name == "__init_fb_base" ) . unwrap ( ) ;
1675+ assert_eq ! ( init_base. linkage, LinkageType :: Internal ) ;
1676+ // Check that a vtable_init for base was generated
1677+ let vtable_init_base = init_unit. implementations . iter ( ) . find ( |it| it. name == "__init___vtable_fb_base" ) . unwrap ( ) ;
1678+ assert_eq ! ( vtable_init_base. linkage, LinkageType :: Internal ) ;
1679+ // Make sure the variable block containing the base vtable is internal
1680+ let variable_block = units[ 0 ] . global_vars . iter ( ) . find ( |it| it. variables . iter ( ) . any ( |it| it. name == "__vtable_FB_Base_instance" ) ) . unwrap ( ) ;
1681+ assert_eq ! ( variable_block. linkage, LinkageType :: Internal ) ;
1682+ // Make sure the derived initializer is still internal
1683+ let init_derived = init_unit. implementations . iter ( ) . find ( |it| it. name == "__init_fb_derived" ) . unwrap ( ) ;
1684+ assert_eq ! ( init_derived. linkage, LinkageType :: Internal ) ;
1685+ // Make sure the derived vtable initializer is still internal
1686+ let vtable_init_derived = init_unit. implementations . iter ( ) . find ( |it| it. name == "__init___vtable_fb_derived" ) . unwrap ( ) ;
1687+ assert_eq ! ( vtable_init_derived. linkage, LinkageType :: Internal ) ;
1688+ // Make sure the variable block containing the derived vtable is internal
1689+ let variable_block = units[ 0 ] . global_vars . iter ( ) . find ( |it| it. variables . iter ( ) . any ( |it| it. name == "__vtable_FB_Derived_instance" ) ) . unwrap ( ) ;
1690+ assert_eq ! ( variable_block. linkage, LinkageType :: Internal ) ;
1691+ }
1692+
1693+
1694+ #[ test]
1695+ fn function_block_initializer_with_external_base_call ( ) {
1696+ let src = r#"
1697+ {external}
1698+ FUNCTION_BLOCK FB_Base
1699+ VAR
1700+ baseValue: DINT := 5;
1701+ END_VAR
1702+ END_FUNCTION_BLOCK
1703+
1704+ FUNCTION_BLOCK FB_Derived EXTENDS FB_Base
1705+ VAR
1706+ derivedValue: DINT := 10;
1707+ END_VAR
1708+ END_FUNCTION_BLOCK
1709+ "# ;
1710+
1711+ let units = parse_and_validate_buffered_ast ( src) ;
1712+ // Check that a base initializer call is generated
1713+ let init_unit = & units
1714+ . iter ( )
1715+ . find ( |unit| unit. file . get_name ( ) . is_some_and ( |name| name == "__initializers" ) )
1716+ . unwrap ( ) ;
1717+ let init_base = init_unit. implementations . iter ( ) . find ( |it| it. name == "__init_fb_base" ) . unwrap ( ) ;
1718+ assert_eq ! ( init_base. linkage, LinkageType :: External ) ;
1719+ // Check that a vtable_init for base was generated
1720+ let vtable_init_base = init_unit. implementations . iter ( ) . find ( |it| it. name == "__init___vtable_fb_base" ) . unwrap ( ) ;
1721+ assert_eq ! ( vtable_init_base. linkage, LinkageType :: External ) ;
1722+ // Make sure the variable block containing the base vtable is internal
1723+ let variable_block = units[ 0 ] . global_vars . iter ( ) . find ( |it| it. variables . iter ( ) . any ( |it| it. name == "__vtable_FB_Base_instance" ) ) . unwrap ( ) ;
1724+ assert_eq ! ( variable_block. linkage, LinkageType :: External ) ;
1725+ // Make sure the derived initializer is still internal
1726+ let init_derived = init_unit. implementations . iter ( ) . find ( |it| it. name == "__init_fb_derived" ) . unwrap ( ) ;
1727+ assert_eq ! ( init_derived. linkage, LinkageType :: Internal ) ;
1728+ // Make sure the derived vtable initializer is still internal
1729+ let vtable_init_derived = init_unit. implementations . iter ( ) . find ( |it| it. name == "__init___vtable_fb_derived" ) . unwrap ( ) ;
1730+ assert_eq ! ( vtable_init_derived. linkage, LinkageType :: Internal ) ;
1731+ // Make sure the variable block containing the derived vtable is internal
1732+ let variable_block = units[ 0 ] . global_vars . iter ( ) . find ( |it| it. variables . iter ( ) . any ( |it| it. name == "__vtable_FB_Derived_instance" ) ) . unwrap ( ) ;
1733+ assert_eq ! ( variable_block. linkage, LinkageType :: Internal ) ;
1734+ }
1735+
1736+
16411737}
0 commit comments