2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Semantics/check-do-forall.cpp ---------------------------------===//
|
2018-09-18 08:19:27 +08:00
|
|
|
//
|
2019-12-21 04:52:07 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-09-18 08:19:27 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-09-18 08:19:27 +08:00
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
#include "check-do-forall.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Common/template.h"
|
|
|
|
#include "flang/Evaluate/call.h"
|
|
|
|
#include "flang/Evaluate/expression.h"
|
|
|
|
#include "flang/Evaluate/tools.h"
|
|
|
|
#include "flang/Parser/message.h"
|
|
|
|
#include "flang/Parser/parse-tree-visitor.h"
|
|
|
|
#include "flang/Parser/tools.h"
|
|
|
|
#include "flang/Semantics/attr.h"
|
|
|
|
#include "flang/Semantics/scope.h"
|
|
|
|
#include "flang/Semantics/semantics.h"
|
|
|
|
#include "flang/Semantics/symbol.h"
|
|
|
|
#include "flang/Semantics/tools.h"
|
|
|
|
#include "flang/Semantics/type.h"
|
2018-09-18 08:19:27 +08:00
|
|
|
|
2020-01-10 03:37:51 +08:00
|
|
|
namespace Fortran::evaluate {
|
|
|
|
using ActualArgumentRef = common::Reference<const ActualArgument>;
|
|
|
|
|
|
|
|
inline bool operator<(ActualArgumentRef x, ActualArgumentRef y) {
|
|
|
|
return &*x < &*y;
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::evaluate
|
2020-01-10 03:37:51 +08:00
|
|
|
|
2018-09-18 08:19:27 +08:00
|
|
|
namespace Fortran::semantics {
|
|
|
|
|
|
|
|
using namespace parser::literals;
|
|
|
|
|
2019-12-07 11:59:54 +08:00
|
|
|
using Bounds = parser::LoopControl::Bounds;
|
2020-02-19 09:14:24 +08:00
|
|
|
using IndexVarKind = SemanticsContext::IndexVarKind;
|
2019-12-07 11:59:54 +08:00
|
|
|
|
2020-02-19 09:14:24 +08:00
|
|
|
static const parser::ConcurrentHeader &GetConcurrentHeader(
|
2019-12-07 11:59:54 +08:00
|
|
|
const parser::LoopControl &loopControl) {
|
|
|
|
const auto &concurrent{
|
|
|
|
std::get<parser::LoopControl::Concurrent>(loopControl.u)};
|
2020-02-19 09:14:24 +08:00
|
|
|
return std::get<parser::ConcurrentHeader>(concurrent.t);
|
|
|
|
}
|
|
|
|
static const parser::ConcurrentHeader &GetConcurrentHeader(
|
|
|
|
const parser::ForallConstruct &construct) {
|
|
|
|
const auto &stmt{
|
|
|
|
std::get<parser::Statement<parser::ForallConstructStmt>>(construct.t)};
|
|
|
|
return std::get<common::Indirection<parser::ConcurrentHeader>>(
|
|
|
|
stmt.statement.t)
|
|
|
|
.value();
|
|
|
|
}
|
|
|
|
static const parser::ConcurrentHeader &GetConcurrentHeader(
|
|
|
|
const parser::ForallStmt &stmt) {
|
|
|
|
return std::get<common::Indirection<parser::ConcurrentHeader>>(stmt.t)
|
|
|
|
.value();
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T>
|
2020-02-19 09:14:24 +08:00
|
|
|
static const std::list<parser::ConcurrentControl> &GetControls(const T &x) {
|
|
|
|
return std::get<std::list<parser::ConcurrentControl>>(
|
|
|
|
GetConcurrentHeader(x).t);
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const Bounds &GetBounds(const parser::DoConstruct &doConstruct) {
|
|
|
|
auto &loopControl{doConstruct.GetLoopControl().value()};
|
|
|
|
return std::get<Bounds>(loopControl.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const parser::Name &GetDoVariable(
|
|
|
|
const parser::DoConstruct &doConstruct) {
|
|
|
|
const Bounds &bounds{GetBounds(doConstruct)};
|
|
|
|
return bounds.name.thing;
|
|
|
|
}
|
|
|
|
|
2019-09-24 11:46:45 +08:00
|
|
|
// Return the (possibly null) name of the construct
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename A>
|
2019-09-24 11:46:45 +08:00
|
|
|
static const parser::Name *MaybeGetConstructName(const A &a) {
|
2019-09-25 06:33:51 +08:00
|
|
|
return common::GetPtrFromOptional(std::get<0>(std::get<0>(a.t).statement.t));
|
2019-09-23 01:01:03 +08:00
|
|
|
}
|
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
static parser::MessageFixedText GetEnclosingDoMsg() {
|
|
|
|
return "Enclosing DO CONCURRENT statement"_en_US;
|
|
|
|
}
|
|
|
|
|
2019-09-24 11:46:45 +08:00
|
|
|
static const parser::Name *MaybeGetConstructName(
|
|
|
|
const parser::BlockConstruct &blockConstruct) {
|
2019-09-25 06:33:51 +08:00
|
|
|
return common::GetPtrFromOptional(
|
2019-09-23 01:01:03 +08:00
|
|
|
std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t)
|
|
|
|
.statement.v);
|
|
|
|
}
|
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
static void SayWithDo(SemanticsContext &context, parser::CharBlock stmtLocation,
|
|
|
|
parser::MessageFixedText &&message, parser::CharBlock doLocation) {
|
|
|
|
context.Say(stmtLocation, message).Attach(doLocation, GetEnclosingDoMsg());
|
2019-09-23 01:01:03 +08:00
|
|
|
}
|
|
|
|
|
2018-10-04 01:24:07 +08:00
|
|
|
// 11.1.7.5 - enforce semantics constraints on a DO CONCURRENT loop body
|
2019-08-15 03:57:09 +08:00
|
|
|
class DoConcurrentBodyEnforce {
|
2018-09-18 08:19:27 +08:00
|
|
|
public:
|
2019-10-12 05:39:33 +08:00
|
|
|
DoConcurrentBodyEnforce(
|
|
|
|
SemanticsContext &context, parser::CharBlock doConcurrentSourcePosition)
|
2020-03-29 12:00:16 +08:00
|
|
|
: context_{context}, doConcurrentSourcePosition_{
|
|
|
|
doConcurrentSourcePosition} {}
|
2018-10-10 05:18:16 +08:00
|
|
|
std::set<parser::Label> labels() { return labels_; }
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> bool Pre(const T &) { return true; }
|
|
|
|
template <typename T> void Post(const T &) {}
|
2019-10-12 05:39:33 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> bool Pre(const parser::Statement<T> &statement) {
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
currentStatementSourcePosition_ = statement.source;
|
|
|
|
if (statement.label.has_value()) {
|
|
|
|
labels_.insert(*statement.label);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> bool Pre(const parser::UnlabeledStatement<T> &stmt) {
|
2020-01-28 06:12:35 +08:00
|
|
|
currentStatementSourcePosition_ = stmt.source;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
// C1140 -- Can't deallocate a polymorphic entity in a DO CONCURRENT.
|
|
|
|
// Deallocation can be caused by exiting a block that declares an allocatable
|
|
|
|
// entity, assignment to an allocatable variable, or an actual DEALLOCATE
|
|
|
|
// statement
|
|
|
|
//
|
|
|
|
// Note also that the deallocation of a derived type entity might cause the
|
2020-01-28 06:12:35 +08:00
|
|
|
// invocation of an IMPURE final subroutine. (C1139)
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
//
|
|
|
|
|
2020-01-28 06:12:35 +08:00
|
|
|
// Only to be called for symbols with ObjectEntityDetails
|
|
|
|
static bool HasImpureFinal(const Symbol &symbol) {
|
|
|
|
if (const Symbol * root{GetAssociationRoot(symbol)}) {
|
|
|
|
CHECK(root->has<ObjectEntityDetails>());
|
|
|
|
if (const DeclTypeSpec * symType{root->GetType()}) {
|
|
|
|
if (const DerivedTypeSpec * derived{symType->AsDerived()}) {
|
|
|
|
return semantics::HasImpureFinal(*derived);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
// Predicate for deallocations caused by block exit and direct deallocation
|
|
|
|
static bool DeallocateAll(const Symbol &) { return true; }
|
|
|
|
|
|
|
|
// Predicate for deallocations caused by intrinsic assignment
|
|
|
|
static bool DeallocateNonCoarray(const Symbol &component) {
|
|
|
|
return !IsCoarray(component);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool WillDeallocatePolymorphic(const Symbol &entity,
|
|
|
|
const std::function<bool(const Symbol &)> &WillDeallocate) {
|
|
|
|
return WillDeallocate(entity) && IsPolymorphicAllocatable(entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is it possible that we will we deallocate a polymorphic entity or one
|
|
|
|
// of its components?
|
|
|
|
static bool MightDeallocatePolymorphic(const Symbol &entity,
|
|
|
|
const std::function<bool(const Symbol &)> &WillDeallocate) {
|
|
|
|
if (const Symbol * root{GetAssociationRoot(entity)}) {
|
|
|
|
// Check the entity itself, no coarray exception here
|
|
|
|
if (IsPolymorphicAllocatable(*root)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Check the components
|
|
|
|
if (const auto *details{root->detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (const DeclTypeSpec * entityType{details->type()}) {
|
|
|
|
if (const DerivedTypeSpec * derivedType{entityType->AsDerived()}) {
|
|
|
|
UltimateComponentIterator ultimates{*derivedType};
|
|
|
|
for (const auto &ultimate : ultimates) {
|
|
|
|
if (WillDeallocatePolymorphic(ultimate, WillDeallocate)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-01-28 06:12:35 +08:00
|
|
|
void SayDeallocateWithImpureFinal(const Symbol &entity, const char *reason) {
|
|
|
|
context_.SayWithDecl(entity, currentStatementSourcePosition_,
|
|
|
|
"Deallocation of an entity with an IMPURE FINAL procedure"
|
|
|
|
" caused by %s not allowed in DO CONCURRENT"_err_en_US,
|
|
|
|
reason);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SayDeallocateOfPolymorph(
|
|
|
|
parser::CharBlock location, const Symbol &entity, const char *reason) {
|
|
|
|
context_.SayWithDecl(entity, location,
|
|
|
|
"Deallocation of a polymorphic entity caused by %s"
|
|
|
|
" not allowed in DO CONCURRENT"_err_en_US,
|
|
|
|
reason);
|
|
|
|
}
|
|
|
|
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
// Deallocation caused by block exit
|
|
|
|
// Allocatable entities and all of their allocatable subcomponents will be
|
|
|
|
// deallocated. This test is different from the other two because it does
|
|
|
|
// not deallocate in cases where the entity itself is not allocatable but
|
|
|
|
// has allocatable polymorphic components
|
|
|
|
void Post(const parser::BlockConstruct &blockConstruct) {
|
|
|
|
const auto &endBlockStmt{
|
|
|
|
std::get<parser::Statement<parser::EndBlockStmt>>(blockConstruct.t)};
|
|
|
|
const Scope &blockScope{context_.FindScope(endBlockStmt.source)};
|
|
|
|
const Scope &doScope{context_.FindScope(doConcurrentSourcePosition_)};
|
|
|
|
if (DoesScopeContain(&doScope, blockScope)) {
|
2020-01-28 06:12:35 +08:00
|
|
|
const char *reason{"block exit"};
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
for (auto &pair : blockScope) {
|
2020-01-28 06:12:35 +08:00
|
|
|
const Symbol &entity{*pair.second};
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
if (IsAllocatable(entity) && !entity.attrs().test(Attr::SAVE) &&
|
|
|
|
MightDeallocatePolymorphic(entity, DeallocateAll)) {
|
2020-01-28 06:12:35 +08:00
|
|
|
SayDeallocateOfPolymorph(endBlockStmt.source, entity, reason);
|
|
|
|
}
|
|
|
|
if (HasImpureFinal(entity)) {
|
|
|
|
SayDeallocateWithImpureFinal(entity, reason);
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deallocation caused by assignment
|
|
|
|
// Note that this case does not cause deallocation of coarray components
|
|
|
|
void Post(const parser::AssignmentStmt &stmt) {
|
|
|
|
const auto &variable{std::get<parser::Variable>(stmt.t)};
|
|
|
|
if (const Symbol * entity{GetLastName(variable).symbol}) {
|
2020-01-28 06:12:35 +08:00
|
|
|
const char *reason{"assignment"};
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
if (MightDeallocatePolymorphic(*entity, DeallocateNonCoarray)) {
|
2020-01-28 06:12:35 +08:00
|
|
|
SayDeallocateOfPolymorph(variable.GetSource(), *entity, reason);
|
|
|
|
}
|
|
|
|
if (HasImpureFinal(*entity)) {
|
|
|
|
SayDeallocateWithImpureFinal(*entity, reason);
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deallocation from a DEALLOCATE statement
|
|
|
|
// This case is different because DEALLOCATE statements deallocate both
|
|
|
|
// ALLOCATABLE and POINTER entities
|
|
|
|
void Post(const parser::DeallocateStmt &stmt) {
|
|
|
|
const auto &allocateObjectList{
|
|
|
|
std::get<std::list<parser::AllocateObject>>(stmt.t)};
|
|
|
|
for (const auto &allocateObject : allocateObjectList) {
|
|
|
|
const parser::Name &name{GetLastName(allocateObject)};
|
2020-01-28 06:12:35 +08:00
|
|
|
const char *reason{"a DEALLOCATE statement"};
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
if (name.symbol) {
|
|
|
|
const Symbol &entity{*name.symbol};
|
|
|
|
const DeclTypeSpec *entityType{entity.GetType()};
|
2020-03-29 12:00:16 +08:00
|
|
|
if ((entityType && entityType->IsPolymorphic()) || // POINTER case
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
MightDeallocatePolymorphic(entity, DeallocateAll)) {
|
2020-01-28 06:12:35 +08:00
|
|
|
SayDeallocateOfPolymorph(
|
|
|
|
currentStatementSourcePosition_, entity, reason);
|
|
|
|
}
|
|
|
|
if (HasImpureFinal(entity)) {
|
|
|
|
SayDeallocateWithImpureFinal(entity, reason);
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// C1137 -- No image control statements in a DO CONCURRENT
|
|
|
|
void Post(const parser::ExecutableConstruct &construct) {
|
|
|
|
if (IsImageControlStmt(construct)) {
|
|
|
|
const parser::CharBlock statementLocation{
|
|
|
|
GetImageControlStmtLocation(construct)};
|
|
|
|
auto &msg{context_.Say(statementLocation,
|
|
|
|
"An image control statement is not allowed in DO"
|
|
|
|
" CONCURRENT"_err_en_US)};
|
|
|
|
if (auto coarrayMsg{GetImageControlStmtCoarrayMsg(construct)}) {
|
|
|
|
msg.Attach(statementLocation, *coarrayMsg);
|
|
|
|
}
|
|
|
|
msg.Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// C1136 -- No RETURN statements in a DO CONCURRENT
|
2018-09-18 08:19:27 +08:00
|
|
|
void Post(const parser::ReturnStmt &) {
|
2019-10-12 05:39:33 +08:00
|
|
|
context_
|
|
|
|
.Say(currentStatementSourcePosition_,
|
|
|
|
"RETURN is not allowed in DO CONCURRENT"_err_en_US)
|
|
|
|
.Attach(doConcurrentSourcePosition_, GetEnclosingDoMsg());
|
2018-09-18 08:19:27 +08:00
|
|
|
}
|
2019-06-05 06:14:34 +08:00
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// C1139: call to impure procedure and ...
|
2018-10-10 05:18:16 +08:00
|
|
|
// C1141: cannot call ieee_get_flag, ieee_[gs]et_halting_mode
|
2019-10-12 05:39:33 +08:00
|
|
|
// It's not necessary to check the ieee_get* procedures because they're
|
|
|
|
// not pure, and impure procedures are caught by checks for constraint C1139
|
2018-09-18 08:19:27 +08:00
|
|
|
void Post(const parser::ProcedureDesignator &procedureDesignator) {
|
2018-10-04 01:24:07 +08:00
|
|
|
if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
|
2019-08-09 06:54:49 +08:00
|
|
|
if (name->symbol && !IsPureProcedure(*name->symbol)) {
|
2019-10-12 05:39:33 +08:00
|
|
|
SayWithDo(context_, currentStatementSourcePosition_,
|
|
|
|
"Call to an impure procedure is not allowed in DO"
|
|
|
|
" CONCURRENT"_err_en_US,
|
|
|
|
doConcurrentSourcePosition_);
|
2018-10-10 05:18:16 +08:00
|
|
|
}
|
2018-10-10 06:33:15 +08:00
|
|
|
if (name->symbol && fromScope(*name->symbol, "ieee_exceptions"s)) {
|
2019-10-12 05:39:33 +08:00
|
|
|
if (name->source == "ieee_set_halting_mode") {
|
|
|
|
SayWithDo(context_, currentStatementSourcePosition_,
|
|
|
|
"IEEE_SET_HALTING_MODE is not allowed in DO "
|
|
|
|
"CONCURRENT"_err_en_US,
|
|
|
|
doConcurrentSourcePosition_);
|
2018-10-10 05:18:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-10-24 06:56:22 +08:00
|
|
|
// C1139: this a procedure component
|
2018-10-10 05:18:16 +08:00
|
|
|
auto &component{std::get<parser::ProcComponentRef>(procedureDesignator.u)
|
|
|
|
.v.thing.component};
|
2019-08-09 06:54:49 +08:00
|
|
|
if (component.symbol && !IsPureProcedure(*component.symbol)) {
|
2019-10-12 05:39:33 +08:00
|
|
|
SayWithDo(context_, currentStatementSourcePosition_,
|
|
|
|
"Call to an impure procedure component is not allowed"
|
|
|
|
" in DO CONCURRENT"_err_en_US,
|
|
|
|
doConcurrentSourcePosition_);
|
2018-09-18 08:19:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// 11.1.7.5, paragraph 5, no ADVANCE specifier in a DO CONCURRENT
|
2018-09-18 08:19:27 +08:00
|
|
|
void Post(const parser::IoControlSpec &ioControlSpec) {
|
2018-10-04 01:24:07 +08:00
|
|
|
if (auto *charExpr{
|
|
|
|
std::get_if<parser::IoControlSpec::CharExpr>(&ioControlSpec.u)}) {
|
2018-09-18 08:19:27 +08:00
|
|
|
if (std::get<parser::IoControlSpec::CharExpr::Kind>(charExpr->t) ==
|
|
|
|
parser::IoControlSpec::CharExpr::Kind::Advance) {
|
2019-10-12 05:39:33 +08:00
|
|
|
SayWithDo(context_, currentStatementSourcePosition_,
|
|
|
|
"ADVANCE specifier is not allowed in DO"
|
|
|
|
" CONCURRENT"_err_en_US,
|
|
|
|
doConcurrentSourcePosition_);
|
2018-09-18 08:19:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2019-10-12 05:39:33 +08:00
|
|
|
// Return the (possibly null) name of the statement
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename A>
|
|
|
|
static const parser::Name *MaybeGetStmtName(const A &a) {
|
2019-10-12 05:39:33 +08:00
|
|
|
return common::GetPtrFromOptional(std::get<0>(a.t));
|
2019-10-01 04:52:05 +08:00
|
|
|
}
|
|
|
|
|
2018-10-10 05:18:16 +08:00
|
|
|
bool fromScope(const Symbol &symbol, const std::string &moduleName) {
|
2018-10-10 07:20:01 +08:00
|
|
|
if (symbol.GetUltimate().owner().IsModule() &&
|
2019-08-21 21:29:11 +08:00
|
|
|
symbol.GetUltimate().owner().GetName().value().ToString() ==
|
|
|
|
moduleName) {
|
2018-10-10 07:20:01 +08:00
|
|
|
return true;
|
2018-10-10 05:18:16 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2019-06-05 06:14:34 +08:00
|
|
|
|
2018-10-10 05:18:16 +08:00
|
|
|
std::set<parser::Label> labels_;
|
2018-10-10 06:50:44 +08:00
|
|
|
parser::CharBlock currentStatementSourcePosition_;
|
2019-06-06 02:57:09 +08:00
|
|
|
SemanticsContext &context_;
|
2019-10-12 05:39:33 +08:00
|
|
|
parser::CharBlock doConcurrentSourcePosition_;
|
2020-03-29 12:00:16 +08:00
|
|
|
}; // class DoConcurrentBodyEnforce
|
2018-09-18 08:19:27 +08:00
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// Class for enforcing C1130 -- in a DO CONCURRENT with DEFAULT(NONE),
|
|
|
|
// variables from enclosing scopes must have their locality specified
|
2019-08-01 05:01:31 +08:00
|
|
|
class DoConcurrentVariableEnforce {
|
|
|
|
public:
|
|
|
|
DoConcurrentVariableEnforce(
|
|
|
|
SemanticsContext &context, parser::CharBlock doConcurrentSourcePosition)
|
2020-03-29 12:00:16 +08:00
|
|
|
: context_{context},
|
|
|
|
doConcurrentSourcePosition_{doConcurrentSourcePosition},
|
|
|
|
blockScope_{context.FindScope(doConcurrentSourcePosition_)} {}
|
2018-10-11 07:52:06 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> bool Pre(const T &) { return true; }
|
|
|
|
template <typename T> void Post(const T &) {}
|
2019-08-01 05:01:31 +08:00
|
|
|
|
|
|
|
// Check to see if the name is a variable from an enclosing scope
|
|
|
|
void Post(const parser::Name &name) {
|
|
|
|
if (const Symbol * symbol{name.symbol}) {
|
|
|
|
if (IsVariableName(*symbol)) {
|
|
|
|
const Scope &variableScope{symbol->owner()};
|
2019-08-06 04:36:01 +08:00
|
|
|
if (DoesScopeContain(&variableScope, blockScope_)) {
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
context_.SayWithDecl(*symbol, name.source,
|
|
|
|
"Variable '%s' from an enclosing scope referenced in DO "
|
|
|
|
"CONCURRENT with DEFAULT(NONE) must appear in a "
|
|
|
|
"locality-spec"_err_en_US,
|
|
|
|
symbol->name());
|
2019-08-01 05:01:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
SemanticsContext &context_;
|
|
|
|
parser::CharBlock doConcurrentSourcePosition_;
|
2019-08-06 04:36:01 +08:00
|
|
|
const Scope &blockScope_;
|
2020-03-29 12:00:16 +08:00
|
|
|
}; // class DoConcurrentVariableEnforce
|
2019-08-01 05:01:31 +08:00
|
|
|
|
2020-02-19 09:14:24 +08:00
|
|
|
// Find a DO or FORALL and enforce semantics checks on its body
|
2019-06-11 05:03:24 +08:00
|
|
|
class DoContext {
|
2018-09-18 08:19:27 +08:00
|
|
|
public:
|
2020-02-19 09:14:24 +08:00
|
|
|
DoContext(SemanticsContext &context, IndexVarKind kind)
|
2020-03-29 12:00:16 +08:00
|
|
|
: context_{context}, kind_{kind} {}
|
2019-03-06 08:52:50 +08:00
|
|
|
|
2019-12-07 11:59:54 +08:00
|
|
|
// Mark this DO construct as a point of definition for the DO variables
|
|
|
|
// or index-names it contains. If they're already defined, emit an error
|
|
|
|
// message. We need to remember both the variable and the source location of
|
|
|
|
// the variable in the DO construct so that we can remove it when we leave
|
|
|
|
// the DO construct and use its location in error messages.
|
|
|
|
void DefineDoVariables(const parser::DoConstruct &doConstruct) {
|
|
|
|
if (doConstruct.IsDoNormal()) {
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.ActivateIndexVar(GetDoVariable(doConstruct), IndexVarKind::DO);
|
2019-12-07 11:59:54 +08:00
|
|
|
} else if (doConstruct.IsDoConcurrent()) {
|
|
|
|
if (const auto &loopControl{doConstruct.GetLoopControl()}) {
|
2020-02-19 09:14:24 +08:00
|
|
|
ActivateIndexVars(GetControls(*loopControl));
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called at the end of a DO construct to deactivate the DO construct
|
|
|
|
void ResetDoVariables(const parser::DoConstruct &doConstruct) {
|
|
|
|
if (doConstruct.IsDoNormal()) {
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.DeactivateIndexVar(GetDoVariable(doConstruct));
|
2019-12-07 11:59:54 +08:00
|
|
|
} else if (doConstruct.IsDoConcurrent()) {
|
|
|
|
if (const auto &loopControl{doConstruct.GetLoopControl()}) {
|
2020-02-19 09:14:24 +08:00
|
|
|
DeactivateIndexVars(GetControls(*loopControl));
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-19 09:14:24 +08:00
|
|
|
void ActivateIndexVars(const std::list<parser::ConcurrentControl> &controls) {
|
|
|
|
for (const auto &control : controls) {
|
|
|
|
context_.ActivateIndexVar(std::get<parser::Name>(control.t), kind_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void DeactivateIndexVars(
|
|
|
|
const std::list<parser::ConcurrentControl> &controls) {
|
|
|
|
for (const auto &control : controls) {
|
|
|
|
context_.DeactivateIndexVar(std::get<parser::Name>(control.t));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-06 08:52:50 +08:00
|
|
|
void Check(const parser::DoConstruct &doConstruct) {
|
2019-06-05 06:14:34 +08:00
|
|
|
if (doConstruct.IsDoConcurrent()) {
|
|
|
|
CheckDoConcurrent(doConstruct);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (doConstruct.IsDoNormal()) {
|
|
|
|
CheckDoNormal(doConstruct);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// TODO: handle the other cases
|
|
|
|
}
|
|
|
|
|
2020-02-19 09:14:24 +08:00
|
|
|
void Check(const parser::ForallStmt &stmt) {
|
|
|
|
CheckConcurrentHeader(GetConcurrentHeader(stmt));
|
|
|
|
}
|
|
|
|
void Check(const parser::ForallConstruct &construct) {
|
|
|
|
CheckConcurrentHeader(GetConcurrentHeader(construct));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Check(const parser::ForallAssignmentStmt &stmt) {
|
|
|
|
const evaluate::Assignment *assignment{std::visit(
|
|
|
|
common::visitors{[&](const auto &x) { return GetAssignment(x); }},
|
|
|
|
stmt.u)};
|
|
|
|
if (assignment) {
|
2020-02-21 06:54:46 +08:00
|
|
|
CheckForallIndexesUsed(*assignment);
|
2020-02-19 09:14:24 +08:00
|
|
|
CheckForImpureCall(assignment->lhs);
|
|
|
|
CheckForImpureCall(assignment->rhs);
|
|
|
|
if (const auto *proc{
|
|
|
|
std::get_if<evaluate::ProcedureRef>(&assignment->u)}) {
|
|
|
|
CheckForImpureCall(*proc);
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
std::visit(common::visitors{
|
|
|
|
[](const evaluate::Assignment::Intrinsic &) {},
|
|
|
|
[&](const evaluate::ProcedureRef &proc) {
|
|
|
|
CheckForImpureCall(proc);
|
|
|
|
},
|
|
|
|
[&](const evaluate::Assignment::BoundsSpec &bounds) {
|
|
|
|
for (const auto &bound : bounds) {
|
|
|
|
CheckForImpureCall(SomeExpr{bound});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](const evaluate::Assignment::BoundsRemapping &bounds) {
|
|
|
|
for (const auto &bound : bounds) {
|
|
|
|
CheckForImpureCall(SomeExpr{bound.first});
|
|
|
|
CheckForImpureCall(SomeExpr{bound.second});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
2020-02-19 09:14:24 +08:00
|
|
|
assignment->u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 06:14:34 +08:00
|
|
|
private:
|
2019-06-11 04:30:29 +08:00
|
|
|
void SayBadDoControl(parser::CharBlock sourceLocation) {
|
|
|
|
context_.Say(sourceLocation, "DO controls should be INTEGER"_err_en_US);
|
|
|
|
}
|
|
|
|
|
2019-08-15 03:57:09 +08:00
|
|
|
void CheckDoControl(const parser::CharBlock &sourceLocation, bool isReal) {
|
|
|
|
const bool warn{context_.warnOnNonstandardUsage() ||
|
2019-11-07 03:15:03 +08:00
|
|
|
context_.ShouldWarn(common::LanguageFeature::RealDoControls)};
|
2019-06-07 05:05:02 +08:00
|
|
|
if (isReal && !warn) {
|
2019-06-05 06:14:34 +08:00
|
|
|
// No messages for the default case
|
2019-06-07 05:05:02 +08:00
|
|
|
} else if (isReal && warn) {
|
2019-06-06 02:57:09 +08:00
|
|
|
context_.Say(sourceLocation, "DO controls should be INTEGER"_en_US);
|
2019-06-05 06:14:34 +08:00
|
|
|
} else {
|
2019-06-11 04:30:29 +08:00
|
|
|
SayBadDoControl(sourceLocation);
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 06:14:34 +08:00
|
|
|
void CheckDoVariable(const parser::ScalarName &scalarName) {
|
|
|
|
const parser::CharBlock &sourceLocation{scalarName.thing.source};
|
2019-08-15 03:57:09 +08:00
|
|
|
if (const Symbol * symbol{scalarName.thing.symbol}) {
|
2019-06-11 04:30:29 +08:00
|
|
|
if (!IsVariableName(*symbol)) {
|
|
|
|
context_.Say(
|
|
|
|
sourceLocation, "DO control must be an INTEGER variable"_err_en_US);
|
|
|
|
} else {
|
|
|
|
const DeclTypeSpec *symType{symbol->GetType()};
|
|
|
|
if (!symType) {
|
|
|
|
SayBadDoControl(sourceLocation);
|
|
|
|
} else {
|
|
|
|
if (!symType->IsNumeric(TypeCategory::Integer)) {
|
|
|
|
CheckDoControl(
|
|
|
|
sourceLocation, symType->IsNumeric(TypeCategory::Real));
|
|
|
|
}
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
} // No messages for INTEGER
|
2019-06-11 04:30:29 +08:00
|
|
|
}
|
2019-06-05 06:14:34 +08:00
|
|
|
}
|
|
|
|
|
2019-08-01 05:01:31 +08:00
|
|
|
// Semantic checks for the limit and step expressions
|
2019-06-05 06:14:34 +08:00
|
|
|
void CheckDoExpression(const parser::ScalarExpr &scalarExpression) {
|
2019-08-01 05:01:31 +08:00
|
|
|
if (const SomeExpr * expr{GetExpr(scalarExpression)}) {
|
|
|
|
if (!ExprHasTypeCategory(*expr, TypeCategory::Integer)) {
|
|
|
|
// No warnings or errors for type INTEGER
|
|
|
|
const parser::CharBlock &loc{scalarExpression.thing.value().source};
|
|
|
|
CheckDoControl(loc, ExprHasTypeCategory(*expr, TypeCategory::Real));
|
2019-06-11 04:30:29 +08:00
|
|
|
}
|
|
|
|
}
|
2019-06-05 06:14:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckDoNormal(const parser::DoConstruct &doConstruct) {
|
2019-10-12 05:39:33 +08:00
|
|
|
// C1120 -- types of DO variables must be INTEGER, extended by allowing
|
|
|
|
// REAL and DOUBLE PRECISION
|
2019-06-05 06:14:34 +08:00
|
|
|
const Bounds &bounds{GetBounds(doConstruct)};
|
|
|
|
CheckDoVariable(bounds.name);
|
|
|
|
CheckDoExpression(bounds.lower);
|
|
|
|
CheckDoExpression(bounds.upper);
|
2019-11-10 01:29:31 +08:00
|
|
|
if (bounds.step) {
|
2019-11-21 05:33:04 +08:00
|
|
|
CheckDoExpression(*bounds.step);
|
|
|
|
if (IsZero(*bounds.step)) {
|
|
|
|
context_.Say(bounds.step->thing.value().source,
|
|
|
|
"DO step expression should not be zero"_en_US);
|
|
|
|
}
|
2019-06-05 06:14:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckDoConcurrent(const parser::DoConstruct &doConstruct) {
|
|
|
|
auto &doStmt{
|
|
|
|
std::get<parser::Statement<parser::NonLabelDoStmt>>(doConstruct.t)};
|
|
|
|
currentStatementSourcePosition_ = doStmt.source;
|
|
|
|
|
2019-08-01 05:01:31 +08:00
|
|
|
const parser::Block &block{std::get<parser::Block>(doConstruct.t)};
|
2019-10-12 05:39:33 +08:00
|
|
|
DoConcurrentBodyEnforce doConcurrentBodyEnforce{context_, doStmt.source};
|
2019-08-15 03:57:09 +08:00
|
|
|
parser::Walk(block, doConcurrentBodyEnforce);
|
2019-06-05 06:14:34 +08:00
|
|
|
|
2019-12-26 13:18:47 +08:00
|
|
|
LabelEnforce doConcurrentLabelEnforce{context_,
|
|
|
|
doConcurrentBodyEnforce.labels(), currentStatementSourcePosition_,
|
|
|
|
"DO CONCURRENT"};
|
2019-08-01 05:01:31 +08:00
|
|
|
parser::Walk(block, doConcurrentLabelEnforce);
|
2019-06-05 06:14:34 +08:00
|
|
|
|
2020-02-19 09:14:24 +08:00
|
|
|
const auto &loopControl{doConstruct.GetLoopControl()};
|
|
|
|
CheckConcurrentLoopControl(*loopControl);
|
|
|
|
CheckLocalitySpecs(*loopControl, block);
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
2019-06-05 06:14:34 +08:00
|
|
|
|
2019-08-15 03:57:09 +08:00
|
|
|
// Return a set of symbols whose names are in a Local locality-spec. Look
|
|
|
|
// the names up in the scope that encloses the DO construct to avoid getting
|
|
|
|
// the local versions of them. Then follow the host-, use-, and
|
|
|
|
// construct-associations to get the root symbols
|
|
|
|
SymbolSet GatherLocals(
|
|
|
|
const std::list<parser::LocalitySpec> &localitySpecs) const {
|
2019-08-09 06:54:49 +08:00
|
|
|
SymbolSet symbols;
|
2019-08-21 06:17:51 +08:00
|
|
|
const Scope &parentScope{
|
2019-08-15 03:57:09 +08:00
|
|
|
context_.FindScope(currentStatementSourcePosition_).parent()};
|
|
|
|
// Loop through the LocalitySpec::Local locality-specs
|
|
|
|
for (const auto &ls : localitySpecs) {
|
|
|
|
if (const auto *names{std::get_if<parser::LocalitySpec::Local>(&ls.u)}) {
|
|
|
|
// Loop through the names in the Local locality-spec getting their
|
|
|
|
// symbols
|
|
|
|
for (const parser::Name &name : names->v) {
|
2019-08-21 06:17:51 +08:00
|
|
|
if (const Symbol * symbol{parentScope.FindSymbol(name.source)}) {
|
2019-08-15 03:57:09 +08:00
|
|
|
if (const Symbol * root{GetAssociationRoot(*symbol)}) {
|
2019-12-28 01:02:28 +08:00
|
|
|
symbols.insert(*root);
|
2019-08-09 06:54:49 +08:00
|
|
|
}
|
2019-08-15 03:57:09 +08:00
|
|
|
}
|
2019-08-09 06:54:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return symbols;
|
|
|
|
}
|
|
|
|
|
2019-08-15 03:57:09 +08:00
|
|
|
static SymbolSet GatherSymbolsFromExpression(const parser::Expr &expression) {
|
2019-09-20 03:19:17 +08:00
|
|
|
SymbolSet result;
|
2019-08-09 06:54:49 +08:00
|
|
|
if (const auto *expr{GetExpr(expression)}) {
|
2019-10-23 07:53:29 +08:00
|
|
|
for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
|
|
|
|
if (const Symbol * root{GetAssociationRoot(symbol)}) {
|
2019-12-28 01:02:28 +08:00
|
|
|
result.insert(*root);
|
2019-08-15 03:57:09 +08:00
|
|
|
}
|
2019-09-20 03:19:17 +08:00
|
|
|
}
|
2019-08-09 06:54:49 +08:00
|
|
|
}
|
2019-09-20 03:19:17 +08:00
|
|
|
return result;
|
2019-08-09 06:54:49 +08:00
|
|
|
}
|
|
|
|
|
2019-08-15 03:57:09 +08:00
|
|
|
// C1121 - procedures in mask must be pure
|
2018-10-11 07:52:06 +08:00
|
|
|
void CheckMaskIsPure(const parser::ScalarLogicalExpr &mask) const {
|
2019-08-15 03:57:09 +08:00
|
|
|
SymbolSet references{GatherSymbolsFromExpression(mask.thing.thing.value())};
|
2019-12-28 01:02:28 +08:00
|
|
|
for (const Symbol &ref : references) {
|
|
|
|
if (IsProcedure(ref) && !IsPureProcedure(ref)) {
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.SayWithDecl(ref, parser::Unwrap<parser::Expr>(mask)->source,
|
|
|
|
"%s mask expression may not reference impure procedure '%s'"_err_en_US,
|
|
|
|
LoopKindName(), ref.name());
|
2018-10-12 06:19:38 +08:00
|
|
|
return;
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-21 08:02:32 +08:00
|
|
|
|
2019-08-21 06:17:51 +08:00
|
|
|
void CheckNoCollisions(const SymbolSet &refs, const SymbolSet &uses,
|
[flang] Changes to check for constraint C1140
This constraint prohibits deallocation of polymorphic entities in a DO
CONCURRENT.
Section 9.7.3.2 specifies the situations that might cause deallocation
of a polymorphic entity. The ones that are applicable to a DO CONCURRENT
are exiting from a block that declares such variables, intrinsic
assignment, and an actual DEALLOCATE statement. This section also
specifies (paragraph 8) that deallocation of a derived type causes
deallocation of all of its allocatable subobjects.
Section 10.2.1.3 specifies what happens during intrinsic assignment.
Paragraph 3 states If the variable is an allocated allocatable variable,
it is deallocated if expr is an array of different shape, any
corresponding length type parameter values of the variable and expr
differ, or the variable is polymorphic and the dynamic type or any
corresponding kind type parameter values of the variable and expr
differ." Thus, an allocatable polymorphic variable on the left hand side
of an assignment statement gets deallocated. Paragraph 13 states that
"For a noncoarray allocatable component the following sequence of
operations is applied.
(1) If the component of the variable is allocated, it is deallocated."
Thus, a variable on the left-hand side of an assignment statement might have noncorray allocatable components. Such components will be deallocated.
Deallocation can be caused by exiting from a block where the entity is
declared, from an assignment, and from direct deallocation.
Original-commit: flang-compiler/f18@7d1932d344308d8266503268a7534532cebe6087
Reviewed-on: https://github.com/flang-compiler/f18/pull/814
2019-11-06 02:18:33 +08:00
|
|
|
parser::MessageFixedText &&errorMessage,
|
2019-08-15 03:57:09 +08:00
|
|
|
const parser::CharBlock &refPosition) const {
|
2019-12-28 01:02:28 +08:00
|
|
|
for (const Symbol &ref : refs) {
|
2019-08-21 06:17:51 +08:00
|
|
|
if (uses.find(ref) != uses.end()) {
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.SayWithDecl(ref, refPosition, std::move(errorMessage),
|
|
|
|
LoopKindName(), ref.name());
|
2019-08-06 04:36:01 +08:00
|
|
|
return;
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-21 08:02:32 +08:00
|
|
|
|
2019-08-15 03:57:09 +08:00
|
|
|
void HasNoReferences(
|
|
|
|
const SymbolSet &indexNames, const parser::ScalarIntExpr &expr) const {
|
|
|
|
CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()),
|
|
|
|
indexNames,
|
2020-02-19 09:14:24 +08:00
|
|
|
"%s limit expression may not reference index variable '%s'"_err_en_US,
|
2019-08-15 03:57:09 +08:00
|
|
|
expr.thing.thing.value().source);
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
2019-06-21 08:02:32 +08:00
|
|
|
|
2019-08-21 06:17:51 +08:00
|
|
|
// C1129, names in local locality-specs can't be in mask expressions
|
2019-08-15 03:57:09 +08:00
|
|
|
void CheckMaskDoesNotReferenceLocal(
|
|
|
|
const parser::ScalarLogicalExpr &mask, const SymbolSet &localVars) const {
|
|
|
|
CheckNoCollisions(GatherSymbolsFromExpression(mask.thing.thing.value()),
|
|
|
|
localVars,
|
2020-02-19 09:14:24 +08:00
|
|
|
"%s mask expression references variable '%s'"
|
2019-08-21 06:17:51 +08:00
|
|
|
" in LOCAL locality-spec"_err_en_US,
|
2019-08-15 03:57:09 +08:00
|
|
|
mask.thing.thing.value().source);
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
2019-08-15 03:57:09 +08:00
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// C1129, names in local locality-specs can't be in limit or step
|
|
|
|
// expressions
|
2019-08-15 03:57:09 +08:00
|
|
|
void CheckExprDoesNotReferenceLocal(
|
|
|
|
const parser::ScalarIntExpr &expr, const SymbolSet &localVars) const {
|
|
|
|
CheckNoCollisions(GatherSymbolsFromExpression(expr.thing.thing.value()),
|
|
|
|
localVars,
|
2020-02-19 09:14:24 +08:00
|
|
|
"%s expression references variable '%s'"
|
2019-08-21 06:17:51 +08:00
|
|
|
" in LOCAL locality-spec"_err_en_US,
|
2019-08-15 03:57:09 +08:00
|
|
|
expr.thing.thing.value().source);
|
|
|
|
}
|
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// C1130, DEFAULT(NONE) locality requires names to be in locality-specs to
|
|
|
|
// be used in the body of the DO loop
|
2018-10-11 07:52:06 +08:00
|
|
|
void CheckDefaultNoneImpliesExplicitLocality(
|
2019-08-01 05:01:31 +08:00
|
|
|
const std::list<parser::LocalitySpec> &localitySpecs,
|
|
|
|
const parser::Block &block) const {
|
|
|
|
bool hasDefaultNone{false};
|
|
|
|
for (auto &ls : localitySpecs) {
|
|
|
|
if (std::holds_alternative<parser::LocalitySpec::DefaultNone>(ls.u)) {
|
2019-08-15 03:57:09 +08:00
|
|
|
if (hasDefaultNone) {
|
|
|
|
// C1127, you can only have one DEFAULT(NONE)
|
|
|
|
context_.Say(currentStatementSourcePosition_,
|
2019-10-12 05:39:33 +08:00
|
|
|
"Only one DEFAULT(NONE) may appear"_en_US);
|
2019-08-15 03:57:09 +08:00
|
|
|
break;
|
|
|
|
}
|
2019-08-01 05:01:31 +08:00
|
|
|
hasDefaultNone = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hasDefaultNone) {
|
|
|
|
DoConcurrentVariableEnforce doConcurrentVariableEnforce{
|
|
|
|
context_, currentStatementSourcePosition_};
|
|
|
|
parser::Walk(block, doConcurrentVariableEnforce);
|
|
|
|
}
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
2019-06-05 06:14:34 +08:00
|
|
|
|
2019-08-15 03:57:09 +08:00
|
|
|
// C1123, concurrent limit or step expressions can't reference index-names
|
|
|
|
void CheckConcurrentHeader(const parser::ConcurrentHeader &header) const {
|
2020-02-19 09:14:24 +08:00
|
|
|
if (const auto &mask{
|
|
|
|
std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) {
|
|
|
|
CheckMaskIsPure(*mask);
|
|
|
|
}
|
2018-10-11 07:52:06 +08:00
|
|
|
auto &controls{std::get<std::list<parser::ConcurrentControl>>(header.t)};
|
2019-08-09 06:54:49 +08:00
|
|
|
SymbolSet indexNames;
|
2020-02-19 09:14:24 +08:00
|
|
|
for (const parser::ConcurrentControl &control : controls) {
|
|
|
|
const auto &indexName{std::get<parser::Name>(control.t)};
|
2019-04-26 04:18:33 +08:00
|
|
|
if (indexName.symbol) {
|
2019-12-28 01:02:28 +08:00
|
|
|
indexNames.insert(*indexName.symbol);
|
2019-04-26 04:18:33 +08:00
|
|
|
}
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
|
|
|
if (!indexNames.empty()) {
|
2020-02-19 09:14:24 +08:00
|
|
|
for (const parser::ConcurrentControl &control : controls) {
|
|
|
|
HasNoReferences(indexNames, std::get<1>(control.t));
|
|
|
|
HasNoReferences(indexNames, std::get<2>(control.t));
|
|
|
|
if (const auto &intExpr{
|
|
|
|
std::get<std::optional<parser::ScalarIntExpr>>(control.t)}) {
|
|
|
|
const parser::Expr &expr{intExpr->thing.thing.value()};
|
|
|
|
CheckNoCollisions(GatherSymbolsFromExpression(expr), indexNames,
|
|
|
|
"%s step expression may not reference index variable '%s'"_err_en_US,
|
|
|
|
expr.source);
|
|
|
|
if (IsZero(expr)) {
|
|
|
|
context_.Say(expr.source,
|
|
|
|
"%s step expression may not be zero"_err_en_US, LoopKindName());
|
2019-11-21 05:33:04 +08:00
|
|
|
}
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-15 03:57:09 +08:00
|
|
|
}
|
2019-08-01 05:01:31 +08:00
|
|
|
|
2020-02-19 09:14:24 +08:00
|
|
|
void CheckLocalitySpecs(
|
|
|
|
const parser::LoopControl &control, const parser::Block &block) const {
|
|
|
|
const auto &concurrent{
|
|
|
|
std::get<parser::LoopControl::Concurrent>(control.u)};
|
2019-08-15 03:57:09 +08:00
|
|
|
const auto &header{std::get<parser::ConcurrentHeader>(concurrent.t)};
|
|
|
|
const auto &localitySpecs{
|
2018-10-11 07:52:06 +08:00
|
|
|
std::get<std::list<parser::LocalitySpec>>(concurrent.t)};
|
2019-08-01 05:01:31 +08:00
|
|
|
if (!localitySpecs.empty()) {
|
2019-08-15 03:57:09 +08:00
|
|
|
const SymbolSet &localVars{GatherLocals(localitySpecs)};
|
2020-02-19 09:14:24 +08:00
|
|
|
for (const auto &c : GetControls(control)) {
|
2019-08-15 03:57:09 +08:00
|
|
|
CheckExprDoesNotReferenceLocal(std::get<1>(c.t), localVars);
|
|
|
|
CheckExprDoesNotReferenceLocal(std::get<2>(c.t), localVars);
|
|
|
|
if (const auto &expr{
|
|
|
|
std::get<std::optional<parser::ScalarIntExpr>>(c.t)}) {
|
|
|
|
CheckExprDoesNotReferenceLocal(*expr, localVars);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (const auto &mask{
|
|
|
|
std::get<std::optional<parser::ScalarLogicalExpr>>(header.t)}) {
|
|
|
|
CheckMaskDoesNotReferenceLocal(*mask, localVars);
|
2019-08-01 05:01:31 +08:00
|
|
|
}
|
|
|
|
CheckDefaultNoneImpliesExplicitLocality(localitySpecs, block);
|
2018-10-11 07:52:06 +08:00
|
|
|
}
|
2018-09-18 08:19:27 +08:00
|
|
|
}
|
|
|
|
|
2019-08-15 03:57:09 +08:00
|
|
|
// check constraints [C1121 .. C1130]
|
2020-02-19 09:14:24 +08:00
|
|
|
void CheckConcurrentLoopControl(const parser::LoopControl &control) const {
|
|
|
|
const auto &concurrent{
|
|
|
|
std::get<parser::LoopControl::Concurrent>(control.u)};
|
|
|
|
CheckConcurrentHeader(std::get<parser::ConcurrentHeader>(concurrent.t));
|
|
|
|
}
|
2019-08-15 03:57:09 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> void CheckForImpureCall(const T &x) {
|
2020-02-19 09:14:24 +08:00
|
|
|
const auto &intrinsics{context_.foldingContext().intrinsics()};
|
|
|
|
if (auto bad{FindImpureCall(intrinsics, x)}) {
|
|
|
|
context_.Say(
|
|
|
|
"Impure procedure '%s' may not be referenced in a %s"_err_en_US, *bad,
|
|
|
|
LoopKindName());
|
2019-08-15 03:57:09 +08:00
|
|
|
}
|
2020-02-19 09:14:24 +08:00
|
|
|
}
|
|
|
|
|
2020-02-21 06:54:46 +08:00
|
|
|
// Each index should be used on the LHS of each assignment in a FORALL
|
|
|
|
void CheckForallIndexesUsed(const evaluate::Assignment &assignment) {
|
|
|
|
SymbolVector indexVars{context_.GetIndexVars(IndexVarKind::FORALL)};
|
|
|
|
if (!indexVars.empty()) {
|
|
|
|
SymbolSet symbols{evaluate::CollectSymbols(assignment.lhs)};
|
|
|
|
std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[&](const evaluate::Assignment::BoundsSpec &spec) {
|
|
|
|
for (const auto &bound : spec) {
|
2020-02-23 03:47:04 +08:00
|
|
|
// TODO: this is working around missing std::set::merge in some versions of
|
|
|
|
// clang that we are building with
|
|
|
|
#ifdef __clang__
|
|
|
|
auto boundSymbols{evaluate::CollectSymbols(bound)};
|
|
|
|
symbols.insert(boundSymbols.begin(), boundSymbols.end());
|
|
|
|
#else
|
2020-02-21 06:54:46 +08:00
|
|
|
symbols.merge(evaluate::CollectSymbols(bound));
|
2020-02-23 03:47:04 +08:00
|
|
|
#endif
|
2020-02-21 06:54:46 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](const evaluate::Assignment::BoundsRemapping &remapping) {
|
|
|
|
for (const auto &bounds : remapping) {
|
2020-02-23 03:47:04 +08:00
|
|
|
#ifdef __clang__
|
|
|
|
auto lbSymbols{evaluate::CollectSymbols(bounds.first)};
|
|
|
|
symbols.insert(lbSymbols.begin(), lbSymbols.end());
|
|
|
|
auto ubSymbols{evaluate::CollectSymbols(bounds.second)};
|
|
|
|
symbols.insert(ubSymbols.begin(), ubSymbols.end());
|
|
|
|
#else
|
2020-02-21 06:54:46 +08:00
|
|
|
symbols.merge(evaluate::CollectSymbols(bounds.first));
|
|
|
|
symbols.merge(evaluate::CollectSymbols(bounds.second));
|
2020-02-23 03:47:04 +08:00
|
|
|
#endif
|
2020-02-21 06:54:46 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
[](const auto &) {},
|
|
|
|
},
|
|
|
|
assignment.u);
|
|
|
|
for (const Symbol &index : indexVars) {
|
|
|
|
if (symbols.count(index) == 0) {
|
|
|
|
context_.Say(
|
|
|
|
"Warning: FORALL index variable '%s' not used on left-hand side"
|
|
|
|
" of assignment"_en_US,
|
|
|
|
index.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-19 09:14:24 +08:00
|
|
|
// For messages where the DO loop must be DO CONCURRENT, make that explicit.
|
|
|
|
const char *LoopKindName() const {
|
|
|
|
return kind_ == IndexVarKind::DO ? "DO CONCURRENT" : "FORALL";
|
2019-08-15 03:57:09 +08:00
|
|
|
}
|
|
|
|
|
2019-06-05 06:14:34 +08:00
|
|
|
SemanticsContext &context_;
|
2020-02-19 09:14:24 +08:00
|
|
|
const IndexVarKind kind_;
|
2018-10-11 07:52:06 +08:00
|
|
|
parser::CharBlock currentStatementSourcePosition_;
|
2020-03-29 12:00:16 +08:00
|
|
|
}; // class DoContext
|
2018-09-18 08:19:27 +08:00
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Enter(const parser::DoConstruct &doConstruct) {
|
2020-02-19 09:14:24 +08:00
|
|
|
DoContext doContext{context_, IndexVarKind::DO};
|
2019-12-07 11:59:54 +08:00
|
|
|
doContext.DefineDoVariables(doConstruct);
|
|
|
|
}
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::DoConstruct &doConstruct) {
|
2020-02-19 09:14:24 +08:00
|
|
|
DoContext doContext{context_, IndexVarKind::DO};
|
2019-12-07 11:59:54 +08:00
|
|
|
doContext.Check(doConstruct);
|
|
|
|
doContext.ResetDoVariables(doConstruct);
|
2018-10-25 20:55:23 +08:00
|
|
|
}
|
2019-03-06 08:52:50 +08:00
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Enter(const parser::ForallConstruct &construct) {
|
2020-02-19 09:14:24 +08:00
|
|
|
DoContext doContext{context_, IndexVarKind::FORALL};
|
|
|
|
doContext.ActivateIndexVars(GetControls(construct));
|
|
|
|
}
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::ForallConstruct &construct) {
|
2020-02-19 09:14:24 +08:00
|
|
|
DoContext doContext{context_, IndexVarKind::FORALL};
|
|
|
|
doContext.Check(construct);
|
|
|
|
doContext.DeactivateIndexVars(GetControls(construct));
|
|
|
|
}
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Enter(const parser::ForallStmt &stmt) {
|
2020-02-19 09:14:24 +08:00
|
|
|
DoContext doContext{context_, IndexVarKind::FORALL};
|
|
|
|
doContext.ActivateIndexVars(GetControls(stmt));
|
|
|
|
}
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::ForallStmt &stmt) {
|
2020-02-19 09:14:24 +08:00
|
|
|
DoContext doContext{context_, IndexVarKind::FORALL};
|
|
|
|
doContext.Check(stmt);
|
|
|
|
doContext.DeactivateIndexVars(GetControls(stmt));
|
|
|
|
}
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::ForallAssignmentStmt &stmt) {
|
2020-02-19 09:14:24 +08:00
|
|
|
DoContext doContext{context_, IndexVarKind::FORALL};
|
|
|
|
doContext.Check(stmt);
|
|
|
|
}
|
|
|
|
|
2019-09-23 01:01:03 +08:00
|
|
|
// Return the (possibly null) name of the ConstructNode
|
2019-09-24 11:46:45 +08:00
|
|
|
static const parser::Name *MaybeGetNodeName(const ConstructNode &construct) {
|
2019-09-23 01:01:03 +08:00
|
|
|
return std::visit(
|
2019-09-24 11:46:45 +08:00
|
|
|
[&](const auto &x) { return MaybeGetConstructName(*x); }, construct);
|
2019-09-23 01:01:03 +08:00
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename A>
|
|
|
|
static parser::CharBlock GetConstructPosition(const A &a) {
|
2019-09-23 01:01:03 +08:00
|
|
|
return std::get<0>(a.t).source;
|
|
|
|
}
|
|
|
|
|
|
|
|
static parser::CharBlock GetNodePosition(const ConstructNode &construct) {
|
|
|
|
return std::visit(
|
|
|
|
[&](const auto &x) { return GetConstructPosition(*x); }, construct);
|
|
|
|
}
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::SayBadLeave(StmtType stmtType,
|
|
|
|
const char *enclosingStmtName, const ConstructNode &construct) const {
|
2019-09-23 01:01:03 +08:00
|
|
|
context_
|
2019-09-25 01:44:44 +08:00
|
|
|
.Say("%s must not leave a %s statement"_err_en_US, EnumToString(stmtType),
|
2019-09-24 11:46:45 +08:00
|
|
|
enclosingStmtName)
|
2019-09-23 01:01:03 +08:00
|
|
|
.Attach(GetNodePosition(construct), "The construct that was left"_en_US);
|
|
|
|
}
|
|
|
|
|
2019-09-24 11:46:45 +08:00
|
|
|
static const parser::DoConstruct *MaybeGetDoConstruct(
|
2019-09-23 01:01:03 +08:00
|
|
|
const ConstructNode &construct) {
|
2019-09-25 06:33:51 +08:00
|
|
|
if (const auto *doNode{
|
2019-09-24 11:46:45 +08:00
|
|
|
std::get_if<const parser::DoConstruct *>(&construct)}) {
|
2019-09-23 01:01:03 +08:00
|
|
|
return *doNode;
|
2019-09-25 06:33:51 +08:00
|
|
|
} else {
|
|
|
|
return nullptr;
|
2019-09-23 01:01:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ConstructIsDoConcurrent(const ConstructNode &construct) {
|
2019-09-25 06:33:51 +08:00
|
|
|
const parser::DoConstruct *doConstruct{MaybeGetDoConstruct(construct)};
|
2019-09-23 01:01:03 +08:00
|
|
|
return doConstruct && doConstruct->IsDoConcurrent();
|
|
|
|
}
|
|
|
|
|
2019-09-24 11:46:45 +08:00
|
|
|
// Check that CYCLE and EXIT statements do not cause flow of control to
|
|
|
|
// leave DO CONCURRENT, CRITICAL, or CHANGE TEAM constructs.
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::CheckForBadLeave(
|
2019-09-25 01:44:44 +08:00
|
|
|
StmtType stmtType, const ConstructNode &construct) const {
|
2020-03-29 12:00:16 +08:00
|
|
|
std::visit(common::visitors{
|
|
|
|
[&](const parser::DoConstruct *doConstructPtr) {
|
|
|
|
if (doConstructPtr->IsDoConcurrent()) {
|
|
|
|
// C1135 and C1167 -- CYCLE and EXIT statements can't leave
|
|
|
|
// a DO CONCURRENT
|
|
|
|
SayBadLeave(stmtType, "DO CONCURRENT", construct);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](const parser::CriticalConstruct *) {
|
|
|
|
// C1135 and C1168 -- similarly, for CRITICAL
|
|
|
|
SayBadLeave(stmtType, "CRITICAL", construct);
|
|
|
|
},
|
|
|
|
[&](const parser::ChangeTeamConstruct *) {
|
|
|
|
// C1135 and C1168 -- similarly, for CHANGE TEAM
|
|
|
|
SayBadLeave(stmtType, "CHANGE TEAM", construct);
|
|
|
|
},
|
|
|
|
[](const auto *) {},
|
|
|
|
},
|
2019-09-24 11:46:45 +08:00
|
|
|
construct);
|
|
|
|
}
|
|
|
|
|
2019-09-25 01:44:44 +08:00
|
|
|
static bool StmtMatchesConstruct(const parser::Name *stmtName,
|
|
|
|
StmtType stmtType, const parser::Name *constructName,
|
|
|
|
const ConstructNode &construct) {
|
2020-03-19 19:04:28 +08:00
|
|
|
bool inDoConstruct{MaybeGetDoConstruct(construct) != nullptr};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!stmtName) {
|
2020-03-29 12:00:16 +08:00
|
|
|
return inDoConstruct; // Unlabeled statements match all DO constructs
|
2019-09-24 11:46:45 +08:00
|
|
|
} else if (constructName && constructName->source == stmtName->source) {
|
2019-09-25 06:33:51 +08:00
|
|
|
return stmtType == StmtType::EXIT || inDoConstruct;
|
2019-09-24 11:46:45 +08:00
|
|
|
} else {
|
|
|
|
return false;
|
2019-09-23 01:01:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// C1167 Can't EXIT from a DO CONCURRENT
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::CheckDoConcurrentExit(
|
2019-09-25 01:44:44 +08:00
|
|
|
StmtType stmtType, const ConstructNode &construct) const {
|
2019-09-25 06:33:51 +08:00
|
|
|
if (stmtType == StmtType::EXIT && ConstructIsDoConcurrent(construct)) {
|
2019-09-25 01:44:44 +08:00
|
|
|
SayBadLeave(StmtType::EXIT, "DO CONCURRENT", construct);
|
2019-08-27 07:26:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// 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.
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::CheckNesting(
|
2019-09-25 01:44:44 +08:00
|
|
|
StmtType stmtType, const parser::Name *stmtName) const {
|
2019-09-23 01:01:03 +08:00
|
|
|
const ConstructStack &stack{context_.constructStack()};
|
2019-09-25 06:33:51 +08:00
|
|
|
for (auto iter{stack.cend()}; iter-- != stack.cbegin();) {
|
|
|
|
const ConstructNode &construct{*iter};
|
2019-09-24 11:46:45 +08:00
|
|
|
const parser::Name *constructName{MaybeGetNodeName(construct)};
|
2019-09-25 01:44:44 +08:00
|
|
|
if (StmtMatchesConstruct(stmtName, stmtType, constructName, construct)) {
|
|
|
|
CheckDoConcurrentExit(stmtType, construct);
|
2020-03-29 12:00:16 +08:00
|
|
|
return; // We got a match, so we're finished checking
|
2019-09-23 01:01:03 +08:00
|
|
|
}
|
2019-09-25 01:44:44 +08:00
|
|
|
CheckForBadLeave(stmtType, construct);
|
2019-08-30 07:02:37 +08:00
|
|
|
}
|
2019-09-23 01:01:03 +08:00
|
|
|
|
|
|
|
// We haven't found a match in the enclosing constructs
|
2019-09-25 06:33:51 +08:00
|
|
|
if (stmtType == StmtType::EXIT) {
|
2019-09-25 01:44:44 +08:00
|
|
|
context_.Say("No matching construct for EXIT statement"_err_en_US);
|
2019-09-24 11:46:45 +08:00
|
|
|
} else {
|
2019-09-25 01:44:44 +08:00
|
|
|
context_.Say("No matching DO construct for CYCLE statement"_err_en_US);
|
2019-09-24 11:46:45 +08:00
|
|
|
}
|
2019-09-23 01:01:03 +08:00
|
|
|
}
|
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// C1135 -- Nesting for CYCLE statements
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Enter(const parser::CycleStmt &cycleStmt) {
|
2019-09-25 06:33:51 +08:00
|
|
|
CheckNesting(StmtType::CYCLE, common::GetPtrFromOptional(cycleStmt.v));
|
2019-09-23 01:01:03 +08:00
|
|
|
}
|
|
|
|
|
2019-10-12 05:39:33 +08:00
|
|
|
// C1167 and C1168 -- Nesting for EXIT statements
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Enter(const parser::ExitStmt &exitStmt) {
|
2019-09-25 06:33:51 +08:00
|
|
|
CheckNesting(StmtType::EXIT, common::GetPtrFromOptional(exitStmt.v));
|
2019-08-30 07:02:37 +08:00
|
|
|
}
|
2019-08-27 07:26:24 +08:00
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::AssignmentStmt &stmt) {
|
2019-12-07 11:59:54 +08:00
|
|
|
const auto &variable{std::get<parser::Variable>(stmt.t)};
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.CheckIndexVarRedefine(variable);
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
|
2020-01-10 03:37:51 +08:00
|
|
|
static void CheckIfArgIsDoVar(const evaluate::ActualArgument &arg,
|
|
|
|
const parser::CharBlock location, SemanticsContext &context) {
|
|
|
|
common::Intent intent{arg.dummyIntent()};
|
|
|
|
if (intent == common::Intent::Out || intent == common::Intent::InOut) {
|
|
|
|
if (const SomeExpr * argExpr{arg.UnwrapExpr()}) {
|
|
|
|
if (const Symbol * var{evaluate::UnwrapWholeSymbolDataRef(*argExpr)}) {
|
|
|
|
if (intent == common::Intent::Out) {
|
2020-02-19 09:14:24 +08:00
|
|
|
context.CheckIndexVarRedefine(location, *var);
|
2020-01-10 03:37:51 +08:00
|
|
|
} else {
|
2020-03-29 12:00:16 +08:00
|
|
|
context.WarnIndexVarRedefine(location, *var); // INTENT(INOUT)
|
2020-01-10 03:37:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-03 04:26:47 +08:00
|
|
|
// Check to see if a DO variable is being passed as an actual argument to a
|
|
|
|
// dummy argument whose intent is OUT or INOUT. To do this, we need to find
|
|
|
|
// the expressions for actual arguments which contain DO variables. We get the
|
|
|
|
// intents of the dummy arguments from the ProcedureRef in the "typedCall"
|
|
|
|
// field of the CallStmt which was filled in during expression checking. At
|
|
|
|
// the same time, we need to iterate over the parser::Expr versions of the
|
|
|
|
// actual arguments to get their source locations of the arguments for the
|
|
|
|
// messages.
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::CallStmt &callStmt) {
|
2020-01-03 04:26:47 +08:00
|
|
|
if (const auto &typedCall{callStmt.typedCall}) {
|
|
|
|
const auto &parsedArgs{
|
|
|
|
std::get<std::list<parser::ActualArgSpec>>(callStmt.v.t)};
|
|
|
|
auto parsedArgIter{parsedArgs.begin()};
|
|
|
|
const evaluate::ActualArguments &checkedArgs{typedCall->arguments()};
|
|
|
|
for (const auto &checkedOptionalArg : checkedArgs) {
|
|
|
|
if (parsedArgIter == parsedArgs.end()) {
|
2020-03-29 12:00:16 +08:00
|
|
|
break; // No more parsed arguments, we're done.
|
2020-01-03 04:26:47 +08:00
|
|
|
}
|
|
|
|
const auto &parsedArg{std::get<parser::ActualArg>(parsedArgIter->t)};
|
|
|
|
++parsedArgIter;
|
|
|
|
if (checkedOptionalArg) {
|
|
|
|
const evaluate::ActualArgument &checkedArg{*checkedOptionalArg};
|
2020-01-10 03:37:51 +08:00
|
|
|
if (const auto *parsedExpr{
|
|
|
|
std::get_if<common::Indirection<parser::Expr>>(&parsedArg.u)}) {
|
|
|
|
CheckIfArgIsDoVar(checkedArg, parsedExpr->value().source, context_);
|
2020-01-03 04:26:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::ConnectSpec &connectSpec) {
|
2019-12-07 11:59:54 +08:00
|
|
|
const auto *newunit{
|
|
|
|
std::get_if<parser::ConnectSpec::Newunit>(&connectSpec.u)};
|
|
|
|
if (newunit) {
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.CheckIndexVarRedefine(newunit->v.thing.thing);
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 03:37:51 +08:00
|
|
|
using ActualArgumentSet = std::set<evaluate::ActualArgumentRef>;
|
|
|
|
|
|
|
|
struct CollectActualArgumentsHelper
|
2020-03-29 12:00:16 +08:00
|
|
|
: public evaluate::SetTraverse<CollectActualArgumentsHelper,
|
|
|
|
ActualArgumentSet> {
|
2020-01-10 03:37:51 +08:00
|
|
|
using Base = SetTraverse<CollectActualArgumentsHelper, ActualArgumentSet>;
|
|
|
|
CollectActualArgumentsHelper() : Base{*this} {}
|
|
|
|
using Base::operator();
|
|
|
|
ActualArgumentSet operator()(const evaluate::ActualArgument &arg) const {
|
|
|
|
return ActualArgumentSet{arg};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename A> ActualArgumentSet CollectActualArguments(const A &x) {
|
2020-01-10 03:37:51 +08:00
|
|
|
return CollectActualArgumentsHelper{}(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
template ActualArgumentSet CollectActualArguments(const SomeExpr &);
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::Expr &parsedExpr) {
|
2020-01-13 00:31:54 +08:00
|
|
|
if (const SomeExpr * expr{GetExpr(parsedExpr)}) {
|
|
|
|
ActualArgumentSet argSet{CollectActualArguments(*expr)};
|
|
|
|
for (const evaluate::ActualArgumentRef &argRef : argSet) {
|
|
|
|
CheckIfArgIsDoVar(*argRef, parsedExpr.source, context_);
|
2020-01-10 03:37:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::InquireSpec &inquireSpec) {
|
2019-12-07 11:59:54 +08:00
|
|
|
const auto *intVar{std::get_if<parser::InquireSpec::IntVar>(&inquireSpec.u)};
|
|
|
|
if (intVar) {
|
|
|
|
const auto &scalar{std::get<parser::ScalarIntVariable>(intVar->t)};
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.CheckIndexVarRedefine(scalar.thing.thing);
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::IoControlSpec &ioControlSpec) {
|
2019-12-07 11:59:54 +08:00
|
|
|
const auto *size{std::get_if<parser::IoControlSpec::Size>(&ioControlSpec.u)};
|
|
|
|
if (size) {
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.CheckIndexVarRedefine(size->v.thing.thing);
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::OutputImpliedDo &outputImpliedDo) {
|
2019-12-07 11:59:54 +08:00
|
|
|
const auto &control{std::get<parser::IoImpliedDoControl>(outputImpliedDo.t)};
|
|
|
|
const parser::Name &name{control.name.thing.thing};
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.CheckIndexVarRedefine(name.source, *name.symbol);
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::StatVariable &statVariable) {
|
2020-02-19 09:14:24 +08:00
|
|
|
context_.CheckIndexVarRedefine(statVariable.v.thing.thing);
|
2019-12-07 11:59:54 +08:00
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::semantics
|