From 867afc3c82fb42dbabd96affd5fbe0472d2cc26f Mon Sep 17 00:00:00 2001 From: Mathias Rieder Date: Thu, 13 Jun 2024 22:16:01 +0200 Subject: [PATCH] refactor(indexer): indexing uses SymbolIndexer instead of visitor.rs The indexer utilizes the visitor-trait to visit nodes that contain symbols. It delegates the concrete work to smaller, specialized indexer-implementations in src/index/indexer --- compiler/plc_ast/src/ast.rs | 5 + compiler/plc_driver/src/pipelines.rs | 4 +- src/index.rs | 4 +- src/index/indexer.rs | 61 ++ src/index/indexer/global_var_indexer.rs | 37 + src/index/indexer/implementation_indexer.rs | 57 ++ src/index/indexer/pou_indexer.rs | 288 +++++++ src/index/indexer/user_type_indexer.rs | 478 ++++++++++ src/index/tests/builtin_tests.rs | 2 +- src/index/tests/index_tests.rs | 10 +- src/index/visitor.rs | 816 ------------------ src/test_utils.rs | 4 +- src/tests/adr/vla_adr.rs | 24 +- .../tests/duplicates_validation_test.rs | 6 +- 14 files changed, 963 insertions(+), 833 deletions(-) create mode 100644 src/index/indexer.rs create mode 100644 src/index/indexer/global_var_indexer.rs create mode 100644 src/index/indexer/implementation_indexer.rs create mode 100644 src/index/indexer/pou_indexer.rs create mode 100644 src/index/indexer/user_type_indexer.rs delete mode 100644 src/index/visitor.rs diff --git a/compiler/plc_ast/src/ast.rs b/compiler/plc_ast/src/ast.rs index 7aa3a0a546..0aa6d77e03 100644 --- a/compiler/plc_ast/src/ast.rs +++ b/compiler/plc_ast/src/ast.rs @@ -1524,6 +1524,11 @@ impl AstFactory { pub fn create_label_statement(name: String, location: SourceLocation, id: AstId) -> AstNode { AstNode { stmt: AstStatement::LabelStatement(LabelStatement { name }), location, id } } + + pub fn create_plus_one_expression(value: AstNode, location: SourceLocation, id: AstId) -> AstNode { + let one = AstFactory::create_literal(AstLiteral::Integer(1), location.clone(), id); + AstFactory::create_binary_expression(value, Operator::Plus, one, id) + } } #[derive(Debug, Clone, PartialEq)] pub struct EmptyStatement {} diff --git a/compiler/plc_driver/src/pipelines.rs b/compiler/plc_driver/src/pipelines.rs index 67d8991311..f80ef435bb 100644 --- a/compiler/plc_driver/src/pipelines.rs +++ b/compiler/plc_driver/src/pipelines.rs @@ -107,7 +107,7 @@ impl ParsedProject { //Preprocess pre_process(&mut unit, id_provider.clone()); //import to index - let index = plc::index::visitor::visit(&unit); + let index = plc::index::indexer::index(&unit); (index, unit) }) @@ -126,7 +126,7 @@ impl ParsedProject { } // import builtin functions let builtins = plc::builtins::parse_built_ins(id_provider); - global_index.import(plc::index::visitor::visit(&builtins)); + global_index.import(plc::index::indexer::index(&builtins)); IndexedProject { project: ParsedProject { project: self.project, units }, index: global_index } } diff --git a/src/index.rs b/src/index.rs index 0370ce073a..ea30f14614 100644 --- a/src/index.rs +++ b/src/index.rs @@ -26,9 +26,9 @@ use self::{ }; pub mod const_expressions; +pub mod indexer; mod instance_iterator; pub mod symbol; -pub mod visitor; #[cfg(test)] mod tests; @@ -97,7 +97,7 @@ pub struct HardwareBinding { } impl HardwareBinding { - fn from_statement(index: &mut Index, it: &AstNode, scope: Option) -> Option { + pub fn from_statement(index: &mut Index, it: &AstNode, scope: Option) -> Option { if let AstStatement::HardwareAccess(data) = it.get_stmt() { Some(HardwareBinding { access: data.access, diff --git a/src/index/indexer.rs b/src/index/indexer.rs new file mode 100644 index 0000000000..8d559bcf5c --- /dev/null +++ b/src/index/indexer.rs @@ -0,0 +1,61 @@ +use global_var_indexer::VarGlobalIndexer; +use implementation_indexer::ImplementationIndexer; +use plc_ast::{ + ast::{CompilationUnit, Implementation, VariableBlockType}, + visitor::{AstVisitor, Walker}, +}; +use pou_indexer::PouIndexer; +use user_type_indexer::UserTypeIndexer; + +use super::Index; + +mod global_var_indexer; +mod implementation_indexer; +mod pou_indexer; +mod user_type_indexer; + +/// Indexes all symbols found in the given Compiliation Unit +/// and returns the resulting Index +pub fn index(unit: &CompilationUnit) -> Index { + // let mut index = Index::default(); + let mut indexer = SymbolIndexer::default(); + unit.walk(&mut indexer); + indexer.index +} + +/// Indexer that registers all symbols in the index +#[derive(Default)] +pub struct SymbolIndexer { + pub index: Index, +} + +/// The SymbolIndexer is responsible for registering all delcared types and symbols in the index. +impl AstVisitor for SymbolIndexer { + /// Visits a VAR_GLOBAL VariableBlock and registers all variables as globals in the index + fn visit_variable_block(&mut self, block: &plc_ast::ast::VariableBlock) { + if block.variable_block_type == VariableBlockType::Global { + // let the global var indexer handle the global variables + let mut indexer = VarGlobalIndexer::new(block.constant, block.linkage, &mut self.index); + for var in &block.variables { + indexer.visit_variable(var); + } + } + } + + /// Visits a user type declaration + /// Registers the user type in the index using the UserTypeIndexer + fn visit_user_type_declaration(&mut self, user_type: &plc_ast::ast::UserTypeDeclaration) { + UserTypeIndexer::new(&mut self.index, user_type).visit_user_type_declaration(user_type); + } + + /// Visits a pou and registers all member variables in the index + /// Also registers the pou's struct type in the index + fn visit_pou(&mut self, pou: &plc_ast::ast::Pou) { + PouIndexer::new(&mut self.index).visit_pou(pou); + } + + /// Visits an implementation and registers the implementation in the index + fn visit_implementation(&mut self, implementation: &Implementation) { + ImplementationIndexer::new(&mut self.index).index_implementation(implementation); + } +} diff --git a/src/index/indexer/global_var_indexer.rs b/src/index/indexer/global_var_indexer.rs new file mode 100644 index 0000000000..c2004ef59f --- /dev/null +++ b/src/index/indexer/global_var_indexer.rs @@ -0,0 +1,37 @@ +use plc_ast::ast::LinkageType; + +use crate::index::{HardwareBinding, Index, VariableIndexEntry}; + +pub struct VarGlobalIndexer<'i> { + constant: bool, + linkage: LinkageType, + index: &'i mut Index, +} + +impl VarGlobalIndexer<'_> { + pub fn new(constant: bool, linkage: LinkageType, index: &mut Index) -> VarGlobalIndexer<'_> { + VarGlobalIndexer { constant, linkage, index } + } + + pub fn visit_variable(&mut self, var: &plc_ast::ast::Variable) { + let target_type = var.data_type_declaration.get_name().unwrap_or_default(); + let initializer = self.index.get_mut_const_expressions().maybe_add_constant_expression( + var.initializer.clone(), + target_type, + None, + ); + let variable = VariableIndexEntry::create_global( + &var.name, + &var.name, + var.data_type_declaration.get_name().expect("named variable datatype"), + var.location.clone(), + ) + .set_initial_value(initializer) + .set_constant(self.constant) + .set_linkage(self.linkage) + .set_hardware_binding( + var.address.as_ref().and_then(|it| HardwareBinding::from_statement(self.index, it, None)), + ); + self.index.register_global_variable(&var.name, variable); + } +} diff --git a/src/index/indexer/implementation_indexer.rs b/src/index/indexer/implementation_indexer.rs new file mode 100644 index 0000000000..7c93f6a294 --- /dev/null +++ b/src/index/indexer/implementation_indexer.rs @@ -0,0 +1,57 @@ +use plc_ast::ast::{Implementation, PouType, TypeNature}; + +use crate::{ + index::{Index, PouIndexEntry}, + typesystem::{self, DataTypeInformation}, +}; + +pub struct ImplementationIndexer<'i> { + index: &'i mut Index, +} + +impl<'i> ImplementationIndexer<'i> { + pub fn new(index: &'i mut Index) -> Self { + Self { index } + } + + pub fn index_implementation(&mut self, implementation: &Implementation) { + let pou_type = &implementation.pou_type; + let start_location = implementation + .statements + .first() + .map(|it| it.get_location()) + .as_ref() + .or(Some(&implementation.location)) + .cloned() + .unwrap(); + self.index.register_implementation( + &implementation.name, + &implementation.type_name, + pou_type.get_optional_owner_class().as_ref(), + pou_type.into(), + implementation.generic, + start_location, + ); + //if we are registing an action, also register a datatype for it + if pou_type == &PouType::Action { + let datatype = typesystem::DataType { + name: implementation.name.to_string(), + initial_value: None, + information: DataTypeInformation::Alias { + name: implementation.name.clone(), + referenced_type: implementation.type_name.clone(), + }, + nature: TypeNature::Derived, + location: implementation.name_location.clone(), + }; + + self.index.register_pou(PouIndexEntry::create_action_entry( + implementation.name.as_str(), + implementation.type_name.as_str(), + implementation.linkage, + implementation.name_location.clone(), + )); + self.index.register_pou_type(datatype); + } + } +} diff --git a/src/index/indexer/pou_indexer.rs b/src/index/indexer/pou_indexer.rs new file mode 100644 index 0000000000..e76a48d81f --- /dev/null +++ b/src/index/indexer/pou_indexer.rs @@ -0,0 +1,288 @@ +use plc_ast::ast::{ + ArgumentProperty, DataTypeDeclaration, PouType, TypeNature, VariableBlock, VariableBlockType, +}; +use plc_source::source_location::SourceLocation; +use plc_util::convention::internal_type_name; + +use crate::{ + index::{ + ArgumentType, HardwareBinding, Index, MemberInfo, PouIndexEntry, VariableIndexEntry, VariableType, + }, + typesystem::{self, DataTypeInformation, StructSource, VarArgs, VOID_TYPE}, +}; + +/// indexer for a single POU +pub struct PouIndexer<'i> { + index: &'i mut Index, +} + +impl<'i> PouIndexer<'i> { + pub fn new(index: &'i mut Index) -> Self { + Self { index } + } + + /// Visits a pou and registers all member variables in the index + /// Also registers the pou's struct type in the index + pub fn visit_pou(&mut self, pou: &plc_ast::ast::Pou) { + //register the pou's member variables + let (count, mut members, member_varargs) = self.index_pou_variables(pou); + + //register a function's return type as a member variable + let return_type_name = pou.return_type.as_ref().and_then(|it| it.get_name()).unwrap_or(VOID_TYPE); + if pou.return_type.is_some() { + let entry = self.index.register_member_variable( + MemberInfo { + container_name: &pou.name, + 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 + binding: None, + varargs: None, + }, + None, + pou.name_location.clone(), + count, + ); + members.push(entry); + } + + // construct the struct-type that holds the POU's `this` state + let pou_struct_type = typesystem::DataType { + name: pou.name.to_string(), + initial_value: None, + information: DataTypeInformation::Struct { + name: pou.name.to_string(), + members, + source: StructSource::Pou(pou.pou_type.clone()), + }, + nature: TypeNature::Any, + location: pou.name_location.clone(), + }; + + match &pou.pou_type { + PouType::Program => { + self.index_program(pou, pou_struct_type); + } + PouType::FunctionBlock => { + self.index_function_block(pou, pou_struct_type); + } + PouType::Class => { + self.index_class(pou, pou_struct_type); + } + PouType::Function => { + self.index_function(pou, return_type_name, member_varargs, pou_struct_type); + } + PouType::Method { owner_class } => { + self.index_method(pou, return_type_name, owner_class, pou_struct_type); + } + _ => {} + }; + } + + /// Registers a program in the index + fn index_program(&mut self, pou: &plc_ast::ast::Pou, pou_struct_type: typesystem::DataType) { + self.index.register_program(&pou.name, pou.name_location.clone(), pou.linkage); + self.index.register_pou_type(pou_struct_type); + } + + /// Registers a method in the index + fn index_method( + &mut self, + pou: &plc_ast::ast::Pou, + return_type_name: &str, + owner_class: &str, + pou_struct_type: typesystem::DataType, + ) { + self.index.register_pou(PouIndexEntry::create_method_entry( + &pou.name, + return_type_name, + owner_class, + pou.linkage, + pou.name_location.clone(), + )); + self.index.register_pou_type(pou_struct_type); + } + + /// Registers a function in the index + fn index_function( + &mut self, + pou: &plc_ast::ast::Pou, + return_type_name: &str, + member_varargs: Option, + pou_struct_type: typesystem::DataType, + ) { + self.index.register_pou(PouIndexEntry::create_function_entry( + &pou.name, + return_type_name, + &pou.generics, + pou.linkage, + member_varargs.is_some(), + pou.name_location.clone(), + )); + self.index.register_pou_type(pou_struct_type); + } + + /// Registers a class in the index + fn index_class(&mut self, pou: &plc_ast::ast::Pou, pou_struct_type: typesystem::DataType) { + let global_struct_name = crate::index::get_initializer_name(&pou.name); + let variable = VariableIndexEntry::create_global( + &global_struct_name, + &global_struct_name, + &pou.name, + pou.name_location.clone(), + ) + .set_constant(true); + self.index.register_global_initializer(&global_struct_name, variable); + self.index.register_pou(PouIndexEntry::create_class_entry( + &pou.name, + pou.linkage, + pou.name_location.clone(), + pou.super_class.clone(), + )); + self.index.register_pou_type(pou_struct_type); + } + + /// Registers a function block in the index + fn index_function_block(&mut self, pou: &plc_ast::ast::Pou, pou_struct_type: typesystem::DataType) { + let global_struct_name = crate::index::get_initializer_name(&pou.name); + let variable = VariableIndexEntry::create_global( + &global_struct_name, + &global_struct_name, + &pou.name, + pou.name_location.clone(), + ) + .set_constant(true); + self.index.register_global_initializer(&global_struct_name, variable); + self.index.register_pou(PouIndexEntry::create_function_block_entry( + &pou.name, + pou.linkage, + pou.name_location.clone(), + pou.super_class.clone().as_deref(), + )); + self.index.register_pou_type(pou_struct_type); + } + + /// Registers all member variables of a POU in the index + fn index_pou_variables( + &mut self, + pou: &plc_ast::ast::Pou, + ) -> (u32, Vec, Option) { + let mut count = 0; + let mut members = Vec::new(); + let mut member_varargs = None; + + for block in &pou.variable_blocks { + for var in &block.variables { + let varargs = if let DataTypeDeclaration::DataTypeDefinition { + data_type: plc_ast::ast::DataType::VarArgs { referenced_type, sized }, + .. + } = &var.data_type_declaration + { + let name = referenced_type + .as_ref() + .map(|it| &**it) + .and_then(DataTypeDeclaration::get_name) + .map(|it| it.to_string()); + Some(if *sized { VarArgs::Sized(name) } else { VarArgs::Unsized(name) }) + } else { + None + }; + + if varargs.is_some() { + member_varargs = varargs.clone(); + } + + let var_type_name = var.data_type_declaration.get_name().unwrap_or(VOID_TYPE); + let block_type = get_declaration_type_for(block, &pou.pou_type); + let type_name = if block_type.is_by_ref() { + //register a pointer type for argument + register_byref_pointer_type_for(self.index, var_type_name) + } else { + var_type_name.to_string() + }; + let initial_value = self.index.get_mut_const_expressions().maybe_add_constant_expression( + var.initializer.clone(), + type_name.as_str(), + Some(pou.name.clone()), + ); + + let binding = var + .address + .as_ref() + .and_then(|it| HardwareBinding::from_statement(self.index, it, Some(pou.name.clone()))); + + let entry = self.index.register_member_variable( + MemberInfo { + container_name: &pou.name, + variable_name: &var.name, + variable_linkage: block_type, + variable_type_name: &type_name, + is_constant: block.constant, + binding, + varargs, + }, + initial_value, + var.location.clone(), + count, + ); + members.push(entry); + count += 1; + } + } + (count, members, member_varargs) + } +} + +/// returns the declaration type (ByRef or ByVal) for the given VariableBlock (VAR_INPUT, VAR_OUTPUT, VAR_INOUT, etc.) +fn get_declaration_type_for(block: &VariableBlock, pou_type: &PouType) -> ArgumentType { + if matches!( + block.variable_block_type, + VariableBlockType::InOut | VariableBlockType::Input(ArgumentProperty::ByRef) + ) { + ArgumentType::ByRef(get_variable_type_from_block(block)) + } else if block.variable_block_type == VariableBlockType::Output { + // outputs differ depending on pou type + match pou_type { + PouType::Function => ArgumentType::ByRef(get_variable_type_from_block(block)), + _ => ArgumentType::ByVal(get_variable_type_from_block(block)), + } + } else { + ArgumentType::ByVal(get_variable_type_from_block(block)) + } +} + +fn get_variable_type_from_block(block: &VariableBlock) -> VariableType { + match block.variable_block_type { + VariableBlockType::Local => VariableType::Local, + VariableBlockType::Temp => VariableType::Temp, + VariableBlockType::Input(_) => VariableType::Input, + VariableBlockType::Output => VariableType::Output, + VariableBlockType::Global => VariableType::Global, + VariableBlockType::InOut => VariableType::InOut, + } +} + +/// registers an auto-deref pointer type for the inner_type_name if it does not already exist +fn register_byref_pointer_type_for(index: &mut Index, inner_type_name: &str) -> String { + //get unique name + let type_name = internal_type_name("auto_pointer_to_", inner_type_name); + + //check if type was already created + if index.find_effective_type_by_name(type_name.as_str()).is_none() { + //generate a pointertype for the variable + index.register_type(typesystem::DataType { + name: type_name.clone(), + initial_value: None, + information: DataTypeInformation::Pointer { + name: type_name.clone(), + inner_type_name: inner_type_name.to_string(), + auto_deref: true, + }, + nature: TypeNature::Any, + location: SourceLocation::internal(), + }); + } + + type_name +} diff --git a/src/index/indexer/user_type_indexer.rs b/src/index/indexer/user_type_indexer.rs new file mode 100644 index 0000000000..67670d0e21 --- /dev/null +++ b/src/index/indexer/user_type_indexer.rs @@ -0,0 +1,478 @@ +use plc_ast::{ + ast::{ + flatten_expression_list, get_enum_element_name, Assignment, AstFactory, AstNode, AstStatement, + DataType, DataTypeDeclaration, RangeStatement, TypeNature, UserTypeDeclaration, Variable, + }, + literals::AstLiteral, + visitor::{AstVisitor, Walker}, +}; +use plc_diagnostics::diagnostics::Diagnostic; +use plc_source::source_location::SourceLocation; + +use crate::{ + index::{ + const_expressions::ConstId, ArgumentType, HardwareBinding, Index, MemberInfo, VariableIndexEntry, + VariableType, + }, + typesystem::{ + self, DataTypeInformation, Dimension, StringEncoding, StructSource, TypeSize, DEFAULT_STRING_LEN, + DINT_TYPE, + }, +}; + +/// Indexer that registers all user-defined types in the index +pub struct UserTypeIndexer<'i, 't> { + index: &'i mut Index, + user_type: &'t UserTypeDeclaration, + pending_initializer: Option, +} + +impl<'i, 't> UserTypeIndexer<'i, 't> { + pub fn new(index: &'i mut Index, user_type: &'t UserTypeDeclaration) -> Self { + UserTypeIndexer { index, user_type, pending_initializer: None } + } + + fn collect_initializer(&mut self, i: AstNode, target_type_name: String) -> ConstId { + let scope = self.current_scope(); + let init = self.index.get_mut_const_expressions().add_constant_expression(i, target_type_name, scope); + init + } +} + +impl AstVisitor for UserTypeIndexer<'_, '_> { + fn visit_user_type_declaration(&mut self, user_type: &UserTypeDeclaration) { + //handle inner types & initializers + user_type.data_type.walk(self); + if let Some(init_index) = user_type.initializer.clone().map(|i| { + self.collect_initializer(i, user_type.data_type.get_name().map(|s| s.to_string()).unwrap()) + }) { + //TODO: without field? + self.pending_initializer = Some(init_index); + }; + + self.visit_data_type(&user_type.data_type); + } + + fn visit_data_type(&mut self, data_type: &DataType) { + match &data_type { + DataType::StructType { name: Some(name), variables } => { + self.index_struct_type(name, variables, StructSource::OriginalDeclaration) + } + DataType::EnumType { name: Some(name), numeric_type, elements } => { + self.index_enum_type(name, numeric_type, elements) + } + DataType::SubRangeType { name: Some(name), referenced_type, bounds } => { + self.index_sub_range_type(name, referenced_type, bounds.as_ref()) + } + DataType::ArrayType { name: Some(name), bounds, referenced_type, is_variable_length: false } => { + self.index_array_type(name, bounds, referenced_type) + } + DataType::ArrayType { name: Some(name), bounds, referenced_type, is_variable_length: true } => { + self.index_vla_array(name, bounds, referenced_type) + } + DataType::PointerType { name, referenced_type } => self.index_pointer_type(name, referenced_type), + DataType::StringType { name: Some(name), is_wide, size } => { + self.index_string_type(name.as_ref(), *is_wide, size.as_ref()) + } + // DataType::VarArgs { referenced_type, sized } => { + // inner_indexer.index_var_args(referenced_type, sized) + // } + DataType::GenericType { name, generic_symbol, nature } => { + self.index_generic_type(name, generic_symbol, nature) + } + _ => {} + } + } +} + +impl UserTypeIndexer<'_, '_> { + fn current_scope(&self) -> Option { + self.user_type.scope.clone() + } + + fn index_vla_array(&mut self, name: &str, bounds: &AstNode, referenced_type: &DataTypeDeclaration) { + let ndims = match bounds.get_stmt() { + AstStatement::VlaRangeStatement => 1, + AstStatement::ExpressionList(expressions) => expressions.len(), + _ => unreachable!("not a bounds statement"), + }; + + let referenced_type = referenced_type.get_name().expect("named datatype").to_string(); + let struct_name = name.to_owned(); + + let dummy_array_name = format!("__arr_vla_{ndims}_{referenced_type}").to_lowercase(); + let member_array_name = format!("__ptr_to_{dummy_array_name}"); + let member_dimensions_name = format!("__bounds_{dummy_array_name}"); + + // check the index if a dummy-array type matching the given VLA (eg. 1 dimension, type INT) already exists. + // if we find a type, we can use references to the internal types. otherwise, register the array in the index + // and declare internal member types. + let (vla_arr_type_declaration, dim_arr_type_declaration) = + if self.index.get_effective_type_by_name(&dummy_array_name).is_ok() { + ( + DataTypeDeclaration::DataTypeReference { + referenced_type: member_array_name, + location: SourceLocation::undefined(), + }, + DataTypeDeclaration::DataTypeReference { + referenced_type: member_dimensions_name, + location: SourceLocation::undefined(), + }, + ) + } else { + // register dummy array type so it can later be annotated as a type hint + self.index.register_type(typesystem::DataType { + name: dummy_array_name.clone(), + initial_value: None, + information: DataTypeInformation::Array { + name: dummy_array_name.clone(), + inner_type_name: referenced_type.clone(), + // dummy dimensions that will never actually be used + dimensions: (0..ndims) + .map(|_| Dimension { + start_offset: TypeSize::Undetermined, + end_offset: TypeSize::Undetermined, + }) + .collect::>(), + }, + nature: TypeNature::__VLA, + location: SourceLocation::internal(), + }); + + // define internal vla members + ( + DataTypeDeclaration::DataTypeDefinition { + data_type: DataType::PointerType { + name: Some(member_array_name), + referenced_type: Box::new(DataTypeDeclaration::DataTypeReference { + referenced_type: dummy_array_name, + location: SourceLocation::undefined(), + }), + }, + location: SourceLocation::undefined(), + scope: None, + }, + DataTypeDeclaration::DataTypeDefinition { + data_type: DataType::ArrayType { + name: Some(member_dimensions_name), + bounds: AstNode::new( + AstStatement::ExpressionList( + (0..ndims) + .map(|_| { + AstFactory::create_range_statement( + AstNode::new_literal( + AstLiteral::new_integer(0), + 0, + SourceLocation::undefined(), + ), + AstNode::new_literal( + AstLiteral::new_integer(1), + 0, + SourceLocation::undefined(), + ), + 0, + ) + }) + .collect::<_>(), + ), + 0, + SourceLocation::undefined(), + ), + referenced_type: Box::new(DataTypeDeclaration::DataTypeReference { + referenced_type: DINT_TYPE.to_string(), + location: SourceLocation::undefined(), + }), + is_variable_length: false, + }, + location: SourceLocation::undefined(), + scope: None, + }, + ) + }; + + // Create variable index entries for VLA struct members + let variables = vec![ + // Pointer + Variable { + name: format!("struct_vla_{referenced_type}_{ndims}").to_lowercase(), + data_type_declaration: vla_arr_type_declaration, + initializer: None, + address: None, + location: SourceLocation::undefined(), + }, + // Dimensions Array + Variable { + name: "dimensions".to_string(), + data_type_declaration: dim_arr_type_declaration, + initializer: None, + address: None, + location: SourceLocation::undefined(), + }, + ]; + + self.index_struct_type( + &struct_name, + &variables, + StructSource::Internal(typesystem::InternalType::VariableLengthArray { + inner_type_name: referenced_type, + ndims, + }), + ); + } + + fn index_array_type(&mut self, name: &str, bounds: &AstNode, referenced_type: &DataTypeDeclaration) { + let scope = self.current_scope(); + let dimensions: Result, Diagnostic> = bounds + .get_as_list() + .iter() + .map(|it| match it.get_stmt() { + AstStatement::RangeStatement(RangeStatement { start, end }) => { + let constants = self.index.get_mut_const_expressions(); + Ok(Dimension { + start_offset: TypeSize::from_expression(constants.add_constant_expression( + *start.clone(), + typesystem::DINT_TYPE.to_string(), + scope.clone(), + )), + end_offset: TypeSize::from_expression(constants.add_constant_expression( + *end.clone(), + typesystem::DINT_TYPE.to_string(), + scope.clone(), + )), + }) + } + + _ => Err(Diagnostic::codegen_error( + "Invalid array definition: RangeStatement expected", + it.get_location(), + )), + }) + .collect(); + + // TODO(mhasel, volsa): This unwrap will panic with `ARRAY[0..5, 5] OF DINT;` + let dimensions = dimensions.unwrap(); + + //TODO hmm we need to talk about all this unwrapping :-/ + let referenced_type_name = referenced_type.get_name().expect("named datatype"); + let information = DataTypeInformation::Array { + name: name.to_string(), + inner_type_name: referenced_type_name.to_string(), + dimensions, + }; + + self.register_type(name, information, TypeNature::Any); + let global_init_name = crate::index::get_initializer_name(name); + + // TODO unfortunately we cannot share const-expressions between multiple + // index-entries + let init2 = self + .user_type + .initializer + .as_ref() + .map(|i| self.collect_initializer(i.clone(), name.to_string())); + if init2.is_some() { + let variable = VariableIndexEntry::create_global( + global_init_name.as_str(), + global_init_name.as_str(), + name, + self.user_type.location.clone(), + ) + .set_constant(true) + .set_initial_value(init2); + self.index.register_global_initializer(&global_init_name, variable); + } + } + + fn index_enum_type(&mut self, name: &str, numeric_type: &str, elements: &AstNode) { + let mut variants = Vec::new(); + + for ele in flatten_expression_list(elements) { + let variant = get_enum_element_name(ele); + if let AstStatement::Assignment(Assignment { right, .. }) = ele.get_stmt() { + let scope = self.current_scope(); + let init = self.index.get_mut_const_expressions().add_constant_expression( + right.as_ref().clone(), + numeric_type.to_string(), + scope, + ); + + variants.push(self.index.register_enum_variant( + name, + &variant, + Some(init), + ele.get_location(), + )) + } else { + unreachable!("the preprocessor should have provided explicit assignments for enum values") + } + } + + let information = DataTypeInformation::Enum { + name: name.to_owned(), + variants, + referenced_type: numeric_type.to_string(), + }; + + self.register_type(name, information, TypeNature::Int); + } + + fn index_sub_range_type(&mut self, name: &str, referenced_type: &str, bounds: Option<&AstNode>) { + let information = if let Some(AstStatement::RangeStatement(RangeStatement { start, end })) = + bounds.as_ref().map(|it| it.get_stmt()) + { + DataTypeInformation::SubRange { + name: name.into(), + referenced_type: referenced_type.into(), + sub_range: (*start.clone()..*end.clone()), + } + } else { + DataTypeInformation::Alias { name: name.into(), referenced_type: referenced_type.into() } + }; + + self.register_type(name, information, TypeNature::Int); + } + + fn register_type(&mut self, name: &str, information: DataTypeInformation, nature: TypeNature) { + self.index.register_type(typesystem::DataType { + name: name.into(), + initial_value: self.pending_initializer, + information, + nature, + location: self.user_type.location.clone(), + }); + } + + fn index_generic_type(&mut self, name: &str, generic_symbol: &str, nature: &TypeNature) { + let information = DataTypeInformation::Generic { + name: name.to_string(), + generic_symbol: generic_symbol.to_string(), + nature: *nature, + }; + + self.register_type(name, information, TypeNature::Any); + } + + fn index_string_type(&mut self, name: &str, is_wide: bool, size: Option<&AstNode>) { + let encoding = if is_wide { StringEncoding::Utf16 } else { StringEncoding::Utf8 }; + + //TODO: handle the case where type_name is None + let scope = self.current_scope(); + + let size = match size { + Some(AstNode { stmt: AstStatement::Literal(AstLiteral::Integer(value)), .. }) => { + TypeSize::from_literal((value + 1) as i64) + } + Some(statement) => { + // construct a "x + 1" expression because we need one additional character for \0 terminator + let len_plus_1 = AstFactory::create_plus_one_expression( + statement.clone(), + statement.get_location(), + statement.get_id(), + ); + + TypeSize::from_expression(self.index.get_mut_const_expressions().add_constant_expression( + len_plus_1, + DINT_TYPE.to_string(), + scope, + )) + } + None => TypeSize::from_literal((DEFAULT_STRING_LEN + 1).into()), + }; + let information = DataTypeInformation::String { size, encoding }; + self.register_type(name, information, TypeNature::String); + + //TODO: can we reuse this? + if let Some(init) = self.pending_initializer { + // register a global variable with the initial value to memcopy from + let global_init_name = crate::index::get_initializer_name(name); + let initializer_global = VariableIndexEntry::create_global( + global_init_name.as_str(), + global_init_name.as_str(), + name, + self.user_type + .initializer + .clone() + .map(|i| i.get_location()) + .unwrap_or_else(|| self.user_type.location.clone()), + ) + .set_constant(true) + .set_initial_value(Some(init)); + self.index.register_global_initializer(global_init_name.as_str(), initializer_global); + } + } + + fn index_pointer_type(&mut self, name: &Option, referenced_type: &DataTypeDeclaration) { + let inner_type_name = referenced_type.get_name().expect("named datatype"); + let name = name.as_deref().unwrap(); + let information = DataTypeInformation::Pointer { + name: name.to_string(), + inner_type_name: inner_type_name.into(), + auto_deref: false, + }; + + self.index.register_type(typesystem::DataType { + name: name.to_string(), + initial_value: self.pending_initializer, + information, + nature: TypeNature::Any, + location: self.user_type.location.clone(), + }); + } + + fn index_struct_type(&mut self, name: &str, variables: &[Variable], source: StructSource) { + let scope = self.current_scope(); + let members = variables + .iter() + .enumerate() + .map(|(count, var)| { + // let inner_indexer = UserTypeIndexer::new(self.index, &var.data_type_declaration); + self.visit_data_type_declaration(&var.data_type_declaration); + + let member_type = var.data_type_declaration.get_name().expect("named variable datatype"); + let init = self.index.get_mut_const_expressions().maybe_add_constant_expression( + var.initializer.clone(), + member_type, + scope.clone(), + ); + + let binding = var + .address + .as_ref() + .and_then(|it| HardwareBinding::from_statement(self.index, it, scope.clone())); + + self.index.register_member_variable( + MemberInfo { + container_name: name, + variable_name: &var.name, + 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?!) + binding, + varargs: None, + }, + init, + var.location.clone(), + count as u32, + ) + }) + .collect::>(); + + let nature = source.get_type_nature(); + let information = DataTypeInformation::Struct { name: name.to_owned(), members, source }; + + self.register_type(name, information, nature); + + //Generate an initializer for the struct + let global_struct_name = crate::index::get_initializer_name(name); + let variable = VariableIndexEntry::create_global( + &global_struct_name, + &global_struct_name, + name, + self.user_type.location.clone(), + ) + .set_initial_value(self.pending_initializer) + .set_constant(true); + + self.index.register_global_initializer(&global_struct_name, variable); + } +} diff --git a/src/index/tests/builtin_tests.rs b/src/index/tests/builtin_tests.rs index 13126fbb8d..646aa9da57 100644 --- a/src/index/tests/builtin_tests.rs +++ b/src/index/tests/builtin_tests.rs @@ -6,7 +6,7 @@ use crate::{builtins, test_utils::tests::index}; fn builtin_functions_added_to_index() { let provider = IdProvider::default(); let builtins = builtins::parse_built_ins(provider); - let index = crate::index::visitor::visit(&builtins); + let index = crate::index::indexer::index(&builtins); assert!(index.find_member("ADR", "in").is_some()); assert!(index.find_member("REF", "in").is_some()); diff --git a/src/index/tests/index_tests.rs b/src/index/tests/index_tests.rs index b9dc72dc62..6195df8171 100644 --- a/src/index/tests/index_tests.rs +++ b/src/index/tests/index_tests.rs @@ -959,7 +959,7 @@ fn global_initializers_are_stored_in_the_const_expression_arena() { ); pre_process(&mut ast, ids); - let index = crate::index::visitor::visit(&ast); + let index = crate::index::indexer::index(&ast); // THEN I expect the index to contain cosntant expressions (x+1), (y+1) and (z+1) as const expressions // associated with the initial values of the globals @@ -1001,7 +1001,7 @@ fn local_initializers_are_stored_in_the_const_expression_arena() { ); pre_process(&mut ast, ids); - let index = crate::index::visitor::visit(&ast); + let index = crate::index::indexer::index(&ast); // THEN I expect the index to contain cosntant expressions (x+1), (y+1) and (z+1) as const expressions // associated with the initial values of the members @@ -1037,7 +1037,7 @@ fn datatype_initializers_are_stored_in_the_const_expression_arena() { ); pre_process(&mut ast, ids); - let index = crate::index::visitor::visit(&ast); + let index = crate::index::indexer::index(&ast); // THEN I expect the index to contain cosntant expressions (7+x) as const expressions // associated with the initial values of the type @@ -1064,7 +1064,7 @@ fn array_dimensions_are_stored_in_the_const_expression_arena() { ); pre_process(&mut ast, ids); - let index = crate::index::visitor::visit(&ast); + let index = crate::index::indexer::index(&ast); // THEN I expect the index to contain constants expressions used in the array-dimensions @@ -1134,7 +1134,7 @@ fn string_dimensions_are_stored_in_the_const_expression_arena() { ); pre_process(&mut ast, ids); - let index = crate::index::visitor::visit(&ast); + let index = crate::index::indexer::index(&ast); // THEN I expect the index to contain constants expressions used in the string-len diff --git a/src/index/visitor.rs b/src/index/visitor.rs deleted file mode 100644 index cff5e49e47..0000000000 --- a/src/index/visitor.rs +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder -use super::{HardwareBinding, PouIndexEntry, VariableIndexEntry, VariableType}; -use crate::index::{ArgumentType, Index, MemberInfo}; -use crate::typesystem::{self, *}; -use plc_ast::ast::{ - self, ArgumentProperty, Assignment, AstFactory, AstNode, AstStatement, CompilationUnit, DataType, - DataTypeDeclaration, Implementation, Pou, PouType, RangeStatement, TypeNature, UserTypeDeclaration, - Variable, VariableBlock, VariableBlockType, -}; -use plc_ast::literals::AstLiteral; -use plc_diagnostics::diagnostics::Diagnostic; -use plc_source::source_location::SourceLocation; -use plc_util::convention::internal_type_name; - -pub fn visit(unit: &CompilationUnit) -> Index { - let mut index = Index::default(); - //Create user defined datatypes - for user_type in &unit.user_types { - visit_data_type(&mut index, user_type); - } - - //Create defined global variables - for global_vars in &unit.global_vars { - visit_global_var_block(&mut index, global_vars); - } - - //Create types and variables for POUs - for pou in &unit.units { - visit_pou(&mut index, pou); - } - - for implementation in &unit.implementations { - visit_implementation(&mut index, implementation); - } - index -} - -pub fn visit_pou(index: &mut Index, pou: &Pou) { - let mut members = vec![]; - - //register the pou's member variables - let mut member_varargs = None; - let mut count = 0; - for block in &pou.variable_blocks { - let block_type = get_declaration_type_for(block, &pou.pou_type); - for var in &block.variables { - let varargs = if let DataTypeDeclaration::DataTypeDefinition { - data_type: ast::DataType::VarArgs { referenced_type, sized }, - .. - } = &var.data_type_declaration - { - let name = referenced_type - .as_ref() - .map(|it| &**it) - .and_then(DataTypeDeclaration::get_name) - .map(|it| it.to_string()); - Some(if *sized { VarArgs::Sized(name) } else { VarArgs::Unsized(name) }) - } else { - None - }; - - if varargs.is_some() { - member_varargs = varargs.clone(); - } - - let var_type_name = var.data_type_declaration.get_name().unwrap_or(VOID_TYPE); - let type_name = if block_type.is_by_ref() { - //register a pointer type for argument - register_byref_pointer_type_for(index, var_type_name) - } else { - var_type_name.to_string() - }; - let initial_value = index.get_mut_const_expressions().maybe_add_constant_expression( - var.initializer.clone(), - type_name.as_str(), - Some(pou.name.clone()), - ); - - let binding = var - .address - .as_ref() - .and_then(|it| HardwareBinding::from_statement(index, it, Some(pou.name.clone()))); - - let entry = index.register_member_variable( - MemberInfo { - container_name: &pou.name, - variable_name: &var.name, - variable_linkage: block_type, - variable_type_name: &type_name, - is_constant: block.constant, - binding, - varargs, - }, - initial_value, - var.location.clone(), - count, - ); - members.push(entry); - count += 1; - } - } - - //register a function's return type as a member variable - let return_type_name = pou.return_type.as_ref().and_then(|it| it.get_name()).unwrap_or(VOID_TYPE); - if pou.return_type.is_some() { - let entry = index.register_member_variable( - MemberInfo { - container_name: &pou.name, - 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 - binding: None, - varargs: None, - }, - None, - pou.name_location.clone(), - count, - ); - members.push(entry); - } - - let has_varargs = member_varargs.is_some(); - let datatype = typesystem::DataType { - name: pou.name.to_string(), - initial_value: None, - information: DataTypeInformation::Struct { - name: pou.name.to_string(), - members, - source: StructSource::Pou(pou.pou_type.clone()), - }, - nature: TypeNature::Any, - location: pou.name_location.clone(), - }; - - match &pou.pou_type { - PouType::Program => { - index.register_program(&pou.name, pou.name_location.clone(), pou.linkage); - index.register_pou_type(datatype); - } - PouType::FunctionBlock => { - let global_struct_name = crate::index::get_initializer_name(&pou.name); - let variable = VariableIndexEntry::create_global( - &global_struct_name, - &global_struct_name, - &pou.name, - pou.name_location.clone(), - ) - .set_constant(true); - index.register_global_initializer(&global_struct_name, variable); - index.register_pou(PouIndexEntry::create_function_block_entry( - &pou.name, - pou.linkage, - pou.name_location.clone(), - pou.super_class.clone().as_deref(), - )); - index.register_pou_type(datatype); - } - PouType::Class => { - let global_struct_name = crate::index::get_initializer_name(&pou.name); - let variable = VariableIndexEntry::create_global( - &global_struct_name, - &global_struct_name, - &pou.name, - pou.name_location.clone(), - ) - .set_constant(true); - index.register_global_initializer(&global_struct_name, variable); - index.register_pou(PouIndexEntry::create_class_entry( - &pou.name, - pou.linkage, - pou.name_location.clone(), - pou.super_class.clone(), - )); - index.register_pou_type(datatype); - } - PouType::Function => { - index.register_pou(PouIndexEntry::create_function_entry( - &pou.name, - return_type_name, - &pou.generics, - pou.linkage, - has_varargs, - pou.name_location.clone(), - )); - index.register_pou_type(datatype); - } - PouType::Method { owner_class } => { - index.register_pou(PouIndexEntry::create_method_entry( - &pou.name, - return_type_name, - owner_class, - pou.linkage, - pou.name_location.clone(), - )); - index.register_pou_type(datatype); - } - _ => {} - }; -} - -/// returns the declaration type (ByRef or ByVal) for the given VariableBlock (VAR_INPUT, VAR_OUTPUT, VAR_INOUT, etc.) -fn get_declaration_type_for(block: &VariableBlock, pou_type: &PouType) -> ArgumentType { - if matches!( - block.variable_block_type, - VariableBlockType::InOut | VariableBlockType::Input(ArgumentProperty::ByRef) - ) { - ArgumentType::ByRef(get_variable_type_from_block(block)) - } else if block.variable_block_type == VariableBlockType::Output { - // outputs differ depending on pou type - match pou_type { - PouType::Function => ArgumentType::ByRef(get_variable_type_from_block(block)), - _ => ArgumentType::ByVal(get_variable_type_from_block(block)), - } - } else { - ArgumentType::ByVal(get_variable_type_from_block(block)) - } -} - -fn visit_implementation(index: &mut Index, implementation: &Implementation) { - let pou_type = &implementation.pou_type; - let start_location = implementation - .statements - .first() - .map(|it| it.get_location()) - .as_ref() - .or(Some(&implementation.location)) - .cloned() - .unwrap(); - index.register_implementation( - &implementation.name, - &implementation.type_name, - pou_type.get_optional_owner_class().as_ref(), - pou_type.into(), - implementation.generic, - start_location, - ); - //if we are registing an action, also register a datatype for it - if pou_type == &PouType::Action { - let datatype = typesystem::DataType { - name: implementation.name.to_string(), - initial_value: None, - information: DataTypeInformation::Alias { - name: implementation.name.clone(), - referenced_type: implementation.type_name.clone(), - }, - nature: TypeNature::Derived, - location: implementation.name_location.clone(), - }; - - index.register_pou(PouIndexEntry::create_action_entry( - implementation.name.as_str(), - implementation.type_name.as_str(), - implementation.linkage, - implementation.name_location.clone(), - )); - index.register_pou_type(datatype); - } -} - -/// registers an auto-deref pointer type for the inner_type_name if it does not already exist -fn register_byref_pointer_type_for(index: &mut Index, inner_type_name: &str) -> String { - //get unique name - let type_name = internal_type_name("auto_pointer_to_", inner_type_name); - - //check if type was already created - if index.find_effective_type_by_name(type_name.as_str()).is_none() { - //generate a pointertype for the variable - index.register_type(typesystem::DataType { - name: type_name.clone(), - initial_value: None, - information: DataTypeInformation::Pointer { - name: type_name.clone(), - inner_type_name: inner_type_name.to_string(), - auto_deref: true, - }, - nature: TypeNature::Any, - location: SourceLocation::internal(), - }); - } - - type_name -} - -fn visit_global_var_block(index: &mut Index, block: &VariableBlock) { - let linkage = block.linkage; - for var in &block.variables { - let target_type = var.data_type_declaration.get_name().unwrap_or_default(); - let initializer = index.get_mut_const_expressions().maybe_add_constant_expression( - var.initializer.clone(), - target_type, - None, - ); - let variable = VariableIndexEntry::create_global( - &var.name, - &var.name, - var.data_type_declaration.get_name().expect("named variable datatype"), - var.location.clone(), - ) - .set_initial_value(initializer) - .set_constant(block.constant) - .set_linkage(linkage) - .set_hardware_binding( - var.address.as_ref().and_then(|it| HardwareBinding::from_statement(index, it, None)), - ); - index.register_global_variable(&var.name, variable); - } -} - -fn get_variable_type_from_block(block: &VariableBlock) -> VariableType { - match block.variable_block_type { - VariableBlockType::Local => VariableType::Local, - VariableBlockType::Temp => VariableType::Temp, - VariableBlockType::Input(_) => VariableType::Input, - VariableBlockType::Output => VariableType::Output, - VariableBlockType::Global => VariableType::Global, - VariableBlockType::InOut => VariableType::InOut, - } -} - -fn visit_data_type(index: &mut Index, type_declaration: &UserTypeDeclaration) { - let data_type = &type_declaration.data_type; - let scope = &type_declaration.scope; - //names should not be empty - match data_type { - DataType::StructType { name: Some(name), variables } => { - visit_struct(name, variables, index, scope, type_declaration, StructSource::OriginalDeclaration); - } - - DataType::EnumType { name: Some(name), elements, numeric_type, .. } => { - let mut variants = Vec::new(); - - for ele in ast::flatten_expression_list(elements) { - let variant = ast::get_enum_element_name(ele); - if let AstStatement::Assignment(Assignment { right, .. }) = ele.get_stmt() { - let init = index.get_mut_const_expressions().add_constant_expression( - right.as_ref().clone(), - numeric_type.clone(), - scope.clone(), - ); - - variants.push(index.register_enum_variant(name, &variant, Some(init), ele.get_location())) - } else { - unreachable!("the preprocessor should have provided explicit assignments for enum values") - } - } - - let information = DataTypeInformation::Enum { - name: name.to_owned(), - variants, - referenced_type: numeric_type.clone(), - }; - - let init = index.get_mut_const_expressions().maybe_add_constant_expression( - type_declaration.initializer.clone(), - name, - scope.clone(), - ); - - index.register_type(typesystem::DataType { - name: name.to_owned(), - initial_value: init, - information, - nature: TypeNature::Int, - location: type_declaration.location.clone(), - }); - } - - DataType::SubRangeType { name: Some(name), referenced_type, bounds } => { - let information = if let Some(AstStatement::RangeStatement(RangeStatement { start, end })) = - bounds.as_ref().map(|it| it.get_stmt()) - { - DataTypeInformation::SubRange { - name: name.into(), - referenced_type: referenced_type.into(), - sub_range: (*start.clone()..*end.clone()), - } - } else { - DataTypeInformation::Alias { name: name.into(), referenced_type: referenced_type.into() } - }; - - let init = index.get_mut_const_expressions().maybe_add_constant_expression( - type_declaration.initializer.clone(), - name, - scope.clone(), - ); - index.register_type(typesystem::DataType { - name: name.to_string(), - initial_value: init, - information, - nature: TypeNature::Int, - location: type_declaration.location.clone(), - }); - } - DataType::ArrayType { name: Some(name), referenced_type, bounds, is_variable_length } - if *is_variable_length => - { - visit_variable_length_array(bounds, referenced_type, name, index, type_declaration); - } - DataType::ArrayType { name: Some(name), bounds, referenced_type, .. } => { - visit_array(bounds, index, scope, referenced_type, name, type_declaration); - } - DataType::PointerType { name: Some(name), referenced_type, .. } => { - let inner_type_name = referenced_type.get_name().expect("named datatype"); - let information = DataTypeInformation::Pointer { - name: name.clone(), - inner_type_name: inner_type_name.into(), - auto_deref: false, - }; - - let init = index.get_mut_const_expressions().maybe_add_constant_expression( - type_declaration.initializer.clone(), - name, - scope.clone(), - ); - index.register_type(typesystem::DataType { - name: name.to_string(), - initial_value: init, - information, - nature: TypeNature::Any, - location: type_declaration.location.clone(), - }); - } - DataType::StringType { name: Some(name), size, is_wide, .. } => { - let type_name = name; - let encoding = if *is_wide { StringEncoding::Utf16 } else { StringEncoding::Utf8 }; - - let size = match size { - Some(AstNode { stmt: AstStatement::Literal(AstLiteral::Integer(value)), .. }) => { - TypeSize::from_literal((value + 1) as i64) - } - Some(statement) => { - // construct a "x + 1" expression because we need one additional character for \0 terminator - let len_plus_1 = AstFactory::create_binary_expression( - statement.clone(), - ast::Operator::Plus, - AstNode::new_literal( - AstLiteral::new_integer(1), - statement.get_id(), - statement.get_location(), - ), - statement.get_id(), - ); - - TypeSize::from_expression(index.get_mut_const_expressions().add_constant_expression( - len_plus_1, - type_name.clone(), - scope.clone(), - )) - } - None => TypeSize::from_literal((DEFAULT_STRING_LEN + 1).into()), - }; - let information = DataTypeInformation::String { size, encoding }; - let init = index.get_mut_const_expressions().maybe_add_constant_expression( - type_declaration.initializer.clone(), - type_name, - scope.clone(), - ); - index.register_type(typesystem::DataType { - name: name.to_string(), - initial_value: init, - information, - nature: TypeNature::String, - location: type_declaration.location.clone(), - }); - - if init.is_some() { - // register a global variable with the initial value to memcopy from - let global_init_name = crate::index::get_initializer_name(name); - let initializer_global = VariableIndexEntry::create_global( - global_init_name.as_str(), - global_init_name.as_str(), - name, - type_declaration.location.clone(), - ) - .set_constant(true) - .set_initial_value(init); - index.register_global_initializer(global_init_name.as_str(), initializer_global); - } - } - DataType::VarArgs { .. } => {} //Varargs are not indexed, - DataType::GenericType { name, generic_symbol, nature } => { - let information = DataTypeInformation::Generic { - name: name.clone(), - generic_symbol: generic_symbol.clone(), - nature: *nature, - }; - index.register_type(typesystem::DataType { - name: name.to_string(), - initial_value: None, - information, - nature: TypeNature::Any, - location: type_declaration.location.clone(), - }); - } - - _ => { /* unnamed datatypes are ignored */ } - }; -} - -/// Internally we create a fat pointer struct for VLAs, which consists of a pointer to the passed array plus -/// its dimensions, such that `ARRAY[*, *, *] OF INT` becomes -/// ```ignore -/// STRUCT -/// ptr : REF_TO ARRAY[*] OF INT; -/// dimensions: ARRAY[0..2, 0..1] OF DINT; -/// ^^^^ --> dimension index -/// ^^^^ --> start- & end-offset -/// END_STRUCT -/// ``` -fn visit_variable_length_array( - bounds: &AstNode, - referenced_type: &DataTypeDeclaration, - name: &str, - index: &mut Index, - type_declaration: &UserTypeDeclaration, -) { - let ndims = match bounds.get_stmt() { - AstStatement::VlaRangeStatement => 1, - AstStatement::ExpressionList(expressions) => expressions.len(), - _ => unreachable!("not a bounds statement"), - }; - - let referenced_type = referenced_type.get_name().expect("named datatype").to_string(); - let struct_name = name.to_owned(); - - let dummy_array_name = format!("__arr_vla_{ndims}_{referenced_type}").to_lowercase(); - let member_array_name = format!("__ptr_to_{dummy_array_name}"); - let member_dimensions_name = format!("__bounds_{dummy_array_name}"); - - // check the index if a dummy-array type matching the given VLA (eg. 1 dimension, type INT) already exists. - // if we find a type, we can use references to the internal types. otherwise, register the array in the index - // and declare internal member types. - let (vla_arr_type_declaration, dim_arr_type_declaration) = - if index.get_effective_type_by_name(&dummy_array_name).is_ok() { - ( - DataTypeDeclaration::DataTypeReference { - referenced_type: member_array_name, - location: SourceLocation::undefined(), - }, - DataTypeDeclaration::DataTypeReference { - referenced_type: member_dimensions_name, - location: SourceLocation::undefined(), - }, - ) - } else { - // register dummy array type so it can later be annotated as a type hint - index.register_type(typesystem::DataType { - name: dummy_array_name.clone(), - initial_value: None, - information: DataTypeInformation::Array { - name: dummy_array_name.clone(), - inner_type_name: referenced_type.clone(), - // dummy dimensions that will never actually be used - dimensions: (0..ndims) - .map(|_| Dimension { - start_offset: TypeSize::Undetermined, - end_offset: TypeSize::Undetermined, - }) - .collect::>(), - }, - nature: TypeNature::__VLA, - location: SourceLocation::internal(), - }); - - // define internal vla members - ( - DataTypeDeclaration::DataTypeDefinition { - data_type: DataType::PointerType { - name: Some(member_array_name), - referenced_type: Box::new(DataTypeDeclaration::DataTypeReference { - referenced_type: dummy_array_name, - location: SourceLocation::undefined(), - }), - }, - location: SourceLocation::undefined(), - scope: None, - }, - DataTypeDeclaration::DataTypeDefinition { - data_type: DataType::ArrayType { - name: Some(member_dimensions_name), - bounds: AstNode::new( - AstStatement::ExpressionList( - (0..ndims) - .map(|_| { - AstFactory::create_range_statement( - AstNode::new_literal( - AstLiteral::new_integer(0), - 0, - SourceLocation::undefined(), - ), - AstNode::new_literal( - AstLiteral::new_integer(1), - 0, - SourceLocation::undefined(), - ), - 0, - ) - }) - .collect::<_>(), - ), - 0, - SourceLocation::undefined(), - ), - referenced_type: Box::new(DataTypeDeclaration::DataTypeReference { - referenced_type: DINT_TYPE.to_string(), - location: SourceLocation::undefined(), - }), - is_variable_length: false, - }, - location: SourceLocation::undefined(), - scope: None, - }, - ) - }; - - // Create variable index entries for VLA struct members - let variables = vec![ - // Pointer - Variable { - name: format!("struct_vla_{referenced_type}_{ndims}").to_lowercase(), - data_type_declaration: vla_arr_type_declaration, - initializer: None, - address: None, - location: SourceLocation::undefined(), - }, - // Dimensions Array - Variable { - name: "dimensions".to_string(), - data_type_declaration: dim_arr_type_declaration, - initializer: None, - address: None, - location: SourceLocation::undefined(), - }, - ]; - - let struct_ty = DataType::StructType { name: Some(struct_name.clone()), variables: variables.clone() }; - let type_dec = UserTypeDeclaration { - data_type: struct_ty, - initializer: None, - location: type_declaration.location.clone(), - scope: type_declaration.scope.clone(), - }; - - // visit the internally created struct type to also index its members - visit_struct( - &struct_name, - &variables, - index, - &type_declaration.scope, - &type_dec, - StructSource::Internal(InternalType::VariableLengthArray { inner_type_name: referenced_type, ndims }), - ) -} - -fn visit_array( - bounds: &AstNode, - index: &mut Index, - scope: &Option, - referenced_type: &DataTypeDeclaration, - name: &String, - type_declaration: &UserTypeDeclaration, -) { - let dimensions: Result, Diagnostic> = bounds - .get_as_list() - .iter() - .map(|it| match it.get_stmt() { - AstStatement::RangeStatement(RangeStatement { start, end }) => { - let constants = index.get_mut_const_expressions(); - Ok(Dimension { - start_offset: TypeSize::from_expression(constants.add_constant_expression( - *start.clone(), - typesystem::DINT_TYPE.to_string(), - scope.clone(), - )), - end_offset: TypeSize::from_expression(constants.add_constant_expression( - *end.clone(), - typesystem::DINT_TYPE.to_string(), - scope.clone(), - )), - }) - } - - _ => Err(Diagnostic::codegen_error( - "Invalid array definition: RangeStatement expected", - it.get_location(), - )), - }) - .collect(); - - // TODO(mhasel, volsa): This unwrap will panic with `ARRAY[0..5, 5] OF DINT;` - let dimensions = dimensions.unwrap(); - - //TODO hmm we need to talk about all this unwrapping :-/ - let referenced_type_name = referenced_type.get_name().expect("named datatype"); - let information = DataTypeInformation::Array { - name: name.clone(), - inner_type_name: referenced_type_name.to_string(), - dimensions, - }; - - let init1 = index.get_mut_const_expressions().maybe_add_constant_expression( - type_declaration.initializer.clone(), - name, - scope.clone(), - ); - // TODO unfortunately we cannot share const-expressions between multiple - // index-entries - let init2 = index.get_mut_const_expressions().maybe_add_constant_expression( - type_declaration.initializer.clone(), - name, - scope.clone(), - ); - - index.register_type(typesystem::DataType { - name: name.to_string(), - initial_value: init1, - information, - nature: TypeNature::Any, - location: type_declaration.location.clone(), - }); - let global_init_name = crate::index::get_initializer_name(name); - if init2.is_some() { - let variable = VariableIndexEntry::create_global( - global_init_name.as_str(), - global_init_name.as_str(), - name, - type_declaration.location.clone(), - ) - .set_constant(true) - .set_initial_value(init2); - index.register_global_initializer(&global_init_name, variable); - } -} - -fn visit_struct( - name: &str, - variables: &[Variable], - index: &mut Index, - scope: &Option, - type_declaration: &UserTypeDeclaration, - source: StructSource, -) { - let members = variables - .iter() - .enumerate() - .map(|(count, var)| { - if let DataTypeDeclaration::DataTypeDefinition { data_type, scope, .. } = - &var.data_type_declaration - { - //first we need to handle the inner type - visit_data_type( - index, - &UserTypeDeclaration { - data_type: data_type.clone(), - initializer: None, - location: SourceLocation::undefined(), - scope: scope.clone(), - }, - ) - } - - let member_type = var.data_type_declaration.get_name().expect("named variable datatype"); - let init = index.get_mut_const_expressions().maybe_add_constant_expression( - var.initializer.clone(), - member_type, - scope.clone(), - ); - - let binding = - var.address.as_ref().and_then(|it| HardwareBinding::from_statement(index, it, scope.clone())); - - index.register_member_variable( - MemberInfo { - container_name: name, - variable_name: &var.name, - 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?!) - binding, - varargs: None, - }, - init, - var.location.clone(), - count as u32, - ) - }) - .collect::>(); - - let nature = source.get_type_nature(); - let information = DataTypeInformation::Struct { name: name.to_owned(), members, source }; - - let init = index.get_mut_const_expressions().maybe_add_constant_expression( - type_declaration.initializer.clone(), - name, - scope.clone(), - ); - index.register_type(typesystem::DataType { - name: name.to_string(), - initial_value: init, - information, - nature, - location: type_declaration.location.clone(), - }); - //Generate an initializer for the struct - let global_struct_name = crate::index::get_initializer_name(name); - let variable = VariableIndexEntry::create_global( - &global_struct_name, - &global_struct_name, - name, - type_declaration.location.clone(), - ) - .set_initial_value(init) - .set_constant(true); - index.register_global_initializer(&global_struct_name, variable); -} diff --git a/src/test_utils.rs b/src/test_utils.rs index f7631b06c5..021aab144a 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -65,7 +65,7 @@ pub mod tests { //Import builtins let builtins = builtins::parse_built_ins(id_provider.clone()); - index.import(index::visitor::visit(&builtins)); + index.import(index::indexer::index(&builtins)); // import built-in types like INT, BOOL, etc. for data_type in get_builtin_types() { index.register_type(data_type); @@ -79,7 +79,7 @@ pub mod tests { ); pre_process(&mut unit, id_provider); - index.import(index::visitor::visit(&unit)); + index.import(index::indexer::index(&unit)); (unit, index, diagnostics) } diff --git a/src/tests/adr/vla_adr.rs b/src/tests/adr/vla_adr.rs index e4ba8d7512..07156f1c1a 100644 --- a/src/tests/adr/vla_adr.rs +++ b/src/tests/adr/vla_adr.rs @@ -107,7 +107,17 @@ fn representation() { }, nature: Any, location: SourceLocation { - span: None, + span: Range( + TextLocation { + line: 3, + column: 22, + offset: 79, + }..TextLocation { + line: 3, + column: 38, + offset: 95, + }, + ), }, } "###); @@ -163,7 +173,17 @@ fn representation() { }, nature: Any, location: SourceLocation { - span: None, + span: Range( + TextLocation { + line: 3, + column: 22, + offset: 79, + }..TextLocation { + line: 3, + column: 38, + offset: 95, + }, + ), }, } "###); diff --git a/src/validation/tests/duplicates_validation_test.rs b/src/validation/tests/duplicates_validation_test.rs index ecc8e1cf3c..a94b87c9e4 100644 --- a/src/validation/tests/duplicates_validation_test.rs +++ b/src/validation/tests/duplicates_validation_test.rs @@ -9,7 +9,7 @@ use plc_source::source_location::SourceLocationFactory; use plc_source::SourceCode; use crate::{ - index::{visitor, Index}, + index::{indexer, Index}, lexer, parser, resolver::TypeAnnotator, test_utils::tests::parse_and_validate_buffered, @@ -352,7 +352,7 @@ fn automatically_generated_output_types_in_different_files_dont_cause_duplicatio "test.st", ); pre_process(&mut unit, id_provider); - index.import(visitor::visit(&unit)); + index.import(indexer::index(&unit)); index } @@ -412,7 +412,7 @@ fn duplicate_with_generic() { file_name, ); pre_process(&mut unit, id_provider); - index.import(visitor::visit(&unit)); + index.import(indexer::index(&unit)); (index, unit) }