Skip to content

feat: Aliasing #1258

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

Merged
merged 16 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 25 additions & 9 deletions compiler/plc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,9 +487,7 @@ pub enum DataType {
PointerType {
name: Option<String>,
referenced_type: Box<DataTypeDeclaration>,
auto_deref: bool,
/// Denotes whether the variable was declared as `REFERENCE TO`, e.g. `foo : REFERENCE TO DINT`
is_reference_to: bool,
auto_deref: Option<AutoDerefType>,
},
StringType {
name: Option<String>,
Expand All @@ -507,6 +505,18 @@ pub enum DataType {
},
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AutoDerefType {
/// A plain pointer variable with the auto-deref trait, e.g. VAR_IN_OUT or VAR_INPUT{ref} variables
Default,

/// An alias pointer variable, e.g. `foo AT bar : DINT`
Alias,

/// A reference pointer variable, e.g. `foo : REFERENCE TO DINT;`
Reference,
}

impl DataType {
pub fn set_name(&mut self, new_name: String) {
match self {
Expand Down Expand Up @@ -1295,8 +1305,11 @@ impl AstFactory {
}

/// creates a new Identifier
pub fn create_identifier(name: &str, location: &SourceLocation, id: AstId) -> AstNode {
AstNode::new(AstStatement::Identifier(name.to_string()), id, location.clone())
pub fn create_identifier<T>(name: &str, location: T, id: AstId) -> AstNode
where
T: Into<SourceLocation>,
{
AstNode::new(AstStatement::Identifier(name.to_string()), id, location.into())
}

pub fn create_unary_expression(
Expand Down Expand Up @@ -1441,18 +1454,21 @@ impl AstFactory {
}
}

pub fn create_call_statement(
pub fn create_call_statement<T>(
operator: AstNode,
parameters: Option<AstNode>,
id: usize,
location: SourceLocation,
) -> AstNode {
location: T,
) -> AstNode
where
T: Into<SourceLocation>,
{
AstNode {
stmt: AstStatement::CallStatement(CallStatement {
operator: Box::new(operator),
parameters: parameters.map(Box::new),
}),
location,
location: location.into(),
id,
}
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/plc_diagnostics/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,10 @@ impl Diagnostic {
.with_location(location)
}

pub fn invalid_assignment(right_type: &str, left_type: &str, location: SourceLocation) -> Diagnostic {
pub fn invalid_assignment<T>(right_type: &str, left_type: &str, location: T) -> Diagnostic
where
T: Into<SourceLocation>,
{
Diagnostic::new(format!("Invalid assignment: cannot assign '{right_type}' to '{left_type}'"))
.with_error_code("E037")
.with_location(location)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ lazy_static! {
E097, Error, include_str!("./error_codes/E097.md"), // Invalid Array Range
E098, Error, include_str!("./error_codes/E098.md"), // Invalid `REF=` assignment
E099, Error, include_str!("./error_codes/E099.md"), // Invalid `REFERENCE TO` declaration
E100, Error, include_str!("./error_codes/E100.md"), // Immutable variable address
);
}

Expand Down
4 changes: 1 addition & 3 deletions compiler/plc_diagnostics/src/diagnostics/error_codes/E099.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,4 @@
`REFERENCE TO` variable declarations are considered valid if the referenced type is not of the following form
* `foo : REFERENCE TO REFERENCE TO (* ... *)`
* `foo : ARRAY[...] OF REFERENCE TO (* ... *)`
* `foo : REF_TO REFERENCE TO (* ... *)`

Furthermore `REFERENCE_TO` variables must not be initialized in their declaration, e.g. `foo : REFERENCE TO DINT := bar`.
* `foo : REF_TO REFERENCE TO (* ... *)`
15 changes: 15 additions & 0 deletions compiler/plc_diagnostics/src/diagnostics/error_codes/E100.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Immutable Variable Address

Alias variables are immutable with regards to their pointer address, thus re-assigning an address will return an error. For example the following code will not compile
```ST
FUNCTION main
VAR
foo AT bar : DINT;
bar : DINT;
baz : DINT;
END_VAR

foo := baz; // Valid, because we are changing the pointers dereferenced value
foo REF= baz; // Invalid, `foo` is immutable with regards to it's pointer address
END_FUNCTION
```
28 changes: 13 additions & 15 deletions src/codegen/generators/expression_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -844,8 +844,9 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
.get(i)
.map(|it| {
let name = it.get_type_name();
if let Some(DataTypeInformation::Pointer { inner_type_name, auto_deref: true, .. }) =
self.index.find_effective_type_info(name)
if let Some(DataTypeInformation::Pointer {
inner_type_name, auto_deref: Some(_), ..
}) = self.index.find_effective_type_info(name)
{
// for auto_deref pointers (VAR_INPUT {ref}, VAR_IN_OUT) we call generate_argument_by_ref()
// we need the inner_type and not pointer to type otherwise we would generate a double pointer
Expand Down Expand Up @@ -1145,7 +1146,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
}

fn get_parameter_type(&self, parameter: &VariableIndexEntry) -> String {
if let Some(DataTypeInformation::Pointer { inner_type_name, auto_deref: true, .. }) =
if let Some(DataTypeInformation::Pointer { inner_type_name, auto_deref: Some(_), .. }) =
self.index.find_effective_type_info(parameter.get_type_name())
{
inner_type_name.into()
Expand Down Expand Up @@ -1253,7 +1254,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
.map(|var| var.get_type_information())
.unwrap_or_else(|| self.index.get_void_type().get_type_information());

if let DataTypeInformation::Pointer { auto_deref: true, inner_type_name, .. } = parameter {
if let DataTypeInformation::Pointer { auto_deref: Some(_), inner_type_name, .. } = parameter {
//this is a VAR_IN_OUT assignment, so don't load the value, assign the pointer
//expression may be empty -> generate a local variable for it
let generated_exp = if expression.is_empty_statement() {
Expand Down Expand Up @@ -1297,13 +1298,12 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {

// don't generate param assignments for empty statements, with the exception
// of VAR_IN_OUT params - they need an address to point to
let is_auto_deref = matches!(
self.index
.find_effective_type_by_name(parameter.get_type_name())
.map(|var| var.get_type_information())
.unwrap_or_else(|| self.index.get_void_type().get_type_information()),
DataTypeInformation::Pointer { auto_deref: true, .. }
);
let is_auto_deref = self
.index
.find_effective_type_by_name(parameter.get_type_name())
.map(|var| var.get_type_information())
.unwrap_or(self.index.get_void_type().get_type_information())
.is_auto_deref();
if !right.is_empty_statement() || is_auto_deref {
self.generate_call_struct_argument_assignment(&CallParameterAssignment {
assignment: right,
Expand Down Expand Up @@ -1419,9 +1419,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
accessor_ptr: PointerValue<'ink>,
statement: &AstNode,
) -> PointerValue<'ink> {
if let Some(StatementAnnotation::Variable { is_auto_deref: true, .. }) =
self.annotations.get(statement)
{
if self.annotations.get(statement).is_some_and(|opt| opt.is_auto_deref()) {
self.deref(accessor_ptr)
} else {
accessor_ptr
Expand Down Expand Up @@ -1976,7 +1974,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
}
}
}
DataTypeInformation::Pointer { inner_type_name, auto_deref: true, .. } => {
DataTypeInformation::Pointer { inner_type_name, auto_deref: Some(_), .. } => {
let inner_type = self.index.get_type_information_or_void(inner_type_name);
self.generate_string_literal_for_type(inner_type, value, location)
}
Expand Down
2 changes: 1 addition & 1 deletion src/codegen/llvm_typesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ impl<'ctx, 'cast> CastInstructionData<'ctx, 'cast> {
let value_type = index.get_intrinsic_type_by_name(value_type.get_name()).get_type_information();

let target_type =
if let DataTypeInformation::Pointer { auto_deref: true, inner_type_name, .. } = target_type {
if let DataTypeInformation::Pointer { auto_deref: Some(_), inner_type_name, .. } = target_type {
// Deref auto-deref pointers before casting
index.get_intrinsic_type_by_name(inner_type_name.as_str()).get_type_information()
} else {
Expand Down
59 changes: 59 additions & 0 deletions src/codegen/tests/statement_codegen_test.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use insta::assert_snapshot;

// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
use crate::test_utils::tests::codegen;

Expand Down Expand Up @@ -308,3 +310,60 @@ fn reference_to_string_assignment() {
attributes #0 = { argmemonly nofree nounwind willreturn }
"###);
}

#[test]
#[ignore = "Not working because of REF(...) initializer; should be resolved with https://github.com/PLC-lang/rusty/pull/1259"]
fn alias_dint() {
let content = codegen(
r#"
FUNCTION main
VAR
foo AT bar : DINT;
bar : DINT;
END_VAR
END_FUNCTION
"#,
);

assert_snapshot!(content, @r"");
}

#[test]
#[ignore = "Not working because of REF(...) initializer; should be resolved with https://github.com/PLC-lang/rusty/pull/1259"]
fn alias_string() {
let content = codegen(
r#"
FUNCTION main
VAR
foo AT bar : STRING;
bar : STRING;
END_VAR
END_FUNCTION
"#,
);

assert_snapshot!(content, @r"");
}

#[test]
#[ignore = "Not working because of REF(...) initializer; should be resolved with https://github.com/PLC-lang/rusty/pull/1259"]
fn alias_struct() {
let content = codegen(
r#"
TYPE Node : STRUCT
id : DINT;
child : REF_TO Node;
parent : REF_TO Node;
END_STRUCT END_TYPE

FUNCTION main
VAR
foo AT bar : STRING;
bar : STRING;
END_VAR
END_FUNCTION
"#,
);

assert_snapshot!(content, @r"");
}
15 changes: 6 additions & 9 deletions src/index/tests/index_tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
use insta::assert_debug_snapshot;
use plc_ast::ast::{
pre_process, AstFactory, DataType, GenericBinding, LinkageType, Operator, TypeNature, UserTypeDeclaration,
pre_process, AstFactory, AutoDerefType, DataType, GenericBinding, LinkageType, Operator, TypeNature,
UserTypeDeclaration,
};
use plc_ast::provider::IdProvider;
use plc_source::source_location::{SourceLocation, SourceLocationFactory};
Expand Down Expand Up @@ -1252,8 +1253,7 @@ fn pointer_and_in_out_pointer_should_not_conflict() {
&DataTypeInformation::Pointer {
name: "__main_x".to_string(),
inner_type_name: "INT".to_string(),
auto_deref: false,
is_reference_to: false,
auto_deref: None,
}
);

Expand All @@ -1264,8 +1264,7 @@ fn pointer_and_in_out_pointer_should_not_conflict() {
&DataTypeInformation::Pointer {
name: "__auto_pointer_to_INT".to_string(),
inner_type_name: "INT".to_string(),
auto_deref: true,
is_reference_to: false,
auto_deref: Some(AutoDerefType::Default),
}
);
}
Expand Down Expand Up @@ -1304,8 +1303,7 @@ fn pointer_and_in_out_pointer_should_not_conflict_2() {
&DataTypeInformation::Pointer {
name: "__main_x".to_string(),
inner_type_name: "INT".to_string(),
auto_deref: false,
is_reference_to: false,
auto_deref: None,
}
);

Expand All @@ -1316,8 +1314,7 @@ fn pointer_and_in_out_pointer_should_not_conflict_2() {
&DataTypeInformation::Pointer {
name: "__auto_pointer_to_INT".to_string(),
inner_type_name: "INT".to_string(),
auto_deref: true,
is_reference_to: false,
auto_deref: Some(AutoDerefType::Default),
}
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ UserTypeDeclaration {
referenced_type: DataTypeReference {
referenced_type: "__foo_inline_pointer_",
},
auto_deref: false,
is_reference_to: false,
auto_deref: None,
},
initializer: None,
scope: Some(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ UserTypeDeclaration {
referenced_type: DataTypeReference {
referenced_type: "INT",
},
auto_deref: false,
is_reference_to: false,
auto_deref: None,
},
initializer: None,
scope: Some(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ UserTypeDeclaration {
referenced_type: DataTypeReference {
referenced_type: "INT",
},
auto_deref: false,
is_reference_to: false,
auto_deref: None,
},
initializer: None,
scope: Some(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ UserTypeDeclaration {
referenced_type: DataTypeReference {
referenced_type: "__pointer_to_pointer",
},
auto_deref: false,
is_reference_to: false,
auto_deref: None,
},
initializer: None,
scope: None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ UserTypeDeclaration {
referenced_type: DataTypeReference {
referenced_type: "INT",
},
auto_deref: false,
is_reference_to: false,
auto_deref: None,
},
initializer: None,
scope: None,
Expand Down
Loading
Loading