Skip to content

Commit

Permalink
Make sure types are generated, add correctness tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ghaith committed Dec 1, 2021
1 parent a598e23 commit c51bfa3
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 38 deletions.
4 changes: 3 additions & 1 deletion src/codegen/generators/data_type_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ pub fn generate_data_types<'ink>(
.filter(|(_, it)| !it.get_type_information().is_generic())
.map(|(a, b)| (a.as_str(), b))
.collect::<Vec<(&str, &DataType)>>();
let pou_types = generator.index.get_pou_types()
let pou_types = generator
.index
.get_pou_types()
.iter()
.filter(|(_, it)| !it.get_type_information().is_generic())
.map(|(a, b)| (a.as_str(), b))
Expand Down
2 changes: 0 additions & 2 deletions src/codegen/tests/generics_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ fn generic_function_call_generates_real_type_call() {
let prg = codegen(
r"
@EXTERNAL FUNCTION MAX<T : ANY_NUM> : T VAR_INPUT in1, in2 : T END_VAR END_FUNCTION
@EXTERNAL FUNCTION MAX__INT : INT VAR_INPUT in1, in2 : INT END_VAR END_FUNCTION
@EXTERNAL FUNCTION MAX__DINT : DINT VAR_INPUT in1, in2 : DINT END_VAR END_FUNCTION
PROGRAM prg
VAR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,11 @@ expression: prg
source_filename = "main"

%prg_interface = type { i16, i16 }
%MAX__INT_interface = type { i16, i16 }
%MAX__DINT_interface = type { i32, i32 }
%MAX__INT_interface = type { i16, i16 }

@prg_instance = global %prg_interface zeroinitializer

declare i16 @MAX__INT(%MAX__INT_interface*)

declare i32 @MAX__DINT(%MAX__DINT_interface*)

define void @prg(%prg_interface* %0) {
entry:
%a = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 0
Expand Down Expand Up @@ -61,3 +57,7 @@ continue5: ; preds = %output4
ret void
}

declare i32 @MAX__DINT(%MAX__DINT_interface*)

declare i16 @MAX__INT(%MAX__INT_interface*)

30 changes: 19 additions & 11 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ pub struct MemberInfo<'b> {
}

impl VariableIndexEntry {
pub fn to_specific(&self, container: &str, new_type: &str) -> Self {
VariableIndexEntry {
qualified_name: format!("{}.{}", container, self.name),
data_type_name: new_type.to_string(),
..self.to_owned()
}
}

pub fn get_name(&self) -> &str {
self.name.as_str()
}
Expand Down Expand Up @@ -182,6 +190,7 @@ impl From<&PouType> for ImplementationType {
/// the TypeIndex carries all types.
/// it is extracted into its seaprate struct so it can be
/// internally borrowed individually from the other maps
#[derive(Debug)]
pub struct TypeIndex {
/// all types (structs, enums, type, POUs, etc.)
types: IndexMap<String, DataType>,
Expand Down Expand Up @@ -250,7 +259,7 @@ impl TypeIndex {
/// The global index of the rusty-compiler
///
/// The index contains information about all referencable elements.
#[derive()]
#[derive(Debug)]
pub struct Index {
/// all global variables
global_variables: IndexMap<String, VariableIndexEntry>,
Expand Down Expand Up @@ -664,11 +673,6 @@ impl Index {
let variable_linkage = member_info.variable_linkage;
let variable_type_name = member_info.variable_type_name;

let members = self
.member_variables
.entry(container_name.to_lowercase())
.or_insert_with(IndexMap::new);

let qualified_name = format!("{}.{}", container_name, variable_name);

let entry = VariableIndexEntry {
Expand All @@ -681,7 +685,14 @@ impl Index {
is_constant: member_info.is_constant,
location_in_parent: location,
};
members.insert(variable_name.to_lowercase(), entry);
self.register_member_entry(container_name, entry);
}
pub fn register_member_entry(&mut self, container_name: &str, entry: VariableIndexEntry) {
let members = self
.member_variables
.entry(container_name.to_lowercase())
.or_insert_with(IndexMap::new);
members.insert(entry.name.to_lowercase(), entry);
}

pub fn register_enum_element(
Expand Down Expand Up @@ -759,10 +770,7 @@ impl Index {
.insert(datatype.get_name().to_lowercase(), datatype);
}

pub fn register_pou_type(
&mut self,
datatype: DataType,
) {
pub fn register_pou_type(&mut self, datatype: DataType) {
self.type_index
.pou_types
.insert(datatype.get_name().to_lowercase(), datatype);
Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,8 @@ pub fn compile_module<'c, T: SourceContainer>(
}

// ### PHASE 1.1 resolve constant literal values
let (full_index, _unresolvables) = resolver::const_evaluator::evaluate_constants(full_index);
let (mut full_index, _unresolvables) =
resolver::const_evaluator::evaluate_constants(full_index);

// ### PHASE 2 ###
// annotation & validation everything
Expand All @@ -377,8 +378,8 @@ pub fn compile_module<'c, T: SourceContainer>(
all_annotations.import(annotations);
}


//Merge the new indices with the full index
full_index.import(std::mem::take(&mut all_annotations.new_index));

// ### PHASE 3 ###
// - codegen
Expand Down
8 changes: 4 additions & 4 deletions src/parser/tests/parse_generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn generic_markers_on_pou_added() {
M : ANY_STRING,
N : ANY_CHAR,
O : ANY_DATE> : INT END_FUNCTION";
let (parse_result, _) = dbg!(parse(src));
let (parse_result, _) = parse(src);
let function = &parse_result.units[0];
//Make sure the function has the generic parametes T: ANY, R : ANY_NUMBER
let generics = &function.generics;
Expand Down Expand Up @@ -150,7 +150,7 @@ fn generic_markers_on_pou_added() {
#[test]
fn generic_markers_on_method_added() {
let src = "CLASS xx METHOD test<T: ANY, R : ANY_NUM> : INT END_METHOD END_CLASS";
let (parse_result, _) = dbg!(parse(src));
let (parse_result, _) = parse(src);
let function = &parse_result.units[1];
//Make sure the function has the generic parametes T: ANY, R : ANY_NUMBER
let generics = &function.generics;
Expand All @@ -176,7 +176,7 @@ fn generic_markers_on_method_added() {
#[test]
fn generic_parameters_are_datatypes() {
let src = "FUNCTION test<T: ANY, R : ANY_NUM> : R VAR_INPUT x : T; y : R; END_VAR END_FUNCTION";
let (parse_result, _) = dbg!(parse(src));
let (parse_result, _) = parse(src);
let function = &parse_result.units[0];
let variables = &function.variable_blocks[0].variables;
assert_eq!(
Expand Down Expand Up @@ -207,7 +207,7 @@ fn generic_parameters_are_datatypes() {
#[test]
fn generic_method_parameters_are_datatypes() {
let src = "CLASS cls METHOD test<T: ANY, R : ANY_NUM> : R VAR_INPUT x : T; y : R; END_VAR END_METHOD END_CLASS";
let (parse_result, _) = dbg!(parse(src));
let (parse_result, _) = parse(src);
let function = &parse_result.units[1];
let variables = &function.variable_blocks[0].variables;
assert_eq!(
Expand Down
69 changes: 61 additions & 8 deletions src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
//! Recursively visits all statements and expressions of a `CompilationUnit` and
//! records all resulting types associated with the statement's id.
use std::{
collections::HashMap,
rc::{Rc, Weak},
};
use std::collections::HashMap;

use indexmap::IndexMap;

Expand Down Expand Up @@ -150,7 +147,7 @@ pub struct AnnotationMap {
generic_nature_map: IndexMap<AstId, TypeNature>,

//An index of newly created types
new_index: Index,
pub new_index: Index,
}

impl AnnotationMap {
Expand Down Expand Up @@ -285,8 +282,6 @@ impl<'i> TypeAnnotator<'i> {
/// Returns an AnnotationMap with the resulting types for all visited Statements. See `AnnotationMap`
pub fn visit_unit(index: &Index, unit: &'i CompilationUnit) -> AnnotationMap {
let mut visitor = TypeAnnotator::new(index);
let new_index = Rc::new(Index::default());

let ctx = &VisitorContext {
pou: None,
qualifier: None,
Expand Down Expand Up @@ -1092,19 +1087,77 @@ impl<'i> TypeAnnotator<'i> {
implementation.get_associated_class_name(),
implementation.get_implementation_type().clone(),
);
self.index_generic_type(pou, &name, generic_map);
}
self.annotation_map.annotate(operator, annotation);
}
//Adjust annotations on the inner statement
if let Some(s) = parameters.as_ref() {
self.visit_statement(&ctx, s);

self.update_generic_function_parameters(s, operator_qualifier, generic_map);
}
}
}
}

/// Only works for Structs
pub fn index_generic_type(
&mut self,
generic_type: &typesystem::DataType,
specific_name: &str,
generics: &HashMap<String, String>,
) {
let information = if let DataTypeInformation::Struct {
member_names,
source,
varargs,
..
} = &generic_type.get_type_information()
{
let interface_name = format!("{}_interface", specific_name);
let information = DataTypeInformation::Struct {
name: interface_name,
member_names: member_names.clone(),
varargs: varargs.clone(),
source: source.clone(),
generics: vec![],
};
for member in member_names {
if let Some(generic_entry) = self.index.find_member(generic_type.get_name(), member)
{
let new_name = if let Some(old_type) = self
.index
.find_effective_type(generic_entry.get_type_name())
{
match old_type.get_type_information() {
DataTypeInformation::Generic { generic_symbol, .. } => generics
.get(generic_symbol)
.map(String::as_str)
.unwrap_or_else(|| old_type.get_name()),
_ => old_type.get_name(),
}
} else {
unreachable!("Member not indexed")
};
let entry = generic_entry.to_specific(specific_name, new_name);
self.annotation_map
.new_index
.register_member_entry(specific_name, entry);
}
}
information
} else {
unreachable!("Not a struct");
};
let datatype = typesystem::DataType {
name: specific_name.to_string(),
initial_value: generic_type.initial_value,
nature: generic_type.nature,
information,
};
self.annotation_map.new_index.register_pou_type(datatype);
}

fn update_generic_function_parameters(
&mut self,
s: &AstStatement,
Expand Down
1 change: 0 additions & 1 deletion src/resolver/tests/resolve_expressions_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2547,7 +2547,6 @@ fn string_compare_should_resolve_to_bool() {
// THEN we want the hint for 'NULL' to be POINTER TO BYTE
let annotations = annotate(&unit, &index);
let a_eq_b = &unit.implementations[1].statements[0];
dbg!(a_eq_b);
assert_eq!(
Some(&StatementAnnotation::value("BOOL")),
annotations.get_annotation(a_eq_b),
Expand Down
27 changes: 26 additions & 1 deletion src/resolver/tests/resolve_generic_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn resolved_generic_call_added_to_index() {
);
let annotations = TypeAnnotator::visit_unit(&index, &unit);
//The implementations are added to the index
let implementations = dbg!(annotations.new_index.get_implementations());
let implementations = annotations.new_index.get_implementations();
assert_eq!(3, implementations.len());
assert!(implementations.contains_key("myfunc__int"));
assert!(implementations.contains_key("myfunc__dint"));
Expand All @@ -40,6 +40,31 @@ fn resolved_generic_call_added_to_index() {
assert!(pous.contains_key("myfunc__dint"));
assert!(pous.contains_key("myfunc__real"));

//Each POU has members
assert_eq!(
"INT",
annotations
.new_index
.find_member("myfunc__int", "x")
.unwrap()
.get_type_name()
);
assert_eq!(
"DINT",
annotations
.new_index
.find_member("myfunc__dint", "x")
.unwrap()
.get_type_name()
);
assert_eq!(
"REAL",
annotations
.new_index
.find_member("myfunc__real", "x")
.unwrap()
.get_type_name()
);
}

#[test]
Expand Down
5 changes: 3 additions & 2 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ pub mod tests {
pub fn codegen_without_unwrap(src: &str) -> Result<String, Diagnostic> {
let (unit, index) = index(src);

let (index, ..) = evaluate_constants(index);
let annotations = TypeAnnotator::visit_unit(&index, &unit);
let (mut index, ..) = evaluate_constants(index);
let mut annotations = TypeAnnotator::visit_unit(&index, &unit);
index.import(std::mem::take(&mut annotations.new_index));

let context = inkwell::context::Context::create();
let code_generator = crate::codegen::CodeGen::new(&context, "main");
Expand Down
2 changes: 1 addition & 1 deletion src/validation/stmt_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ impl StatementValidator {
statement.get_location(),
))
} else if let Some(nature) = context.ast_annotation.get_generic_nature(statement) {
if !dbg!(statement_type).has_nature(*dbg!(nature), context.index) {
if !statement_type.has_nature(*nature, context.index) {
self.diagnostics.push(Diagnostic::invalid_type_nature(
statement_type.get_name(),
format!("{:?}", nature).as_str(),
Expand Down
Loading

0 comments on commit c51bfa3

Please sign in to comment.