1 //===-- lib/Semantics/check-do-forall.cpp ---------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "check-do-forall.h"
10 #include "definable.h"
11 #include "flang/Common/template.h"
12 #include "flang/Evaluate/call.h"
13 #include "flang/Evaluate/expression.h"
14 #include "flang/Evaluate/tools.h"
15 #include "flang/Evaluate/traverse.h"
16 #include "flang/Parser/message.h"
17 #include "flang/Parser/parse-tree-visitor.h"
18 #include "flang/Parser/tools.h"
19 #include "flang/Semantics/attr.h"
20 #include "flang/Semantics/scope.h"
21 #include "flang/Semantics/semantics.h"
22 #include "flang/Semantics/symbol.h"
23 #include "flang/Semantics/tools.h"
24 #include "flang/Semantics/type.h"
26 namespace Fortran::evaluate
{
27 using ActualArgumentRef
= common::Reference
<const ActualArgument
>;
29 inline bool operator<(ActualArgumentRef x
, ActualArgumentRef y
) {
32 } // namespace Fortran::evaluate
34 namespace Fortran::semantics
{
36 using namespace parser::literals
;
38 using Bounds
= parser::LoopControl::Bounds
;
39 using IndexVarKind
= SemanticsContext::IndexVarKind
;
41 static const parser::ConcurrentHeader
&GetConcurrentHeader(
42 const parser::LoopControl
&loopControl
) {
43 const auto &concurrent
{
44 std::get
<parser::LoopControl::Concurrent
>(loopControl
.u
)};
45 return std::get
<parser::ConcurrentHeader
>(concurrent
.t
);
47 static const parser::ConcurrentHeader
&GetConcurrentHeader(
48 const parser::ForallConstruct
&construct
) {
50 std::get
<parser::Statement
<parser::ForallConstructStmt
>>(construct
.t
)};
51 return std::get
<common::Indirection
<parser::ConcurrentHeader
>>(
55 static const parser::ConcurrentHeader
&GetConcurrentHeader(
56 const parser::ForallStmt
&stmt
) {
57 return std::get
<common::Indirection
<parser::ConcurrentHeader
>>(stmt
.t
)
61 static const std::list
<parser::ConcurrentControl
> &GetControls(const T
&x
) {
62 return std::get
<std::list
<parser::ConcurrentControl
>>(
63 GetConcurrentHeader(x
).t
);
66 static const Bounds
&GetBounds(const parser::DoConstruct
&doConstruct
) {
67 auto &loopControl
{doConstruct
.GetLoopControl().value()};
68 return std::get
<Bounds
>(loopControl
.u
);
71 static const parser::Name
&GetDoVariable(
72 const parser::DoConstruct
&doConstruct
) {
73 const Bounds
&bounds
{GetBounds(doConstruct
)};
74 return bounds
.name
.thing
;
77 static parser::MessageFixedText
GetEnclosingDoMsg() {
78 return "Enclosing DO CONCURRENT statement"_en_US
;
81 static void SayWithDo(SemanticsContext
&context
, parser::CharBlock stmtLocation
,
82 parser::MessageFixedText
&&message
, parser::CharBlock doLocation
) {
83 context
.Say(stmtLocation
, message
).Attach(doLocation
, GetEnclosingDoMsg());
86 // 11.1.7.5 - enforce semantics constraints on a DO CONCURRENT loop body
87 class DoConcurrentBodyEnforce
{
89 DoConcurrentBodyEnforce(
90 SemanticsContext
&context
, parser::CharBlock doConcurrentSourcePosition
)
92 doConcurrentSourcePosition_
{doConcurrentSourcePosition
} {}
93 std::set
<parser::Label
> labels() { return labels_
; }
94 template <typename T
> bool Pre(const T
&x
) {
95 if (const auto *expr
{GetExpr(context_
, x
)}) {
96 if (auto bad
{FindImpureCall(context_
.foldingContext(), *expr
)}) {
97 context_
.Say(currentStatementSourcePosition_
,
98 "Impure procedure '%s' may not be referenced in DO CONCURRENT"_err_en_US
,
104 template <typename T
> bool Pre(const parser::Statement
<T
> &statement
) {
105 currentStatementSourcePosition_
= statement
.source
;
106 if (statement
.label
.has_value()) {
107 labels_
.insert(*statement
.label
);
111 template <typename T
> bool Pre(const parser::UnlabeledStatement
<T
> &stmt
) {
112 currentStatementSourcePosition_
= stmt
.source
;
115 bool Pre(const parser::CallStmt
&x
) {
116 if (x
.typedCall
.get()) {
117 if (auto bad
{FindImpureCall(context_
.foldingContext(), *x
.typedCall
)}) {
118 context_
.Say(currentStatementSourcePosition_
,
119 "Impure procedure '%s' may not be referenced in DO CONCURRENT"_err_en_US
,
125 bool Pre(const parser::ConcurrentHeader
&) {
126 // handled in CheckConcurrentHeader
129 template <typename T
> void Post(const T
&) {}
131 // C1140 -- Can't deallocate a polymorphic entity in a DO CONCURRENT.
132 // Deallocation can be caused by exiting a block that declares an allocatable
133 // entity, assignment to an allocatable variable, or an actual DEALLOCATE
136 // Note also that the deallocation of a derived type entity might cause the
137 // invocation of an IMPURE final subroutine. (C1139)
140 // Predicate for deallocations caused by block exit and direct deallocation
141 static bool DeallocateAll(const Symbol
&) { return true; }
143 // Predicate for deallocations caused by intrinsic assignment
144 static bool DeallocateNonCoarray(const Symbol
&component
) {
145 return !evaluate::IsCoarray(component
);
148 static bool WillDeallocatePolymorphic(const Symbol
&entity
,
149 const std::function
<bool(const Symbol
&)> &WillDeallocate
) {
150 return WillDeallocate(entity
) && IsPolymorphicAllocatable(entity
);
153 // Is it possible that we will we deallocate a polymorphic entity or one
154 // of its components?
155 static bool MightDeallocatePolymorphic(const Symbol
&original
,
156 const std::function
<bool(const Symbol
&)> &WillDeallocate
) {
157 const Symbol
&symbol
{ResolveAssociations(original
)};
158 // Check the entity itself, no coarray exception here
159 if (IsPolymorphicAllocatable(symbol
)) {
162 // Check the components
163 if (const auto *details
{symbol
.detailsIf
<ObjectEntityDetails
>()}) {
164 if (const DeclTypeSpec
* entityType
{details
->type()}) {
165 if (const DerivedTypeSpec
* derivedType
{entityType
->AsDerived()}) {
166 UltimateComponentIterator ultimates
{*derivedType
};
167 for (const auto &ultimate
: ultimates
) {
168 if (WillDeallocatePolymorphic(ultimate
, WillDeallocate
)) {
178 void SayDeallocateWithImpureFinal(
179 const Symbol
&entity
, const char *reason
, const Symbol
&impure
) {
180 context_
.SayWithDecl(entity
, currentStatementSourcePosition_
,
181 "Deallocation of an entity with an IMPURE FINAL procedure '%s' caused by %s not allowed in DO CONCURRENT"_err_en_US
,
182 impure
.name(), reason
);
185 void SayDeallocateOfPolymorph(
186 parser::CharBlock location
, const Symbol
&entity
, const char *reason
) {
187 context_
.SayWithDecl(entity
, location
,
188 "Deallocation of a polymorphic entity caused by %s"
189 " not allowed in DO CONCURRENT"_err_en_US
,
193 // Deallocation caused by block exit
194 // Allocatable entities and all of their allocatable subcomponents will be
195 // deallocated. This test is different from the other two because it does
196 // not deallocate in cases where the entity itself is not allocatable but
197 // has allocatable polymorphic components
198 void Post(const parser::BlockConstruct
&blockConstruct
) {
199 const auto &endBlockStmt
{
200 std::get
<parser::Statement
<parser::EndBlockStmt
>>(blockConstruct
.t
)};
201 const Scope
&blockScope
{context_
.FindScope(endBlockStmt
.source
)};
202 const Scope
&doScope
{context_
.FindScope(doConcurrentSourcePosition_
)};
203 if (DoesScopeContain(&doScope
, blockScope
)) {
204 const char *reason
{"block exit"};
205 for (auto &pair
: blockScope
) {
206 const Symbol
&entity
{*pair
.second
};
207 if (IsAllocatable(entity
) && !IsSaved(entity
) &&
208 MightDeallocatePolymorphic(entity
, DeallocateAll
)) {
209 SayDeallocateOfPolymorph(endBlockStmt
.source
, entity
, reason
);
211 if (const Symbol
* impure
{HasImpureFinal(entity
)}) {
212 SayDeallocateWithImpureFinal(entity
, reason
, *impure
);
218 // Deallocation caused by assignment
219 // Note that this case does not cause deallocation of coarray components
220 void Post(const parser::AssignmentStmt
&stmt
) {
221 const auto &variable
{std::get
<parser::Variable
>(stmt
.t
)};
222 if (const Symbol
* entity
{GetLastName(variable
).symbol
}) {
223 const char *reason
{"assignment"};
224 if (MightDeallocatePolymorphic(*entity
, DeallocateNonCoarray
)) {
225 SayDeallocateOfPolymorph(variable
.GetSource(), *entity
, reason
);
227 if (const auto *assignment
{GetAssignment(stmt
)}) {
228 const auto &lhs
{assignment
->lhs
};
229 if (const Symbol
* impure
{HasImpureFinal(*entity
, lhs
.Rank())}) {
230 SayDeallocateWithImpureFinal(*entity
, reason
, *impure
);
234 if (const auto *assignment
{GetAssignment(stmt
)}) {
235 if (const auto *call
{
236 std::get_if
<evaluate::ProcedureRef
>(&assignment
->u
)}) {
237 if (auto bad
{FindImpureCall(context_
.foldingContext(), *call
)}) {
238 context_
.Say(currentStatementSourcePosition_
,
239 "The defined assignment subroutine '%s' is not pure"_err_en_US
,
246 // Deallocation from a DEALLOCATE statement
247 // This case is different because DEALLOCATE statements deallocate both
248 // ALLOCATABLE and POINTER entities
249 void Post(const parser::DeallocateStmt
&stmt
) {
250 const auto &allocateObjectList
{
251 std::get
<std::list
<parser::AllocateObject
>>(stmt
.t
)};
252 for (const auto &allocateObject
: allocateObjectList
) {
253 const parser::Name
&name
{GetLastName(allocateObject
)};
254 const char *reason
{"a DEALLOCATE statement"};
256 const Symbol
&entity
{*name
.symbol
};
257 const DeclTypeSpec
*entityType
{entity
.GetType()};
258 if ((entityType
&& entityType
->IsPolymorphic()) || // POINTER case
259 MightDeallocatePolymorphic(entity
, DeallocateAll
)) {
260 SayDeallocateOfPolymorph(
261 currentStatementSourcePosition_
, entity
, reason
);
263 if (const Symbol
* impure
{HasImpureFinal(entity
)}) {
264 SayDeallocateWithImpureFinal(entity
, reason
, *impure
);
270 // C1137 -- No image control statements in a DO CONCURRENT
271 void Post(const parser::ExecutableConstruct
&construct
) {
272 if (IsImageControlStmt(construct
)) {
273 const parser::CharBlock statementLocation
{
274 GetImageControlStmtLocation(construct
)};
275 auto &msg
{context_
.Say(statementLocation
,
276 "An image control statement is not allowed in DO CONCURRENT"_err_en_US
)};
277 if (auto coarrayMsg
{GetImageControlStmtCoarrayMsg(construct
)}) {
278 msg
.Attach(statementLocation
, *coarrayMsg
);
280 msg
.Attach(doConcurrentSourcePosition_
, GetEnclosingDoMsg());
284 // C1136 -- No RETURN statements in a DO CONCURRENT
285 void Post(const parser::ReturnStmt
&) {
287 .Say(currentStatementSourcePosition_
,
288 "RETURN is not allowed in DO CONCURRENT"_err_en_US
)
289 .Attach(doConcurrentSourcePosition_
, GetEnclosingDoMsg());
292 // C1145, C1146: cannot call ieee_[gs]et_flag, ieee_[gs]et_halting_mode,
293 // ieee_[gs]et_status, ieee_set_rounding_mode, or ieee_set_underflow_mode
294 void Post(const parser::ProcedureDesignator
&procedureDesignator
) {
295 if (auto *name
{std::get_if
<parser::Name
>(&procedureDesignator
.u
)}) {
297 const Symbol
&ultimate
{name
->symbol
->GetUltimate()};
298 const Scope
&scope
{ultimate
.owner()};
299 if (const Symbol
* module
{scope
.IsModule() ? scope
.symbol() : nullptr};
301 (module
->name() == "__fortran_ieee_arithmetic" ||
302 module
->name() == "__fortran_ieee_exceptions")) {
303 std::string s
{ultimate
.name().ToString()};
304 static constexpr const char *badName
[]{"ieee_get_flag",
305 "ieee_set_flag", "ieee_get_halting_mode", "ieee_set_halting_mode",
306 "ieee_get_status", "ieee_set_status", "ieee_set_rounding_mode",
307 "ieee_set_underflow_mode", nullptr};
308 for (std::size_t j
{0}; badName
[j
]; ++j
) {
309 if (s
.find(badName
[j
]) != s
.npos
) {
312 "'%s' may not be called in DO CONCURRENT"_err_en_US
,
314 .Attach(doConcurrentSourcePosition_
, GetEnclosingDoMsg());
323 // 11.1.7.5, paragraph 5, no ADVANCE specifier in a DO CONCURRENT
324 void Post(const parser::IoControlSpec
&ioControlSpec
) {
326 std::get_if
<parser::IoControlSpec::CharExpr
>(&ioControlSpec
.u
)}) {
327 if (std::get
<parser::IoControlSpec::CharExpr::Kind
>(charExpr
->t
) ==
328 parser::IoControlSpec::CharExpr::Kind::Advance
) {
329 SayWithDo(context_
, currentStatementSourcePosition_
,
330 "ADVANCE specifier is not allowed in DO"
331 " CONCURRENT"_err_en_US
,
332 doConcurrentSourcePosition_
);
338 std::set
<parser::Label
> labels_
;
339 parser::CharBlock currentStatementSourcePosition_
;
340 SemanticsContext
&context_
;
341 parser::CharBlock doConcurrentSourcePosition_
;
342 }; // class DoConcurrentBodyEnforce
344 // Class for enforcing C1130 -- in a DO CONCURRENT with DEFAULT(NONE),
345 // variables from enclosing scopes must have their locality specified
346 class DoConcurrentVariableEnforce
{
348 DoConcurrentVariableEnforce(
349 SemanticsContext
&context
, parser::CharBlock doConcurrentSourcePosition
)
351 doConcurrentSourcePosition_
{doConcurrentSourcePosition
},
352 blockScope_
{context
.FindScope(doConcurrentSourcePosition_
)} {}
354 template <typename T
> bool Pre(const T
&) { return true; }
355 template <typename T
> void Post(const T
&) {}
357 // Check to see if the name is a variable from an enclosing scope
358 void Post(const parser::Name
&name
) {
359 if (const Symbol
* symbol
{name
.symbol
}) {
360 if (IsVariableName(*symbol
)) {
361 const Scope
&variableScope
{symbol
->owner()};
362 if (DoesScopeContain(&variableScope
, blockScope_
)) {
363 context_
.SayWithDecl(*symbol
, name
.source
,
364 "Variable '%s' from an enclosing scope referenced in DO "
365 "CONCURRENT with DEFAULT(NONE) must appear in a "
366 "locality-spec"_err_en_US
,
374 SemanticsContext
&context_
;
375 parser::CharBlock doConcurrentSourcePosition_
;
376 const Scope
&blockScope_
;
377 }; // class DoConcurrentVariableEnforce
379 // Find a DO or FORALL and enforce semantics checks on its body
382 DoContext(SemanticsContext
&context
, IndexVarKind kind
,
383 const std::list
<IndexVarKind
> nesting
)
384 : context_
{context
}, kind_
{kind
} {
385 if (!nesting
.empty()) {
386 concurrentNesting_
= nesting
.back();
390 // Mark this DO construct as a point of definition for the DO variables
391 // or index-names it contains. If they're already defined, emit an error
392 // message. We need to remember both the variable and the source location of
393 // the variable in the DO construct so that we can remove it when we leave
394 // the DO construct and use its location in error messages.
395 void DefineDoVariables(const parser::DoConstruct
&doConstruct
) {
396 if (doConstruct
.IsDoNormal()) {
397 context_
.ActivateIndexVar(GetDoVariable(doConstruct
), IndexVarKind::DO
);
398 } else if (doConstruct
.IsDoConcurrent()) {
399 if (const auto &loopControl
{doConstruct
.GetLoopControl()}) {
400 ActivateIndexVars(GetControls(*loopControl
));
405 // Called at the end of a DO construct to deactivate the DO construct
406 void ResetDoVariables(const parser::DoConstruct
&doConstruct
) {
407 if (doConstruct
.IsDoNormal()) {
408 context_
.DeactivateIndexVar(GetDoVariable(doConstruct
));
409 } else if (doConstruct
.IsDoConcurrent()) {
410 if (const auto &loopControl
{doConstruct
.GetLoopControl()}) {
411 DeactivateIndexVars(GetControls(*loopControl
));
416 void ActivateIndexVars(const std::list
<parser::ConcurrentControl
> &controls
) {
417 for (const auto &control
: controls
) {
418 context_
.ActivateIndexVar(std::get
<parser::Name
>(control
.t
), kind_
);
421 void DeactivateIndexVars(
422 const std::list
<parser::ConcurrentControl
> &controls
) {
423 for (const auto &control
: controls
) {
424 context_
.DeactivateIndexVar(std::get
<parser::Name
>(control
.t
));
428 void Check(const parser::DoConstruct
&doConstruct
) {
429 if (doConstruct
.IsDoConcurrent()) {
430 CheckDoConcurrent(doConstruct
);
431 } else if (doConstruct
.IsDoNormal()) {
432 CheckDoNormal(doConstruct
);
434 // TODO: handle the other cases
438 void Check(const parser::ForallStmt
&stmt
) {
439 CheckConcurrentHeader(GetConcurrentHeader(stmt
));
441 void Check(const parser::ForallConstruct
&construct
) {
442 CheckConcurrentHeader(GetConcurrentHeader(construct
));
445 void Check(const parser::ForallAssignmentStmt
&stmt
) {
446 if (const evaluate::Assignment
*
447 assignment
{common::visit(
448 common::visitors
{[&](const auto &x
) { return GetAssignment(x
); }},
450 CheckForallIndexesUsed(*assignment
);
451 CheckForImpureCall(assignment
->lhs
, kind_
);
452 CheckForImpureCall(assignment
->rhs
, kind_
);
454 if (IsVariable(assignment
->lhs
)) {
455 if (const Symbol
* symbol
{GetLastSymbol(assignment
->lhs
)}) {
456 if (auto impureFinal
{
457 HasImpureFinal(*symbol
, assignment
->lhs
.Rank())}) {
458 context_
.SayWithDecl(*symbol
, parser::FindSourceLocation(stmt
),
459 "Impure procedure '%s' is referenced by finalization in a %s"_err_en_US
,
460 impureFinal
->name(), LoopKindName());
465 if (const auto *proc
{
466 std::get_if
<evaluate::ProcedureRef
>(&assignment
->u
)}) {
467 CheckForImpureCall(*proc
, kind_
);
471 [](const evaluate::Assignment::Intrinsic
&) {},
472 [&](const evaluate::ProcedureRef
&proc
) {
473 CheckForImpureCall(proc
, kind_
);
475 [&](const evaluate::Assignment::BoundsSpec
&bounds
) {
476 for (const auto &bound
: bounds
) {
477 CheckForImpureCall(SomeExpr
{bound
}, kind_
);
480 [&](const evaluate::Assignment::BoundsRemapping
&bounds
) {
481 for (const auto &bound
: bounds
) {
482 CheckForImpureCall(SomeExpr
{bound
.first
}, kind_
);
483 CheckForImpureCall(SomeExpr
{bound
.second
}, kind_
);
492 void SayBadDoControl(parser::CharBlock sourceLocation
) {
493 context_
.Say(sourceLocation
, "DO controls should be INTEGER"_err_en_US
);
496 void CheckDoControl(const parser::CharBlock
&sourceLocation
, bool isReal
) {
498 context_
.Warn(common::LanguageFeature::RealDoControls
, sourceLocation
,
499 "DO controls should be INTEGER"_port_en_US
);
501 SayBadDoControl(sourceLocation
);
505 void CheckDoVariable(const parser::ScalarName
&scalarName
) {
506 const parser::CharBlock
&sourceLocation
{scalarName
.thing
.source
};
507 if (const Symbol
* symbol
{scalarName
.thing
.symbol
}) {
508 if (!IsVariableName(*symbol
)) {
510 sourceLocation
, "DO control must be an INTEGER variable"_err_en_US
);
511 } else if (auto why
{WhyNotDefinable(sourceLocation
,
512 context_
.FindScope(sourceLocation
), DefinabilityFlags
{},
516 "'%s' may not be used as a DO variable"_err_en_US
,
518 .Attach(std::move(why
->set_severity(parser::Severity::Because
)));
520 const DeclTypeSpec
*symType
{symbol
->GetType()};
522 SayBadDoControl(sourceLocation
);
524 if (!symType
->IsNumeric(TypeCategory::Integer
)) {
526 sourceLocation
, symType
->IsNumeric(TypeCategory::Real
));
529 } // No messages for INTEGER
533 // Semantic checks for the limit and step expressions
534 void CheckDoExpression(const parser::ScalarExpr
&scalarExpression
) {
535 if (const SomeExpr
* expr
{GetExpr(context_
, scalarExpression
)}) {
536 if (!ExprHasTypeCategory(*expr
, TypeCategory::Integer
)) {
537 // No warnings or errors for type INTEGER
538 const parser::CharBlock
&loc
{scalarExpression
.thing
.value().source
};
539 CheckDoControl(loc
, ExprHasTypeCategory(*expr
, TypeCategory::Real
));
544 void CheckDoNormal(const parser::DoConstruct
&doConstruct
) {
545 // C1120 -- types of DO variables must be INTEGER, extended by allowing
546 // REAL and DOUBLE PRECISION
547 const Bounds
&bounds
{GetBounds(doConstruct
)};
548 CheckDoVariable(bounds
.name
);
549 CheckDoExpression(bounds
.lower
);
550 CheckDoExpression(bounds
.upper
);
552 CheckDoExpression(*bounds
.step
);
553 if (IsZero(*bounds
.step
)) {
554 context_
.Warn(common::UsageWarning::ZeroDoStep
,
555 bounds
.step
->thing
.value().source
,
556 "DO step expression should not be zero"_warn_en_US
);
561 void CheckDoConcurrent(const parser::DoConstruct
&doConstruct
) {
563 std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(doConstruct
.t
)};
564 currentStatementSourcePosition_
= doStmt
.source
;
566 const parser::Block
&block
{std::get
<parser::Block
>(doConstruct
.t
)};
567 DoConcurrentBodyEnforce doConcurrentBodyEnforce
{context_
, doStmt
.source
};
568 parser::Walk(block
, doConcurrentBodyEnforce
);
570 LabelEnforce doConcurrentLabelEnforce
{context_
,
571 doConcurrentBodyEnforce
.labels(), currentStatementSourcePosition_
,
573 parser::Walk(block
, doConcurrentLabelEnforce
);
575 const auto &loopControl
{doConstruct
.GetLoopControl()};
576 CheckConcurrentLoopControl(*loopControl
);
577 CheckLocalitySpecs(*loopControl
, block
);
580 // Return a set of symbols whose names are in a Local locality-spec. Look
581 // the names up in the scope that encloses the DO construct to avoid getting
582 // the local versions of them. Then follow the host-, use-, and
583 // construct-associations to get the root symbols
584 UnorderedSymbolSet
GatherLocals(
585 const std::list
<parser::LocalitySpec
> &localitySpecs
) const {
586 UnorderedSymbolSet symbols
;
587 const Scope
&parentScope
{
588 context_
.FindScope(currentStatementSourcePosition_
).parent()};
589 // Loop through the LocalitySpec::Local locality-specs
590 for (const auto &ls
: localitySpecs
) {
591 if (const auto *names
{std::get_if
<parser::LocalitySpec::Local
>(&ls
.u
)}) {
592 // Loop through the names in the Local locality-spec getting their
594 for (const parser::Name
&name
: names
->v
) {
595 if (const Symbol
* symbol
{parentScope
.FindSymbol(name
.source
)}) {
596 symbols
.insert(ResolveAssociations(*symbol
));
604 UnorderedSymbolSet
GatherSymbolsFromExpression(
605 const parser::Expr
&expression
) const {
606 UnorderedSymbolSet result
;
607 if (const auto *expr
{GetExpr(context_
, expression
)}) {
608 for (const Symbol
&symbol
: evaluate::CollectSymbols(*expr
)) {
609 result
.insert(ResolveAssociations(symbol
));
615 // C1121 - procedures in mask must be pure
616 void CheckMaskIsPure(const parser::ScalarLogicalExpr
&mask
) const {
617 UnorderedSymbolSet references
{
618 GatherSymbolsFromExpression(mask
.thing
.thing
.value())};
619 for (const Symbol
&ref
: OrderBySourcePosition(references
)) {
620 if (IsProcedure(ref
) && !IsPureProcedure(ref
)) {
621 context_
.SayWithDecl(ref
, parser::Unwrap
<parser::Expr
>(mask
)->source
,
622 "%s mask expression may not reference impure procedure '%s'"_err_en_US
,
623 LoopKindName(), ref
.name());
629 void CheckNoCollisions(const UnorderedSymbolSet
&refs
,
630 const UnorderedSymbolSet
&uses
, parser::MessageFixedText
&&errorMessage
,
631 const parser::CharBlock
&refPosition
) const {
632 for (const Symbol
&ref
: OrderBySourcePosition(refs
)) {
633 if (uses
.find(ref
) != uses
.end()) {
634 context_
.SayWithDecl(ref
, refPosition
, std::move(errorMessage
),
635 LoopKindName(), ref
.name());
641 void HasNoReferences(const UnorderedSymbolSet
&indexNames
,
642 const parser::ScalarIntExpr
&expr
) const {
643 CheckNoCollisions(GatherSymbolsFromExpression(expr
.thing
.thing
.value()),
645 "%s limit expression may not reference index variable '%s'"_err_en_US
,
646 expr
.thing
.thing
.value().source
);
649 // C1129, names in local locality-specs can't be in mask expressions
650 void CheckMaskDoesNotReferenceLocal(const parser::ScalarLogicalExpr
&mask
,
651 const UnorderedSymbolSet
&localVars
) const {
652 CheckNoCollisions(GatherSymbolsFromExpression(mask
.thing
.thing
.value()),
654 "%s mask expression references variable '%s'"
655 " in LOCAL locality-spec"_err_en_US
,
656 mask
.thing
.thing
.value().source
);
659 // C1129, names in local locality-specs can't be in limit or step
661 void CheckExprDoesNotReferenceLocal(const parser::ScalarIntExpr
&expr
,
662 const UnorderedSymbolSet
&localVars
) const {
663 CheckNoCollisions(GatherSymbolsFromExpression(expr
.thing
.thing
.value()),
665 "%s expression references variable '%s'"
666 " in LOCAL locality-spec"_err_en_US
,
667 expr
.thing
.thing
.value().source
);
670 // C1130, DEFAULT(NONE) locality requires names to be in locality-specs to
671 // be used in the body of the DO loop
672 void CheckDefaultNoneImpliesExplicitLocality(
673 const std::list
<parser::LocalitySpec
> &localitySpecs
,
674 const parser::Block
&block
) const {
675 bool hasDefaultNone
{false};
676 for (auto &ls
: localitySpecs
) {
677 if (std::holds_alternative
<parser::LocalitySpec::DefaultNone
>(ls
.u
)) {
678 if (hasDefaultNone
) {
679 // F'2023 C1129, you can only have one DEFAULT(NONE)
680 context_
.Warn(common::LanguageFeature::BenignRedundancy
,
681 currentStatementSourcePosition_
,
682 "Only one DEFAULT(NONE) may appear"_port_en_US
);
685 hasDefaultNone
= true;
688 if (hasDefaultNone
) {
689 DoConcurrentVariableEnforce doConcurrentVariableEnforce
{
690 context_
, currentStatementSourcePosition_
};
691 parser::Walk(block
, doConcurrentVariableEnforce
);
695 void CheckReduce(const parser::LocalitySpec::Reduce
&reduce
) const {
696 const parser::ReductionOperator
&reductionOperator
{
697 std::get
<parser::ReductionOperator
>(reduce
.t
)};
698 // F'2023 C1132, reduction variables should have suitable intrinsic type
699 for (const parser::Name
&x
: std::get
<std::list
<parser::Name
>>(reduce
.t
)) {
700 bool supportedIdentifier
{false};
701 if (x
.symbol
&& x
.symbol
->GetType()) {
702 const auto *type
{x
.symbol
->GetType()};
703 auto typeMismatch
{[&](const char *suitable_types
) {
704 context_
.Say(currentStatementSourcePosition_
,
705 "Reduction variable '%s' ('%s') does not have a suitable type ('%s')."_err_en_US
,
706 x
.symbol
->name(), type
->AsFortran(), suitable_types
);
708 supportedIdentifier
= true;
709 switch (reductionOperator
.v
) {
710 case parser::ReductionOperator::Operator::Plus
:
711 case parser::ReductionOperator::Operator::Multiply
:
712 if (!(type
->IsNumeric(TypeCategory::Complex
) ||
713 type
->IsNumeric(TypeCategory::Integer
) ||
714 type
->IsNumeric(TypeCategory::Real
))) {
715 typeMismatch("COMPLEX', 'INTEGER', or 'REAL");
718 case parser::ReductionOperator::Operator::And
:
719 case parser::ReductionOperator::Operator::Or
:
720 case parser::ReductionOperator::Operator::Eqv
:
721 case parser::ReductionOperator::Operator::Neqv
:
722 if (type
->category() != DeclTypeSpec::Category::Logical
) {
723 typeMismatch("LOGICAL");
726 case parser::ReductionOperator::Operator::Max
:
727 case parser::ReductionOperator::Operator::Min
:
728 if (!(type
->IsNumeric(TypeCategory::Integer
) ||
729 type
->IsNumeric(TypeCategory::Real
))) {
730 typeMismatch("INTEGER', or 'REAL");
733 case parser::ReductionOperator::Operator::Iand
:
734 case parser::ReductionOperator::Operator::Ior
:
735 case parser::ReductionOperator::Operator::Ieor
:
736 if (!type
->IsNumeric(TypeCategory::Integer
)) {
737 typeMismatch("INTEGER");
742 if (!supportedIdentifier
) {
743 context_
.Say(currentStatementSourcePosition_
,
744 "Invalid identifier in REDUCE clause."_err_en_US
);
749 // C1123, concurrent limit or step expressions can't reference index-names
750 void CheckConcurrentHeader(const parser::ConcurrentHeader
&header
) const {
751 if (const auto &mask
{
752 std::get
<std::optional
<parser::ScalarLogicalExpr
>>(header
.t
)}) {
753 CheckMaskIsPure(*mask
);
755 const auto &controls
{
756 std::get
<std::list
<parser::ConcurrentControl
>>(header
.t
)};
757 UnorderedSymbolSet indexNames
;
758 for (const parser::ConcurrentControl
&control
: controls
) {
759 const auto &indexName
{std::get
<parser::Name
>(control
.t
)};
760 if (indexName
.symbol
) {
761 indexNames
.insert(*indexName
.symbol
);
763 CheckForImpureCall(std::get
<1>(control
.t
), concurrentNesting_
);
764 CheckForImpureCall(std::get
<2>(control
.t
), concurrentNesting_
);
765 if (const auto &stride
{std::get
<3>(control
.t
)}) {
766 CheckForImpureCall(*stride
, concurrentNesting_
);
769 if (!indexNames
.empty()) {
770 for (const parser::ConcurrentControl
&control
: controls
) {
771 HasNoReferences(indexNames
, std::get
<1>(control
.t
));
772 HasNoReferences(indexNames
, std::get
<2>(control
.t
));
773 if (const auto &intExpr
{
774 std::get
<std::optional
<parser::ScalarIntExpr
>>(control
.t
)}) {
775 const parser::Expr
&expr
{intExpr
->thing
.thing
.value()};
776 CheckNoCollisions(GatherSymbolsFromExpression(expr
), indexNames
,
777 "%s step expression may not reference index variable '%s'"_err_en_US
,
780 context_
.Say(expr
.source
,
781 "%s step expression may not be zero"_err_en_US
, LoopKindName());
788 void CheckLocalitySpecs(
789 const parser::LoopControl
&control
, const parser::Block
&block
) const {
790 const auto &concurrent
{
791 std::get
<parser::LoopControl::Concurrent
>(control
.u
)};
792 const auto &header
{std::get
<parser::ConcurrentHeader
>(concurrent
.t
)};
793 const auto &localitySpecs
{
794 std::get
<std::list
<parser::LocalitySpec
>>(concurrent
.t
)};
795 if (!localitySpecs
.empty()) {
796 const UnorderedSymbolSet
&localVars
{GatherLocals(localitySpecs
)};
797 for (const auto &c
: GetControls(control
)) {
798 CheckExprDoesNotReferenceLocal(std::get
<1>(c
.t
), localVars
);
799 CheckExprDoesNotReferenceLocal(std::get
<2>(c
.t
), localVars
);
800 if (const auto &expr
{
801 std::get
<std::optional
<parser::ScalarIntExpr
>>(c
.t
)}) {
802 CheckExprDoesNotReferenceLocal(*expr
, localVars
);
805 if (const auto &mask
{
806 std::get
<std::optional
<parser::ScalarLogicalExpr
>>(header
.t
)}) {
807 CheckMaskDoesNotReferenceLocal(*mask
, localVars
);
809 for (auto &ls
: localitySpecs
) {
810 if (const auto *reduce
{
811 std::get_if
<parser::LocalitySpec::Reduce
>(&ls
.u
)}) {
812 CheckReduce(*reduce
);
815 CheckDefaultNoneImpliesExplicitLocality(localitySpecs
, block
);
819 // check constraints [C1121 .. C1130]
820 void CheckConcurrentLoopControl(const parser::LoopControl
&control
) const {
821 const auto &concurrent
{
822 std::get
<parser::LoopControl::Concurrent
>(control
.u
)};
823 CheckConcurrentHeader(std::get
<parser::ConcurrentHeader
>(concurrent
.t
));
826 template <typename T
>
827 void CheckForImpureCall(
828 const T
&x
, std::optional
<IndexVarKind
> nesting
) const {
829 if (auto bad
{FindImpureCall(context_
.foldingContext(), x
)}) {
832 "Impure procedure '%s' may not be referenced in a %s"_err_en_US
,
833 *bad
, LoopKindName(*nesting
));
836 "Impure procedure '%s' should not be referenced in a %s header"_warn_en_US
,
837 *bad
, LoopKindName(kind_
));
841 void CheckForImpureCall(const parser::ScalarIntExpr
&x
,
842 std::optional
<IndexVarKind
> nesting
) const {
843 const auto &parsedExpr
{x
.thing
.thing
.value()};
844 auto oldLocation
{context_
.location()};
845 context_
.set_location(parsedExpr
.source
);
846 if (const auto &typedExpr
{parsedExpr
.typedExpr
}) {
847 if (const auto &expr
{typedExpr
->v
}) {
848 CheckForImpureCall(*expr
, nesting
);
851 context_
.set_location(oldLocation
);
854 // Each index should be used on the LHS of each assignment in a FORALL
855 void CheckForallIndexesUsed(const evaluate::Assignment
&assignment
) {
856 SymbolVector indexVars
{context_
.GetIndexVars(IndexVarKind::FORALL
)};
857 if (!indexVars
.empty()) {
858 UnorderedSymbolSet symbols
{evaluate::CollectSymbols(assignment
.lhs
)};
861 [&](const evaluate::Assignment::BoundsSpec
&spec
) {
862 for (const auto &bound
: spec
) {
863 // TODO: this is working around missing std::set::merge in some versions of
864 // clang that we are building with
866 auto boundSymbols
{evaluate::CollectSymbols(bound
)};
867 symbols
.insert(boundSymbols
.begin(), boundSymbols
.end());
869 symbols
.merge(evaluate::CollectSymbols(bound
));
873 [&](const evaluate::Assignment::BoundsRemapping
&remapping
) {
874 for (const auto &bounds
: remapping
) {
876 auto lbSymbols
{evaluate::CollectSymbols(bounds
.first
)};
877 symbols
.insert(lbSymbols
.begin(), lbSymbols
.end());
878 auto ubSymbols
{evaluate::CollectSymbols(bounds
.second
)};
879 symbols
.insert(ubSymbols
.begin(), ubSymbols
.end());
881 symbols
.merge(evaluate::CollectSymbols(bounds
.first
));
882 symbols
.merge(evaluate::CollectSymbols(bounds
.second
));
889 for (const Symbol
&index
: indexVars
) {
890 if (symbols
.count(index
) == 0) {
891 context_
.Warn(common::UsageWarning::UnusedForallIndex
,
892 "FORALL index variable '%s' not used on left-hand side of assignment"_warn_en_US
,
899 // For messages where the DO loop must be DO CONCURRENT, make that explicit.
900 const char *LoopKindName(IndexVarKind kind
) const {
901 return kind
== IndexVarKind::DO
? "DO CONCURRENT" : "FORALL";
903 const char *LoopKindName() const { return LoopKindName(kind_
); }
905 SemanticsContext
&context_
;
906 const IndexVarKind kind_
;
907 parser::CharBlock currentStatementSourcePosition_
;
908 std::optional
<IndexVarKind
> concurrentNesting_
;
909 }; // class DoContext
911 void DoForallChecker::Enter(const parser::DoConstruct
&doConstruct
) {
912 DoContext doContext
{context_
, IndexVarKind::DO
, nestedWithinConcurrent_
};
913 if (doConstruct
.IsDoConcurrent()) {
914 nestedWithinConcurrent_
.push_back(IndexVarKind::DO
);
916 doContext
.DefineDoVariables(doConstruct
);
917 doContext
.Check(doConstruct
);
920 void DoForallChecker::Leave(const parser::DoConstruct
&doConstruct
) {
921 DoContext doContext
{context_
, IndexVarKind::DO
, nestedWithinConcurrent_
};
922 doContext
.ResetDoVariables(doConstruct
);
923 if (doConstruct
.IsDoConcurrent()) {
924 nestedWithinConcurrent_
.pop_back();
928 void DoForallChecker::Enter(const parser::ForallConstruct
&construct
) {
929 DoContext doContext
{context_
, IndexVarKind::FORALL
, nestedWithinConcurrent_
};
930 doContext
.ActivateIndexVars(GetControls(construct
));
931 nestedWithinConcurrent_
.push_back(IndexVarKind::FORALL
);
932 doContext
.Check(construct
);
934 void DoForallChecker::Leave(const parser::ForallConstruct
&construct
) {
935 DoContext doContext
{context_
, IndexVarKind::FORALL
, nestedWithinConcurrent_
};
936 doContext
.DeactivateIndexVars(GetControls(construct
));
937 nestedWithinConcurrent_
.pop_back();
940 void DoForallChecker::Enter(const parser::ForallStmt
&stmt
) {
941 DoContext doContext
{context_
, IndexVarKind::FORALL
, nestedWithinConcurrent_
};
942 nestedWithinConcurrent_
.push_back(IndexVarKind::FORALL
);
943 doContext
.Check(stmt
);
944 doContext
.ActivateIndexVars(GetControls(stmt
));
946 void DoForallChecker::Leave(const parser::ForallStmt
&stmt
) {
947 DoContext doContext
{context_
, IndexVarKind::FORALL
, nestedWithinConcurrent_
};
948 doContext
.DeactivateIndexVars(GetControls(stmt
));
949 nestedWithinConcurrent_
.pop_back();
951 void DoForallChecker::Leave(const parser::ForallAssignmentStmt
&stmt
) {
952 DoContext doContext
{context_
, IndexVarKind::FORALL
, nestedWithinConcurrent_
};
953 doContext
.Check(stmt
);
956 template <typename A
>
957 static parser::CharBlock
GetConstructPosition(const A
&a
) {
958 return std::get
<0>(a
.t
).source
;
961 static parser::CharBlock
GetNodePosition(const ConstructNode
&construct
) {
962 return common::visit(
963 [&](const auto &x
) { return GetConstructPosition(*x
); }, construct
);
966 void DoForallChecker::SayBadLeave(StmtType stmtType
,
967 const char *enclosingStmtName
, const ConstructNode
&construct
) const {
969 .Say("%s must not leave a %s statement"_err_en_US
, EnumToString(stmtType
),
971 .Attach(GetNodePosition(construct
), "The construct that was left"_en_US
);
974 static const parser::DoConstruct
*MaybeGetDoConstruct(
975 const ConstructNode
&construct
) {
976 if (const auto *doNode
{
977 std::get_if
<const parser::DoConstruct
*>(&construct
)}) {
984 static bool ConstructIsDoConcurrent(const ConstructNode
&construct
) {
985 const parser::DoConstruct
*doConstruct
{MaybeGetDoConstruct(construct
)};
986 return doConstruct
&& doConstruct
->IsDoConcurrent();
989 // Check that CYCLE and EXIT statements do not cause flow of control to
990 // leave DO CONCURRENT, CRITICAL, or CHANGE TEAM constructs.
991 void DoForallChecker::CheckForBadLeave(
992 StmtType stmtType
, const ConstructNode
&construct
) const {
993 common::visit(common::visitors
{
994 [&](const parser::DoConstruct
*doConstructPtr
) {
995 if (doConstructPtr
->IsDoConcurrent()) {
996 // C1135 and C1167 -- CYCLE and EXIT statements can't
997 // leave a DO CONCURRENT
998 SayBadLeave(stmtType
, "DO CONCURRENT", construct
);
1001 [&](const parser::CriticalConstruct
*) {
1002 // C1135 and C1168 -- similarly, for CRITICAL
1003 SayBadLeave(stmtType
, "CRITICAL", construct
);
1005 [&](const parser::ChangeTeamConstruct
*) {
1006 // C1135 and C1168 -- similarly, for CHANGE TEAM
1007 SayBadLeave(stmtType
, "CHANGE TEAM", construct
);
1009 [](const auto *) {},
1014 static bool StmtMatchesConstruct(const parser::Name
*stmtName
,
1015 StmtType stmtType
, const std::optional
<parser::Name
> &constructName
,
1016 const ConstructNode
&construct
) {
1017 bool inDoConstruct
{MaybeGetDoConstruct(construct
) != nullptr};
1019 return inDoConstruct
; // Unlabeled statements match all DO constructs
1020 } else if (constructName
&& constructName
->source
== stmtName
->source
) {
1021 return stmtType
== StmtType::EXIT
|| inDoConstruct
;
1027 // C1167 Can't EXIT from a DO CONCURRENT
1028 void DoForallChecker::CheckDoConcurrentExit(
1029 StmtType stmtType
, const ConstructNode
&construct
) const {
1030 if (stmtType
== StmtType::EXIT
&& ConstructIsDoConcurrent(construct
)) {
1031 SayBadLeave(StmtType::EXIT
, "DO CONCURRENT", construct
);
1035 // Check nesting violations for a CYCLE or EXIT statement. Loop up the
1036 // nesting levels looking for a construct that matches the CYCLE or EXIT
1037 // statment. At every construct, check for a violation. If we find a match
1038 // without finding a violation, the check is complete.
1039 void DoForallChecker::CheckNesting(
1040 StmtType stmtType
, const parser::Name
*stmtName
) const {
1041 const ConstructStack
&stack
{context_
.constructStack()};
1042 for (auto iter
{stack
.cend()}; iter
-- != stack
.cbegin();) {
1043 const ConstructNode
&construct
{*iter
};
1044 const std::optional
<parser::Name
> &constructName
{
1045 MaybeGetNodeName(construct
)};
1046 if (StmtMatchesConstruct(stmtName
, stmtType
, constructName
, construct
)) {
1047 CheckDoConcurrentExit(stmtType
, construct
);
1048 return; // We got a match, so we're finished checking
1050 CheckForBadLeave(stmtType
, construct
);
1053 // We haven't found a match in the enclosing constructs
1054 if (stmtType
== StmtType::EXIT
) {
1055 context_
.Say("No matching construct for EXIT statement"_err_en_US
);
1057 context_
.Say("No matching DO construct for CYCLE statement"_err_en_US
);
1061 // C1135 -- Nesting for CYCLE statements
1062 void DoForallChecker::Enter(const parser::CycleStmt
&cycleStmt
) {
1063 CheckNesting(StmtType::CYCLE
, common::GetPtrFromOptional(cycleStmt
.v
));
1066 // C1167 and C1168 -- Nesting for EXIT statements
1067 void DoForallChecker::Enter(const parser::ExitStmt
&exitStmt
) {
1068 CheckNesting(StmtType::EXIT
, common::GetPtrFromOptional(exitStmt
.v
));
1071 void DoForallChecker::Leave(const parser::AssignmentStmt
&stmt
) {
1072 const auto &variable
{std::get
<parser::Variable
>(stmt
.t
)};
1073 context_
.CheckIndexVarRedefine(variable
);
1076 static void CheckIfArgIsDoVar(const evaluate::ActualArgument
&arg
,
1077 const parser::CharBlock location
, SemanticsContext
&context
) {
1078 common::Intent intent
{arg
.dummyIntent()};
1079 if (intent
== common::Intent::Out
|| intent
== common::Intent::InOut
) {
1080 if (const SomeExpr
* argExpr
{arg
.UnwrapExpr()}) {
1081 if (const Symbol
* var
{evaluate::UnwrapWholeSymbolDataRef(*argExpr
)}) {
1082 if (intent
== common::Intent::Out
) {
1083 context
.CheckIndexVarRedefine(location
, *var
);
1085 context
.WarnIndexVarRedefine(location
, *var
); // INTENT(INOUT)
1092 // Check to see if a DO variable is being passed as an actual argument to a
1093 // dummy argument whose intent is OUT or INOUT. To do this, we need to find
1094 // the expressions for actual arguments which contain DO variables. We get the
1095 // intents of the dummy arguments from the ProcedureRef in the "typedCall"
1096 // field of the CallStmt which was filled in during expression checking. At
1097 // the same time, we need to iterate over the parser::Expr versions of the
1098 // actual arguments to get their source locations of the arguments for the
1100 void DoForallChecker::Leave(const parser::CallStmt
&callStmt
) {
1101 if (const auto &typedCall
{callStmt
.typedCall
}) {
1102 const auto &parsedArgs
{
1103 std::get
<std::list
<parser::ActualArgSpec
>>(callStmt
.call
.t
)};
1104 auto parsedArgIter
{parsedArgs
.begin()};
1105 const evaluate::ActualArguments
&checkedArgs
{typedCall
->arguments()};
1106 for (const auto &checkedOptionalArg
: checkedArgs
) {
1107 if (parsedArgIter
== parsedArgs
.end()) {
1108 break; // No more parsed arguments, we're done.
1110 const auto &parsedArg
{std::get
<parser::ActualArg
>(parsedArgIter
->t
)};
1112 if (checkedOptionalArg
) {
1113 const evaluate::ActualArgument
&checkedArg
{*checkedOptionalArg
};
1114 if (const auto *parsedExpr
{
1115 std::get_if
<common::Indirection
<parser::Expr
>>(&parsedArg
.u
)}) {
1116 CheckIfArgIsDoVar(checkedArg
, parsedExpr
->value().source
, context_
);
1123 void DoForallChecker::Leave(const parser::ConnectSpec
&connectSpec
) {
1124 const auto *newunit
{
1125 std::get_if
<parser::ConnectSpec::Newunit
>(&connectSpec
.u
)};
1127 context_
.CheckIndexVarRedefine(newunit
->v
.thing
.thing
);
1131 using ActualArgumentSet
= std::set
<evaluate::ActualArgumentRef
>;
1133 struct CollectActualArgumentsHelper
1134 : public evaluate::SetTraverse
<CollectActualArgumentsHelper
,
1135 ActualArgumentSet
> {
1136 using Base
= SetTraverse
<CollectActualArgumentsHelper
, ActualArgumentSet
>;
1137 CollectActualArgumentsHelper() : Base
{*this} {}
1138 using Base::operator();
1139 ActualArgumentSet
operator()(const evaluate::ActualArgument
&arg
) const {
1140 return Combine(ActualArgumentSet
{arg
},
1141 CollectActualArgumentsHelper
{}(arg
.UnwrapExpr()));
1145 template <typename A
> ActualArgumentSet
CollectActualArguments(const A
&x
) {
1146 return CollectActualArgumentsHelper
{}(x
);
1149 template ActualArgumentSet
CollectActualArguments(const SomeExpr
&);
1151 void DoForallChecker::Enter(const parser::Expr
&parsedExpr
) { ++exprDepth_
; }
1153 void DoForallChecker::Leave(const parser::Expr
&parsedExpr
) {
1154 CHECK(exprDepth_
> 0);
1155 if (--exprDepth_
== 0) { // Only check top level expressions
1156 if (const SomeExpr
* expr
{GetExpr(context_
, parsedExpr
)}) {
1157 ActualArgumentSet argSet
{CollectActualArguments(*expr
)};
1158 for (const evaluate::ActualArgumentRef
&argRef
: argSet
) {
1159 CheckIfArgIsDoVar(*argRef
, parsedExpr
.source
, context_
);
1165 void DoForallChecker::Leave(const parser::InquireSpec
&inquireSpec
) {
1166 const auto *intVar
{std::get_if
<parser::InquireSpec::IntVar
>(&inquireSpec
.u
)};
1168 const auto &scalar
{std::get
<parser::ScalarIntVariable
>(intVar
->t
)};
1169 context_
.CheckIndexVarRedefine(scalar
.thing
.thing
);
1173 void DoForallChecker::Leave(const parser::IoControlSpec
&ioControlSpec
) {
1174 const auto *size
{std::get_if
<parser::IoControlSpec::Size
>(&ioControlSpec
.u
)};
1176 context_
.CheckIndexVarRedefine(size
->v
.thing
.thing
);
1180 void DoForallChecker::Leave(const parser::OutputImpliedDo
&outputImpliedDo
) {
1181 const auto &control
{std::get
<parser::IoImpliedDoControl
>(outputImpliedDo
.t
)};
1182 const parser::Name
&name
{control
.name
.thing
.thing
};
1183 context_
.CheckIndexVarRedefine(name
.source
, *name
.symbol
);
1186 void DoForallChecker::Leave(const parser::StatVariable
&statVariable
) {
1187 context_
.CheckIndexVarRedefine(statVariable
.v
.thing
.thing
);
1190 } // namespace Fortran::semantics