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-10-12 05:39:33 +08:00
|
|
|
static parser::MessageFixedText GetEnclosingDoMsg() {
|
|
|
|
return "Enclosing DO CONCURRENT statement"_en_US;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
static bool HasImpureFinal(const Symbol &original) {
|
|
|
|
const Symbol &symbol{ResolveAssociations(original)};
|
|
|
|
if (symbol.has<ObjectEntityDetails>()) {
|
|
|
|
if (const DeclTypeSpec * symType{symbol.GetType()}) {
|
2020-01-28 06:12:35 +08:00
|
|
|
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?
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
static bool MightDeallocatePolymorphic(const Symbol &original,
|
[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
|
|
|
const std::function<bool(const Symbol &)> &WillDeallocate) {
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
const Symbol &symbol{ResolveAssociations(original)};
|
|
|
|
// Check the entity itself, no coarray exception here
|
|
|
|
if (IsPolymorphicAllocatable(symbol)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Check the components
|
|
|
|
if (const auto *details{symbol.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;
|
[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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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};
|
2020-06-19 08:17:04 +08:00
|
|
|
if (IsAllocatable(entity) && !IsSaved(entity) &&
|
[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(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:
|
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
|
2021-03-19 01:26:23 +08:00
|
|
|
UnorderedSymbolSet GatherLocals(
|
2019-08-15 03:57:09 +08:00
|
|
|
const std::list<parser::LocalitySpec> &localitySpecs) const {
|
2021-03-19 01:26:23 +08:00
|
|
|
UnorderedSymbolSet 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)}) {
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
symbols.insert(ResolveAssociations(*symbol));
|
2019-08-15 03:57:09 +08:00
|
|
|
}
|
2019-08-09 06:54:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return symbols;
|
|
|
|
}
|
|
|
|
|
2021-03-19 01:26:23 +08:00
|
|
|
static UnorderedSymbolSet GatherSymbolsFromExpression(
|
|
|
|
const parser::Expr &expression) {
|
|
|
|
UnorderedSymbolSet 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)) {
|
[flang] Fix classification of shape inquiries in specification exprs
In some contexts, including the motivating case of determining whether
the expressions that define the shape of a variable are "constant expressions"
in the sense of the Fortran standard, expression rewriting via Fold()
is not necessary, and should not be required. The inquiry intrinsics LBOUND,
UBOUND, and SIZE work correctly now in specification expressions and are
classified correctly as being constant expressions (or not). Getting this right
led to a fair amount of API clean-up as a consequence, including the
folding of shapes and TypeAndShape objects, and new APIs for shapes
that do not fold for those cases where folding isn't needed. Further,
the symbol-testing predicate APIs in Evaluate/tools.h now all resolve any
associations of their symbols and work transparently on use-, host-, and
construct-association symbols; the tools used to resolve those associations have
been defined and documented more precisely, and their clients adjusted as needed.
Differential Revision: https://reviews.llvm.org/D94561
2021-01-13 07:36:45 +08:00
|
|
|
result.insert(ResolveAssociations(symbol));
|
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 {
|
2021-03-19 01:26:23 +08:00
|
|
|
UnorderedSymbolSet references{
|
|
|
|
GatherSymbolsFromExpression(mask.thing.thing.value())};
|
|
|
|
for (const Symbol &ref : OrderBySourcePosition(references)) {
|
2019-12-28 01:02:28 +08:00
|
|
|
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
|
|
|
|
2021-03-19 01:26:23 +08:00
|
|
|
void CheckNoCollisions(const UnorderedSymbolSet &refs,
|
|
|
|
const UnorderedSymbolSet &uses, parser::MessageFixedText &&errorMessage,
|
2019-08-15 03:57:09 +08:00
|
|
|
const parser::CharBlock &refPosition) const {
|
2021-03-19 01:26:23 +08:00
|
|
|
for (const Symbol &ref : OrderBySourcePosition(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
|
|
|
|
2021-03-19 01:26:23 +08:00
|
|
|
void HasNoReferences(const UnorderedSymbolSet &indexNames,
|
|
|
|
const parser::ScalarIntExpr &expr) const {
|
2019-08-15 03:57:09 +08:00
|
|
|
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
|
2021-03-19 01:26:23 +08:00
|
|
|
void CheckMaskDoesNotReferenceLocal(const parser::ScalarLogicalExpr &mask,
|
|
|
|
const UnorderedSymbolSet &localVars) const {
|
2019-08-15 03:57:09 +08:00
|
|
|
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
|
2021-03-19 01:26:23 +08:00
|
|
|
void CheckExprDoesNotReferenceLocal(const parser::ScalarIntExpr &expr,
|
|
|
|
const UnorderedSymbolSet &localVars) const {
|
2019-08-15 03:57:09 +08:00
|
|
|
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)};
|
2021-03-19 01:26:23 +08:00
|
|
|
UnorderedSymbolSet 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()) {
|
2021-03-19 01:26:23 +08:00
|
|
|
const UnorderedSymbolSet &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) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
if (auto bad{FindImpureCall(context_.foldingContext(), x)}) {
|
2020-02-19 09:14:24 +08:00
|
|
|
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()) {
|
2021-03-19 01:26:23 +08:00
|
|
|
UnorderedSymbolSet symbols{evaluate::CollectSymbols(assignment.lhs)};
|
2020-02-21 06:54:46 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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,
|
2020-10-06 12:51:06 +08:00
|
|
|
StmtType stmtType, const std::optional<parser::Name> &constructName,
|
2019-09-25 01:44:44 +08:00
|
|
|
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};
|
2020-10-06 12:51:06 +08:00
|
|
|
const std::optional<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 {
|
2020-06-03 23:03:14 +08:00
|
|
|
return Combine(ActualArgumentSet{arg},
|
|
|
|
CollectActualArgumentsHelper{}(arg.UnwrapExpr()));
|
2020-01-10 03:37:51 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
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-06-03 23:03:14 +08:00
|
|
|
void DoForallChecker::Enter(const parser::Expr &parsedExpr) { ++exprDepth_; }
|
|
|
|
|
2020-02-20 05:28:19 +08:00
|
|
|
void DoForallChecker::Leave(const parser::Expr &parsedExpr) {
|
2020-06-03 23:03:14 +08:00
|
|
|
CHECK(exprDepth_ > 0);
|
|
|
|
if (--exprDepth_ == 0) { // Only check top level expressions
|
|
|
|
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
|