diff --git a/examples/class_method.st b/examples/class_method.st new file mode 100644 index 0000000000..f89d2e6fa3 --- /dev/null +++ b/examples/class_method.st @@ -0,0 +1,12 @@ +CLASS MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod + VAR_INPUT myMethodArg : INT; END_VAR + VAR myMethodLocalVar : INT; END_VAR + + x := myMethodArg; + END_METHOD +END_CLASS \ No newline at end of file diff --git a/examples/simple_program.st b/examples/simple_program.st index e99d328c29..6f74ac33b8 100644 --- a/examples/simple_program.st +++ b/examples/simple_program.st @@ -1,8 +1,8 @@ - TYPE the_struct : STRUCT END_STRUCT END_TYPE - - PROGRAM prg - VAR - my_struct : STRUCT - END_STRUCT - END_VAR - END_PROGRAM \ No newline at end of file +TYPE the_struct : STRUCT END_STRUCT END_TYPE + +PROGRAM prg + VAR + my_struct : STRUCT + END_STRUCT + END_VAR +END_PROGRAM \ No newline at end of file diff --git a/src/ast.rs b/src/ast.rs index 99b6c86399..79c95e18fd 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -50,6 +50,16 @@ impl Debug for Pou { } } +impl Pou { + pub fn get_return_name(&self) -> &str { + Pou::calc_return_name(&self.name) + } + + pub fn calc_return_name(pou_name: &str) -> &str { + pou_name.split('.').last().unwrap_or_default() + } +} + #[derive(Debug, PartialEq)] pub struct Implementation { pub name: String, @@ -76,14 +86,25 @@ pub enum AccessModifier { Internal, } -#[derive(Debug, Copy, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone)] pub enum PouType { Program, Function, FunctionBlock, Action, Class, - Method, + Method { owner_class: String }, +} + +impl PouType { + /// returns Some(owner_class) if this is a `Method` or otherwhise `None` + pub fn get_optional_owner_class(&self) -> Option { + if let PouType::Method { owner_class } = self { + Some(owner_class.clone()) + } else { + None + } + } } #[derive(Debug, PartialEq)] diff --git a/src/codegen.rs b/src/codegen.rs index 6e41833792..850b4579ac 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -71,21 +71,33 @@ impl<'ink> CodeGen<'ink> { fn generate_llvm_index( &self, module: &Module<'ink>, + annotations: &AnnotationMap, global_index: &Index, ) -> Result, CompileError> { let llvm = Llvm::new(self.context, self.context.create_builder()); let mut index = LlvmTypedIndex::new(); //Generate types index, and any global variables associated with them. - let llvm_type_index = data_type_generator::generate_data_types(&llvm, global_index)?; + let llvm_type_index = + data_type_generator::generate_data_types(&llvm, global_index, annotations)?; index.merge(llvm_type_index); //Generate global variables - let llvm_gv_index = - variable_generator::generate_global_variables(module, &llvm, global_index, &index)?; + let llvm_gv_index = variable_generator::generate_global_variables( + module, + &llvm, + global_index, + annotations, + &index, + )?; index.merge(llvm_gv_index); //Generate opaque functions for implementations and associate them with their types let llvm = Llvm::new(self.context, self.context.create_builder()); - let llvm_impl_index = - pou_generator::generate_implementation_stubs(module, llvm, global_index, &index)?; + let llvm_impl_index = pou_generator::generate_implementation_stubs( + module, + llvm, + global_index, + annotations, + &index, + )?; index.merge(llvm_impl_index); Ok(index) } @@ -94,15 +106,15 @@ impl<'ink> CodeGen<'ink> { pub fn generate( &self, unit: &CompilationUnit, - _annotations: &AnnotationMap, + annotations: &AnnotationMap, global_index: &Index, ) -> Result { //Associate the index type with LLVM types - let llvm_index = self.generate_llvm_index(&self.module, global_index)?; + let llvm_index = self.generate_llvm_index(&self.module, annotations, global_index)?; //generate all pous let llvm = Llvm::new(self.context, self.context.create_builder()); - let pou_generator = PouGenerator::new(llvm, global_index, &llvm_index); + let pou_generator = PouGenerator::new(llvm, global_index, annotations, &llvm_index); //Generate the POU stubs in the first go to make sure they can be referenced. for implementation in &unit.implementations { //Don't generate external functions diff --git a/src/codegen/generators/data_type_generator.rs b/src/codegen/generators/data_type_generator.rs index 5e9cb3517d..a1906beefe 100644 --- a/src/codegen/generators/data_type_generator.rs +++ b/src/codegen/generators/data_type_generator.rs @@ -7,6 +7,7 @@ /// - Alias types /// - sized Strings use crate::index::{Index, VariableIndexEntry}; +use crate::resolver::AnnotationMap; use crate::{ ast::{AstStatement, Dimension}, compile_error::CompileError, @@ -29,6 +30,13 @@ use super::{ expression_generator::ExpressionCodeGenerator, llvm::Llvm, struct_generator::StructGenerator, }; +pub struct DataTypeGenerator<'ink, 'b> { + llvm: &'b Llvm<'ink>, + index: &'b Index, + annotations: &'b AnnotationMap, + types_index: LlvmTypedIndex<'ink>, +} + /// generates the llvm-type for the given data-type and registers it at the index /// this function may create and register a ... /// - Struct type for a STRUCT @@ -39,267 +47,262 @@ use super::{ pub fn generate_data_types<'ink>( llvm: &Llvm<'ink>, index: &Index, + annotations: &AnnotationMap, ) -> Result, CompileError> { - let mut types_index = LlvmTypedIndex::new(); - let types = index.get_types(); + let mut generator = DataTypeGenerator { + llvm, + index, + annotations, + types_index: LlvmTypedIndex::new(), + }; + + let types = generator.index.get_types(); for (name, user_type) in types { if let DataTypeInformation::Struct { name: struct_name, .. } = user_type.get_type_information() { - types_index.associate_type(name, llvm.create_struct_stub(struct_name).into())?; + generator + .types_index + .associate_type(name, llvm.create_struct_stub(struct_name).into())?; } } for (name, user_type) in types { - let gen_type = create_type(llvm, index, &types_index, name, user_type)?; - types_index.associate_type(name, gen_type)? + let gen_type = generator.create_type(name, user_type)?; + generator.types_index.associate_type(name, gen_type)? } for (name, user_type) in types { - expand_opaque_types(llvm, index, &mut types_index, user_type)?; - if let Some(initial_value) = generate_initial_value(index, &types_index, llvm, user_type) { - types_index.associate_initial_value(name, initial_value)? + generator.expand_opaque_types(user_type)?; + if let Some(initial_value) = generator.generate_initial_value(user_type) { + generator + .types_index + .associate_initial_value(name, initial_value)? } } - Ok(types_index) + Ok(generator.types_index) } -/// generates the members of an opaque struct and associates its initial values -fn expand_opaque_types<'ink>( - llvm: &Llvm<'ink>, - index: &Index, - types_index: &mut LlvmTypedIndex<'ink>, - data_type: &DataType, -) -> Result<(), CompileError> { - let information = data_type.get_type_information(); - if let DataTypeInformation::Struct { member_names, .. } = information { - let mut struct_generator = StructGenerator::new(llvm, index, types_index); - let members: Vec<&VariableIndexEntry> = member_names - .iter() - .map(|variable_name| { - index - .find_member(data_type.get_name(), variable_name) - .unwrap() - }) - .filter(|var| !var.is_return()) - .collect(); - let ((_, initial_value), member_values) = - struct_generator.generate_struct_type(&members, data_type.get_name())?; - for (member, value) in member_values { - let qualified_name = format!("{}.{}", data_type.get_name(), member); - types_index.associate_initial_value(&qualified_name, value)?; +impl<'ink, 'b> DataTypeGenerator<'ink, 'b> { + /// generates the members of an opaque struct and associates its initial values + fn expand_opaque_types(&mut self, data_type: &DataType) -> Result<(), CompileError> { + let information = data_type.get_type_information(); + if let DataTypeInformation::Struct { member_names, .. } = information { + let mut struct_generator = + StructGenerator::new(self.llvm, self.index, self.annotations, &self.types_index); + let members: Vec<&VariableIndexEntry> = member_names + .iter() + .map(|variable_name| { + self.index + .find_member(data_type.get_name(), variable_name) + .unwrap() + }) + .filter(|var| !var.is_return()) + .collect(); + let ((_, initial_value), member_values) = + struct_generator.generate_struct_type(&members, data_type.get_name())?; + for (member, value) in member_values { + let qualified_name = format!("{}.{}", data_type.get_name(), member); + self.types_index + .associate_initial_value(&qualified_name, value)?; + } + self.types_index + .associate_initial_value(data_type.get_name(), initial_value)?; } - types_index.associate_initial_value(data_type.get_name(), initial_value)?; + Ok(()) } - Ok(()) -} -/// Creates an llvm type to be associated with the given data type. -/// Generates only an opaque type for structs. -/// Eagerly generates but does not associate nested array and referenced aliased types -fn create_type<'ink>( - llvm: &Llvm<'ink>, - index: &Index, - types_index: &LlvmTypedIndex<'ink>, - name: &str, - data_type: &DataType, -) -> Result, CompileError> { - let information = data_type.get_type_information(); - match information { - DataTypeInformation::Struct { .. } => types_index.get_associated_type(data_type.get_name()), - DataTypeInformation::Array { - inner_type_name, - dimensions, - .. - } => { - let inner_type = create_type( - llvm, - index, - types_index, + /// Creates an llvm type to be associated with the given data type. + /// Generates only an opaque type for structs. + /// Eagerly generates but does not associate nested array and referenced aliased types + fn create_type( + &self, + name: &str, + data_type: &DataType, + ) -> Result, CompileError> { + let information = data_type.get_type_information(); + match information { + DataTypeInformation::Struct { .. } => { + self.types_index.get_associated_type(data_type.get_name()) + } + DataTypeInformation::Array { inner_type_name, - index.find_type(inner_type_name).unwrap(), - ) - .unwrap(); - let array_type = create_nested_array_type(inner_type, dimensions.clone()).into(); - Ok(array_type) + dimensions, + .. + } => { + let inner_type = self + .create_type( + inner_type_name, + self.index.find_type(inner_type_name).unwrap(), + ) + .unwrap(); + let array_type = self + .create_nested_array_type(inner_type, dimensions.clone()) + .into(); + Ok(array_type) + } + DataTypeInformation::Integer { size, .. } => { + get_llvm_int_type(self.llvm.context, *size, name).map(|it| it.into()) + } + DataTypeInformation::Float { size, .. } => { + get_llvm_float_type(self.llvm.context, *size, name).map(|it| it.into()) + } + DataTypeInformation::String { size, encoding } => Ok(self + .llvm + .context + .i8_type() + .array_type(*size * encoding.get_bytes_per_char()) + .into()), + DataTypeInformation::SubRange { + referenced_type, .. + } => { + let ref_type = + self.create_type(name, self.index.find_type(referenced_type).unwrap())?; + Ok(ref_type) + } + DataTypeInformation::Alias { + referenced_type, .. + } => { + let ref_type = + self.create_type(name, self.index.find_type(referenced_type).unwrap())?; + Ok(ref_type) + } + // REVIEW: Void types are not basic type enums, so we return an int here + DataTypeInformation::Void => { + get_llvm_int_type(self.llvm.context, 32, "Void").map(Into::into) + } + DataTypeInformation::Pointer { + inner_type_name, .. + } => { + let inner_type = + self.create_type(inner_type_name, self.index.get_type(inner_type_name)?)?; + Ok(inner_type.ptr_type(AddressSpace::Generic).into()) + } } - DataTypeInformation::Integer { size, .. } => { - get_llvm_int_type(llvm.context, *size, name).map(|it| it.into()) - } - DataTypeInformation::Float { size, .. } => { - get_llvm_float_type(llvm.context, *size, name).map(|it| it.into()) - } - DataTypeInformation::String { size, encoding } => Ok(llvm - .context - .i8_type() - .array_type(*size * encoding.get_bytes_per_char()) - .into()), - DataTypeInformation::SubRange { - referenced_type, .. - } => { - let ref_type = create_type( - llvm, - index, - types_index, - name, - index.find_type(referenced_type).unwrap(), - )?; - Ok(ref_type) - } - DataTypeInformation::Alias { - referenced_type, .. - } => { - let ref_type = create_type( - llvm, - index, - types_index, - name, - index.find_type(referenced_type).unwrap(), - )?; - Ok(ref_type) - } - // REVIEW: Void types are not basic type enums, so we return an int here - DataTypeInformation::Void => get_llvm_int_type(llvm.context, 32, "Void").map(Into::into), - DataTypeInformation::Pointer { - inner_type_name, .. - } => { - let inner_type = create_type( - llvm, - index, - types_index, - inner_type_name, - index.get_type(inner_type_name)?, - )?; - Ok(inner_type.ptr_type(AddressSpace::Generic).into()) - } - } -} - -fn generate_initial_value<'ink>( - index: &Index, - types_index: &LlvmTypedIndex<'ink>, - llvm: &Llvm<'ink>, - data_type: &DataType, -) -> Option> { - let information = data_type.get_type_information(); - match information { - DataTypeInformation::Struct { .. } => None, //Done elsewhere - DataTypeInformation::Array { .. } => generate_array_initializer( - data_type, - data_type.get_name(), - index, - types_index, - llvm, - |stmt| matches!(stmt, AstStatement::LiteralArray { .. }), - "LiteralArray", - ) - .unwrap(), - DataTypeInformation::Integer { .. } => None, - DataTypeInformation::Float { .. } => None, - DataTypeInformation::String { .. } => generate_array_initializer( - data_type, - data_type.get_name(), - index, - types_index, - llvm, - |stmt| matches!(stmt, AstStatement::LiteralString { .. }), - "LiteralString", - ) - .unwrap(), - DataTypeInformation::SubRange { - referenced_type, .. - } => register_aliased_initial_value(index, types_index, llvm, data_type, referenced_type), - DataTypeInformation::Alias { - referenced_type, .. - } => register_aliased_initial_value(index, types_index, llvm, data_type, referenced_type), - // Void types are not basic type enums, so we return an int here - DataTypeInformation::Void => None, //get_llvm_int_type(llvm.context, 32, "Void").map(Into::into), - DataTypeInformation::Pointer { .. } => None, } -} -/// generates and returns an optional inital value at the given dataType -/// if no initial value is defined, it returns an optional initial value of -/// the aliased type (referenced_type) -fn register_aliased_initial_value<'ink>( - index: &Index, - types_index: &LlvmTypedIndex<'ink>, - llvm: &Llvm<'ink>, - data_type: &DataType, - referenced_type: &str, -) -> Option> { - if let Some(initializer) = &data_type.initial_value { - let generator = ExpressionCodeGenerator::new_context_free(llvm, index, types_index, None); - let (_, initial_value) = generator.generate_expression(initializer).unwrap(); - Some(initial_value) - } else { - // if there's no initializer defined for this alias, we go and check the aliased type for an initial value - index - .get_type(referenced_type) - .ok() - .and_then(|referenced_data_type| { - generate_initial_value(index, types_index, llvm, referenced_data_type) - }) + fn generate_initial_value(&self, data_type: &DataType) -> Option> { + let information = data_type.get_type_information(); + match information { + DataTypeInformation::Struct { .. } => None, //Done elsewhere + DataTypeInformation::Array { .. } => self + .generate_array_initializer( + data_type, + data_type.get_name(), + |stmt| matches!(stmt, AstStatement::LiteralArray { .. }), + "LiteralArray", + ) + .unwrap(), + DataTypeInformation::Integer { .. } => None, + DataTypeInformation::Float { .. } => None, + DataTypeInformation::String { .. } => self + .generate_array_initializer( + data_type, + data_type.get_name(), + |stmt| matches!(stmt, AstStatement::LiteralString { .. }), + "LiteralString", + ) + .unwrap(), + DataTypeInformation::SubRange { + referenced_type, .. + } => self.register_aliased_initial_value(data_type, referenced_type), + DataTypeInformation::Alias { + referenced_type, .. + } => self.register_aliased_initial_value(data_type, referenced_type), + // Void types are not basic type enums, so we return an int here + DataTypeInformation::Void => None, //get_llvm_int_type(llvm.context, 32, "Void").map(Into::into), + DataTypeInformation::Pointer { .. } => None, + } } -} -/// generates and associates the given array-datatype (used for arrays and strings) -fn generate_array_initializer<'ink>( - data_type: &DataType, - name: &str, - index: &Index, - types_index: &LlvmTypedIndex<'ink>, - llvm: &Llvm<'ink>, - predicate: fn(&AstStatement) -> bool, - expected_ast: &str, -) -> Result>, CompileError> { - if let Some(initializer) = &data_type.initial_value { - if predicate(initializer) { - let array_type = index.get_type_information(name)?; + /// generates and returns an optional inital value at the given dataType + /// if no initial value is defined, it returns an optional initial value of + /// the aliased type (referenced_type) + fn register_aliased_initial_value( + &self, + data_type: &DataType, + referenced_type: &str, + ) -> Option> { + if let Some(initializer) = &data_type.initial_value { let generator = ExpressionCodeGenerator::new_context_free( - llvm, - index, - types_index, - Some(array_type), + self.llvm, + self.index, + self.annotations, + &self.types_index, + None, ); - let (_, initial_value) = generator.generate_literal(initializer)?; - Ok(Some(initial_value)) + let (_, initial_value) = generator.generate_expression(initializer).unwrap(); + Some(initial_value) } else { - Err(CompileError::codegen_error( - format!("Expected {} but found {:?}", expected_ast, initializer), - initializer.get_location(), - )) + // if there's no initializer defined for this alias, we go and check the aliased type for an initial value + self.index + .get_type(referenced_type) + .ok() + .and_then(|referenced_data_type| self.generate_initial_value(referenced_data_type)) + } + } + + /// generates and associates the given array-datatype (used for arrays and strings) + fn generate_array_initializer( + &self, + data_type: &DataType, + name: &str, + predicate: fn(&AstStatement) -> bool, + expected_ast: &str, + ) -> Result>, CompileError> { + if let Some(initializer) = &data_type.initial_value { + if predicate(initializer) { + let array_type = self.index.get_type_information(name)?; + let generator = ExpressionCodeGenerator::new_context_free( + self.llvm, + self.index, + self.annotations, + &self.types_index, + Some(array_type), + ); + let (_, initial_value) = generator.generate_literal(initializer)?; + Ok(Some(initial_value)) + } else { + Err(CompileError::codegen_error( + format!("Expected {} but found {:?}", expected_ast, initializer), + initializer.get_location(), + )) + } + } else { + Ok(None) } - } else { - Ok(None) } -} -/// creates the llvm types for a multi-dimensional array -fn create_nested_array_type(end_type: BasicTypeEnum, dimensions: Vec) -> ArrayType { - let mut result: Option = None; - let mut current_type = end_type; - for dimension in dimensions.iter().rev() { - result = Some(match current_type { - BasicTypeEnum::IntType(..) => current_type - .into_int_type() - .array_type(dimension.get_length()), - BasicTypeEnum::FloatType(..) => current_type - .into_float_type() - .array_type(dimension.get_length()), - BasicTypeEnum::StructType(..) => current_type - .into_struct_type() - .array_type(dimension.get_length()), - BasicTypeEnum::ArrayType(..) => current_type - .into_array_type() - .array_type(dimension.get_length()), - BasicTypeEnum::PointerType(..) => current_type - .into_pointer_type() - .array_type(dimension.get_length()), - BasicTypeEnum::VectorType(..) => current_type - .into_vector_type() - .array_type(dimension.get_length()), - }); - current_type = result.unwrap().into(); + /// creates the llvm types for a multi-dimensional array + fn create_nested_array_type( + &self, + end_type: BasicTypeEnum<'ink>, + dimensions: Vec, + ) -> ArrayType<'ink> { + let mut result: Option = None; + let mut current_type = end_type; + for dimension in dimensions.iter().rev() { + result = Some(match current_type { + BasicTypeEnum::IntType(..) => current_type + .into_int_type() + .array_type(dimension.get_length()), + BasicTypeEnum::FloatType(..) => current_type + .into_float_type() + .array_type(dimension.get_length()), + BasicTypeEnum::StructType(..) => current_type + .into_struct_type() + .array_type(dimension.get_length()), + BasicTypeEnum::ArrayType(..) => current_type + .into_array_type() + .array_type(dimension.get_length()), + BasicTypeEnum::PointerType(..) => current_type + .into_pointer_type() + .array_type(dimension.get_length()), + BasicTypeEnum::VectorType(..) => current_type + .into_vector_type() + .array_type(dimension.get_length()), + }); + current_type = result.unwrap().into(); + } + result.unwrap() } - result.unwrap() } diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index b1c8794069..bb7e3e0ade 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -1,5 +1,9 @@ // Copyright (c) 2020 Ghaith Hachem and Mathias Rieder -use crate::{ast::SourceRange, index::Index}; +use crate::{ + ast::{Pou, SourceRange}, + index::{ImplementationType, Index}, + resolver::{AnnotationMap, StatementAnnotation}, +}; use inkwell::{ basic_block::BasicBlock, types::BasicTypeEnum, @@ -31,6 +35,7 @@ use chrono::{LocalResult, TimeZone, Utc}; pub struct ExpressionCodeGenerator<'a, 'b> { llvm: &'b Llvm<'a>, index: &'b Index, + annotations: &'b AnnotationMap, llvm_index: &'b LlvmTypedIndex<'a>, /// an optional type hint for generating literals type_hint: Option, @@ -62,6 +67,7 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { pub fn new( llvm: &'b Llvm<'a>, index: &'b Index, + annotations: &'b AnnotationMap, llvm_index: &'b LlvmTypedIndex<'a>, type_hint: Option, function_context: &'b FunctionContext<'a>, @@ -71,6 +77,7 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { index, llvm_index, type_hint, + annotations, function_context: Some(function_context), temp_variable_prefix: "load_".to_string(), temp_variable_suffix: "".to_string(), @@ -87,6 +94,7 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { pub fn new_context_free( llvm: &'b Llvm<'a>, index: &'b Index, + annotations: &'b AnnotationMap, llvm_index: &'b LlvmTypedIndex<'a>, type_hint: Option, ) -> ExpressionCodeGenerator<'a, 'b> { @@ -95,6 +103,7 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { index, llvm_index, type_hint, + annotations, function_context: None, temp_variable_prefix: "load_".to_string(), temp_variable_suffix: "".to_string(), @@ -106,13 +115,10 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { type_hint: &DataTypeInformation, ) -> ExpressionCodeGenerator<'a, 'b> { ExpressionCodeGenerator { - llvm: self.llvm, - index: self.index, - llvm_index: self.llvm_index, type_hint: Some(type_hint.clone()), - function_context: self.function_context, temp_variable_prefix: self.temp_variable_prefix.clone(), temp_variable_suffix: self.temp_variable_suffix.clone(), + ..*self } } @@ -326,7 +332,7 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { } }; - Ok((callable_reference, implementation)) + Ok((None, callable_reference, implementation)) } AstStatement::QualifiedReference { .. } => { let loaded_value = self.generate_element_pointer_for_rec(None, operator); @@ -335,12 +341,33 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { type_entry, ptr_value, }| { - Ok(( - ptr_value, - self.index - .find_implementation(type_entry.get_name()) - .unwrap(), - )) + self.index + .find_implementation(type_entry.get_name()) + .map(|implementation| { + let (class_struct, method_struct) = if matches!( + implementation.get_implementation_type(), + &ImplementationType::Method + ) { + ( + Some(ptr_value), + self.allocate_function_struct_instance( + implementation.get_call_name(), + operator, + ) + .unwrap(), + ) + } else { + (None, ptr_value) + }; + (class_struct, method_struct, implementation) + }) + .ok_or_else(|| CompileError::CodeGenError { + message: format!( + "cannot generate call statement for {:?}", + operator + ), + location: operator.get_location(), + }) }, )? } @@ -350,7 +377,7 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { }), }; - let (instance, index_entry) = instance_and_index_entry?; + let (class_struct, instance, index_entry) = instance_and_index_entry?; let function_name = index_entry.get_call_name(); //Create parameters for input and output blocks let current_f = function_context.function; @@ -365,6 +392,7 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { //Generate all parameters, this function may jump to the output block let parameters = self.generate_function_parameters( function_name, + class_struct, instance, parameters, &input_block, @@ -377,7 +405,7 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { builder.position_at_end(call_block); let return_type = self .index - .find_member(function_name, function_name) + .find_member(function_name, Pou::calc_return_name(function_name)) .map(VariableIndexEntry::get_type_name) .or(Some("__VOID")) .and_then(|it| self.index.find_type_information(it)); @@ -445,12 +473,20 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { fn generate_function_parameters( &self, function_name: &str, + class_struct: Option>, parameter_struct: PointerValue<'a>, parameters: &Option, input_block: &BasicBlock, output_block: &BasicBlock, ) -> Result>, CompileError> { - let mut result = vec![parameter_struct.as_basic_value_enum()]; + let mut result = if let Some(class_struct) = class_struct { + vec![ + class_struct.as_basic_value_enum(), + parameter_struct.as_basic_value_enum(), + ] + } else { + vec![parameter_struct.as_basic_value_enum()] + }; match ¶meters { Some(AstStatement::ExpressionList { expressions, .. }) => { for (index, exp) in expressions.iter().enumerate() { @@ -752,6 +788,21 @@ impl<'a, 'b> ExpressionCodeGenerator<'a, 'b> { let variable_index_entry = self .index .find_variable(Some(type_name), &[name.to_string()]) + .or_else(|| { + let annotation = self.annotations.get(context)?; + match annotation { + StatementAnnotation::Variable { + resulting_type: _, + qualified_name, + } => { + //TODO introduce qualified names! + let qualifier = &qualified_name[..qualified_name.rfind('.')?]; + self.index + .find_variable(Some(qualifier), &[name.to_string()]) + } + _ => None, + } + }) .ok_or_else(|| CompileError::InvalidReference { reference: name.to_string(), location: offset.clone(), diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index fe55331ff7..beecbe2077 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -4,7 +4,10 @@ use super::{ llvm::Llvm, statement_generator::{FunctionContext, StatementCodeGenerator}, }; -use crate::codegen::llvm_index::LlvmTypedIndex; +use crate::{ + ast::Pou, codegen::llvm_index::LlvmTypedIndex, index::ImplementationType, + resolver::AnnotationMap, +}; /// The pou_generator contains functions to generate the code for POUs (PROGRAM, FUNCTION, FUNCTION_BLOCK) /// # responsibilities @@ -29,6 +32,7 @@ use inkwell::{ pub struct PouGenerator<'ink, 'cg> { llvm: Llvm<'ink>, index: &'cg Index, + annotations: &'cg AnnotationMap, llvm_index: &'cg LlvmTypedIndex<'ink>, } @@ -38,10 +42,11 @@ pub fn generate_implementation_stubs<'ink>( module: &Module<'ink>, llvm: Llvm<'ink>, index: &Index, + annotations: &AnnotationMap, types_index: &LlvmTypedIndex<'ink>, ) -> Result, CompileError> { let mut llvm_index = LlvmTypedIndex::new(); - let pou_generator = PouGenerator::new(llvm, index, types_index); + let pou_generator = PouGenerator::new(llvm, index, annotations, types_index); for (name, implementation) in index.get_implementations() { let curr_f = pou_generator.generate_implementation_stub(implementation, module)?; llvm_index.associate_implementation(name, curr_f)?; @@ -57,11 +62,13 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { pub fn new( llvm: Llvm<'ink>, index: &'cg Index, + annotations: &'cg AnnotationMap, llvm_index: &'cg LlvmTypedIndex<'ink>, ) -> PouGenerator<'ink, 'cg> { PouGenerator { llvm, index, + annotations, llvm_index, } } @@ -74,16 +81,32 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { let global_index = self.index; //generate a function that takes a instance-struct parameter let pou_name = implementation.get_call_name(); + + let mut parameters = vec![]; + if implementation.get_implementation_type() == &ImplementationType::Method { + let class_name = implementation.get_associated_class_name().unwrap(); + let instance_members_struct_type: StructType = self + .llvm_index + .get_associated_type(class_name) + .map(|it| it.into_struct_type())?; + parameters.push( + instance_members_struct_type + .ptr_type(AddressSpace::Generic) + .into(), + ); + } + let instance_struct_type: StructType = self .llvm_index .get_associated_type(implementation.get_type_name()) .map(|it| it.into_struct_type())?; + parameters.push(instance_struct_type.ptr_type(AddressSpace::Generic).into()); + let return_type: Option<&DataType> = global_index.find_return_type(implementation.get_type_name()); let return_type = return_type .map(DataType::get_name) .map(|it| self.llvm_index.get_associated_type(it).unwrap()); - let parameters = vec![instance_struct_type.ptr_type(AddressSpace::Generic).into()]; let variadic = global_index .find_type_information(implementation.get_type_name()) .map(|it| it.is_variadic()) @@ -106,8 +129,6 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { let pou_name = &implementation.name; - let pou_members = self.index.find_local_members(&implementation.type_name); - let current_function = self .llvm_index .find_associated_implementation(pou_name) @@ -122,8 +143,25 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { let block = context.append_basic_block(current_function, "entry"); self.llvm.builder.position_at_end(block); + let mut param_index = 0; + + if let PouType::Method { .. } = implementation.pou_type { + let class_name = implementation.type_name.split('.').collect::>()[0]; + let class_members = self.index.find_local_members(class_name); + self.generate_local_variable_accessors( + param_index, + &mut local_index, + class_name, + current_function, + &class_members, + )?; + param_index += 1; + } + // generate loads for all the parameters + let pou_members = self.index.find_local_members(&implementation.type_name); self.generate_local_variable_accessors( + param_index, &mut local_index, &implementation.type_name, current_function, @@ -138,25 +176,23 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { let statement_gen = StatementCodeGenerator::new( &self.llvm, self.index, + self.annotations, self, - implementation.pou_type, &local_index, &function_context, ); //if this is a function, we need to initilialize the VAR-variables - if implementation.pou_type == PouType::Function { + if matches!( + implementation.pou_type, + PouType::Function | PouType::Method { .. } + ) { self.generate_initialization_of_local_vars(pou_members, &statement_gen)?; } statement_gen.generate_body(&implementation.statements)? } // generate return statement - self.generate_return_statement( - &function_context, - &local_index, - implementation.pou_type, - None, - )?; //TODO location + self.generate_return_statement(&function_context, &local_index, None)?; //TODO location Ok(()) } @@ -192,6 +228,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { /// generates a load-statement for the given member fn generate_local_variable_accessors( &self, + arg_index: u32, index: &mut LlvmTypedIndex<'ink>, type_name: &str, current_function: FunctionValue<'ink>, @@ -204,12 +241,12 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { let (name, variable) = if m.is_return() { let return_type = index.get_associated_type(m.get_type_name())?; ( - type_name, + Pou::calc_return_name(type_name), self.llvm.create_local_variable(type_name, &return_type), ) } else { let ptr_value = current_function - .get_first_param() + .get_nth_param(arg_index) .map(BasicValueEnum::into_pointer_value) .ok_or_else(|| CompileError::MissingFunctionError { location: m.source_location.clone(), @@ -261,31 +298,33 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { &self, function_context: &FunctionContext<'ink>, local_index: &LlvmTypedIndex<'ink>, - pou_type: PouType, location: Option, ) -> Result<(), CompileError> { - match pou_type { - PouType::Function => { - let reference = AstStatement::Reference { - name: function_context.linking_context.get_call_name().into(), - location: location.unwrap_or_else(SourceRange::undefined), - id: 0, //TODO - }; - let mut exp_gen = ExpressionCodeGenerator::new( - &self.llvm, - self.index, - local_index, - None, - function_context, - ); - exp_gen.temp_variable_prefix = "".to_string(); - exp_gen.temp_variable_suffix = "_ret".to_string(); - let (_, value) = exp_gen.generate_expression(&reference)?; - self.llvm.builder.build_return(Some(&value)); - } - _ => { - self.llvm.builder.build_return(None); - } + if self + .index + .find_return_variable(function_context.linking_context.get_type_name()) + .is_some() + { + let reference = AstStatement::Reference { + name: Pou::calc_return_name(function_context.linking_context.get_call_name()) + .into(), + location: location.unwrap_or_else(SourceRange::undefined), + id: 0, //TODO + }; + let mut exp_gen = ExpressionCodeGenerator::new( + &self.llvm, + self.index, + self.annotations, + local_index, + None, + function_context, + ); + exp_gen.temp_variable_prefix = "".to_string(); + exp_gen.temp_variable_suffix = "_ret".to_string(); + let (_, value) = exp_gen.generate_expression(&reference)?; + self.llvm.builder.build_return(Some(&value)); + } else { + self.llvm.builder.build_return(None); } Ok(()) } diff --git a/src/codegen/generators/statement_generator.rs b/src/codegen/generators/statement_generator.rs index 003b524a67..7b98dba2e3 100644 --- a/src/codegen/generators/statement_generator.rs +++ b/src/codegen/generators/statement_generator.rs @@ -3,12 +3,11 @@ use super::{ expression_generator::ExpressionCodeGenerator, llvm::Llvm, pou_generator::PouGenerator, }; use crate::{ - ast::{ - flatten_expression_list, AstStatement, ConditionalBlock, Operator, PouType, SourceRange, - }, + ast::{flatten_expression_list, AstStatement, ConditionalBlock, Operator, SourceRange}, codegen::{llvm_typesystem::cast_if_needed, LlvmTypedIndex}, compile_error::CompileError, index::{ImplementationIndexEntry, Index}, + resolver::AnnotationMap, typesystem::{ DataTypeInformation, RANGE_CHECK_LS_FN, RANGE_CHECK_LU_FN, RANGE_CHECK_S_FN, RANGE_CHECK_U_FN, @@ -33,8 +32,8 @@ pub struct FunctionContext<'a> { pub struct StatementCodeGenerator<'a, 'b> { llvm: &'b Llvm<'a>, index: &'b Index, + annotations: &'b AnnotationMap, pou_generator: &'b PouGenerator<'a, 'b>, - pou_type: PouType, llvm_index: &'b LlvmTypedIndex<'a>, function_context: &'b FunctionContext<'a>, @@ -52,16 +51,16 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> { pub fn new( llvm: &'b Llvm<'a>, index: &'b Index, + annotations: &'b AnnotationMap, pou_generator: &'b PouGenerator<'a, 'b>, - pou_type: PouType, llvm_index: &'b LlvmTypedIndex<'a>, linking_context: &'b FunctionContext<'a>, ) -> StatementCodeGenerator<'a, 'b> { StatementCodeGenerator { llvm, index, + annotations, pou_generator, - pou_type, llvm_index, function_context: linking_context, load_prefix: "load_".to_string(), @@ -76,6 +75,7 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> { ExpressionCodeGenerator::new( self.llvm, self.index, + self.annotations, self.llvm_index, None, self.function_context, @@ -151,7 +151,6 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> { self.pou_generator.generate_return_statement( self.function_context, self.llvm_index, - self.pou_type, Some(location.clone()), )?; self.generate_buffer_block(); @@ -387,6 +386,7 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> { let exp_gen = ExpressionCodeGenerator::new( self.llvm, self.index, + self.annotations, self.llvm_index, Some(selector_type), self.function_context, diff --git a/src/codegen/generators/struct_generator.rs b/src/codegen/generators/struct_generator.rs index db0b7008dc..b8a19360d2 100644 --- a/src/codegen/generators/struct_generator.rs +++ b/src/codegen/generators/struct_generator.rs @@ -1,6 +1,7 @@ // Copyright (c) 2020 Ghaith Hachem and Mathias Rieder use super::{expression_generator::ExpressionCodeGenerator, llvm::Llvm}; use crate::index::Index; +use crate::resolver::AnnotationMap; use crate::{ codegen::llvm_index::LlvmTypedIndex, compile_error::CompileError, index::VariableIndexEntry, }; @@ -13,6 +14,7 @@ use inkwell::{ pub struct StructGenerator<'a, 'b> { llvm: &'b Llvm<'a>, index: &'b Index, + annotations: &'b AnnotationMap, llvm_index: &'b LlvmTypedIndex<'a>, } @@ -27,11 +29,13 @@ impl<'a, 'b> StructGenerator<'a, 'b> { pub fn new( llvm: &'b Llvm<'a>, index: &'b Index, + annotations: &'b AnnotationMap, llvm_index: &'b LlvmTypedIndex<'a>, ) -> StructGenerator<'a, 'b> { StructGenerator { llvm, index, + annotations, llvm_index, } } @@ -101,6 +105,7 @@ impl<'a, 'b> StructGenerator<'a, 'b> { let exp_gen = ExpressionCodeGenerator::new_context_free( self.llvm, self.index, + self.annotations, self.llvm_index, Some(variable_type), ); diff --git a/src/codegen/generators/variable_generator.rs b/src/codegen/generators/variable_generator.rs index 838631b151..33d627ec37 100644 --- a/src/codegen/generators/variable_generator.rs +++ b/src/codegen/generators/variable_generator.rs @@ -1,7 +1,7 @@ // Copyright (c) 2020 Ghaith Hachem and Mathias Rieder /// offers operations to generate global variables -use crate::index::Index; +use crate::{index::Index, resolver::AnnotationMap}; use inkwell::{module::Module, values::GlobalValue}; use crate::{ @@ -14,13 +14,20 @@ pub fn generate_global_variables<'ctx, 'b>( module: &'b Module<'ctx>, llvm: &'b Llvm<'ctx>, global_index: &'b Index, + annotations: &'b AnnotationMap, types_index: &'b LlvmTypedIndex<'ctx>, ) -> Result, CompileError> { let mut index = LlvmTypedIndex::new(); let globals = global_index.get_globals(); for (name, variable) in globals { - let global_variable = - generate_global_variable(module, llvm, global_index, types_index, variable)?; + let global_variable = generate_global_variable( + module, + llvm, + global_index, + annotations, + types_index, + variable, + )?; index.associate_global(name, global_variable)? } Ok(index) @@ -36,6 +43,7 @@ pub fn generate_global_variable<'ctx, 'b>( module: &'b Module<'ctx>, llvm: &'b Llvm<'ctx>, global_index: &'b Index, + annotations: &'b AnnotationMap, index: &'b LlvmTypedIndex<'ctx>, global_variable: &VariableIndexEntry, ) -> Result, CompileError> { @@ -46,6 +54,7 @@ pub fn generate_global_variable<'ctx, 'b>( let expr_generator = ExpressionCodeGenerator::new_context_free( llvm, global_index, + annotations, index, Some(global_index.get_type_information(type_name).unwrap()), ); diff --git a/src/codegen/tests/code_gen_tests.rs b/src/codegen/tests/code_gen_tests.rs index 9c0260408b..9e20a26fe8 100644 --- a/src/codegen/tests/code_gen_tests.rs +++ b/src/codegen/tests/code_gen_tests.rs @@ -1434,6 +1434,333 @@ continue: ; preds = %for_body, %conditio assert_eq!(result, expected); } +#[test] +fn class_method_in_pou() { + let result = codegen!( + " + CLASS MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod + VAR_INPUT myMethodArg : INT; END_VAR + VAR myMethodLocalVar : INT; END_VAR + + x := myMethodArg; + y := x; + myMethodLocalVar = y; + END_METHOD + END_CLASS + + PROGRAM prg + VAR + cl : MyClass; + x : INT; + END_VAR + x := cl.x; + cl.testMethod(x); + cl.testMethod(myMethodArg:= x); + END_PROGRAM + " + ); + + let expected = r#"; ModuleID = 'main' +source_filename = "main" + +%prg_interface = type { %MyClass_interface, i16 } +%MyClass_interface = type { i16, i16 } +%MyClass.testMethod_interface = type { i16, i16 } + +@prg_instance = global %prg_interface zeroinitializer + +define void @MyClass.testMethod(%MyClass_interface* %0, %MyClass.testMethod_interface* %1) { +entry: + %x = getelementptr inbounds %MyClass_interface, %MyClass_interface* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass_interface, %MyClass_interface* %0, i32 0, i32 1 + %myMethodArg = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 0 + %myMethodLocalVar = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 1 + %load_myMethodArg = load i16, i16* %myMethodArg, align 2 + store i16 %load_myMethodArg, i16* %x, align 2 + %load_x = load i16, i16* %x, align 2 + store i16 %load_x, i16* %y, align 2 + %load_myMethodLocalVar = load i16, i16* %myMethodLocalVar, align 2 + %load_y = load i16, i16* %y, align 2 + %tmpVar = icmp eq i16 %load_myMethodLocalVar, %load_y + ret void +} + +define void @prg(%prg_interface* %0) { +entry: + %cl = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 0 + %x = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 1 + %x1 = getelementptr inbounds %MyClass_interface, %MyClass_interface* %cl, i32 0, i32 0 + %load_ = load i16, i16* %x1, align 2 + store i16 %load_, i16* %x, align 2 + %MyClass.testMethod_instance = alloca %MyClass.testMethod_interface, align 8 + br label %input + +input: ; preds = %entry + %1 = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %MyClass.testMethod_instance, i32 0, i32 0 + %load_x = load i16, i16* %x, align 2 + store i16 %load_x, i16* %1, align 2 + br label %call + +call: ; preds = %input + call void @MyClass.testMethod(%MyClass_interface* %cl, %MyClass.testMethod_interface* %MyClass.testMethod_instance) + br label %output + +output: ; preds = %call + br label %continue + +continue: ; preds = %output + %MyClass.testMethod_instance2 = alloca %MyClass.testMethod_interface, align 8 + br label %input3 + +input3: ; preds = %continue + %2 = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %MyClass.testMethod_instance2, i32 0, i32 0 + %load_x7 = load i16, i16* %x, align 2 + store i16 %load_x7, i16* %2, align 2 + br label %call4 + +call4: ; preds = %input3 + call void @MyClass.testMethod(%MyClass_interface* %cl, %MyClass.testMethod_interface* %MyClass.testMethod_instance2) + br label %output5 + +output5: ; preds = %call4 + br label %continue6 + +continue6: ; preds = %output5 + ret void +} +"#; + + assert_eq!(result, expected.to_string()); +} + +#[test] +fn fb_method_in_pou() { + let result = codegen!( + " + FUNCTION_BLOCK MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod + VAR_INPUT myMethodArg : INT; END_VAR + VAR myMethodLocalVar : INT; END_VAR + + x := myMethodArg; + y := x; + myMethodLocalVar = y; + END_METHOD + END_FUNCTION_BLOCK + + PROGRAM prg + VAR + cl : MyClass; + x : INT; + END_VAR + x := cl.x; + cl.testMethod(x); + cl.testMethod(myMethodArg:= x); + END_PROGRAM + " + ); + + let expected = r#"; ModuleID = 'main' +source_filename = "main" + +%prg_interface = type { %MyClass_interface, i16 } +%MyClass_interface = type { i16, i16 } +%MyClass.testMethod_interface = type { i16, i16 } + +@prg_instance = global %prg_interface zeroinitializer + +define void @MyClass.testMethod(%MyClass_interface* %0, %MyClass.testMethod_interface* %1) { +entry: + %x = getelementptr inbounds %MyClass_interface, %MyClass_interface* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass_interface, %MyClass_interface* %0, i32 0, i32 1 + %myMethodArg = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 0 + %myMethodLocalVar = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 1 + %load_myMethodArg = load i16, i16* %myMethodArg, align 2 + store i16 %load_myMethodArg, i16* %x, align 2 + %load_x = load i16, i16* %x, align 2 + store i16 %load_x, i16* %y, align 2 + %load_myMethodLocalVar = load i16, i16* %myMethodLocalVar, align 2 + %load_y = load i16, i16* %y, align 2 + %tmpVar = icmp eq i16 %load_myMethodLocalVar, %load_y + ret void +} + +define void @MyClass(%MyClass_interface* %0) { +entry: + %x = getelementptr inbounds %MyClass_interface, %MyClass_interface* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass_interface, %MyClass_interface* %0, i32 0, i32 1 + ret void +} + +define void @prg(%prg_interface* %0) { +entry: + %cl = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 0 + %x = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 1 + %x1 = getelementptr inbounds %MyClass_interface, %MyClass_interface* %cl, i32 0, i32 0 + %load_ = load i16, i16* %x1, align 2 + store i16 %load_, i16* %x, align 2 + %MyClass.testMethod_instance = alloca %MyClass.testMethod_interface, align 8 + br label %input + +input: ; preds = %entry + %1 = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %MyClass.testMethod_instance, i32 0, i32 0 + %load_x = load i16, i16* %x, align 2 + store i16 %load_x, i16* %1, align 2 + br label %call + +call: ; preds = %input + call void @MyClass.testMethod(%MyClass_interface* %cl, %MyClass.testMethod_interface* %MyClass.testMethod_instance) + br label %output + +output: ; preds = %call + br label %continue + +continue: ; preds = %output + %MyClass.testMethod_instance2 = alloca %MyClass.testMethod_interface, align 8 + br label %input3 + +input3: ; preds = %continue + %2 = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %MyClass.testMethod_instance2, i32 0, i32 0 + %load_x7 = load i16, i16* %x, align 2 + store i16 %load_x7, i16* %2, align 2 + br label %call4 + +call4: ; preds = %input3 + call void @MyClass.testMethod(%MyClass_interface* %cl, %MyClass.testMethod_interface* %MyClass.testMethod_instance2) + br label %output5 + +output5: ; preds = %call4 + br label %continue6 + +continue6: ; preds = %output5 + ret void +} +"#; + + assert_eq!(result, expected.to_string()); +} + +#[test] +fn method_codegen_return() { + let result = codegen!( + " + CLASS MyClass + METHOD testMethod : INT + VAR_INPUT myMethodArg : INT; END_VAR + testMethod := 1; + END_METHOD + END_CLASS + " + ); + + let expected = r#"; ModuleID = 'main' +source_filename = "main" + +%MyClass_interface = type {} +%MyClass.testMethod_interface = type { i16 } + +define i16 @MyClass.testMethod(%MyClass_interface* %0, %MyClass.testMethod_interface* %1) { +entry: + %myMethodArg = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 0 + %MyClass.testMethod = alloca i16, align 2 + store i16 1, i16* %MyClass.testMethod, align 2 + %testMethod_ret = load i16, i16* %MyClass.testMethod, align 2 + ret i16 %testMethod_ret +} +"#; + + assert_eq!(result, expected.to_string()); +} + +#[test] +fn method_codegen_void() { + let result = codegen!( + " + CLASS MyClass + METHOD testMethod + VAR_INPUT myMethodArg : INT; END_VAR + VAR myMethodLocalVar : INT; END_VAR + + myMethodLocalVar := 1; + END_METHOD + END_CLASS + " + ); + + let expected = r#"; ModuleID = 'main' +source_filename = "main" + +%MyClass_interface = type {} +%MyClass.testMethod_interface = type { i16, i16 } + +define void @MyClass.testMethod(%MyClass_interface* %0, %MyClass.testMethod_interface* %1) { +entry: + %myMethodArg = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 0 + %myMethodLocalVar = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 1 + store i16 1, i16* %myMethodLocalVar, align 2 + ret void +} +"#; + + assert_eq!(result, expected.to_string()); +} + +#[test] +fn class_member_access_from_method() { + let result = codegen!( + " + CLASS MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod + VAR_INPUT myMethodArg : INT; END_VAR + VAR myMethodLocalVar : INT; END_VAR + + x := myMethodArg; + y := x; + myMethodLocalVar = y; + END_METHOD + END_CLASS + " + ); + + let expected = r#"; ModuleID = 'main' +source_filename = "main" + +%MyClass_interface = type { i16, i16 } +%MyClass.testMethod_interface = type { i16, i16 } + +define void @MyClass.testMethod(%MyClass_interface* %0, %MyClass.testMethod_interface* %1) { +entry: + %x = getelementptr inbounds %MyClass_interface, %MyClass_interface* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass_interface, %MyClass_interface* %0, i32 0, i32 1 + %myMethodArg = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 0 + %myMethodLocalVar = getelementptr inbounds %MyClass.testMethod_interface, %MyClass.testMethod_interface* %1, i32 0, i32 1 + %load_myMethodArg = load i16, i16* %myMethodArg, align 2 + store i16 %load_myMethodArg, i16* %x, align 2 + %load_x = load i16, i16* %x, align 2 + store i16 %load_x, i16* %y, align 2 + %load_myMethodLocalVar = load i16, i16* %myMethodLocalVar, align 2 + %load_y = load i16, i16* %y, align 2 + %tmpVar = icmp eq i16 %load_myMethodLocalVar, %load_y + ret void +} +"#; + + assert_eq!(result, expected.to_string()); +} + #[test] fn while_loop_with_if_exit() { let result = codegen!( diff --git a/src/index.rs b/src/index.rs index 76229d4690..b67fc5698e 100644 --- a/src/index.rs +++ b/src/index.rs @@ -85,7 +85,7 @@ pub enum DataTypeType { AliasType, // a Custom-Alias-dataType } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum ImplementationType { Program, Function, @@ -99,6 +99,7 @@ pub enum ImplementationType { pub struct ImplementationIndexEntry { call_name: String, type_name: String, + associated_class: Option, implementation_type: ImplementationType, } @@ -109,6 +110,9 @@ impl ImplementationIndexEntry { pub fn get_type_name(&self) -> &str { &self.type_name } + pub fn get_associated_class_name(&self) -> Option<&String> { + self.associated_class.as_ref() + } pub fn get_implementation_type(&self) -> &ImplementationType { &self.implementation_type } @@ -120,6 +124,7 @@ impl From<&Implementation> for ImplementationIndexEntry { ImplementationIndexEntry { call_name: implementation.name.clone(), type_name: implementation.type_name.clone(), + associated_class: pou_type.get_optional_owner_class(), implementation_type: pou_type.into(), } } @@ -133,7 +138,7 @@ impl From<&PouType> for ImplementationType { PouType::FunctionBlock => ImplementationType::FunctionBlock, PouType::Action => ImplementationType::Action, PouType::Class => ImplementationType::Class, - PouType::Method => ImplementationType::Method, + PouType::Method { .. } => ImplementationType::Method, } } } @@ -370,6 +375,7 @@ impl Index { &mut self, call_name: &str, type_name: &str, + associated_class_name: Option<&String>, impl_type: ImplementationType, ) { self.implementations.insert( @@ -377,6 +383,7 @@ impl Index { ImplementationIndexEntry { call_name: call_name.into(), type_name: type_name.into(), + associated_class: associated_class_name.map(|str| str.into()), implementation_type: impl_type, }, ); diff --git a/src/index/tests/index_tests.rs b/src/index/tests/index_tests.rs index fe338fe569..01b0236c4d 100644 --- a/src/index/tests/index_tests.rs +++ b/src/index/tests/index_tests.rs @@ -176,7 +176,7 @@ fn fb_methods_are_indexed() { let foo_impl = index.find_implementation("myFuncBlock.foo").unwrap(); assert_eq!("myFuncBlock.foo", foo_impl.call_name); - assert_eq!("myFuncBlock", foo_impl.type_name); + assert_eq!("myFuncBlock.foo", foo_impl.type_name); let info = index .get_type("myFuncBlock.foo") .unwrap() @@ -208,7 +208,7 @@ fn class_methods_are_indexed() { let foo_impl = index.find_implementation("myClass.foo").unwrap(); assert_eq!("myClass.foo", foo_impl.call_name); - assert_eq!("myClass", foo_impl.type_name); + assert_eq!("myClass.foo", foo_impl.type_name); let info = index .get_type("myClass.foo") .unwrap() diff --git a/src/index/visitor.rs b/src/index/visitor.rs index e38e627a82..c13abf5064 100644 --- a/src/index/visitor.rs +++ b/src/index/visitor.rs @@ -104,14 +104,14 @@ pub fn visit_pou(index: &mut Index, pou: &Pou) { //register a function's return type as a member variable if let Some(return_type) = &pou.return_type { - member_names.push(pou.name.clone()); + member_names.push(pou.get_return_name().into()); let source_location = SourceRange::new(pou.location.get_end()..pou.location.get_end()); index.register_member_variable( &MemberInfo { container_name: &pou.name, - variable_name: &pou.name, + variable_name: pou.get_return_name(), variable_linkage: VariableType::Return, - variable_type_name: return_type.get_name().unwrap(), + variable_type_name: return_type.get_name().unwrap_or_default(), }, None, source_location, @@ -135,6 +135,7 @@ fn visit_implementation(index: &mut Index, implementation: &Implementation) { index.register_implementation( &implementation.name, &implementation.type_name, + pou_type.get_optional_owner_class().as_ref(), pou_type.into(), ); //if we are registing an action, also register a datatype for it diff --git a/src/parser.rs b/src/parser.rs index 35348e1c00..4d8c1be961 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -141,9 +141,9 @@ fn parse_pou( ]; let pou = parse_any_in_region(lexer, closing_tokens.clone(), |lexer| { let poly_mode = match pou_type { - PouType::Class | PouType::FunctionBlock | PouType::Method => { + PouType::Class | PouType::FunctionBlock | PouType::Method { .. } => { // classes and function blocks can be ABSTRACT, FINAL or neither. - parse_polymorphism_mode(lexer, pou_type) + parse_polymorphism_mode(lexer, &pou_type) } _ => None, }; @@ -156,7 +156,7 @@ fn parse_pou( let return_type = if pou_type != PouType::Class { // parse an optional return type - parse_return_type(lexer, pou_type) + parse_return_type(lexer, &pou_type) } else { // classes do not have a return type None @@ -198,7 +198,13 @@ fn parse_pou( } if pou_type != PouType::Class { // a class may not contain an implementation - implementations.push(parse_implementation(lexer, linkage, pou_type, &name, &name)); + implementations.push(parse_implementation( + lexer, + linkage, + pou_type.clone(), + &name, + &name, + )); } let mut pous = vec![Pou { @@ -227,10 +233,10 @@ fn parse_pou( fn parse_polymorphism_mode( lexer: &mut ParseSession, - pou_type: PouType, + pou_type: &PouType, ) -> Option { match pou_type { - PouType::Class | PouType::FunctionBlock | PouType::Method => { + PouType::Class | PouType::FunctionBlock | PouType::Method { .. } => { Some( // See if the method/pou was declared FINAL or ABSTRACT if lexer.allow(&KeywordFinal) { @@ -246,13 +252,13 @@ fn parse_polymorphism_mode( } } -fn parse_return_type(lexer: &mut ParseSession, pou_type: PouType) -> Option { +fn parse_return_type(lexer: &mut ParseSession, pou_type: &PouType) -> Option { let start_return_type = lexer.range().start; if lexer.allow(&KeywordColon) { if lexer.token == Identifier || lexer.token == KeywordString { - if pou_type != PouType::Function && pou_type != PouType::Method { + if !matches!(pou_type, PouType::Function | PouType::Method { .. }) { lexer.accept_diagnostic(Diagnostic::return_type_not_supported( - &pou_type, + pou_type, SourceRange::new(start_return_type..lexer.range().end), )); } @@ -291,10 +297,13 @@ fn parse_method( lexer.advance(); // eat METHOD keyword let access = Some(parse_access_modifier(lexer)); - let poly_mode = parse_polymorphism_mode(lexer, PouType::Method); + let pou_type = PouType::Method { + owner_class: class_name.into(), + }; + let poly_mode = parse_polymorphism_mode(lexer, &pou_type); let overriding = lexer.allow(&KeywordOverride); let name = parse_identifier(lexer)?; - let return_type = parse_return_type(lexer, PouType::Method); + let return_type = parse_return_type(lexer, &pou_type); let mut variable_blocks = vec![]; while lexer.token == KeywordVar @@ -310,8 +319,15 @@ fn parse_method( } let call_name = format!("{}.{}", class_name, name); - let implementation = - parse_implementation(lexer, linkage, PouType::Class, &call_name, class_name); + let implementation = parse_implementation( + lexer, + linkage, + PouType::Method { + owner_class: class_name.into(), + }, + &call_name, + &call_name, + ); // parse_implementation() will default-initialize the fields it // doesn't know. thus, we have to complete the information. @@ -325,7 +341,7 @@ fn parse_method( Some(( Pou { name: call_name, - pou_type: PouType::Method, + pou_type, variable_blocks, return_type, location: SourceRange::new(method_start..method_end), diff --git a/src/parser/tests/class_parser_tests.rs b/src/parser/tests/class_parser_tests.rs index 6832afd260..b7c17fc08b 100644 --- a/src/parser/tests/class_parser_tests.rs +++ b/src/parser/tests/class_parser_tests.rs @@ -52,7 +52,12 @@ fn method_with_defaults_can_be_parsed() { assert_eq!(unit.implementations.len(), 1); let method_pou = &unit.units[1]; - assert_eq!(method_pou.pou_type, PouType::Method); + assert_eq!( + method_pou.pou_type, + PouType::Method { + owner_class: "MyClass".into() + } + ); let method = &unit.implementations[0]; assert_eq!(method_pou.name, "MyClass.testMethod"); @@ -73,7 +78,12 @@ fn method_can_be_parsed() { assert_eq!(unit.implementations.len(), 1); let method_pou = &unit.units[1]; - assert_eq!(method_pou.pou_type, PouType::Method); + assert_eq!( + method_pou.pou_type, + PouType::Method { + owner_class: "MyClass".into() + } + ); let method = &unit.implementations[0]; assert_eq!(method_pou.name, "MyClass.testMethod2"); @@ -113,7 +123,12 @@ fn method_with_return_type_can_be_parsed() { assert_eq!(class.pou_type, PouType::Class); let method_pou = &unit.units[1]; - assert_eq!(method_pou.pou_type, PouType::Method); + assert_eq!( + method_pou.pou_type, + PouType::Method { + owner_class: "MyClass".into() + } + ); let method = &unit.implementations[0]; assert_eq!(unit.implementations.len(), 1); @@ -245,7 +260,12 @@ fn fb_method_can_be_parsed() { assert_eq!(unit.implementations.len(), 2); let method_pou = &unit.units[1]; - assert_eq!(method_pou.pou_type, PouType::Method); + assert_eq!( + method_pou.pou_type, + PouType::Method { + owner_class: "MyFb".into() + } + ); let method = &unit.implementations[0]; assert_eq!(method_pou.name, "MyFb.testMethod2"); @@ -291,7 +311,12 @@ fn fb_method_with_return_type_can_be_parsed() { assert_eq!(class.pou_type, PouType::FunctionBlock); let method_pou = &unit.units[1]; - assert_eq!(method_pou.pou_type, PouType::Method); + assert_eq!( + method_pou.pou_type, + PouType::Method { + owner_class: "MyShinyFb".into() + } + ); let method = &unit.implementations[0]; assert_eq!(unit.implementations.len(), 2); diff --git a/src/resolver.rs b/src/resolver.rs index 2039a8bb11..4f8c946a1b 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -421,9 +421,15 @@ impl<'i> TypeAnnotator<'i> { AstStatement::Reference { name, .. } => { let annotation = if let Some(qualifier) = ctx.qualifier.as_deref() { // if we see a qualifier, we only consider [qualifier].[name] as candidates - self.index - .find_member(qualifier, name) - .map(|v| to_variable_annotation(v, self.index)) + self.index.find_member(qualifier, name).map_or_else( + || { + find_implementation_annotation( + format!("{}.{}", qualifier, name).as_str(), + self.index, + ) + }, + |v| Some(to_variable_annotation(v, self.index)), + ) } else { // if we see no qualifier, we try some strategies ... ctx.pou @@ -431,24 +437,42 @@ impl<'i> TypeAnnotator<'i> { // ... first look at POU-local variables self.index .find_member(qualifier, name) + .or_else(|| { + // ... then check if we're in a method and we're referencing + // a member variable of the corresponding class + self.index + .find_implementation(ctx.pou.unwrap()) + .and_then( + ImplementationIndexEntry::get_associated_class_name, + ) + .and_then(|it| self.index.find_member(it, name)) + }) .map(|v| to_variable_annotation(v, self.index)) + .or_else(|| { + //Try to find an action with this name + let action_call_name = format!("{}.{}", qualifier, name); + self.index.find_implementation(&action_call_name).and_then( + |entry| { + find_implementation_annotation( + entry.get_call_name(), + self.index, + ) + }, + ) + }) }) .or_else(|| { // ... then try if we find a pou with that name (maybe it's a call?) - self.index.find_implementation(name).and_then(|it| { - match it.get_implementation_type() { - crate::index::ImplementationType::Program => { - Some(to_programm_annotation(it)) - } - crate::index::ImplementationType::Function => { - Some(to_function_annotation(it, self.index)) - } - crate::index::ImplementationType::FunctionBlock => { - Some(to_type_annotation(name)) - } - _ => None, - } - }) + let class_name = self + .index + .find_implementation(ctx.pou.unwrap()) + .and_then(ImplementationIndexEntry::get_associated_class_name); + + //TODO introduce qualified names! + let call_name = class_name + .map(|it| format!("{}.{}", it, name)) + .unwrap_or_else(|| name.into()); + find_implementation_annotation(&call_name, self.index) }) .or_else(|| { // ... last option is a global variable, where we ignore the current pou's name as a qualifier @@ -634,6 +658,20 @@ impl<'i> TypeAnnotator<'i> { } } } +fn find_implementation_annotation(name: &str, index: &Index) -> Option { + index + .find_implementation(name) + .and_then(|it| match it.get_implementation_type() { + ImplementationType::Program | &ImplementationType::Action => { + Some(to_programm_annotation(it)) + } + ImplementationType::Function | ImplementationType::Method => { + Some(to_function_annotation(it, index)) + } + ImplementationType::FunctionBlock => Some(to_type_annotation(name)), + _ => None, + }) +} fn to_type_annotation(name: &str) -> StatementAnnotation { StatementAnnotation::Type { @@ -657,10 +695,7 @@ fn to_variable_annotation(v: &VariableIndexEntry, index: &Index) -> StatementAnn } } -fn to_function_annotation( - it: &crate::index::ImplementationIndexEntry, - index: &Index, -) -> StatementAnnotation { +fn to_function_annotation(it: &ImplementationIndexEntry, index: &Index) -> StatementAnnotation { StatementAnnotation::Function { qualified_name: it.get_call_name().into(), return_type: index diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index e8c8bdd2dc..0a6d07944d 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -595,6 +595,7 @@ fn function_expression_resolves_to_the_function_itself_not_its_return_type() { let (unit, index) = parse( " FUNCTION foo : INT + foo; END_FUNCTION PROGRAM PRG @@ -616,10 +617,19 @@ fn function_expression_resolves_to_the_function_itself_not_its_return_type() { }), foo_annotation ); - // AND we expect no type to be associated with the expression let associated_type = annotations.get_type(&statements[0], &index); assert_eq!(None, associated_type); + + let statements = &unit.implementations[0].statements; + let foo_annotation = annotations.get_annotation(&statements[0]); + assert_eq!( + Some(&StatementAnnotation::Variable { + qualified_name: "foo.foo".into(), + resulting_type: "INT".into(), + }), + foo_annotation + ); } #[test] @@ -951,6 +961,101 @@ fn type_initial_values_are_resolved() { } } +#[test] +fn actions_are_resolved() { + let (unit, index) = parse( + " + PROGRAM prg + foo; + prg.foo; + END_PROGRAM + ACTIONS prg + ACTION foo + END_ACTION + END_ACTIONS + + FUNCTION buz : INT + prg.foo(); + prg.foo; + END_FUNCTION + ", + ); + + let annotations = annotate(&unit, &index); + let foo_reference = &unit.implementations[0].statements[0]; + let annotation = annotations.get_annotation(foo_reference); + assert_eq!( + Some(&StatementAnnotation::Program { + qualified_name: "prg.foo".into(), + }), + annotation + ); + let foo_reference = &unit.implementations[0].statements[1]; + let annotation = annotations.get_annotation(foo_reference); + assert_eq!( + Some(&StatementAnnotation::Program { + qualified_name: "prg.foo".into(), + }), + annotation + ); + let method_call = &unit.implementations[2].statements[0]; + if let AstStatement::CallStatement { operator, .. } = method_call { + assert_eq!( + Some(&StatementAnnotation::Program { + qualified_name: "prg.foo".into(), + }), + annotations.get(operator) + ); + assert_eq!(None, annotations.get(method_call)); + } else { + panic!("Unexpcted statemet : {:?}", method_call); + } +} +#[test] +fn method_references_are_resolved() { + let (unit, index) = parse( + " + CLASS cls + METHOD foo : INT + foo; + END_METHOD + END_CLASS + + FUNCTION buz : INT + VAR cl : cls; END_VAR + cl.foo(); + END_FUNCTION + ", + ); + + let annotations = annotate(&unit, &index); + let foo_reference = &unit.implementations[0].statements[0]; + let annotation = annotations.get_annotation(foo_reference); + assert_eq!( + Some(&StatementAnnotation::Variable { + qualified_name: "cls.foo.foo".into(), + resulting_type: "INT".into(), + }), + annotation + ); + let method_call = &unit.implementations[1].statements[0]; + if let AstStatement::CallStatement { operator, .. } = method_call { + assert_eq!( + Some(&StatementAnnotation::Function { + return_type: "INT".into(), + qualified_name: "cls.foo".into(), + }), + annotations.get(operator) + ); + assert_eq!( + Some(&StatementAnnotation::expression("INT")), + annotations.get(method_call) + ); + } else { + panic!("Unexpcted statemet : {:?}", method_call); + } +} + fn get_expression_from_list(stmt: &Option, index: usize) -> &AstStatement { if let Some(AstStatement::ExpressionList { expressions, .. }) = stmt { &expressions[index] diff --git a/tests/correctness/classes.rs b/tests/correctness/classes.rs new file mode 100644 index 0000000000..b03a011ce1 --- /dev/null +++ b/tests/correctness/classes.rs @@ -0,0 +1,60 @@ +// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder +use super::super::*; + +#[test] +fn class_reference_in_pou() { + #[allow(dead_code)] + #[repr(C)] + struct MyClass { + x: i16, + y: i16, + } + + #[allow(dead_code)] + #[repr(C)] + struct MainType { + cl: MyClass, + x: i16, + } + + let source = " + CLASS MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod : INT + VAR_INPUT myMethodArg : INT; END_VAR + VAR myMethodLocalVar : INT; END_VAR + + x := myMethodArg; + y := x + 1; + myMethodLocalVar := y + 1; + testMethod := myMethodLocalVar + 1; + END_METHOD + END_CLASS + + FUNCTION main : DINT + VAR + cl : MyClass; + x : INT := 0; + END_VAR + x := 1; + cl.x := 1; + x := x + cl.x; + x := x + cl.testMethod(x); + x := cl.testMethod(myMethodArg:= x); + main := x; + END_FUNCTION + " + .into(); + + let (res, _) = compile_and_run( + source, + &mut MainType { + cl: MyClass { x: 0, y: 0 }, + x: 0, + }, + ); + assert_eq!(res, 10); +} diff --git a/tests/tests.rs b/tests/tests.rs index 6a57a37262..c8c80fc984 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -7,6 +7,7 @@ type MainFunction = unsafe extern "C" fn(*mut T) -> i32; mod correctness { mod arrays; + mod classes; mod control_flow; mod custom_datatypes; mod datatypes;