Skip to content

Explanation of how to implement a semantic check #939

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 1 commit into from
Feb 3, 2020
Merged

Conversation

psteinfeld
Copy link
Collaborator

This is the story of implementing semantic checks for passing DO
variables to functions with dummy arguments with INTENT(OUT) or
INTENT(INOUT). I wrote it as an example of how to make changes
to the compiler for a newcomer.

Copy link
Collaborator

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

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

Thanks @psteinfeld for writing this document based on your experience of implementing the do index variable redefinition check. Should be helpful for new people writing semantic checks.

I like the title of this PR (Explanation of how to implement a semantic check) but the filename probably has to be modified to look something like the title of the PR.

What information would you have missed if you had the visitor at the ActualArgSpec level? Is it just the source location?

Have noted some minor nits inline.

shall neither be redefined nor become undefined while the DO construct is active.
```
One of the ways that DO variables might be redefined is if they are passed to
functions with dummy arguments whose ```INTENT``` is ```INTENT(IN)``` or
Copy link
Collaborator

Choose a reason for hiding this comment

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

INTENT(OUT)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You can use single backquotes (grave accents) for inline code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks!

Comment on lines 142 to 143
```INTENT``` of the dummy argument associated with the actual argument from the
a function called ```dummyIntent()``` in the class
Copy link
Collaborator

Choose a reason for hiding this comment

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

typo "the a function called"

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks!

nodes. I would look at each of these nodes to determine the ```INTENT``` of
the associated dummy argument.

This combination of the traveral framework and ```dummyIntent()``` would give
Copy link
Collaborator

Choose a reason for hiding this comment

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

typo traversal

@psteinfeld
Copy link
Collaborator Author

Thanks @psteinfeld for writing this document based on your experience of implementing the do index variable redefinition check. Should be helpful for new people writing semantic checks.

I like the title of this PR (Explanation of how to implement a semantic check) but the filename probably has to be modified to look something like the title of the PR.

What information would you have missed if you had the visitor at the ActualArgSpec level? Is it just the source location?

Have noted some minor nits inline.

Thanks for reviewing this, @kiranchandramohan.

I wasn't sure what to call the document. How about "ImplementingASemanticCheck.md"?

I probably should have discussed this in the document, but at one point I had a visitor that triggered on the type parser::ActualArg. As you mention, this node does not contain source information. But more importantly, the expression it contains does not contain any evaluate::ActualArgument nodes. These are needed to discover the intent of the dummy arguments. To get at these evaluate::ActualArgument nodes, I had to start with the expression that contained the function call.

@kiranchandramohan
Copy link
Collaborator

Thanks @psteinfeld for writing this document based on your experience of implementing the do index variable redefinition check. Should be helpful for new people writing semantic checks.
I like the title of this PR (Explanation of how to implement a semantic check) but the filename probably has to be modified to look something like the title of the PR.
What information would you have missed if you had the visitor at the ActualArgSpec level? Is it just the source location?
Have noted some minor nits inline.

Thanks for reviewing this, @kiranchandramohan.

I wasn't sure what to call the document. How about "ImplementingASemanticCheck.md"?

I probably should have discussed this in the document, but at one point I had a visitor that triggered on the type parser::ActualArg. As you mention, this node does not contain source information.

Since you have written in a conversational style it will be good to have this info that you went the parser::ActualArg path and found that the information that you need is not available. A word about why source information is there in some nodes and not there in others would also be useful.

Also, some information about how an expression models a function call might be useful. In the case of subroutines, you have a call statement so it is obvious there.

But more importantly, the expression it contains does not contain any evaluate::ActualArgument nodes. These are needed to discover the intent of the dummy arguments. To get at these evaluate::ActualArgument nodes, I had to start with the expression that contained the function call.

Are you hinting here that the Subroutine's arguments have the Actual Argument info but a Function's arguments do not have?
A simplistic first thought would be that once you get the argument list of the Function/Subroutine call the processing should be the same.

@kiranchandramohan
Copy link
Collaborator

A bit of time spent on where a semantic check should be added would also be good. That is probably the first thing that hits a first-time contributor. Some high-level guidance like if it is an Execution Construct it should be here or if it is a Declaration Construct it should be there. When should a person write his own checker and when not. Should a check be added during name or label resolution? Also in some cases, it is obvious like If it is something related to a do loop, OpenMP or co-array.

Feel free to ignore if this is outside the scope of this document.

@psteinfeld
Copy link
Collaborator Author

Since you have written in a conversational style it will be good to have this info that you went the parser::ActualArg path and found that the information that you need is not available. A word about why source information is there in some nodes and not there in others would also be useful.

Thanks, Kiran.

The very fact that you're wondering about this means that I should have included it in the document. I'll do that now. I plan to explain the difference between this case (for function calls), and the case for subroutine calls along with their relevant differences that affected the implementation. Please review again after I add this information.

Also, some information about how an expression models a function call might be useful. In the case of subroutines, you have a call statement so it is obvious there.

Will do.

But more importantly, the expression it contains does not contain any evaluate::ActualArgument nodes. These are needed to discover the intent of the dummy arguments. To get at these evaluate::ActualArgument nodes, I had to start with the expression that contained the function call.

Are you hinting here that the Subroutine's arguments have the Actual Argument info but a Function's arguments do not have?
A simplistic first thought would be that once you get the argument list of the Function/Subroutine call the processing should be the same.

I plan to explain this further in the addition I describe above, but here's a preview. In both cases, the most difficult piece of information I need is the INTENT of the dummy argument. This is accessible through the evaluate::ActualArgument. In the case of a subroutine call, the evaluate::ActualArgument nodes are accessible through the typedCall field of a parser::CallStmt:

struct CallStmt {
  WRAPPER_CLASS_BOILERPLATE(CallStmt, Call);
  mutable std::unique_ptr<evaluate::ProcedureRef,
      common::Deleter<evaluate::ProcedureRef>>
      typedCall;  // filled by semantics
};

The evaluate::ProcedureRef``` contains a list of evaluate::ActualArgument` nodes.

For a function call, though, there is no way to map from a parse tree node to an evaluate::ProcedureRef node. But I can take advantage of the fact that all function calls are embedded in expressions, either directly or indirectly. Once I have an expression, I can traverse it to gather all of the evaluate::ActualArgument nodes as described in the document.

For a parser::CallStmt node, there's no way (other than through the typedCall field) to get to the evaluate::ActualArgument node.

@psteinfeld
Copy link
Collaborator Author

A bit of time spent on where a semantic check should be added would also be good. That is probably the first thing that hits a first-time contributor. Some high-level guidance like if it is an Execution Construct it should be here or if it is a Declaration Construct it should be there. When should a person write his own checker and when not. Should a check be added during name or label resolution? Also in some cases, it is obvious like If it is something related to a do loop, OpenMP or co-array.

Feel free to ignore if this is outside the scope of this document.

I can certainly add a little more information about how I decided where to implement this check. I think it is out of the scope of this document to cover the topic generally.

Copy link
Collaborator

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

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

LGTM.

@psteinfeld psteinfeld added the enhancement New feature or request label Jan 29, 2020

I also used this program to produce a parse tree for the program using the command:
```bash
./bin/f18 -fdebug-dump-parse-tree -fparse-only testfun.f90
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe just f18 instead of ./bin/f18?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes. I think your choice is better.

Comment on lines 114 to 116
checking is implemented in the files .../lib/semantics/semantics.[cc,h].
Here's a fragment of the declaration of the framework's parse tree visitor from
.../lib/semantics/semantics.cc:
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you could simply put lib/semantics/semantics.[cpp,h] & lib/semantics/semantics.cpp. (They are in code format)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch. Given the rearrangement of the header files, I think it's best not to mention them at all. I'll remove them and change all of the references to ".cc" to ".cpp".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually, it looks like there are several header files I need to mention. I'll fix up their path names.

node.

Since my semantic check was focused on DO CONCURRENT statements, I added it to
the file .../lib/semantics/check-do.cc where most of the semantic checking for
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here: lib/semantics/check-do.cpp.


## Taking advantage of prior work
When implementing a similar check for SUBROUTINE calls, I created a utility
functions in .../lib/semantics/semantics.[cc.h] to emit messages if
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here & the rest of this doc. (note that .cc was changed to .cpp too)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yep.

`evaluate::ActualArgument` nodes. The code that I planned to model it on
was the existing infrastructure that collected all of the `semantics::Symbol` nodes from an
`evaluate::Expr`. I found this implementation in
.../lib/evaluate/tools.cc:
Copy link
Collaborator

Choose a reason for hiding this comment

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

I strongly recommend getting rid of all the .../.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Will do.

@@ -0,0 +1,813 @@
<!--===- documentation/FrontEndTutorial.md
Copy link
Collaborator

Choose a reason for hiding this comment

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

haven't decided the final name yet?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch!

And thanks for reading through this and providing feedback.

They hold all of the information I needed.

So my plan was to start with the `parser::Expr` node and extract its
associated `evaluate::Expr` field. I would then traverse the
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think a little bit explanation between parser::Expr and evaluate::Expr would be better?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point. I was certainly confused when I first learned that there were two different types named Expr in the compiler. I'll add a short explanation.

This is the story of implementing semantic checks for passing DO
variables to functions with dummy arguments with INTENT(OUT) or
INTENT(INOUT).
@psteinfeld psteinfeld merged commit bedca14 into master Feb 3, 2020
@psteinfeld psteinfeld deleted the ps-dev-story branch February 3, 2020 17:28
Comment on lines +223 to +232
The second `Expr` type is in the `evaluate` namespace. The `evaluate`
namespace contains many types associated with semantic checking of expressions.
`evaluate::Expr` is defined in the file `include/flang/evaluate/expression.h`.
It represents an expression after it has undergone semantic checking and
contains information that is only available after semantic analysis. This
information includes the Fortran type of the expression, whether it's a
reference to a function, whether it's an actual argument, etc. After an
expression has undergone semantic analysis, the field `typedExpr` in the
`parser::Expr` node is filled in with a pointer to the analyzed expression in
`evaluate::Expr`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Very nice, thanks!

Copy link
Collaborator

@klausler klausler Feb 3, 2020

Choose a reason for hiding this comment

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

The evaluate::Expr<T> template classes, which are parameterized over the various types of Fortran, constitute a suite of strongly-typed representations of valid Fortran expressions of type T that have been fully elaborated with conversion operations and subjected to constant folding.

swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request Apr 9, 2020
This is the story of implementing semantic checks for passing DO
variables to functions with dummy arguments with INTENT(OUT) or
INTENT(INOUT).

Original-commit: flang-compiler/f18@889f909
Reviewed-on: flang-compiler/f18#939
swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request Apr 9, 2020
…/ps-dev-story

Explanation of how to implement a semantic check

Original-commit: flang-compiler/f18@bedca14
Reviewed-on: flang-compiler/f18#939
mem-frob pushed a commit to draperlaboratory/hope-llvm-project that referenced this pull request Oct 7, 2022
This is the story of implementing semantic checks for passing DO
variables to functions with dummy arguments with INTENT(OUT) or
INTENT(INOUT).

Original-commit: flang-compiler/f18@889f909
Reviewed-on: flang-compiler/f18#939
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants