Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 375 add support for any type #380

Merged
merged 23 commits into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7750771
Add generics type to the AST
ghaith Nov 15, 2021
e3fbdc1
Add support for parsing generic types
ghaith Nov 15, 2021
8f03a6b
Use vec instead of map, add generics to index
ghaith Nov 18, 2021
265abfd
Merge remote-tracking branch 'origin/master' into issue-375-Add_suppo…
ghaith Nov 18, 2021
ccdb789
Codegen tests
ghaith Nov 18, 2021
560eb00
Add indexing support for generics
ghaith Nov 20, 2021
9098079
Merge remote-tracking branch 'origin/master' into issue-375-Add_suppo…
ghaith Nov 20, 2021
8037f58
Add resolver support for generics
ghaith Nov 27, 2021
43a53a6
Merge remote-tracking branch 'origin/master' into issue-375-Add_suppo…
ghaith Nov 27, 2021
276da9d
Clippy fixes
ghaith Nov 27, 2021
8b7bca3
Code generation for generics
ghaith Nov 27, 2021
fe7fdc4
Add natures as keywords, add initial validation scaffold
ghaith Nov 29, 2021
9a54f4c
Merge remote-tracking branch 'origin/master' into issue-375-Add_suppo…
ghaith Nov 29, 2021
71e8a41
Validation wip
ghaith Nov 29, 2021
aafb8e8
Add validation for generics
ghaith Nov 30, 2021
fd7bb31
Deleted some demo files
ghaith Nov 30, 2021
d2b77fe
Add intermdiate index
ghaith Nov 30, 2021
807a3b9
Merge remote-tracking branch 'origin/master' into issue-375-Add_suppo…
ghaith Nov 30, 2021
a598e23
Create implementation entries for resolved generics - wip
ghaith Nov 30, 2021
c51bfa3
Make sure types are generated, add correctness tests
ghaith Dec 1, 2021
4b8b977
additional tests for generic-parameter annotation
riederm Dec 1, 2021
96ee719
Fix some review comments, missing : Tokens
ghaith Dec 2, 2021
a7946cc
Remove the keywords from the lexer and treat type natures as Identifiers
ghaith Dec 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 35 additions & 77 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ mod pre_processor;

pub type AstId = usize;

#[derive(Clone, Debug, PartialEq)]
pub struct GenericBinding {
pub name: String,
pub nature: String,
}

#[derive(PartialEq)]
pub struct Pou {
pub name: String,
Expand All @@ -18,6 +24,7 @@ pub struct Pou {
pub return_type: Option<DataTypeDeclaration>,
pub location: SourceRange,
pub poly_mode: Option<PolymorphismMode>,
pub generics: Vec<GenericBinding>,
}

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -64,12 +71,15 @@ impl DirectAccessType {

impl Debug for Pou {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("POU")
.field("name", &self.name)
let mut str = f.debug_struct("POU");
str.field("name", &self.name)
.field("variable_blocks", &self.variable_blocks)
.field("pou_type", &self.pou_type)
.field("return_type", &self.return_type)
.finish()
.field("return_type", &self.return_type);
if !self.generics.is_empty() {
str.field("generics", &self.generics);
}
str.finish()
}
}

Expand Down Expand Up @@ -335,7 +345,7 @@ impl Debug for UserTypeDeclaration {
}
}

#[derive(Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub enum DataType {
StructType {
name: Option<String>, //maybe None for inline structs
Expand Down Expand Up @@ -367,88 +377,36 @@ pub enum DataType {
VarArgs {
referenced_type: Option<Box<DataTypeDeclaration>>,
},
}

impl Debug for DataType {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
DataType::StructType { name, variables } => f
.debug_struct("StructType")
.field("name", name)
.field("variables", variables)
.finish(),
DataType::EnumType { name, elements } => f
.debug_struct("EnumType")
.field("name", name)
.field("elements", elements)
.finish(),
DataType::SubRangeType {
name,
referenced_type,
bounds,
} => f
.debug_struct("SubRangeType")
.field("name", name)
.field("referenced_type", referenced_type)
.field("bounds", bounds)
.finish(),
DataType::ArrayType {
name,
bounds,
referenced_type,
} => f
.debug_struct("ArrayType")
.field("name", name)
.field("bounds", bounds)
.field("referenced_type", referenced_type)
.finish(),
DataType::PointerType {
name,
referenced_type,
} => f
.debug_struct("PointerType")
.field("name", name)
.field("referenced_type", referenced_type)
.finish(),
DataType::StringType {
name,
is_wide,
size,
} => f
.debug_struct("StringType")
.field("name", name)
.field("is_wide", is_wide)
.field("size", size)
.finish(),
DataType::VarArgs { referenced_type } => f
.debug_struct("VarArgs")
.field("referenced_type", referenced_type)
.finish(),
}
}
GenericType {
name: String,
generic_symbol: String,
nature: String,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would this be usefull as an enum?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the 'nature' field? So we could limit it to only possible ANY_XXX type natures? I think that might be good yes. I think once we're in OOP territory we are just working with interfaces / derived types anyway

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry yes, I'm talking about the nature. I thought it would be highlighted when i mark it when commenting

},
}

impl DataType {
pub fn set_name(&mut self, new_name: String) {
match self {
DataType::StructType { name, variables: _ } => *name = Some(new_name),
DataType::EnumType { name, elements: _ } => *name = Some(new_name),
DataType::SubRangeType { name, .. } => *name = Some(new_name),
DataType::ArrayType { name, .. } => *name = Some(new_name),
DataType::PointerType { name, .. } => *name = Some(new_name),
DataType::StringType { name, .. } => *name = Some(new_name),
DataType::StructType { name, .. }
| DataType::EnumType { name, .. }
| DataType::SubRangeType { name, .. }
| DataType::ArrayType { name, .. }
| DataType::PointerType { name, .. }
| DataType::StringType { name, .. } => *name = Some(new_name),
DataType::GenericType { name, .. } => *name = new_name,
DataType::VarArgs { .. } => {} //No names on varargs
}
}

pub fn get_name(&self) -> Option<&str> {
match self {
DataType::StructType { name, variables: _ } => name.as_ref().map(|x| x.as_str()),
DataType::EnumType { name, elements: _ } => name.as_ref().map(|x| x.as_str()),
DataType::ArrayType { name, .. } => name.as_ref().map(|x| x.as_str()),
DataType::PointerType { name, .. } => name.as_ref().map(|x| x.as_str()),
DataType::StringType { name, .. } => name.as_ref().map(|x| x.as_str()),
DataType::SubRangeType { name, .. } => name.as_ref().map(|x| x.as_str()),
match &self {
DataType::StructType { name, .. }
| DataType::EnumType { name, .. }
| DataType::ArrayType { name, .. }
| DataType::PointerType { name, .. }
| DataType::StringType { name, .. }
| DataType::SubRangeType { name, .. } => name.as_ref().map(|x| x.as_str()),
DataType::GenericType { name, .. } => Some(name.as_str()),
DataType::VarArgs { .. } => None,
}
}
Expand Down
63 changes: 60 additions & 3 deletions src/ast/pre_processor.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,48 @@
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder

use crate::ast::DataTypeDeclaration;

use super::{
super::ast::{CompilationUnit, DataType, DataTypeDeclaration, UserTypeDeclaration, Variable},
super::ast::{CompilationUnit, DataType, UserTypeDeclaration, Variable},
Pou, SourceRange,
};
use std::vec;
use std::{collections::HashMap, vec};

pub fn pre_process(unit: &mut CompilationUnit) {
//process all local variables from POUs
for mut pou in unit.units.iter_mut() {
//Find all generic types in that pou
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we put this into it's own function?. It kindo'f disturbs the level of detail of the rest of this function

let mut generic_types = HashMap::new();
for binding in &pou.generics {
let new_name = format!("__{}__{}", pou.name, binding.name);
//Generate a type for the generic
let data_type = UserTypeDeclaration {
data_type: DataType::GenericType {
name: new_name.clone(),
generic_symbol: binding.name.clone(),
nature: binding.nature.clone(),
},
initializer: None,
scope: Some(pou.name.clone()),
location: pou.location.clone(),
};
unit.types.push(data_type);
generic_types.insert(binding.name.clone(), new_name);
}
//Find all variables that reference a generic type
//Replace the reference with the generic type's name
for var in pou
.variable_blocks
.iter_mut()
.flat_map(|it| it.variables.iter_mut())
{
replace_generic_type_name(&mut var.data_type, &generic_types);
}
//Replace the return type's reference if needed
if let Some(datatype) = pou.return_type.as_mut() {
replace_generic_type_name(datatype, &generic_types);
}

let all_variables = pou
.variable_blocks
.iter_mut()
Expand Down Expand Up @@ -39,7 +73,9 @@ pub fn pre_process(unit: &mut CompilationUnit) {
for dt in unit.types.iter_mut() {
{
match &mut dt.data_type {
DataType::StructType { name, variables } => {
DataType::StructType {
name, variables, ..
} => {
let name: &str = name.as_ref().map(|it| it.as_str()).unwrap_or("undefined");
variables
.iter_mut()
Expand Down Expand Up @@ -185,3 +221,24 @@ fn add_nested_datatypes(
});
}
}

fn replace_generic_type_name(dt: &mut DataTypeDeclaration, generics: &HashMap<String, String>) {
match dt {
DataTypeDeclaration::DataTypeDefinition { data_type, .. } => match data_type {
DataType::ArrayType {
referenced_type, ..
}
| DataType::PointerType {
referenced_type, ..
} => replace_generic_type_name(referenced_type.as_mut(), generics),
_ => {}
},
DataTypeDeclaration::DataTypeReference {
referenced_type, ..
} => {
if let Some(type_name) = generics.get(referenced_type) {
*referenced_type = type_name.clone();
}
}
}
}
3 changes: 3 additions & 0 deletions src/codegen/generators/data_type_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ impl<'ink, 'b> DataTypeGenerator<'ink, 'b> {
self.create_type(inner_type_name, self.index.get_type(inner_type_name)?)?;
Ok(inner_type.ptr_type(AddressSpace::Generic).into())
}
DataTypeInformation::Generic { .. } => {
unreachable!("Generic types should not be generated")
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/codegen/generators/pou_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ pub fn generate_implementation_stubs<'ink>(
let mut llvm_index = LlvmTypedIndex::new();
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)?;
let type_info = index.get_type_information_or_void(implementation.get_type_name());
if !type_info.is_generic() {
let curr_f = pou_generator.generate_implementation_stub(implementation, module)?;
llvm_index.associate_implementation(name, curr_f)?;
}
}

Ok(llvm_index)
Expand Down
1 change: 1 addition & 0 deletions src/codegen/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mod code_gen_tests;
mod codegen_error_messages_tests;
mod expression_tests;
mod generics_test;
mod statement_codegen_test;
mod string_tests;
mod typesystem_test;
Expand Down
34 changes: 34 additions & 0 deletions src/codegen/tests/generics_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::test_utils::tests::codegen;

#[test]
#[ignore]
fn generic_function_has_no_declaration() {
let prg = codegen(
r"
@EXTERNAL FUNCTION MAX<T : ANY_NUMBER> : T VAR_INPUT in1, in2 : T END_VAR END_FUNCTION
",
);

insta::assert_snapshot!(prg);
}

#[test]
#[ignore]
fn generic_function_call_generates_real_type_call() {
let prg = codegen(
r"
@EXTERNAL FUNCTION MAX<T : ANY_NUMBER> : T VAR_INPUT in1, in2 : T END_VAR END_FUNCTION

PROGRAM prg
VAR
a, b : INT;
END_VAR

MAX(1,2);
MAX(a,b);
END_PROGRAM
",
);

insta::assert_snapshot!(prg);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: src/codegen/tests/generics_test.rs
expression: prg

---
; ModuleID = 'main'
source_filename = "main"

1 change: 1 addition & 0 deletions src/index/tests.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
mod generic_tests;
mod index_tests;
27 changes: 27 additions & 0 deletions src/index/tests/generic_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use pretty_assertions::assert_eq;

use crate::{ast::GenericBinding, test_utils::tests::index, typesystem::DataTypeInformation};

#[test]
fn generics_saved_in_index() {
let (_, index) = index(
r"
FUNCTION foo<T: ANY> : T; END_FUNCTION
",
);

let foo_info = index.find_effective_type_info("foo").unwrap();
assert!(foo_info.is_generic());
if let DataTypeInformation::Struct { generics, .. } = foo_info {
let t = &generics[0];
assert_eq!(
&GenericBinding {
name: "T".into(),
nature: "ANY".into()
},
t
);
} else {
panic!("{:#?} not a struct", foo_info);
}
}
Loading