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
Changes to add an executable construct stack #686
Conversation
Could you do a quick test of the following code:
|
lib/semantics/semantics.h
Outdated
@@ -120,6 +121,12 @@ class SemanticsContext { | |||
const Scope &FindScope(parser::CharBlock) const; | |||
Scope &FindScope(parser::CharBlock); | |||
|
|||
const std::vector<const parser::ExecutableConstruct *> executables() const { |
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 would create a copy of executables_
if not for copy elision.
Question for other reviewers: As a general rule, do we depend on such optimizations or code for non existence of such optimizations ?
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.
Unless there is a reason to return a copy I think it should return a const reference.
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.
Yes, you can depend on RVO in C++17. That said, this routine either needs to add a &
or remove a const
.
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.
Thanks. I'll make this a reference.
lib/semantics/check-do.cc
Outdated
@@ -647,4 +647,21 @@ void DoChecker::Leave(const parser::DoConstruct &x) { | |||
doContext.Check(x); | |||
} | |||
|
|||
void DoChecker::CheckLoneStmt(const char *stmtString) const { |
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.
CheckLoStmt
doesn't really say what it's doing. How about CheckInsideDO()
or something like that?
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.
Good suggestion. I'll make that change.
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.
Note that, in my latest version of the code, I added the function InsideDoConstruct()
to the classSemanticsContext
which supersedes this code.
lib/semantics/semantics.h
Outdated
@@ -20,6 +20,7 @@ | |||
#include "../evaluate/intrinsics.h" | |||
#include "../parser/features.h" | |||
#include "../parser/message.h" | |||
#include "../parser/parse-tree.h" |
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.
You only need a forward declaration of ExecutableConstruct
, not all of parse-tree.h.
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.
Thanks. I'll change that.
Brian, I'm not sure how to construct a complete test. Here's the complete source code for a test file that I constructed: subroutine s1() It compiles without error. |
lib/semantics/semantics.cc
Outdated
} | ||
|
||
void SemanticsContext::PopExecutable() { | ||
CHECK(executables_.size() > 0); |
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.
!empty()
is what you're trying to say, I think.
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.
Thanks. I'll change this.
lib/semantics/check-do.cc
Outdated
@@ -647,4 +647,21 @@ void DoChecker::Leave(const parser::DoConstruct &x) { | |||
doContext.Check(x); | |||
} | |||
|
|||
void DoChecker::CheckLoneStmt(const char *stmtString) const { | |||
for (auto &executable : context_.executables()) { |
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.
Use const auto &
and make sure that executables()
returns a const &
.
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.
Thanks. I'll make those changes.
lib/semantics/semantics.h
Outdated
@@ -136,6 +143,7 @@ class SemanticsContext { | |||
evaluate::FoldingContext foldingContext_{defaultKinds_}; | |||
|
|||
bool CheckError(bool); | |||
std::vector<const parser::ExecutableConstruct *> executables_; |
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 vector represents a stack, but neither its name nor its type mention "stack".
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.
Good suggestion. I'll change the name of the data member and accessor to executableStack
.
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.
How about the type?
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 the use case is only to lookup top of the stack then std::stack
should suffice. However, if one needs to peel the layers for more information then (either now or in the future), implementing a stack using std::vector
would be better, just like it is right now with ompContext_
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'm pretty sure that we'll need the more general capabilities of std::vector
as we add more semantic checks.
lib/semantics/check-do.h
Outdated
|
||
private: | ||
void CheckLoneStmt(const char *const) const; |
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.
The second const
here doesn't do anything.
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.
Thanks for spotting this. I'll fix it.
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.
Note that this function has been eliminated in the latest version of the code.
1a06fe2
to
d699c47
Compare
lib/semantics/semantics.cc
Outdated
@@ -168,6 +298,24 @@ Scope &SemanticsContext::FindScope(parser::CharBlock source) { | |||
} | |||
} | |||
|
|||
void SemanticsContext::PushConstruct(const ConstructNode &construct) { |
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'm actually surprised that C++ implicitly converts the pointers at the various call sites into a ConstructNode
that can be passed by reference.
This function might be more clear if it used move semantics.
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 want to make sure that I understand your suggestion. Are you saying that, for example, the code for the Pre
function for a DoConstruct
should look like this?
bool Pre(const parser::DoConstruct &doConstruct) {
ConstructNode doNode{std::move(&doConstruct)};
context_.PushConstruct(doNode);
Enter(doConstruct);
return true;
}
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.
No, that doesn't work. I meant something like context_.PushConstruct(ConstructNode{doConstruct});
in Pre()
with the definition of PushConstruct
accepting a ConstructNode &&construct
argument.
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.
Thanks, Peter. I'll change the Pre()
functions.
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.
Take a look at the version in PR #708
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.
The template suggestion doesn't have anything to do with PushConstruct
or constructStack_
being templated. This PR has roughly 10 Pre()
and Post()
routines manually defined, functionally they all do exactly the same thing. Instead you could use a templated routine each for Pre()
and Post()
.
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.
Please disregard my earlier comment. I misunderstood things.
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.
Thanks, Tim. I'll change my code to match Brian's.
} | ||
|
||
void SemanticsContext::PopConstruct() { | ||
CHECK(!constructStack_.empty()); |
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 CHECK
may not add any value; the pop_back()
will crash if the stack is empty.
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.
CHECK
has the advantage that the message coming from the compiler contains that name of the file and source line of the failing check. When pop_back()
fails, the compiler just produces a segmentation fault with no indication of the location of the failure.
} | ||
|
||
bool SemanticsContext::InsideDoConstruct() const { | ||
for (const ConstructNode construct : constructStack_) { |
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.
Rather than looping over the entries in the stack and looking for DO
loops, you could maintain a count of active DO
loops.
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.
Actually, that was my original implementation. When I reviewed that with Tim, he suggested using a stack. The stack is heavier weight, but it has the promise of being useful in other kinds of semantic analysis.
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.
You can still maintain a counter.
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.
Or maybe a cumulative ancestor set for all ancestor types, not just do loops. That is, for top level constructs, this set is empty. For nested constructs, the set is the parent's set plus the parent's type. That way, queries of the form "this construct has at least one kind X ancestor" can be answered without searching. And queries of the form "this construct is immediately nested in a kind X parent' can also be directly answered by looking one up the stack. Those two queries probably cover a large majority of all potential queries.
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.
Val's suggestion is more efficient compared to the stack approach. However one would still have to traverse up to get a count of active DO
loops. So a cumulative ancestor set + depth record for DO
(and any other construct that needs its depth saved) seems like the best idea.
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.
Thanks for the suggestions. One of the checks I plan to implement (although not in this pull request) is to semantic checking for CYCLE do-construct-name
. For that case, I'll need to walk up the currently active set of nested DO
constructs looking for one with do-construct-name
. For just checking a lone CYCLE
statement, a counter is sufficient and more efficient. Since I don't think I can get rid of the stack, and since I believe that it's sufficient for the cases of CYCLE
statements with and without do-construct-name
s, I plan to just go with a stack. I'll keep the notion of counters in mind as I add the rest of the checks, though.
d699c47
to
30ab8e6
Compare
I changed the implementation of |
lib/semantics/semantics.cc
Outdated
context_.set_location(std::nullopt); | ||
} | ||
|
||
bool Pre(const parser::AssociateConstruct &associateConstruct) { |
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.
It would be great if you could combine all of these repetitive Pre
overloads. It's hard to see the differences between them, if any, and it's hard to tell which ones are paired up with Post
overloads and which are not.
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.
Specifically, you could augment the default Pre()
and Post()
member function templates above (ca. line #60) with something like
if constexpr (common::HasMember<const N *, ConstructNode>) {
context_.PushConstruct(node);
}
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.
Peter, you're a genius! I wanted to combine those calls, but I couldn't figure out how to do it.
I added a stack of ExecutableConstruct's to SemanticsContext along with functions to push and pop constructs. I added code to the SemanticsVisitor to use these new functions. I also added functions Pre and Post functions for UnlabeledStatement's so that we could isolate the source positions for statements embedded in "if" statements to improve error messages. I also added code to check-do.[h,cc] to use this new infrastructure to check for CYCLE and EXIT statements that are not contained within DO constructs along with a test.
The most significant change is that I replaced the stack of ExecutableConstruct's with a stack composed of ConstructNode's, each of which is a variant of the constructs that made up the type ExecutableConstruct. This change allows the nodes of the stack to be extended to include the types needed for OMP semantic checking. I also extended the existing test to include some correct DO loops with CYCLE and EXIT statements to test the code more completely.
I changed the interface of ```PushConstruct()``` to take an rvalue reference as its only parameter and made the construction of the ```ConstructNode```s explicit in all of the ```Pre()``` functions for the various construct types.
creation of the ```ConstructNode``` into ```PushConstruct()```.
…s that call `PushConstruct()``` and ```PopConstruct()``` following a genius suggestion from Peter.
30ab8e6
to
be2a03e
Compare
I combined all of the functions that called |
I added a stack of ExecutableConstruct's to SemanticsContext along with functions to push and pop constructs. I added code to the SemanticsVisitor to use these new functions. I also added functions Pre and Post functions for UnlabeledStatement's so that we could isolate the source positions for statements embedded in "if" statements to improve error messages. I also added code to check-do.[h,cc] to use this new infrastructure to check for CYCLE and EXIT statements that are not contained within DO constructs along with a test. Original-commit: flang-compiler/f18@b8370bd Reviewed-on: flang-compiler/f18#686 Tree-same-pre-rewrite: false
The most significant change is that I replaced the stack of ExecutableConstruct's with a stack composed of ConstructNode's, each of which is a variant of the constructs that made up the type ExecutableConstruct. This change allows the nodes of the stack to be extended to include the types needed for OMP semantic checking. I also extended the existing test to include some correct DO loops with CYCLE and EXIT statements to test the code more completely. Original-commit: flang-compiler/f18@d26f34e Reviewed-on: flang-compiler/f18#686 Tree-same-pre-rewrite: false
I changed the interface of ```PushConstruct()``` to take an rvalue reference as its only parameter and made the construction of the ```ConstructNode```s explicit in all of the ```Pre()``` functions for the various construct types. Original-commit: flang-compiler/f18@f8be813 Reviewed-on: flang-compiler/f18#686 Tree-same-pre-rewrite: false
…he implicit creation of the ```ConstructNode``` into ```PushConstruct()```. Original-commit: flang-compiler/f18@3984566 Reviewed-on: flang-compiler/f18#686 Tree-same-pre-rewrite: false
…functions that call `PushConstruct()``` and ```PopConstruct()``` following a genius suggestion from Peter. Original-commit: flang-compiler/f18@be2a03e Reviewed-on: flang-compiler/f18#686
…/ps-stmt-stack Changes to add an executable construct stack Original-commit: flang-compiler/f18@2fc28c7 Reviewed-on: flang-compiler/f18#686
I added a stack of ExecutableConstruct's to SemanticsContext along with functions to push and pop constructs. I added code to the SemanticsVisitor to use these new functions. I also added functions Pre and Post functions for UnlabeledStatement's so that we could isolate the source positions for statements embedded in "if" statements to improve error messages. I also added code to check-do.[h,cc] to use this new infrastructure to check for CYCLE and EXIT statements that are not contained within DO constructs along with a test. Original-commit: flang-compiler/f18@b8370bd Reviewed-on: flang-compiler/f18#686 Tree-same-pre-rewrite: false
The most significant change is that I replaced the stack of ExecutableConstruct's with a stack composed of ConstructNode's, each of which is a variant of the constructs that made up the type ExecutableConstruct. This change allows the nodes of the stack to be extended to include the types needed for OMP semantic checking. I also extended the existing test to include some correct DO loops with CYCLE and EXIT statements to test the code more completely. Original-commit: flang-compiler/f18@d26f34e Reviewed-on: flang-compiler/f18#686 Tree-same-pre-rewrite: false
I changed the interface of ```PushConstruct()``` to take an rvalue reference as its only parameter and made the construction of the ```ConstructNode```s explicit in all of the ```Pre()``` functions for the various construct types. Original-commit: flang-compiler/f18@f8be813 Reviewed-on: flang-compiler/f18#686 Tree-same-pre-rewrite: false
…he implicit creation of the ```ConstructNode``` into ```PushConstruct()```. Original-commit: flang-compiler/f18@3984566 Reviewed-on: flang-compiler/f18#686 Tree-same-pre-rewrite: false
…functions that call `PushConstruct()``` and ```PopConstruct()``` following a genius suggestion from Peter. Original-commit: flang-compiler/f18@be2a03e Reviewed-on: flang-compiler/f18#686
I added a stack of ExecutableConstruct's to SemanticsContext along with
functions to push and pop constructs. I added code to the SemanticsVisitor
to use these new functions. I also added functions Pre and Post functions
for UnlabeledStatement's so that we could isolate the source positions for
statements embedded in "if" statements to improve error messages.
I also added code to check-do.[h,cc] to use this new infrastructure to check
for CYCLE and EXIT statements that are not contained within DO constructs
along with a test.