Skip to content

Commit 01be035

Browse files
authored
Merge pull request #606 from 99NIMI/604-var_input-and-var_in_out-assignment-error
#604 recursive function calls
2 parents c4d61ce + e520524 commit 01be035

4 files changed

+220
-1
lines changed

src/resolver.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub struct VisitorContext<'s> {
5757
/// Inside the left hand side of an assignment is in the context of the call's POU
5858
/// `foo(a := a)` actually means: `foo(foo.a := POU.a)`
5959
call: Option<&'s str>,
60+
/// true if visiting a call statement
61+
is_call: bool,
6062

6163
/// true if the expression passed a constant-variable on the way
6264
/// e.g. true for `x` if x is declared in a constant block
@@ -74,6 +76,7 @@ impl<'s> VisitorContext<'s> {
7476
pou: self.pou,
7577
qualifier: Some(qualifier),
7678
call: self.call,
79+
is_call: self.is_call,
7780
constant: false,
7881
in_body: self.in_body,
7982
}
@@ -85,6 +88,7 @@ impl<'s> VisitorContext<'s> {
8588
pou: Some(pou),
8689
qualifier: self.qualifier.clone(),
8790
call: self.call,
91+
is_call: self.is_call,
8892
constant: false,
8993
in_body: self.in_body,
9094
}
@@ -96,6 +100,19 @@ impl<'s> VisitorContext<'s> {
96100
pou: self.pou,
97101
qualifier: self.qualifier.clone(),
98102
call: Some(lhs_pou),
103+
is_call: self.is_call,
104+
constant: false,
105+
in_body: self.in_body,
106+
}
107+
}
108+
109+
/// returns a copy of the current context and changes the `is_call` to true
110+
fn set_is_call(&self) -> VisitorContext<'s> {
111+
VisitorContext {
112+
pou: self.pou,
113+
qualifier: self.qualifier.clone(),
114+
call: self.call,
115+
is_call: true,
99116
constant: false,
100117
in_body: self.in_body,
101118
}
@@ -107,6 +124,7 @@ impl<'s> VisitorContext<'s> {
107124
pou: self.pou,
108125
qualifier: self.qualifier.clone(),
109126
call: self.call,
127+
is_call: self.is_call,
110128
constant: self.constant,
111129
in_body: true,
112130
}
@@ -443,6 +461,7 @@ impl<'i> TypeAnnotator<'i> {
443461
pou: None,
444462
qualifier: None,
445463
call: None,
464+
is_call: false,
446465
constant: false,
447466
in_body: false,
448467
};
@@ -843,6 +862,7 @@ impl<'i> TypeAnnotator<'i> {
843862
self.visit_statement(
844863
&VisitorContext {
845864
call: None,
865+
is_call: false,
846866
constant: false,
847867
pou: ctx.pou,
848868
qualifier: None,
@@ -1088,6 +1108,18 @@ impl<'i> TypeAnnotator<'i> {
10881108
// ... first look at POU-local variables
10891109
self.index
10901110
.find_member(qualifier, name)
1111+
.and_then(|m| {
1112+
// #604 needed for recursive function calls
1113+
// if we are in a call statement and the member name equals the pou name
1114+
// we are in a recursive function call -> FUNCTION foo : INT foo(); END_FUNCTION
1115+
if ctx.is_call & (m.get_name() == qualifier) {
1116+
// return `None` because this would be foo.foo pointing to the function return
1117+
// we need the POU
1118+
None
1119+
} else {
1120+
Some(m)
1121+
}
1122+
})
10911123
.map(|v| to_variable_annotation(v, self.index, ctx.constant))
10921124
.or_else(|| {
10931125
// ... then check if we're in a method and we're referencing
@@ -1254,7 +1286,9 @@ impl<'i> TypeAnnotator<'i> {
12541286
} else {
12551287
unreachable!("Always a call statement");
12561288
};
1257-
self.visit_statement(ctx, operator);
1289+
// #604 needed for recursive function calls
1290+
let ctx = ctx.set_is_call();
1291+
self.visit_statement(&ctx, operator);
12581292
let operator_qualifier = self.get_call_name(operator);
12591293
let ctx = ctx.with_call(operator_qualifier.as_str());
12601294
let parameters = if let Some(parameters) = parameters_stmt {

src/resolver/tests/resolve_expressions_tests.rs

+62
Original file line numberDiff line numberDiff line change
@@ -3136,6 +3136,68 @@ fn and_statement_of_dints_results_in_dint() {
31363136
}
31373137

31383138
#[test]
3139+
fn resolve_recursive_function_call() {
3140+
//GIVEN
3141+
let (unit, index) = index(
3142+
"
3143+
FUNCTION foo : DINT
3144+
VAR_INPUT
3145+
input1 : DINT;
3146+
END_VAR
3147+
VAR_IN_OUT
3148+
inout1 : DINT;
3149+
END_VAR
3150+
VAR_OUTPUT
3151+
output1 : DINT;
3152+
END_VAR
3153+
VAR
3154+
var1, var2, var3 : DINT;
3155+
END_VAR
3156+
foo(input1 := var1, inout1 := var2, output1 => var3, );
3157+
foo := var1;
3158+
END_FUNCTION
3159+
",
3160+
);
3161+
3162+
//WHEN the AST is annotated
3163+
let (annotations, _) = TypeAnnotator::visit_unit(&index, &unit);
3164+
let type_map = annotations.type_map;
3165+
let annotated_types = format!("{:#?}", type_map);
3166+
3167+
insta::assert_snapshot!(annotated_types);
3168+
}
3169+
3170+
#[test]
3171+
fn resolve_recursive_program_call() {
3172+
//GIVEN
3173+
let (unit, index) = index(
3174+
"
3175+
PROGRAM mainProg
3176+
VAR_INPUT
3177+
input1 : DINT;
3178+
END_VAR
3179+
VAR_IN_OUT
3180+
inout1 : DINT;
3181+
END_VAR
3182+
VAR_OUTPUT
3183+
output1 : DINT;
3184+
END_VAR
3185+
VAR
3186+
var1, var2, var3 : DINT;
3187+
END_VAR
3188+
mainProg(input1 := var1, inout1 := var2, output1 => var3, );
3189+
END_PROGRAM
3190+
",
3191+
);
3192+
3193+
//WHEN the AST is annotated
3194+
let (annotations, _) = TypeAnnotator::visit_unit(&index, &unit);
3195+
let type_map = annotations.type_map;
3196+
let annotated_types = format!("{:#?}", type_map);
3197+
3198+
insta::assert_snapshot!(annotated_types);
3199+
}
3200+
31393201
fn function_block_initialization_test() {
31403202
let (unit, mut index) = index(
31413203
"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
source: src/resolver/tests/resolve_expressions_tests.rs
3+
assertion_line: 3167
4+
expression: annotated_types
5+
---
6+
{
7+
1: Function {
8+
return_type: "DINT",
9+
qualified_name: "foo",
10+
call_name: None,
11+
},
12+
3: Variable {
13+
resulting_type: "DINT",
14+
qualified_name: "foo.var1",
15+
constant: false,
16+
variable_type: Local,
17+
is_auto_deref: false,
18+
},
19+
2: Variable {
20+
resulting_type: "DINT",
21+
qualified_name: "foo.input1",
22+
constant: false,
23+
variable_type: Input,
24+
is_auto_deref: false,
25+
},
26+
6: Variable {
27+
resulting_type: "DINT",
28+
qualified_name: "foo.var2",
29+
constant: false,
30+
variable_type: Local,
31+
is_auto_deref: false,
32+
},
33+
5: Variable {
34+
resulting_type: "DINT",
35+
qualified_name: "foo.inout1",
36+
constant: false,
37+
variable_type: InOut,
38+
is_auto_deref: true,
39+
},
40+
8: Variable {
41+
resulting_type: "DINT",
42+
qualified_name: "foo.output1",
43+
constant: false,
44+
variable_type: Output,
45+
is_auto_deref: true,
46+
},
47+
9: Variable {
48+
resulting_type: "DINT",
49+
qualified_name: "foo.var3",
50+
constant: false,
51+
variable_type: Local,
52+
is_auto_deref: false,
53+
},
54+
12: Value {
55+
resulting_type: "DINT",
56+
},
57+
14: Variable {
58+
resulting_type: "DINT",
59+
qualified_name: "foo.var1",
60+
constant: false,
61+
variable_type: Local,
62+
is_auto_deref: false,
63+
},
64+
13: Variable {
65+
resulting_type: "DINT",
66+
qualified_name: "foo.foo",
67+
constant: false,
68+
variable_type: Return,
69+
is_auto_deref: false,
70+
},
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
source: src/resolver/tests/resolve_expressions_tests.rs
3+
assertion_line: 3198
4+
expression: annotated_types
5+
---
6+
{
7+
1: Program {
8+
qualified_name: "mainProg",
9+
},
10+
3: Variable {
11+
resulting_type: "DINT",
12+
qualified_name: "mainProg.var1",
13+
constant: false,
14+
variable_type: Local,
15+
is_auto_deref: false,
16+
},
17+
2: Variable {
18+
resulting_type: "DINT",
19+
qualified_name: "mainProg.input1",
20+
constant: false,
21+
variable_type: Input,
22+
is_auto_deref: false,
23+
},
24+
6: Variable {
25+
resulting_type: "DINT",
26+
qualified_name: "mainProg.var2",
27+
constant: false,
28+
variable_type: Local,
29+
is_auto_deref: false,
30+
},
31+
5: Variable {
32+
resulting_type: "DINT",
33+
qualified_name: "mainProg.inout1",
34+
constant: false,
35+
variable_type: InOut,
36+
is_auto_deref: true,
37+
},
38+
8: Variable {
39+
resulting_type: "DINT",
40+
qualified_name: "mainProg.output1",
41+
constant: false,
42+
variable_type: Output,
43+
is_auto_deref: false,
44+
},
45+
9: Variable {
46+
resulting_type: "DINT",
47+
qualified_name: "mainProg.var3",
48+
constant: false,
49+
variable_type: Local,
50+
is_auto_deref: false,
51+
},
52+
}

0 commit comments

Comments
 (0)