diff --git a/compiler/plc_ast/src/ast.rs b/compiler/plc_ast/src/ast.rs index 889bb58680..2ea725a07f 100644 --- a/compiler/plc_ast/src/ast.rs +++ b/compiler/plc_ast/src/ast.rs @@ -360,6 +360,7 @@ pub enum VariableBlockType { Output, Global, InOut, + External, } impl Display for VariableBlockType { @@ -371,6 +372,7 @@ impl Display for VariableBlockType { VariableBlockType::Output => write!(f, "Output"), VariableBlockType::Global => write!(f, "Global"), VariableBlockType::InOut => write!(f, "InOut"), + VariableBlockType::External => write!(f, "External"), } } } diff --git a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs index 4721ab3afc..e705809f0d 100644 --- a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs +++ b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs @@ -207,6 +207,7 @@ lazy_static! { E103, Error, include_str!("./error_codes/E103.md"), // Immutable Hardware Binding E104, Error, include_str!("./error_codes/E104.md"), // Config Variable With Incomplete Address E105, Error, include_str!("./error_codes/E105.md"), // CONSTANT keyword in POU + E106, Warning, include_str!("./error_codes/E106.md"), // VAR_EXTERNAL have no effect E107, Error, include_str!("./error_codes/E107.md"), // Missing configuration for template variable E108, Error, include_str!("./error_codes/E108.md"), // Template variable is configured multiple times ); diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E106.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E106.md new file mode 100644 index 0000000000..16414f6415 --- /dev/null +++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E106.md @@ -0,0 +1,23 @@ +# VAR_EXTERNAL blocks have no effect + +Variables declared in a `VAR_EXTERNAL` block are currently ignored and the referenced globals will be used instead. + +Example: +``` +VAR_GLOBAL + myArray : ARRAY [0..10] OF INT; + myString: STRING; +END_VAR + +FUNCTION main +VAR_EXTERNAL CONSTANT + myArray : ARRAY [0..10] OF INT; +END_VAR + myArray[5] := 42; + myString := 'Hello, world!'; +END_FUNCTION +``` + +In this example, even though `arr` is declared as `VAR_EXTERNAL CONSTANT`, the `CONSTANT` constraint will be ignored and +the global `myArray` will be mutated. The global `myString` can be read from and written to from within `main` even though it +is not declared in a `VAR_EXTERNAL` block. \ No newline at end of file diff --git a/src/codegen/generators/data_type_generator.rs b/src/codegen/generators/data_type_generator.rs index b3d39496cb..3c6c951de2 100644 --- a/src/codegen/generators/data_type_generator.rs +++ b/src/codegen/generators/data_type_generator.rs @@ -177,7 +177,7 @@ impl<'ink, 'b> DataTypeGenerator<'ink, 'b> { if let DataTypeInformation::Struct { source, members, .. } = information { let members = members .iter() - .filter(|it| !it.is_temp() && !it.is_return()) + .filter(|it| !(it.is_temp() || it.is_return() || it.is_var_external())) .map(|m| self.types_index.get_associated_type(m.get_type_name())) .collect::, Diagnostic>>()?; diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index cae6025e52..a57a6940f7 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -596,7 +596,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { } // handle all parameters (without return!) - for m in members.iter().filter(|it| !it.is_return()) { + for m in members.iter().filter(|it| !(it.is_return() || it.is_var_external())) { let parameter_name = m.get_name(); let (name, variable) = if m.is_parameter() { @@ -713,7 +713,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { //Generate reference to parameter // cannot use index from members because return and temp variables may not be considered for index in build_struct_gep let mut var_count = 0; - for m in members.iter() { + for m in members.iter().filter(|it| !it.is_var_external()) { let parameter_name = m.get_name(); let (name, variable) = if m.is_temp() || m.is_return() { diff --git a/src/codegen/tests/code_gen_tests.rs b/src/codegen/tests/code_gen_tests.rs index f9f349e997..c993dd5fa7 100644 --- a/src/codegen/tests/code_gen_tests.rs +++ b/src/codegen/tests/code_gen_tests.rs @@ -3516,3 +3516,127 @@ fn array_of_struct_as_member_of_another_struct_and_variable_declaration_is_initi insta::assert_snapshot!(res); } + +#[test] +// XXX: this behaviour might change in future, for now `VAR_EXTERNAL` variables are ignored +fn variables_in_var_external_block_are_not_generated() { + let res = codegen( + " + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + + FUNCTION_BLOCK bar + VAR_EXTERNAL CONSTANT + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION_BLOCK + + PROGRAM baz + VAR_EXTERNAL CONSTANT + arr : ARRAY [0..100] OF INT; + END_VAR + END_PROGRAM + + CLASS qux + VAR_EXTERNAL + arr : ARRAY [0..100] OF INT; + END_VAR + END_CLASS + ", + ); + + insta::assert_snapshot!(res, @r###" + ; ModuleID = '' + source_filename = "" + + %bar = type {} + %baz = type {} + %qux = type {} + + @arr = global [101 x i16] zeroinitializer + @__bar__init = unnamed_addr constant %bar zeroinitializer + @baz_instance = global %baz zeroinitializer + @__qux__init = unnamed_addr constant %qux zeroinitializer + + define void @foo() { + entry: + ret void + } + + define void @bar(%bar* %0) { + entry: + ret void + } + + define void @baz(%baz* %0) { + entry: + ret void + } + + define void @qux(%qux* %0) { + entry: + ret void + } + ; ModuleID = '__initializers' + source_filename = "__initializers" + + %baz = type {} + %bar = type {} + %qux = type {} + + @baz_instance = external global %baz + @__bar__init = external global %bar + @__qux__init = external global %qux + + define void @__init_baz(%baz* %0) { + entry: + %self = alloca %baz*, align 8 + store %baz* %0, %baz** %self, align 8 + ret void + } + + declare void @baz(%baz*) + + define void @__init_bar(%bar* %0) { + entry: + %self = alloca %bar*, align 8 + store %bar* %0, %bar** %self, align 8 + ret void + } + + declare void @bar(%bar*) + + define void @__init_qux(%qux* %0) { + entry: + %self = alloca %qux*, align 8 + store %qux* %0, %qux** %self, align 8 + ret void + } + + declare void @qux(%qux*) + ; ModuleID = '__init___testproject' + source_filename = "__init___testproject" + + %baz = type {} + + @baz_instance = external global %baz + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + + define void @__init___testproject() { + entry: + call void @__init_baz(%baz* @baz_instance) + ret void + } + + declare void @__init_baz(%baz*) + + declare void @baz(%baz*) + "###); +} diff --git a/src/codegen/tests/initialization_test/complex_initializers.rs b/src/codegen/tests/initialization_test/complex_initializers.rs index f057939e6f..bbe44a304b 100644 --- a/src/codegen/tests/initialization_test/complex_initializers.rs +++ b/src/codegen/tests/initialization_test/complex_initializers.rs @@ -1296,3 +1296,75 @@ fn var_config_aliased_variables_initialized() { declare void @FB(%FB*) "###); } + +#[test] +fn var_external_blocks_are_ignored_in_init_functions() { + let res = codegen( + r" + VAR_GLOBAL + s: STRING; + refString AT s : STRING; + END_VAR + + FUNCTION_BLOCK foo + VAR_EXTERNAL + refString : STRING; + END_VAR + END_FUNCTION + + FUNCTION bar + VAR_EXTERNAL + refString : STRING; + END_VAR + END_FUNCTION + ", + ); + + insta::assert_snapshot!(res, @r###" + ; ModuleID = '' + source_filename = "" + + %foo = type {} + + @s = global [81 x i8] zeroinitializer + @refString = global [81 x i8]* null + @__foo__init = unnamed_addr constant %foo zeroinitializer + + define void @foo(%foo* %0) { + entry: + ret void + } + + define void @bar() { + entry: + ret void + } + ; ModuleID = '__initializers' + source_filename = "__initializers" + + %foo = type {} + + @__foo__init = external global %foo + + define void @__init_foo(%foo* %0) { + entry: + %self = alloca %foo*, align 8 + store %foo* %0, %foo** %self, align 8 + ret void + } + + declare void @foo(%foo*) + ; ModuleID = '__init___testproject' + source_filename = "__init___testproject" + + @s = external global [81 x i8] + @refString = external global [81 x i8]* + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + + define void @__init___testproject() { + entry: + store [81 x i8]* @s, [81 x i8]** @refString, align 8 + ret void + } + "###) +} diff --git a/src/index.rs b/src/index.rs index fbe0805902..a3c30a1bec 100644 --- a/src/index.rs +++ b/src/index.rs @@ -70,6 +70,8 @@ pub struct VariableIndexEntry { pub argument_type: ArgumentType, /// true if this variable is a compile-time-constant is_constant: bool, + // true if this variable is in a 'VAR_EXTERNAL' block + is_var_external: bool, /// the variable's datatype pub data_type_name: String, /// the index of the member-variable in it's container (e.g. struct). defautls to 0 (Single variables) @@ -130,6 +132,7 @@ pub struct MemberInfo<'b> { variable_type_name: &'b str, binding: Option, is_constant: bool, + is_var_external: bool, varargs: Option, } @@ -148,6 +151,7 @@ impl VariableIndexEntry { initial_value: None, argument_type, is_constant: false, + is_var_external: false, data_type_name: data_type_name.to_string(), location_in_parent, linkage: LinkageType::Internal, @@ -169,6 +173,7 @@ impl VariableIndexEntry { initial_value: None, argument_type: ArgumentType::ByVal(VariableType::Global), is_constant: false, + is_var_external: false, data_type_name: data_type_name.to_string(), location_in_parent: 0, linkage: LinkageType::Internal, @@ -203,6 +208,11 @@ impl VariableIndexEntry { self } + pub fn set_var_external(mut self, var_external: bool) -> Self { + self.is_var_external = var_external; + self + } + /// Creates a new VariableIndexEntry from the current entry with a new container and type /// This is used to create new entries from previously generic entries pub fn into_typed(&self, container: &str, new_type: &str) -> Self { @@ -253,6 +263,10 @@ impl VariableIndexEntry { self.linkage == LinkageType::External } + pub fn is_var_external(&self) -> bool { + self.is_var_external + } + pub fn get_declaration_type(&self) -> ArgumentType { self.argument_type } @@ -350,6 +364,7 @@ pub enum VariableType { InOut, Global, Return, + External, } impl VariableType { @@ -368,6 +383,7 @@ impl std::fmt::Display for VariableType { VariableType::InOut => write!(f, "InOut"), VariableType::Global => write!(f, "Global"), VariableType::Return => write!(f, "Return"), + VariableType::External => write!(f, "External"), } } } @@ -1138,13 +1154,18 @@ impl Index { /// Searches for variable name in the given container, if not found, attempts to search for it in super classes pub fn find_member(&self, container_name: &str, variable_name: &str) -> Option<&VariableIndexEntry> { // Find pou in index - self.find_local_member(container_name, variable_name).or_else(|| { - if let Some(class) = self.find_pou(container_name).and_then(|it| it.get_super_class()) { - self.find_member(class, variable_name) - } else { - None - } - }) + self.find_local_member(container_name, variable_name) + .or_else(|| { + if let Some(class) = self.find_pou(container_name).and_then(|it| it.get_super_class()) { + self.find_member(class, variable_name) + } else { + None + } + }) + .filter(|it| { + // VAR_EXTERNAL variables are not local members + !it.is_var_external() + }) } /// Searches for method names in the given container, if not found, attempts to search for it in super class @@ -1518,6 +1539,7 @@ impl Index { .set_initial_value(initial_value) .set_hardware_binding(member_info.binding) .set_varargs(member_info.varargs) + .set_var_external(member_info.is_var_external) } pub fn register_enum_variant( diff --git a/src/index/tests/index_tests.rs b/src/index/tests/index_tests.rs index 4b5e2828a4..a588501d12 100644 --- a/src/index/tests/index_tests.rs +++ b/src/index/tests/index_tests.rs @@ -1359,6 +1359,7 @@ fn a_program_pou_is_indexed() { initial_value: None, argument_type: ArgumentType::ByVal(VariableType::Global), is_constant: false, + is_var_external: false, data_type_name: "myProgram".into(), location_in_parent: 0, linkage: LinkageType::Internal, @@ -1630,6 +1631,7 @@ fn internal_vla_struct_type_is_indexed_correctly() { initial_value: None, argument_type: ArgumentType::ByVal(VariableType::Input), is_constant: false, + is_var_external: false, data_type_name: "__ptr_to___arr_vla_1_int".to_string(), location_in_parent: 0, linkage: LinkageType::Internal, @@ -1643,6 +1645,7 @@ fn internal_vla_struct_type_is_indexed_correctly() { initial_value: None, argument_type: ArgumentType::ByVal(VariableType::Input), is_constant: false, + is_var_external: false, data_type_name: "__bounds___arr_vla_1_int".to_string(), location_in_parent: 1, linkage: LinkageType::Internal, @@ -1711,6 +1714,7 @@ fn aliased_hardware_access_variable_has_implicit_initial_value_declaration() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "__global_foo", location_in_parent: 0, linkage: Internal, @@ -1791,6 +1795,7 @@ fn aliased_hardware_access_variable_creates_global_var_for_address() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -1942,6 +1947,7 @@ fn address_used_in_2_aliases_only_created_once() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -1991,6 +1997,7 @@ fn aliased_variable_with_in_or_out_directions_create_the_same_variable() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -2022,6 +2029,7 @@ fn aliased_variable_with_in_or_out_directions_create_the_same_variable() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "WORD", location_in_parent: 0, linkage: Internal, @@ -2069,6 +2077,7 @@ fn if_two_aliased_var_of_different_types_use_the_same_address_the_first_wins() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -2113,6 +2122,7 @@ fn var_config_hardware_address_creates_global_variable() { Global, ), is_constant: false, + is_var_external: false, data_type_name: "BOOL", location_in_parent: 0, linkage: Internal, @@ -2134,3 +2144,47 @@ fn var_config_hardware_address_creates_global_variable() { } "###); } + +#[test] +fn var_externals_are_distinctly_indexed() { + let (_, index) = index( + " + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + ", + ); + + let external = &index.get_pou_members("foo")[0]; + let global = index.get_globals().get("arr").expect("global 'arr' must exist"); + assert!(external.is_var_external()); + assert_eq!(external.get_name(), global.get_name()); + assert_eq!(external.get_variable_type(), VariableType::External); + assert_ne!(external, global); +} + +#[test] +fn var_externals_constants_are_both_flagged_as_external_and_constant() { + let (_, index) = index( + " + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL CONSTANT + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + ", + ); + + let external = &index.get_pou_members("foo")[0]; + assert!(external.is_var_external() && external.is_constant()); +} diff --git a/src/index/tests/snapshots/rusty__index__tests__index_tests__fb_parameters_variable_type.snap b/src/index/tests/snapshots/rusty__index__tests__index_tests__fb_parameters_variable_type.snap index e068ad0b1d..97a0e1f54b 100644 --- a/src/index/tests/snapshots/rusty__index__tests__index_tests__fb_parameters_variable_type.snap +++ b/src/index/tests/snapshots/rusty__index__tests__index_tests__fb_parameters_variable_type.snap @@ -11,6 +11,7 @@ expression: members Input, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -38,6 +39,7 @@ expression: members Output, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 1, linkage: Internal, @@ -65,6 +67,7 @@ expression: members InOut, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 2, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__index_tests__function_parameters_variable_type.snap b/src/index/tests/snapshots/rusty__index__tests__index_tests__function_parameters_variable_type.snap index ff411aff36..e53decb26d 100644 --- a/src/index/tests/snapshots/rusty__index__tests__index_tests__function_parameters_variable_type.snap +++ b/src/index/tests/snapshots/rusty__index__tests__index_tests__function_parameters_variable_type.snap @@ -11,6 +11,7 @@ expression: members Input, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -38,6 +39,7 @@ expression: members Output, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 1, linkage: Internal, @@ -65,6 +67,7 @@ expression: members InOut, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 2, linkage: Internal, @@ -92,6 +95,7 @@ expression: members Return, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 3, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__index_tests__program_parameters_variable_type.snap b/src/index/tests/snapshots/rusty__index__tests__index_tests__program_parameters_variable_type.snap index f37056b140..8f988d5d27 100644 --- a/src/index/tests/snapshots/rusty__index__tests__index_tests__program_parameters_variable_type.snap +++ b/src/index/tests/snapshots/rusty__index__tests__index_tests__program_parameters_variable_type.snap @@ -11,6 +11,7 @@ expression: members Input, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -38,6 +39,7 @@ expression: members Output, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 1, linkage: Internal, @@ -65,6 +67,7 @@ expression: members InOut, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 2, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_instances_are_repeated.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_instances_are_repeated.snap index bd7abe288d..ec03f8fc06 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_instances_are_repeated.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_instances_are_repeated.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_aFb", location_in_parent: 0, linkage: Internal, @@ -118,6 +120,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -178,6 +181,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -217,6 +221,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_aFb1", location_in_parent: 1, linkage: Internal, @@ -291,6 +296,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -365,6 +371,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -404,6 +411,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_aFb3", location_in_parent: 2, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_with_const_instances_are_repeated.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_with_const_instances_are_repeated.snap index 0c8494932e..ea872a659b 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_with_const_instances_are_repeated.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__array_with_const_instances_are_repeated.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -63,6 +64,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: true, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -102,6 +104,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_aFb", location_in_parent: 1, linkage: Internal, @@ -162,6 +165,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -222,6 +226,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__filter_on_variables_are_applied.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__filter_on_variables_are_applied.snap index a2b720aae3..f701df3f46 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__filter_on_variables_are_applied.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__filter_on_variables_are_applied.snap @@ -24,6 +24,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Global, ), is_constant: true, + is_var_external: false, data_type_name: "fb", location_in_parent: 0, linkage: Internal, @@ -60,6 +61,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -99,6 +101,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Local, ), is_constant: false, + is_var_external: false, data_type_name: "fb", location_in_parent: 0, linkage: Internal, @@ -141,6 +144,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -183,6 +187,7 @@ expression: "index.filter_instances(|it, _|\n !it.is_constant()).coll Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_fb_variables_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_fb_variables_are_retrieved.snap index 2c62c3f94c..1eca2a81a4 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_fb_variables_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_fb_variables_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "fb", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -97,6 +99,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -133,6 +136,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -172,6 +176,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "fb", location_in_parent: 0, linkage: Internal, @@ -214,6 +219,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -256,6 +262,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_struct_variables_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_struct_variables_are_retrieved.snap index 92adee3445..6429b0db75 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_struct_variables_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_struct_variables_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "str", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -97,6 +99,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -133,6 +136,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -172,6 +176,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "str", location_in_parent: 0, linkage: Internal, @@ -214,6 +219,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -256,6 +262,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_vars_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_vars_are_retrieved.snap index 0dffe1a309..238571884b 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_vars_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__global_vars_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -55,6 +56,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__nested_global_struct_variables_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__nested_global_struct_variables_are_retrieved.snap index 8f13a244db..8bb283471e 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__nested_global_struct_variables_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__nested_global_struct_variables_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "str", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "str2", location_in_parent: 0, linkage: Internal, @@ -100,6 +102,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -142,6 +145,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -181,6 +185,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "str2", location_in_parent: 1, linkage: Internal, @@ -223,6 +228,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -265,6 +271,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -301,6 +308,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -340,6 +348,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "str", location_in_parent: 0, linkage: Internal, @@ -382,6 +391,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "str2", location_in_parent: 0, linkage: Internal, @@ -427,6 +437,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -472,6 +483,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, @@ -514,6 +526,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "str2", location_in_parent: 1, linkage: Internal, @@ -559,6 +572,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -604,6 +618,7 @@ expression: "index.find_instances().collect::>>()" Input, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__pointer_variables_are_not_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__pointer_variables_are_not_retrieved.snap index 33902d0def..e134605617 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__pointer_variables_are_not_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__pointer_variables_are_not_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "__MainProg_rFb", location_in_parent: 0, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__program_variables_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__program_variables_are_retrieved.snap index 647e98356b..39df1e6921 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__program_variables_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__program_variables_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, @@ -58,6 +59,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 0, linkage: Internal, @@ -97,6 +99,7 @@ expression: "index.find_instances().collect::>>()" Local, ), is_constant: false, + is_var_external: false, data_type_name: "DINT", location_in_parent: 1, linkage: Internal, diff --git a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__programs_are_retrieved.snap b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__programs_are_retrieved.snap index 5fbe48e94d..6dec1d8e7b 100644 --- a/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__programs_are_retrieved.snap +++ b/src/index/tests/snapshots/rusty__index__tests__instance_resolver_tests__programs_are_retrieved.snap @@ -19,6 +19,7 @@ expression: "index.find_instances().collect::>>()" Global, ), is_constant: false, + is_var_external: false, data_type_name: "MainProg", location_in_parent: 0, linkage: Internal, diff --git a/src/index/visitor.rs b/src/index/visitor.rs index 9acd66cc3c..c481772dba 100644 --- a/src/index/visitor.rs +++ b/src/index/visitor.rs @@ -94,6 +94,7 @@ pub fn visit_pou(index: &mut Index, pou: &Pou) { variable_linkage: block_type, variable_type_name: &type_name, is_constant: block.constant, + is_var_external: matches!(block.variable_block_type, VariableBlockType::External), binding, varargs, }, @@ -115,7 +116,8 @@ pub fn visit_pou(index: &mut Index, pou: &Pou) { variable_name: pou.get_return_name(), variable_linkage: ArgumentType::ByVal(VariableType::Return), variable_type_name: return_type_name, - is_constant: false, //return variables are not constants + is_constant: false, //return variables are not constants + is_var_external: false, // see above binding: None, varargs: None, }, @@ -325,6 +327,7 @@ fn get_variable_type_from_block(block: &VariableBlock) -> VariableType { VariableBlockType::Output => VariableType::Output, VariableBlockType::Global => VariableType::Global, VariableBlockType::InOut => VariableType::InOut, + VariableBlockType::External => VariableType::External, } } @@ -799,6 +802,7 @@ fn visit_struct( variable_linkage: ArgumentType::ByVal(VariableType::Input), // struct members act like VAR_INPUT in terms of visibility variable_type_name: member_type, is_constant: false, //struct members are not constants //TODO thats probably not true (you can define a struct in an CONST-block?!) + is_var_external: false, // structs cannot have VAR_EXTERNAL blocks binding, varargs: None, }, diff --git a/src/lexer/tokens.rs b/src/lexer/tokens.rs index d6e3d26a62..10f1e0bc95 100644 --- a/src/lexer/tokens.rs +++ b/src/lexer/tokens.rs @@ -103,6 +103,9 @@ pub enum Token { #[token("VARINOUT", ignore(case))] KeywordVarInOut, + #[token("VAR_EXTERNAL", ignore(case))] + KeywordVarExternal, + #[token("END_VAR", ignore(case))] #[token("ENDVAR", ignore(case))] KeywordEndVar, diff --git a/src/lowering.rs b/src/lowering.rs index 4b7216ddf1..885a629a43 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -105,7 +105,9 @@ impl AstLowerer { fn add_init_statements(&mut self, implementation: &mut plc_ast::ast::Implementation) { let predicate = |var: &VariableIndexEntry| { - var.is_temp() || (implementation.pou_type == PouType::Function && var.is_local()) + var.is_temp() + || var.is_var_external() + || (implementation.pou_type == PouType::Function && var.is_local()) }; let strip_temporaries = |inits: &mut InitAssignments| { let mut temps = InitAssignments::default(); diff --git a/src/parser.rs b/src/parser.rs index f778066818..d446f6af32 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -220,8 +220,14 @@ fn parse_pou( // parse variable declarations. note that var in/out/inout // blocks are not allowed inside of class declarations. let mut variable_blocks = vec![]; - let allowed_var_types = - [KeywordVar, KeywordVarInput, KeywordVarOutput, KeywordVarInOut, KeywordVarTemp]; + let allowed_var_types = [ + KeywordVar, + KeywordVarInput, + KeywordVarOutput, + KeywordVarInOut, + KeywordVarTemp, + KeywordVarExternal, + ]; while allowed_var_types.contains(&lexer.token) { variable_blocks.push(parse_variable_block(lexer, LinkageType::Internal)); } @@ -1069,6 +1075,7 @@ fn parse_variable_block_type(lexer: &mut ParseSession) -> VariableBlockType { KeywordVarOutput => VariableBlockType::Output, KeywordVarGlobal => VariableBlockType::Global, KeywordVarInOut => VariableBlockType::InOut, + KeywordVarExternal => VariableBlockType::External, _ => VariableBlockType::Local, } } @@ -1086,7 +1093,7 @@ fn parse_variable_block(lexer: &mut ParseSession, linkage: LinkageType) -> Varia let mut variables = parse_any_in_region(lexer, vec![KeywordEndVar], parse_variable_list); - if constant { + if constant && !matches!(variable_block_type, VariableBlockType::External) { // sneak in the DefaultValue-Statements if no initializers were defined variables.iter_mut().filter(|it| it.initializer.is_none()).for_each(|it| { it.initializer = Some(AstFactory::create_default_value(it.location.clone(), lexer.next_id())); diff --git a/src/parser/tests/variable_parser_tests.rs b/src/parser/tests/variable_parser_tests.rs index 0987b88d98..d6d885c220 100644 --- a/src/parser/tests/variable_parser_tests.rs +++ b/src/parser/tests/variable_parser_tests.rs @@ -422,3 +422,251 @@ fn var_config_location() { assert_eq!("main.instance.foo", &src[result.var_config[0].location.to_range().unwrap()]); } + +#[test] +fn var_external() { + let src = r#" + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + "#; + + let (result, _) = parse(src); + + insta::assert_debug_snapshot!(result, @r###" + CompilationUnit { + global_vars: [ + VariableBlock { + variables: [ + Variable { + name: "arr", + data_type: DataTypeDefinition { + data_type: ArrayType { + name: None, + bounds: RangeStatement { + start: LiteralInteger { + value: 0, + }, + end: LiteralInteger { + value: 100, + }, + }, + referenced_type: DataTypeReference { + referenced_type: "INT", + }, + is_variable_length: false, + }, + }, + }, + ], + variable_block_type: Global, + }, + ], + var_config: [], + units: [ + POU { + name: "foo", + variable_blocks: [ + VariableBlock { + variables: [ + Variable { + name: "arr", + data_type: DataTypeDefinition { + data_type: ArrayType { + name: None, + bounds: RangeStatement { + start: LiteralInteger { + value: 0, + }, + end: LiteralInteger { + value: 100, + }, + }, + referenced_type: DataTypeReference { + referenced_type: "INT", + }, + is_variable_length: false, + }, + }, + }, + ], + variable_block_type: External, + }, + ], + pou_type: Function, + return_type: None, + }, + ], + implementations: [ + Implementation { + name: "foo", + type_name: "foo", + linkage: Internal, + pou_type: Function, + statements: [], + location: SourceLocation { + span: Range( + TextLocation { + line: 9, + column: 4, + offset: 155, + }..TextLocation { + line: 9, + column: 16, + offset: 167, + }, + ), + }, + name_location: SourceLocation { + span: Range( + TextLocation { + line: 5, + column: 13, + offset: 80, + }..TextLocation { + line: 5, + column: 16, + offset: 83, + }, + ), + }, + overriding: false, + generic: false, + access: None, + }, + ], + user_types: [], + file_name: "test.st", + } + "###); +} + +#[test] +fn var_external_constant() { + let src = r#" + VAR_GLOBAL + arr: ARRAY [0..100] OF INT; + END_VAR + + FUNCTION foo + VAR_EXTERNAL CONSTANT + arr : ARRAY [0..100] OF INT; + END_VAR + END_FUNCTION + "#; + + let (result, _) = parse(src); + + insta::assert_debug_snapshot!(result, @r###" + CompilationUnit { + global_vars: [ + VariableBlock { + variables: [ + Variable { + name: "arr", + data_type: DataTypeDefinition { + data_type: ArrayType { + name: None, + bounds: RangeStatement { + start: LiteralInteger { + value: 0, + }, + end: LiteralInteger { + value: 100, + }, + }, + referenced_type: DataTypeReference { + referenced_type: "INT", + }, + is_variable_length: false, + }, + }, + }, + ], + variable_block_type: Global, + }, + ], + var_config: [], + units: [ + POU { + name: "foo", + variable_blocks: [ + VariableBlock { + variables: [ + Variable { + name: "arr", + data_type: DataTypeDefinition { + data_type: ArrayType { + name: None, + bounds: RangeStatement { + start: LiteralInteger { + value: 0, + }, + end: LiteralInteger { + value: 100, + }, + }, + referenced_type: DataTypeReference { + referenced_type: "INT", + }, + is_variable_length: false, + }, + }, + }, + ], + variable_block_type: External, + }, + ], + pou_type: Function, + return_type: None, + }, + ], + implementations: [ + Implementation { + name: "foo", + type_name: "foo", + linkage: Internal, + pou_type: Function, + statements: [], + location: SourceLocation { + span: Range( + TextLocation { + line: 9, + column: 4, + offset: 163, + }..TextLocation { + line: 9, + column: 16, + offset: 175, + }, + ), + }, + name_location: SourceLocation { + span: Range( + TextLocation { + line: 5, + column: 13, + offset: 80, + }..TextLocation { + line: 5, + column: 16, + offset: 83, + }, + ), + }, + overriding: false, + generic: false, + access: None, + }, + ], + user_types: [], + file_name: "test.st", + } + "###); +} diff --git a/src/tests/adr/pou_adr.rs b/src/tests/adr/pou_adr.rs index 879f160f58..78c9b25aaf 100644 --- a/src/tests/adr/pou_adr.rs +++ b/src/tests/adr/pou_adr.rs @@ -68,6 +68,7 @@ fn programs_state_is_stored_in_a_struct() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 0, linkage: Internal, @@ -95,6 +96,7 @@ fn programs_state_is_stored_in_a_struct() { InOut, ), is_constant: false, + is_var_external: false, data_type_name: "__auto_pointer_to_INT", location_in_parent: 1, linkage: Internal, @@ -122,6 +124,7 @@ fn programs_state_is_stored_in_a_struct() { Output, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 2, linkage: Internal, @@ -149,6 +152,7 @@ fn programs_state_is_stored_in_a_struct() { Local, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 3, linkage: Internal, @@ -176,6 +180,7 @@ fn programs_state_is_stored_in_a_struct() { Temp, ), is_constant: false, + is_var_external: false, data_type_name: "INT", location_in_parent: 4, linkage: Internal, diff --git a/src/tests/adr/vla_adr.rs b/src/tests/adr/vla_adr.rs index 818a4cb7eb..2c0842ed91 100644 --- a/src/tests/adr/vla_adr.rs +++ b/src/tests/adr/vla_adr.rs @@ -43,6 +43,7 @@ fn representation() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "__ptr_to___arr_vla_1_dint", location_in_parent: 0, linkage: Internal, @@ -60,6 +61,7 @@ fn representation() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "__bounds___arr_vla_1_dint", location_in_parent: 1, linkage: Internal, @@ -269,6 +271,7 @@ fn pass() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "__ptr_to___arr_vla_1_dint", location_in_parent: 0, linkage: Internal, @@ -286,6 +289,7 @@ fn pass() { Input, ), is_constant: false, + is_var_external: false, data_type_name: "__bounds___arr_vla_1_dint", location_in_parent: 1, linkage: Internal, diff --git a/src/validation/tests/variable_validation_tests.rs b/src/validation/tests/variable_validation_tests.rs index e9cd07e445..c51689a455 100644 --- a/src/validation/tests/variable_validation_tests.rs +++ b/src/validation/tests/variable_validation_tests.rs @@ -1030,3 +1030,108 @@ fn array_access_with_non_integer_literal_causes_error() { "###); } + +#[test] +fn use_of_var_external_block_gives_a_warning() { + let diagnostics = parse_and_validate_buffered( + r#" + VAR_GLOBAL + b: BOOL; + END_VAR + FUNCTION_BLOCK foo_fb + VAR_EXTERNAL + b : BOOL; + END_VAR + END_FUNCTION_BLOCK + "#, + ); + + assert_snapshot!(diagnostics, @r###" + warning[E106]: VAR_EXTERNAL blocks have no effect + ┌─ :6:13 + │ + 6 │ VAR_EXTERNAL + │ ^^^^^^^^^^^^ VAR_EXTERNAL blocks have no effect + + "###); +} + +#[test] +fn unresolved_var_external_reference_does_not_lead_to_errors() { + let diagnostics = parse_and_validate_buffered( + r#" + VAR_GLOBAL + a: BOOL; + END_VAR + FUNCTION_BLOCK foo_fb + VAR_EXTERNAL + b : BOOL; + END_VAR + END_FUNCTION_BLOCK + "#, + ); + + assert_snapshot!(diagnostics, @r###" + warning[E106]: VAR_EXTERNAL blocks have no effect + ┌─ :6:13 + │ + 6 │ VAR_EXTERNAL + │ ^^^^^^^^^^^^ VAR_EXTERNAL blocks have no effect + + "###); +} + +#[test] +fn var_external_with_initializer_does_not_err() { + let diagnostics = parse_and_validate_buffered( + r#" + VAR_GLOBAL + b: BOOL; + END_VAR + FUNCTION_BLOCK foo_fb + VAR_EXTERNAL + b : BOOL := TRUE; + END_VAR + END_FUNCTION_BLOCK + "#, + ); + + assert_snapshot!(diagnostics, @r###" + warning[E106]: VAR_EXTERNAL blocks have no effect + ┌─ :6:13 + │ + 6 │ VAR_EXTERNAL + │ ^^^^^^^^^^^^ VAR_EXTERNAL blocks have no effect + + "###); +} + +#[test] +fn using_var_external_variable_without_matching_global_will_not_resolve() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK foo_fb + VAR_EXTERNAL + b : BOOL := TRUE; + END_VAR + + b := FALSE; + END_FUNCTION_BLOCK + "#, + ); + + assert_snapshot!(diagnostics, @r###" + warning[E106]: VAR_EXTERNAL blocks have no effect + ┌─ :3:13 + │ + 3 │ VAR_EXTERNAL + │ ^^^^^^^^^^^^ VAR_EXTERNAL blocks have no effect + + error[E048]: Could not resolve reference to b + ┌─ :7:13 + │ + 7 │ b := FALSE; + │ ^ Could not resolve reference to b + + "###); +} diff --git a/src/validation/variable.rs b/src/validation/variable.rs index 181db7ea90..a0dd66abfb 100644 --- a/src/validation/variable.rs +++ b/src/validation/variable.rs @@ -134,8 +134,19 @@ pub fn visit_variable_block( } fn validate_variable_block(validator: &mut Validator, block: &VariableBlock) { + if matches!(block.variable_block_type, VariableBlockType::External) { + validator.push_diagnostic( + Diagnostic::new("VAR_EXTERNAL blocks have no effect") + .with_error_code("E106") + .with_location(&block.location), + ); + } + if block.constant - && !matches!(block.variable_block_type, VariableBlockType::Global | VariableBlockType::Local) + && !matches!( + block.variable_block_type, + VariableBlockType::Global | VariableBlockType::Local | VariableBlockType::External + ) { validator.push_diagnostic( Diagnostic::new("This variable block does not support the CONSTANT modifier") @@ -278,7 +289,7 @@ fn validate_variable( .with_location(statement.get_location()), ); } - None if v_entry.is_constant() => { + None if v_entry.is_constant() && !v_entry.is_var_external() => { validator.push_diagnostic( Diagnostic::new(format!("Unresolved constant `{}` variable", variable.name.as_str(),)) .with_error_code("E033") diff --git a/tests/lit/single/var_external/var_external.st b/tests/lit/single/var_external/var_external.st new file mode 100644 index 0000000000..9ca55aa42c --- /dev/null +++ b/tests/lit/single/var_external/var_external.st @@ -0,0 +1,31 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +VAR_GLOBAL + arr : ARRAY [0..10] OF INT; +END_VAR + +FUNCTION write_to_array +VAR_EXTERNAL CONSTANT + arr : ARRAY [0..10] OF INT; +END_VAR +VAR + i: DINT; +END_VAR + FOR i := 0 TO 10 DO + arr[i] := i; + END_FOR; +END_FUNCTION + +FUNCTION main : DINT + write_to_array(); + printf('%d$N', arr[0]); // CHECK: 0 + printf('%d$N', arr[1]); // CHECK: 1 + printf('%d$N', arr[2]); // CHECK: 2 + printf('%d$N', arr[3]); // CHECK: 3 + printf('%d$N', arr[4]); // CHECK: 4 + printf('%d$N', arr[5]); // CHECK: 5 + printf('%d$N', arr[6]); // CHECK: 6 + printf('%d$N', arr[7]); // CHECK: 7 + printf('%d$N', arr[8]); // CHECK: 8 + printf('%d$N', arr[9]); // CHECK: 9 + printf('%d$N', arr[10]); // CHECK: 10 +END_FUNCTION \ No newline at end of file diff --git a/tests/lit/single/var_external/var_external_initializer_has_no_effect.st b/tests/lit/single/var_external/var_external_initializer_has_no_effect.st new file mode 100644 index 0000000000..72bb36fdff --- /dev/null +++ b/tests/lit/single/var_external/var_external_initializer_has_no_effect.st @@ -0,0 +1,11 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +VAR_GLOBAL + i: DINT := 42; +END_VAR + +FUNCTION main: DINT +VAR_EXTERNAL + i : DINT := 1337; +END_VAR + printf('%d$N', i); // CHECK: 42 +END_FUNCTION \ No newline at end of file