Skip to content

Commit 446f073

Browse files
authored
feat: Aliasing (#1258)
This commit introduces aliasing, where the `AT` keyword can be used on a reference declaration to create a `REFERENCE TO` pointer. Specifically `foo AT bar : DINT` internally resolves to `foo : REFERENCE TO DINT := REF(bar)`.
1 parent 0c04d54 commit 446f073

File tree

48 files changed

+901
-318
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+901
-318
lines changed

compiler/plc_ast/src/ast.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,7 @@ pub enum DataType {
487487
PointerType {
488488
name: Option<String>,
489489
referenced_type: Box<DataTypeDeclaration>,
490-
auto_deref: bool,
491-
/// Denotes whether the variable was declared as `REFERENCE TO`, e.g. `foo : REFERENCE TO DINT`
492-
is_reference_to: bool,
490+
auto_deref: Option<AutoDerefType>,
493491
},
494492
StringType {
495493
name: Option<String>,
@@ -507,6 +505,18 @@ pub enum DataType {
507505
},
508506
}
509507

508+
#[derive(Debug, Clone, Copy, PartialEq)]
509+
pub enum AutoDerefType {
510+
/// A plain pointer variable with the auto-deref trait, e.g. VAR_IN_OUT or VAR_INPUT{ref} variables
511+
Default,
512+
513+
/// An alias pointer variable, e.g. `foo AT bar : DINT`
514+
Alias,
515+
516+
/// A reference pointer variable, e.g. `foo : REFERENCE TO DINT;`
517+
Reference,
518+
}
519+
510520
impl DataType {
511521
pub fn set_name(&mut self, new_name: String) {
512522
match self {
@@ -1295,8 +1305,11 @@ impl AstFactory {
12951305
}
12961306

12971307
/// creates a new Identifier
1298-
pub fn create_identifier(name: &str, location: &SourceLocation, id: AstId) -> AstNode {
1299-
AstNode::new(AstStatement::Identifier(name.to_string()), id, location.clone())
1308+
pub fn create_identifier<T>(name: &str, location: T, id: AstId) -> AstNode
1309+
where
1310+
T: Into<SourceLocation>,
1311+
{
1312+
AstNode::new(AstStatement::Identifier(name.to_string()), id, location.into())
13001313
}
13011314

13021315
pub fn create_unary_expression(
@@ -1441,18 +1454,21 @@ impl AstFactory {
14411454
}
14421455
}
14431456

1444-
pub fn create_call_statement(
1457+
pub fn create_call_statement<T>(
14451458
operator: AstNode,
14461459
parameters: Option<AstNode>,
14471460
id: usize,
1448-
location: SourceLocation,
1449-
) -> AstNode {
1461+
location: T,
1462+
) -> AstNode
1463+
where
1464+
T: Into<SourceLocation>,
1465+
{
14501466
AstNode {
14511467
stmt: AstStatement::CallStatement(CallStatement {
14521468
operator: Box::new(operator),
14531469
parameters: parameters.map(Box::new),
14541470
}),
1455-
location,
1471+
location: location.into(),
14561472
id,
14571473
}
14581474
}

compiler/plc_diagnostics/src/diagnostics.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,10 @@ impl Diagnostic {
234234
.with_location(location)
235235
}
236236

237-
pub fn invalid_assignment(right_type: &str, left_type: &str, location: SourceLocation) -> Diagnostic {
237+
pub fn invalid_assignment<T>(right_type: &str, left_type: &str, location: T) -> Diagnostic
238+
where
239+
T: Into<SourceLocation>,
240+
{
238241
Diagnostic::new(format!("Invalid assignment: cannot assign '{right_type}' to '{left_type}'"))
239242
.with_error_code("E037")
240243
.with_location(location)

compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ lazy_static! {
201201
E097, Error, include_str!("./error_codes/E097.md"), // Invalid Array Range
202202
E098, Error, include_str!("./error_codes/E098.md"), // Invalid `REF=` assignment
203203
E099, Error, include_str!("./error_codes/E099.md"), // Invalid `REFERENCE TO` declaration
204+
E100, Error, include_str!("./error_codes/E100.md"), // Immutable variable address
204205
);
205206
}
206207

compiler/plc_diagnostics/src/diagnostics/error_codes/E099.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,4 @@
33
`REFERENCE TO` variable declarations are considered valid if the referenced type is not of the following form
44
* `foo : REFERENCE TO REFERENCE TO (* ... *)`
55
* `foo : ARRAY[...] OF REFERENCE TO (* ... *)`
6-
* `foo : REF_TO REFERENCE TO (* ... *)`
7-
8-
Furthermore `REFERENCE_TO` variables must not be initialized in their declaration, e.g. `foo : REFERENCE TO DINT := bar`.
6+
* `foo : REF_TO REFERENCE TO (* ... *)`
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Immutable Variable Address
2+
3+
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
4+
```ST
5+
FUNCTION main
6+
VAR
7+
foo AT bar : DINT;
8+
bar : DINT;
9+
baz : DINT;
10+
END_VAR
11+
12+
foo := baz; // Valid, because we are changing the pointers dereferenced value
13+
foo REF= baz; // Invalid, `foo` is immutable with regards to it's pointer address
14+
END_FUNCTION
15+
```

src/codegen/generators/expression_generator.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -844,8 +844,9 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
844844
.get(i)
845845
.map(|it| {
846846
let name = it.get_type_name();
847-
if let Some(DataTypeInformation::Pointer { inner_type_name, auto_deref: true, .. }) =
848-
self.index.find_effective_type_info(name)
847+
if let Some(DataTypeInformation::Pointer {
848+
inner_type_name, auto_deref: Some(_), ..
849+
}) = self.index.find_effective_type_info(name)
849850
{
850851
// for auto_deref pointers (VAR_INPUT {ref}, VAR_IN_OUT) we call generate_argument_by_ref()
851852
// we need the inner_type and not pointer to type otherwise we would generate a double pointer
@@ -1145,7 +1146,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
11451146
}
11461147

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

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

12981299
// don't generate param assignments for empty statements, with the exception
12991300
// of VAR_IN_OUT params - they need an address to point to
1300-
let is_auto_deref = matches!(
1301-
self.index
1302-
.find_effective_type_by_name(parameter.get_type_name())
1303-
.map(|var| var.get_type_information())
1304-
.unwrap_or_else(|| self.index.get_void_type().get_type_information()),
1305-
DataTypeInformation::Pointer { auto_deref: true, .. }
1306-
);
1301+
let is_auto_deref = self
1302+
.index
1303+
.find_effective_type_by_name(parameter.get_type_name())
1304+
.map(|var| var.get_type_information())
1305+
.unwrap_or(self.index.get_void_type().get_type_information())
1306+
.is_auto_deref();
13071307
if !right.is_empty_statement() || is_auto_deref {
13081308
self.generate_call_struct_argument_assignment(&CallParameterAssignment {
13091309
assignment: right,
@@ -1419,9 +1419,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
14191419
accessor_ptr: PointerValue<'ink>,
14201420
statement: &AstNode,
14211421
) -> PointerValue<'ink> {
1422-
if let Some(StatementAnnotation::Variable { is_auto_deref: true, .. }) =
1423-
self.annotations.get(statement)
1424-
{
1422+
if self.annotations.get(statement).is_some_and(|opt| opt.is_auto_deref()) {
14251423
self.deref(accessor_ptr)
14261424
} else {
14271425
accessor_ptr
@@ -1976,7 +1974,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
19761974
}
19771975
}
19781976
}
1979-
DataTypeInformation::Pointer { inner_type_name, auto_deref: true, .. } => {
1977+
DataTypeInformation::Pointer { inner_type_name, auto_deref: Some(_), .. } => {
19801978
let inner_type = self.index.get_type_information_or_void(inner_type_name);
19811979
self.generate_string_literal_for_type(inner_type, value, location)
19821980
}

src/codegen/llvm_typesystem.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ impl<'ctx, 'cast> CastInstructionData<'ctx, 'cast> {
114114
let value_type = index.get_intrinsic_type_by_name(value_type.get_name()).get_type_information();
115115

116116
let target_type =
117-
if let DataTypeInformation::Pointer { auto_deref: true, inner_type_name, .. } = target_type {
117+
if let DataTypeInformation::Pointer { auto_deref: Some(_), inner_type_name, .. } = target_type {
118118
// Deref auto-deref pointers before casting
119119
index.get_intrinsic_type_by_name(inner_type_name.as_str()).get_type_information()
120120
} else {

src/codegen/tests/statement_codegen_test.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use insta::assert_snapshot;
2+
13
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
24
use crate::test_utils::tests::codegen;
35

@@ -308,3 +310,60 @@ fn reference_to_string_assignment() {
308310
attributes #0 = { argmemonly nofree nounwind willreturn }
309311
"###);
310312
}
313+
314+
#[test]
315+
#[ignore = "Not working because of REF(...) initializer; should be resolved with https://github.com/PLC-lang/rusty/pull/1259"]
316+
fn alias_dint() {
317+
let content = codegen(
318+
r#"
319+
FUNCTION main
320+
VAR
321+
foo AT bar : DINT;
322+
bar : DINT;
323+
END_VAR
324+
END_FUNCTION
325+
"#,
326+
);
327+
328+
assert_snapshot!(content, @r"");
329+
}
330+
331+
#[test]
332+
#[ignore = "Not working because of REF(...) initializer; should be resolved with https://github.com/PLC-lang/rusty/pull/1259"]
333+
fn alias_string() {
334+
let content = codegen(
335+
r#"
336+
FUNCTION main
337+
VAR
338+
foo AT bar : STRING;
339+
bar : STRING;
340+
END_VAR
341+
END_FUNCTION
342+
"#,
343+
);
344+
345+
assert_snapshot!(content, @r"");
346+
}
347+
348+
#[test]
349+
#[ignore = "Not working because of REF(...) initializer; should be resolved with https://github.com/PLC-lang/rusty/pull/1259"]
350+
fn alias_struct() {
351+
let content = codegen(
352+
r#"
353+
TYPE Node : STRUCT
354+
id : DINT;
355+
child : REF_TO Node;
356+
parent : REF_TO Node;
357+
END_STRUCT END_TYPE
358+
359+
FUNCTION main
360+
VAR
361+
foo AT bar : STRING;
362+
bar : STRING;
363+
END_VAR
364+
END_FUNCTION
365+
"#,
366+
);
367+
368+
assert_snapshot!(content, @r"");
369+
}

src/index/tests/index_tests.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
22
use insta::assert_debug_snapshot;
33
use plc_ast::ast::{
4-
pre_process, AstFactory, DataType, GenericBinding, LinkageType, Operator, TypeNature, UserTypeDeclaration,
4+
pre_process, AstFactory, AutoDerefType, DataType, GenericBinding, LinkageType, Operator, TypeNature,
5+
UserTypeDeclaration,
56
};
67
use plc_ast::provider::IdProvider;
78
use plc_source::source_location::{SourceLocation, SourceLocationFactory};
@@ -1252,8 +1253,7 @@ fn pointer_and_in_out_pointer_should_not_conflict() {
12521253
&DataTypeInformation::Pointer {
12531254
name: "__main_x".to_string(),
12541255
inner_type_name: "INT".to_string(),
1255-
auto_deref: false,
1256-
is_reference_to: false,
1256+
auto_deref: None,
12571257
}
12581258
);
12591259

@@ -1264,8 +1264,7 @@ fn pointer_and_in_out_pointer_should_not_conflict() {
12641264
&DataTypeInformation::Pointer {
12651265
name: "__auto_pointer_to_INT".to_string(),
12661266
inner_type_name: "INT".to_string(),
1267-
auto_deref: true,
1268-
is_reference_to: false,
1267+
auto_deref: Some(AutoDerefType::Default),
12691268
}
12701269
);
12711270
}
@@ -1304,8 +1303,7 @@ fn pointer_and_in_out_pointer_should_not_conflict_2() {
13041303
&DataTypeInformation::Pointer {
13051304
name: "__main_x".to_string(),
13061305
inner_type_name: "INT".to_string(),
1307-
auto_deref: false,
1308-
is_reference_to: false,
1306+
auto_deref: None,
13091307
}
13101308
);
13111309

@@ -1316,8 +1314,7 @@ fn pointer_and_in_out_pointer_should_not_conflict_2() {
13161314
&DataTypeInformation::Pointer {
13171315
name: "__auto_pointer_to_INT".to_string(),
13181316
inner_type_name: "INT".to_string(),
1319-
auto_deref: true,
1320-
is_reference_to: false,
1317+
auto_deref: Some(AutoDerefType::Default),
13211318
}
13221319
);
13231320
}

src/index/tests/snapshots/rusty__index__tests__index_tests__pre_processing_generates_inline_pointer_to_pointer-2.snap

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ UserTypeDeclaration {
1010
referenced_type: DataTypeReference {
1111
referenced_type: "__foo_inline_pointer_",
1212
},
13-
auto_deref: false,
14-
is_reference_to: false,
13+
auto_deref: None,
1514
},
1615
initializer: None,
1716
scope: Some(

0 commit comments

Comments
 (0)