[flang] Implementation of semantic checks C1135, C1167, and C1168

These constraints state that CYCLE and EXIT statements should not leave DO
CONCURRENT, CRITICAL, or CHANGE TEAM constructs.

I added checking code to check-do.cc and removed some superseded code from
check-do.cc and semantics.cc.  The new code uses the construct stack
implemented in my previous pull request.

I also added a new test -- dosemantics11.f90 and modified the tests
dosemantics10.f90, doconcurrent05.f90, and doconcurrent06.f90 to adapt to
the new error messages.  I converted these latter two tests to use
test_error.sh since they only reported errors.

Original-commit: flang-compiler/f18@b0bea7da64
Reviewed-on: https://github.com/flang-compiler/f18/pull/756
Tree-same-pre-rewrite: false
This commit is contained in:
Pete Steinfeld 2019-09-22 10:01:03 -07:00
parent 505b214493
commit ab12314514
9 changed files with 538 additions and 87 deletions

View File

@ -28,12 +28,34 @@ namespace Fortran::semantics {
using namespace parser::literals;
static NamePtr GetNamePtr(const std::optional<parser::Name> &name) {
if (name.has_value()) {
return &(name.value());
} else {
return nullptr;
}
}
template<typename A> static NamePtr GetConstructName(const A &a) {
return GetNamePtr(std::get<0>(std::get<0>(a.t).statement.t));
}
static NamePtr GetConstructName(const parser::BlockConstruct &blockConstruct) {
return GetNamePtr(
std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t)
.statement.v);
}
template<typename A> static NamePtr GetStmtName(const A &a) {
return GetNamePtr(std::get<0>(a.t));
}
// 11.1.7.5 - enforce semantics constraints on a DO CONCURRENT loop body
class DoConcurrentBodyEnforce {
public:
DoConcurrentBodyEnforce(SemanticsContext &context) : context_{context} {}
std::set<parser::Label> labels() { return labels_; }
std::set<parser::CharBlock> names() { return names_; }
std::set<SourceName> names() { return names_; }
template<typename T> bool Pre(const T &) { return true; }
template<typename T> void Post(const T &) {}
template<typename T> bool Pre(const parser::Statement<T> &statement) {
@ -45,53 +67,53 @@ public:
}
// C1167
bool Pre(const parser::WhereConstructStmt &s) {
addName(std::get<std::optional<parser::Name>>(s.t));
bool Pre(const parser::WhereConstruct &s) {
addName(GetConstructName(s));
return true;
}
bool Pre(const parser::ForallConstructStmt &s) {
addName(std::get<std::optional<parser::Name>>(s.t));
bool Pre(const parser::ForallConstruct &s) {
addName(GetConstructName(s));
return true;
}
bool Pre(const parser::ChangeTeamStmt &s) {
addName(std::get<std::optional<parser::Name>>(s.t));
bool Pre(const parser::ChangeTeamConstruct &s) {
addName(GetConstructName(s));
return true;
}
bool Pre(const parser::CriticalStmt &s) {
addName(std::get<std::optional<parser::Name>>(s.t));
bool Pre(const parser::CriticalConstruct &s) {
addName(GetConstructName(s));
return true;
}
bool Pre(const parser::LabelDoStmt &s) {
addName(std::get<std::optional<parser::Name>>(s.t));
addName(GetStmtName(s));
return true;
}
bool Pre(const parser::NonLabelDoStmt &s) {
addName(std::get<std::optional<parser::Name>>(s.t));
addName(GetStmtName(s));
return true;
}
bool Pre(const parser::IfThenStmt &s) {
addName(std::get<std::optional<parser::Name>>(s.t));
addName(GetStmtName(s));
return true;
}
bool Pre(const parser::SelectCaseStmt &s) {
addName(std::get<std::optional<parser::Name>>(s.t));
addName(GetStmtName(s));
return true;
}
bool Pre(const parser::SelectRankStmt &s) {
addName(std::get<0>(s.t));
addName(GetStmtName(s));
return true;
}
bool Pre(const parser::SelectTypeStmt &s) {
addName(std::get<0>(s.t));
addName(GetStmtName(s));
return true;
}
@ -213,9 +235,9 @@ private:
return false;
}
void addName(const std::optional<parser::Name> &nm) {
if (nm.has_value()) {
names_.insert(nm.value().source);
void addName(NamePtr nm) {
if (nm) {
names_.insert(nm->source);
}
}
@ -238,15 +260,8 @@ public:
return true;
}
bool Pre(const parser::DoConstruct &) {
++do_depth_;
return true;
}
template<typename T> void Post(const T &) {}
// C1138: branch from within a DO CONCURRENT shall not target outside loop
void Post(const parser::ExitStmt &exitStmt) { checkName(exitStmt.v); }
void Post(const parser::GotoStmt &gotoStmt) { checkLabelUse(gotoStmt.v); }
void Post(const parser::ComputedGotoStmt &computedGotoStmt) {
for (auto &i : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
@ -277,22 +292,6 @@ public:
void Post(const parser::ErrLabel &errLabel) { checkLabelUse(errLabel.v); }
void Post(const parser::EndLabel &endLabel) { checkLabelUse(endLabel.v); }
void Post(const parser::EorLabel &eorLabel) { checkLabelUse(eorLabel.v); }
void Post(const parser::DoConstruct &) { --do_depth_; }
void checkName(const std::optional<parser::Name> &nm) {
if (!nm.has_value()) {
if (do_depth_ == 0) {
context_.Say(currentStatementSourcePosition_,
"exit from DO CONCURRENT construct (%s)"_err_en_US,
doConcurrentSourcePosition_);
}
// nesting of named constructs is assumed to have been previously checked
// by the name/label resolution pass
} else if (names_.find(nm.value().source) == names_.end()) {
context_.Say(currentStatementSourcePosition_,
"exit from DO CONCURRENT construct (%s) to construct with name '%s'"_err_en_US,
doConcurrentSourcePosition_, nm.value().source);
}
}
void checkLabelUse(const parser::Label &labelUsed) {
if (labels_.find(labelUsed) == labels_.end()) {
@ -305,7 +304,6 @@ private:
SemanticsContext &context_;
std::set<parser::Label> labels_;
std::set<parser::CharBlock> names_;
int do_depth_{0};
parser::CharBlock currentStatementSourcePosition_{nullptr};
parser::CharBlock doConcurrentSourcePosition_{nullptr};
}; // class DoConcurrentLabelEnforce
@ -647,20 +645,131 @@ void DoChecker::Leave(const parser::DoConstruct &x) {
doContext.Check(x);
}
// C1134
void DoChecker::Enter(const parser::CycleStmt &) {
if (!context_.InsideDoConstruct()) {
context_.Say(
*context_.location(), "CYCLE must be within a DO construct"_err_en_US);
// Return the (possibly null) name of the ConstructNode
static NamePtr MaybeGetNodeName(const ConstructNode &construct) {
return std::visit(
[&](const auto &x) { return GetConstructName(*x); }, construct);
}
template<typename A> static parser::CharBlock GetConstructPosition(const A &a) {
return std::get<0>(a.t).source;
}
static parser::CharBlock GetNodePosition(const ConstructNode &construct) {
return std::visit(
[&](const auto &x) { return GetConstructPosition(*x); }, construct);
}
void DoChecker::SayBadLeave(const char *stmtChecked,
const char *enclosingStmtType, const ConstructNode &construct) const {
context_
.Say(*context_.location(), "%s must not leave a %s statement"_err_en_US,
stmtChecked, enclosingStmtType)
.Attach(GetNodePosition(construct), "The construct that was left"_en_US);
}
static parser::DoConstruct const *MaybeGetDoConstruct(
const ConstructNode &construct) {
if (const auto doNode{std::get_if<const parser::DoConstruct *>(&construct)}) {
return *doNode;
}
return nullptr;
}
// Check that CYCLE and EXIT statements do not cause flow of control to
// leave DO CONCURRENT, CRITICAL, or CHANGE TEAM constructs.
void DoChecker::CheckForBadLeave(
const char *stmtName, const ConstructNode &construct) const {
// C1135 and C1167
if (const auto doConstructPtr{MaybeGetDoConstruct(construct)}) {
if (doConstructPtr->IsDoConcurrent()) {
SayBadLeave(stmtName, "DO CONCURRENT", construct);
}
return;
}
// C1135 and C1168
if (const auto criticalConstructPtr{
std::get_if<const parser::CriticalConstruct *>(&construct)}) {
SayBadLeave(stmtName, "CRITICAL", construct);
return;
}
if (const auto changeTeamConstructPtr{
std::get_if<const parser::ChangeTeamConstruct *>(&construct)}) {
SayBadLeave(stmtName, "CHANGE TEAM", construct);
return;
}
}
// C1166
void DoChecker::Enter(const parser::ExitStmt &) {
if (!context_.InsideDoConstruct()) {
context_.Say(
*context_.location(), "EXIT must be within a DO construct"_err_en_US);
static bool ConstructIsDoConcurrent(const ConstructNode &construct) {
parser::DoConstruct const *doConstruct{MaybeGetDoConstruct(construct)};
return doConstruct && doConstruct->IsDoConcurrent();
}
static bool isExitStmt(const char *stmtType) {
return std::strncmp("EXIT", stmtType, 4) == 0;
}
static bool StmtMatchesConstruct(NamePtr stmtName, const char *stmtType,
NamePtr constructName, const ConstructNode &construct) {
bool inDoConstruct{MaybeGetDoConstruct(construct) != nullptr};
if (stmtName == nullptr) {
if (inDoConstruct) {
return true; // Unlabeled statements match all DO constructs
} else {
return false; // Unlabeled statements match no non-DO constructs
}
} else if (!constructName) {
return false; // name on CYCLE/EXIT, but not on the construct
} else if (constructName->source == stmtName->source) {
if (isExitStmt(stmtType)) {
return true; // EXIT name matches any construct name
} else {
if (inDoConstruct) {
return true; // CYCLE name matches only DO construct name
}
}
}
return false;
}
// C1167 Can't EXIT from a DO CONCURRENT
void DoChecker::CheckDoConcurrentExit(
const char *stmtType, const ConstructNode &construct) const {
if (isExitStmt(stmtType) && ConstructIsDoConcurrent(construct)) {
SayBadLeave("EXIT", "DO CONCURRENT", construct);
}
}
// Check nesting violations for a CYCLE or EXIT statement Loop up the nesting
// levels looking for a construct that matches the CYCLE or EXIT statment. At
// every construct, check for a violation. If we find a match without finding
// a violation, the check is complete.
void DoChecker::CheckNesting(const char *stmtType, NamePtr stmtName) const {
const ConstructStack &stack{context_.constructStack()};
for (ConstructStack::const_reverse_iterator riter = stack.crbegin();
riter != stack.crend(); ++riter) {
const ConstructNode &construct{*riter};
NamePtr constructName{MaybeGetNodeName(construct)};
if (StmtMatchesConstruct(stmtName, stmtType, constructName, construct)) {
CheckDoConcurrentExit(stmtType, construct);
return; // We got a match, so we're finished checking
}
CheckForBadLeave(stmtType, construct);
}
// We haven't found a match in the enclosing constructs
context_.Say(*context_.location(),
"No matching construct for %s statement"_err_en_US, stmtType);
}
// C1135
void DoChecker::Enter(const parser::CycleStmt &cycleStmt) {
CheckNesting("CYCLE", GetNamePtr(cycleStmt.v));
}
// C1167 and C1168
void DoChecker::Enter(const parser::ExitStmt &exitStmt) {
CheckNesting("EXIT", GetNamePtr(exitStmt.v));
}
} // namespace Fortran::semantics

View File

@ -25,6 +25,8 @@ struct ExitStmt;
namespace Fortran::semantics {
using NamePtr = parser::Name const *;
class DoChecker : public virtual BaseChecker {
public:
explicit DoChecker(SemanticsContext &context) : context_{context} {}
@ -34,6 +36,12 @@ public:
private:
SemanticsContext &context_;
void SayBadLeave(const char *stmtChecked, const char *enclosingStmt,
const ConstructNode &) const;
void CheckDoConcurrentExit(const char *s, const ConstructNode &) const;
void CheckForBadLeave(const char *, const ConstructNode &) const;
void CheckNesting(const char *, NamePtr) const;
};
}
#endif // FORTRAN_SEMANTICS_CHECK_DO_H_

View File

@ -206,15 +206,6 @@ void SemanticsContext::PopConstruct() {
constructStack_.pop_back();
}
bool SemanticsContext::InsideDoConstruct() const {
for (const ConstructNode construct : constructStack_) {
if (std::holds_alternative<const parser::DoConstruct *>(construct)) {
return true;
}
}
return false;
}
bool Semantics::Perform() {
return ValidateLabels(context_, program_) &&
parser::CanonicalizeDo(program_) && // force line break

View File

@ -36,9 +36,9 @@ struct AssociateConstruct;
struct BlockConstruct;
struct CaseConstruct;
struct DoConstruct;
struct CriticalConstruct;
struct ChangeTeamConstruct;
struct ForAllConstruct;
struct CriticalConstruct;
struct ForallConstruct;
struct IfConstruct;
struct SelectRankConstruct;
struct SelectTypeConstruct;
@ -52,7 +52,7 @@ class Symbol;
using ConstructNode = std::variant<const parser::AssociateConstruct *,
const parser::BlockConstruct *, const parser::CaseConstruct *,
const parser::ChangeTeamConstruct *, const parser::CriticalConstruct *,
const parser::DoConstruct *, const parser::ForAllConstruct *,
const parser::DoConstruct *, const parser::ForallConstruct *,
const parser::IfConstruct *, const parser::SelectRankConstruct *,
const parser::SelectTypeConstruct *, const parser::WhereConstruct *>;
using ConstructStack = std::vector<ConstructNode>;
@ -144,7 +144,6 @@ public:
constructStack_.emplace_back(&node);
}
void PopConstruct();
bool InsideDoConstruct() const;
private:
const common::IntrinsicTypeDefaultKinds &defaultKinds_;

View File

@ -137,16 +137,17 @@ set(ERROR_TESTS
allocate12.f90
allocate13.f90
doconcurrent01.f90
dosemantics05.f90
dosemantics06.f90
dosemantics01.f90
dosemantics02.f90
dosemantics03.f90
dosemantics04.f90
dosemantics05.f90
dosemantics06.f90
dosemantics07.f90
dosemantics08.f90
dosemantics09.f90
dosemantics10.f90
dosemantics11.f90
expr-errors01.f90
null01.f90
omp-clause-validity01.f90

View File

@ -11,14 +11,9 @@
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
! CHECK: exit from DO CONCURRENT construct \\(mydoc: do concurrent\\(j=1:n\\)\\) to construct with name 'mydoc'
! CHECK: exit from DO CONCURRENT construct \\(mydoc: do concurrent\\(j=1:n\\)\\)
! CHECK: exit from DO CONCURRENT construct \\(mydoc: do concurrent\\(j=1:n\\)\\) to construct with name 'mytest3'
! CHECK: exit from DO CONCURRENT construct \\(do concurrent\\(k=1:n\\)\\)
! CHECK: exit from DO CONCURRENT construct \\(do concurrent\\(k=1:n\\)\\) to construct with name 'mytest4'
! CHECK: exit from DO CONCURRENT construct \\(mydoc: do concurrent\\(j=1:n\\)\\) to construct with name 'mytest4'
!
! C1167 -- An exit-stmt shall not appear within a DO CONCURRENT construct if
! it belongs to that construct or an outer construct.
subroutine do_concurrent_test1(n)
implicit none
@ -26,6 +21,7 @@ subroutine do_concurrent_test1(n)
integer :: j,k
mydoc: do concurrent(j=1:n)
mydo: do k=1,n
!ERROR: EXIT must not leave a DO CONCURRENT statement
if (k==5) exit mydoc
if (j==10) exit mydo
end do mydo
@ -36,6 +32,7 @@ subroutine do_concurrent_test2(n)
implicit none
integer :: j,k,n
mydoc: do concurrent(j=1:n)
!ERROR: EXIT must not leave a DO CONCURRENT statement
if (k==5) exit
end do mydoc
end subroutine do_concurrent_test2
@ -46,6 +43,7 @@ subroutine do_concurrent_test3(n)
mytest3: if (n>0) then
mydoc: do concurrent(j=1:n)
do k=1,n
!ERROR: EXIT must not leave a DO CONCURRENT statement
if (j==10) exit mytest3
end do
end do mydoc
@ -58,7 +56,10 @@ subroutine do_concurrent_test4(n)
mytest4: if (n>0) then
mydoc: do concurrent(j=1:n)
do concurrent(k=1:n)
!ERROR: EXIT must not leave a DO CONCURRENT statement
if (k==5) exit
!ERROR: EXIT must not leave a DO CONCURRENT statement
!ERROR: EXIT must not leave a DO CONCURRENT statement
if (j==10) exit mytest4
end do
end do mydoc

View File

@ -11,14 +11,9 @@
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
! CHECK: exit from DO CONCURRENT construct \\(nc5: do concurrent\\(i5=1:n\\)\\) to construct with name 'mytest1'
! CHECK: exit from DO CONCURRENT construct \\(nc3: do concurrent\\(i3=1:n\\)\\) to construct with name 'mytest1'
! CHECK: exit from DO CONCURRENT construct \\(nc1: do concurrent\\(i1=1:n\\)\\) to construct with name 'mytest1'
! CHECK: exit from DO CONCURRENT construct \\(nc5: do concurrent\\(i5=1:n\\)\\) to construct with name 'nc3'
! CHECK: exit from DO CONCURRENT construct \\(nc3: do concurrent\\(i3=1:n\\)\\) to construct with name 'nc3'
! CHECK: exit from DO CONCURRENT construct \\(nc3: do concurrent\\(i3=1:n\\)\\) to construct with name 'nc2'
!
! C1167 -- An exit-stmt shall not appear within a DO CONCURRENT construct if
! it belongs to that construct or an outer construct.
subroutine do_concurrent_test1(n)
implicit none
@ -30,6 +25,9 @@ subroutine do_concurrent_test1(n)
nc4: do i4=1,n
nc5: do concurrent(i5=1:n)
nc6: do i6=1,n
!ERROR: EXIT must not leave a DO CONCURRENT statement
!ERROR: EXIT must not leave a DO CONCURRENT statement
!ERROR: EXIT must not leave a DO CONCURRENT statement
if (i6==10) exit mytest1
end do nc6
end do nc5
@ -50,6 +48,8 @@ subroutine do_concurrent_test2(n)
nc4: do i4=1,n
nc5: do concurrent(i5=1:n)
nc6: do i6=1,n
!ERROR: EXIT must not leave a DO CONCURRENT statement
!ERROR: EXIT must not leave a DO CONCURRENT statement
if (i6==10) exit nc3
end do nc6
end do nc5
@ -67,6 +67,7 @@ subroutine do_concurrent_test3(n)
nc1: do concurrent(i1=1:n)
nc2: do i2=1,n
nc3: do concurrent(i3=1:n)
!ERROR: EXIT must not leave a DO CONCURRENT statement
if (i3==4) exit nc2
nc4: do i4=1,n
nc5: do concurrent(i5=1:n)

View File

@ -36,16 +36,16 @@ subroutine s1()
cycle
end do outer
!ERROR: CYCLE must be within a DO construct
!ERROR: No matching construct for CYCLE statement
cycle
!ERROR: EXIT must be within a DO construct
!ERROR: No matching construct for EXIT statement
exit
!ERROR: CYCLE must be within a DO construct
!ERROR: No matching construct for CYCLE statement
if(.true.) cycle
!ERROR: EXIT must be within a DO construct
!ERROR: No matching construct for EXIT statement
if(.true.) exit
end subroutine s1

View File

@ -0,0 +1,341 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!
! C1135 A cycle-stmt shall not appear within a CHANGE TEAM, CRITICAL, or DO
! CONCURRENT construct if it belongs to an outer construct.
!
! C1167 -- An exit-stmt shall not appear within a DO CONCURRENT construct if
! it belongs to that construct or an outer construct.
!
! C1168 -- An exit-stmt shall not appear within a CHANGE TEAM or CRITICAL
! construct if it belongs to an outer construct.
subroutine s1()
!ERROR: No matching construct for CYCLE statement
cycle
end subroutine s1
subroutine s2()
!ERROR: No matching construct for EXIT statement
exit
end subroutine s2
subroutine s3()
level0: block
!ERROR: No matching construct for CYCLE statement
cycle level0
end block level0
end subroutine s3
subroutine s4()
level0: do i = 1, 10
level1: do concurrent (j = 1:20)
!ERROR: CYCLE must not leave a DO CONCURRENT statement
cycle level0
end do level1
end do level0
end subroutine s4
subroutine s5()
level0: do i = 1, 10
level1: do concurrent (j = 1:20)
!ERROR: EXIT must not leave a DO CONCURRENT statement
exit level0
end do level1
end do level0
end subroutine s5
subroutine s6()
level0: do i = 1, 10
level1: critical
!ERROR: CYCLE must not leave a CRITICAL statement
cycle level0
end critical level1
end do level0
end subroutine s6
subroutine s7()
level0: do i = 1, 10
level1: critical
!ERROR: EXIT must not leave a CRITICAL statement
exit level0
end critical level1
end do level0
end subroutine s7
subroutine s8()
use :: iso_fortran_env
type(team_type) team_var
level0: do i = 1, 10
level1: change team(team_var)
!ERROR: CYCLE must not leave a CHANGE TEAM statement
cycle level0
end team level1
end do level0
end subroutine s8
subroutine s9()
use :: iso_fortran_env
type(team_type) team_var
level0: do i = 1, 10
level1: change team(team_var)
!ERROR: EXIT must not leave a CHANGE TEAM statement
exit level0
end team level1
end do level0
end subroutine s9
subroutine s10(table)
! A complex, but all legal example
integer :: table(..)
type point
real :: x, y
end type point
type, extends(point) :: color_point
integer :: color
end type color_point
type(point), target :: target_var
class(point), pointer :: p_or_c
p_or_c => target_var
level0: do i = 1, 10
level1: associate (avar => ivar)
level2: block
level3: select case (l)
case default
print*, "default"
case (1)
level4: if (.true.) then
level5: select rank(table)
rank default
level6: select type ( a => p_or_c )
type is ( point )
cycle level0
end select level6
end select level5
end if level4
end select level3
end block level2
end associate level1
end do level0
end subroutine s10
subroutine s11(table)
! A complex, but all legal example with a CYCLE statement
integer :: table(..)
type point
real :: x, y
end type point
type, extends(point) :: color_point
integer :: color
end type color_point
type(point), target :: target_var
class(point), pointer :: p_or_c
p_or_c => target_var
level0: do i = 1, 10
level1: associate (avar => ivar)
level2: block
level3: select case (l)
case default
print*, "default"
case (1)
level4: if (.true.) then
level5: select rank(table)
rank default
level6: select type ( a => p_or_c )
type is ( point )
cycle level0
end select level6
end select level5
end if level4
end select level3
end block level2
end associate level1
end do level0
end subroutine s11
subroutine s12(table)
! A complex, but all legal example with an EXIT statement
integer :: table(..)
type point
real :: x, y
end type point
type, extends(point) :: color_point
integer :: color
end type color_point
type(point), target :: target_var
class(point), pointer :: p_or_c
p_or_c => target_var
level0: do i = 1, 10
level1: associate (avar => ivar)
level2: block
level3: select case (l)
case default
print*, "default"
case (1)
level4: if (.true.) then
level5: select rank(table)
rank default
level6: select type ( a => p_or_c )
type is ( point )
exit level0
end select level6
end select level5
end if level4
end select level3
end block level2
end associate level1
end do level0
end subroutine s12
subroutine s13(table)
! Similar example without construct names
integer :: table(..)
type point
real :: x, y
end type point
type, extends(point) :: color_point
integer :: color
end type color_point
type(point), target :: target_var
class(point), pointer :: p_or_c
p_or_c => target_var
do i = 1, 10
associate (avar => ivar)
block
select case (l)
case default
print*, "default"
case (1)
if (.true.) then
select rank(table)
rank default
select type ( a => p_or_c )
type is ( point )
cycle
end select
end select
end if
end select
end block
end associate
end do
end subroutine s13
subroutine s14(table)
integer :: table(..)
type point
real :: x, y
end type point
type, extends(point) :: color_point
integer :: color
end type color_point
type(point), target :: target_var
class(point), pointer :: p_or_c
p_or_c => target_var
do i = 1, 10
associate (avar => ivar)
block
critical
select case (l)
case default
print*, "default"
case (1)
if (.true.) then
select rank(table)
rank default
select type ( a => p_or_c )
type is ( point )
!ERROR: CYCLE must not leave a CRITICAL statement
cycle
!ERROR: EXIT must not leave a CRITICAL statement
exit
end select
end select
end if
end select
end critical
end block
end associate
end do
end subroutine s14
subroutine s15(table)
! Illegal EXIT to an intermediated construct
integer :: table(..)
type point
real :: x, y
end type point
type, extends(point) :: color_point
integer :: color
end type color_point
type(point), target :: target_var
class(point), pointer :: p_or_c
p_or_c => target_var
level0: do i = 1, 10
level1: associate (avar => ivar)
level2: block
level3: select case (l)
case default
print*, "default"
case (1)
level4: if (.true.) then
level5: critical
level6: select rank(table)
rank default
level7: select type ( a => p_or_c )
type is ( point )
exit level6
!ERROR: EXIT must not leave a CRITICAL statement
exit level4
end select level7
end select level6
end critical level5
end if level4
end select level3
end block level2
end associate level1
end do level0
end subroutine s15