-
Notifications
You must be signed in to change notification settings - Fork 66
fix: extended function block body calls #1545
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
base: master
Are you sure you want to change the base?
Conversation
89ff513 to
e5139c0
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1545 +/- ##
==========================================
+ Coverage 94.03% 95.02% +0.99%
==========================================
Files 174 175 +1
Lines 58611 56078 -2533
==========================================
- Hits 55112 53287 -1825
+ Misses 3499 2791 -708 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
mhasel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly nits (sorry) and a couple of questions/suggestions. Overall, I like it. Nice! 🚀
| let Some(StatementAnnotation::Argument { position, depth, pou, .. }) = | ||
| self.annotations.get_hint(assignment_statement) | ||
| else { | ||
| panic!() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we return an error here instead of an explicit panic? If we are confident this code-path can't be reached, we might wanna change this to unreachable instead
|
|
||
| /// The name of the POU where the parameter is declared | ||
| /// See also [`StatementAnnotation::Argument::pou`]. | ||
| pou_parameter: &'b str, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it weren't for the doc-comment, I'd have no idea that pou_parameter refers to the name of the POU. How do you feel about something like declaring_pou or source_pou?
| let function_name = param_context.function_name; | ||
| let index = param_context.index; | ||
| let parameter_struct = param_context.parameter_struct; | ||
| // let function_name = param_context.function_name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missed a comment
| .map(|value| i32_type.const_int(value as u64, false)) | ||
| .collect::<Vec<_>>(); | ||
|
|
||
| self.llvm.builder.build_gep(context.parameter_struct, &gep_index, "").unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you tell me why we aren't using build_in_bounds_gep here? In your PR message you mention
GEPing directly, i.e. not inbound
An inbound GEP is still a direct GEP, just with additional safety guarantees and it allows LLVM to optimize more aggressively. Inheritance hierarchy access is well defined and guaranteed to be within bounds of the allocated struct, or am I missing something?
I have changed this line to use build_in_bounds_gep and the LIT tests still pass locally
| error[E037]: Invalid assignment: cannot assign 'SEL with wrong parameter names a := SEL(WRONG := sel, IN0 := a, IN1 := b); a := SEL(G := sel, INVALID := a,' to 'ARRAY[0..5] OF INT' | ||
| ┌─ <internal>:14:13 | ||
| │ | ||
| 14 │ arr2 := MOVE(SOURCE := arr); | ||
| │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid assignment: cannot assign 'SEL with wrong parameter names a := SEL(WRONG := sel, IN0 := a, IN1 := b); a := SEL(G := sel, INVALID := a,' to 'ARRAY[0..5] OF INT' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the reported location is off here. Also, I'd expect to get two such errors, since we have two call statements to SEL with different invalid argument names in the test
| // XXX: This is super specific and currently only being used in the context of argument annotations. Do we | ||
| // want to move this to the resolver? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering how many specialized public methods we have on our Index, I'd be tempted to say one more won't matter, but I think I'd prefer this to be a private method/function in the resolver.
| self.get_pou_members(pou_name).iter().any(|member| member.is_parameter() && member.is_variadic()) | ||
| /// Returns all declared parameters (INPUT, OUTPUT or IN_OUT) of a POU, including those defined in parent | ||
| /// POUs. The returned list is ordered by the inheritance chain, from base to derived. | ||
| pub fn get_declared_parameters(&self, pou: &str) -> Vec<&VariableIndexEntry> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really nitty, but I think the doc comment and method name here are semantically inconsistent. Parameters from parent POUs are not declared but inherited. Here I'd almost like this to be two methods, one get_declared_parameters that only fetches the parameters declared by the given POU and another one (e.g. get_available_parameters) which can include the inherited params aswell.
Alternatively, if there is no need for a method that only returns the declared parameters, rename this one to get_available_parameters?
| let parameters = if let Some(parameters) = parameters_stmt { | ||
| self.visit_statement(ctx, parameters); | ||
| flatten_expression_list(parameters) | ||
| pub fn annotate_arguments(&mut self, operator: &AstNode, arguments_node: &AstNode, ctx: &VisitorContext) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yet another nitpick: we aren't just annotating the arguments here, but also the operator (in the tail call to update_generic_call_statement), so I once again feel like the original method name was more fitting
| /// The position of the parameter within its declared POU. | ||
| position: usize, | ||
|
|
||
| /// The depth between where the arguments parameter is declared versus where it is being called from. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitty gritty: what do you think about
/// Inheritance depth from parameter declaration to calling context.?
| var2 : DINT; | ||
| END_VAR | ||
| foo(7, 8, output1 => var1, output2 => var2); | ||
| foo(input1 := 7, input2 := 8, output1 => var1, output2 => var2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought this change in the test looked like a masked regression for a second here, but this exact example doesn't compile on master using the regular pipeline, but apparently it works in the test. Another case of where our test results don't match reality?
Fixes an issue where calling the body of an extended function block would fail. Essentially the compiler today does not resolve inherited variables in a function block call statement context, which results in an error in the validation.
This commit correctly annotates them and changes the codegen logic to access inherited variables by GEPing directly, i.e. not inbound, onto the underlying struct. This is achieved by updating the
StatementAnnotation:Argumentvariant slightly to also include metadata to find out at which depth and position a given argument is declared in. The depth is then used to GEP n times to fetch the underlying struct.Note: This is currently only supported for named arguments. For positional arguments the logic is untouched.
A minimal reproducible example would be
which should return