FLANG
check-omp-structure.h
1//===-- lib/Semantics/check-omp-structure.h ---------------------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9// OpenMP structure validity check list
10// 1. invalid clauses on directive
11// 2. invalid repeated clauses on directive
12// 3. TODO: invalid nesting of regions
13
14#ifndef FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
15#define FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
16
17#include "check-directive-structure.h"
18#include "flang/Common/enum-set.h"
19#include "flang/Parser/parse-tree.h"
20#include "flang/Semantics/openmp-directive-sets.h"
21#include "flang/Semantics/semantics.h"
22
23using OmpClauseSet =
25
26#define GEN_FLANG_DIRECTIVE_CLAUSE_SETS
27#include "llvm/Frontend/OpenMP/OMP.inc"
28
29namespace llvm {
30namespace omp {
31static OmpClauseSet privateSet{
32 Clause::OMPC_private, Clause::OMPC_firstprivate, Clause::OMPC_lastprivate};
33static OmpClauseSet privateReductionSet{
34 OmpClauseSet{Clause::OMPC_reduction} | privateSet};
35// omp.td cannot differentiate allowed/not allowed clause list for few
36// directives for fortran. nowait is not allowed on begin directive clause list
37// for below list of directives. Directives with conflicting list of clauses are
38// included in below list.
39static const OmpDirectiveSet noWaitClauseNotAllowedSet{
40 Directive::OMPD_do,
41 Directive::OMPD_do_simd,
42 Directive::OMPD_sections,
43 Directive::OMPD_single,
44 Directive::OMPD_workshare,
45};
46} // namespace omp
47} // namespace llvm
48
49namespace Fortran::semantics {
50struct AnalyzedCondStmt;
51
52namespace omp {
53struct LoopSequence;
54}
55
56// Mapping from 'Symbol' to 'Source' to keep track of the variables
57// used in multiple clauses
58using SymbolSourceMap = std::multimap<const Symbol *, parser::CharBlock>;
59// Multimap to check the triple <current_dir, enclosing_dir, enclosing_clause>
60using DirectivesClauseTriple = std::multimap<llvm::omp::Directive,
61 std::pair<llvm::omp::Directive, const OmpClauseSet>>;
62
63using OmpStructureCheckerBase = DirectiveStructureChecker<llvm::omp::Directive,
64 llvm::omp::Clause, parser::OmpClause, llvm::omp::Clause_enumSize>;
65
66class OmpStructureChecker : public OmpStructureCheckerBase {
67public:
68 using Base = OmpStructureCheckerBase;
69
70 OmpStructureChecker(SemanticsContext &context);
71
72 void Enter(const parser::ProgramUnit &);
73 bool Enter(const parser::MainProgram &);
74 void Leave(const parser::MainProgram &);
75 bool Enter(const parser::BlockData &);
76 void Leave(const parser::BlockData &);
77 bool Enter(const parser::Module &);
78 void Leave(const parser::Module &);
79 bool Enter(const parser::Submodule &);
80 void Leave(const parser::Submodule &);
81 bool Enter(const parser::SubroutineStmt &);
82 bool Enter(const parser::EndSubroutineStmt &);
83 bool Enter(const parser::FunctionStmt &);
84 bool Enter(const parser::EndFunctionStmt &);
85 bool Enter(const parser::BlockConstruct &);
86 void Leave(const parser::BlockConstruct &);
87 void Enter(const parser::InternalSubprogram &);
88 void Enter(const parser::ModuleSubprogram &);
89
90 void Enter(const parser::SpecificationPart &);
91 void Leave(const parser::SpecificationPart &);
92 void Enter(const parser::ExecutionPart &);
93 void Leave(const parser::ExecutionPart &);
94
95 void Enter(const parser::OpenMPConstruct &);
96 void Leave(const parser::OpenMPConstruct &);
97 void Enter(const parser::OpenMPDeclarativeConstruct &);
98 void Leave(const parser::OpenMPDeclarativeConstruct &);
99
100 void Enter(const parser::OpenMPMisplacedEndDirective &);
101 void Leave(const parser::OpenMPMisplacedEndDirective &);
102 void Enter(const parser::OpenMPInvalidDirective &);
103 void Leave(const parser::OpenMPInvalidDirective &);
104
105 void Enter(const parser::OpenMPLoopConstruct &);
106 void Leave(const parser::OpenMPLoopConstruct &);
107
108 void Enter(const parser::OpenMPAssumeConstruct &);
109 void Leave(const parser::OpenMPAssumeConstruct &);
110 void Enter(const parser::OpenMPDeclarativeAssumes &);
111 void Leave(const parser::OpenMPDeclarativeAssumes &);
112 void Enter(const parser::OpenMPInteropConstruct &);
113 void Leave(const parser::OpenMPInteropConstruct &);
114 void Enter(const parser::OmpBlockConstruct &);
115 void Leave(const parser::OmpBlockConstruct &);
116 void Leave(const parser::OmpBeginDirective &);
117 void Enter(const parser::OmpEndDirective &);
118 void Leave(const parser::OmpEndDirective &);
119
120 void Enter(const parser::OpenMPSectionsConstruct &);
121 void Leave(const parser::OpenMPSectionsConstruct &);
122 void Enter(const parser::OmpEndSectionsDirective &);
123 void Leave(const parser::OmpEndSectionsDirective &);
124
125 void Enter(const parser::OmpDeclareVariantDirective &);
126 void Leave(const parser::OmpDeclareVariantDirective &);
127 void Enter(const parser::OpenMPDeclareSimdConstruct &);
128 void Leave(const parser::OpenMPDeclareSimdConstruct &);
129 void Enter(const parser::OmpAllocateDirective &);
130 void Leave(const parser::OmpAllocateDirective &);
131 void Enter(const parser::OpenMPDeclareMapperConstruct &);
132 void Leave(const parser::OpenMPDeclareMapperConstruct &);
133 void Enter(const parser::OpenMPDeclareReductionConstruct &);
134 void Leave(const parser::OpenMPDeclareReductionConstruct &);
135 void Enter(const parser::OpenMPDeclareTargetConstruct &);
136 void Leave(const parser::OpenMPDeclareTargetConstruct &);
137 void Enter(const parser::OpenMPDepobjConstruct &);
138 void Leave(const parser::OpenMPDepobjConstruct &);
139 void Enter(const parser::OpenMPDispatchConstruct &);
140 void Leave(const parser::OpenMPDispatchConstruct &);
141 void Enter(const parser::OmpErrorDirective &);
142 void Leave(const parser::OmpErrorDirective &);
143 void Enter(const parser::OmpNothingDirective &);
144 void Leave(const parser::OmpNothingDirective &);
145 void Enter(const parser::OpenMPAllocatorsConstruct &);
146 void Leave(const parser::OpenMPAllocatorsConstruct &);
147 void Enter(const parser::OpenMPRequiresConstruct &);
148 void Leave(const parser::OpenMPRequiresConstruct &);
149 void Enter(const parser::OpenMPGroupprivate &);
150 void Leave(const parser::OpenMPGroupprivate &);
151 void Enter(const parser::OpenMPThreadprivate &);
152 void Leave(const parser::OpenMPThreadprivate &);
153
154 void Enter(const parser::OpenMPSimpleStandaloneConstruct &);
155 void Leave(const parser::OpenMPSimpleStandaloneConstruct &);
156 void Enter(const parser::OpenMPFlushConstruct &);
157 void Leave(const parser::OpenMPFlushConstruct &);
158 void Enter(const parser::OpenMPCancelConstruct &);
159 void Leave(const parser::OpenMPCancelConstruct &);
162 void Enter(const parser::OpenMPCriticalConstruct &);
163 void Leave(const parser::OpenMPCriticalConstruct &);
164 void Enter(const parser::OpenMPAtomicConstruct &);
165 void Leave(const parser::OpenMPAtomicConstruct &);
166
167 void Leave(const parser::OmpClauseList &);
168 void Enter(const parser::OmpClause &);
169
170 void Enter(const parser::DoConstruct &);
171 void Leave(const parser::DoConstruct &);
172
173 void Enter(const parser::OmpDirectiveSpecification &);
174 void Leave(const parser::OmpDirectiveSpecification &);
175
176 void Enter(const parser::OmpMetadirectiveDirective &);
177 void Leave(const parser::OmpMetadirectiveDirective &);
178
179 void Enter(const parser::OmpContextSelector &);
180 void Leave(const parser::OmpContextSelector &);
181
182 template <typename A> void Enter(const parser::Statement<A> &);
183 void Leave(const parser::GotoStmt &);
184 void Leave(const parser::ComputedGotoStmt &);
185 void Leave(const parser::ArithmeticIfStmt &);
186 void Leave(const parser::AssignedGotoStmt &);
187 void Leave(const parser::AltReturnSpec &);
188 void Leave(const parser::ErrLabel &);
189 void Leave(const parser::EndLabel &);
190 void Leave(const parser::EorLabel &);
191
192#define GEN_FLANG_CLAUSE_CHECK_ENTER
193#include "llvm/Frontend/OpenMP/OMP.inc"
194
195private:
196 using LoopOrConstruct = std::variant<const parser::DoConstruct *,
198
199 // Most of these functions are defined in check-omp-structure.cpp, but
200 // some groups have their own files.
201
202 // check-omp-atomic.cpp
203 void CheckStorageOverlap(const evaluate::Expr<evaluate::SomeType> &,
205 void ErrorShouldBeVariable(const MaybeExpr &expr, parser::CharBlock source);
206 void CheckAtomicType(SymbolRef sym, parser::CharBlock source,
207 std::string_view name, bool checkTypeOnPointer = true);
208 void CheckAtomicVariable(const evaluate::Expr<evaluate::SomeType> &,
209 parser::CharBlock, bool checkTypeOnPointer = true);
210 std::pair<const parser::ExecutionPartConstruct *,
212 CheckUpdateCapture(const parser::ExecutionPartConstruct *ec1,
214 void CheckAtomicCaptureAssignment(const evaluate::Assignment &capture,
215 const SomeExpr &atom, parser::CharBlock source);
216 void CheckAtomicReadAssignment(
217 const evaluate::Assignment &read, parser::CharBlock source);
218 void CheckAtomicWriteAssignment(
219 const evaluate::Assignment &write, parser::CharBlock source);
220 std::optional<evaluate::Assignment> CheckAtomicUpdateAssignment(
221 const evaluate::Assignment &update, parser::CharBlock source);
222 std::pair<bool, bool> CheckAtomicUpdateAssignmentRhs(const SomeExpr &atom,
223 const SomeExpr &rhs, parser::CharBlock source, bool suppressDiagnostics);
224 void CheckAtomicConditionalUpdateAssignment(const SomeExpr &cond,
225 parser::CharBlock condSource, const evaluate::Assignment &assign,
226 parser::CharBlock assignSource);
227 void CheckAtomicConditionalUpdateStmt(
228 const AnalyzedCondStmt &update, parser::CharBlock source);
229 void CheckAtomicUpdateOnly(const parser::OpenMPAtomicConstruct &x,
230 const parser::Block &body, parser::CharBlock source);
231 void CheckAtomicConditionalUpdate(const parser::OpenMPAtomicConstruct &x,
232 const parser::Block &body, parser::CharBlock source);
233 void CheckAtomicUpdateCapture(const parser::OpenMPAtomicConstruct &x,
234 const parser::Block &body, parser::CharBlock source);
235 void CheckAtomicConditionalUpdateCapture(
236 const parser::OpenMPAtomicConstruct &x, const parser::Block &body,
237 parser::CharBlock source);
238 void CheckAtomicRead(const parser::OpenMPAtomicConstruct &x);
239 void CheckAtomicWrite(const parser::OpenMPAtomicConstruct &x);
240 void CheckAtomicUpdate(const parser::OpenMPAtomicConstruct &x);
241
242 // check-omp-loop.cpp
243 void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);
244 void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);
245 void CheckSIMDNest(const parser::OpenMPConstruct &x);
246 void CheckRectangularNest(const parser::OmpDirectiveSpecification &spec,
247 const omp::LoopSequence &nest);
248 void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x);
249 const parser::Name GetLoopIndex(const parser::DoConstruct *x);
250 void SetLoopInfo(const parser::OpenMPLoopConstruct &x);
251 void CheckIterationVariableType(const parser::OpenMPLoopConstruct &x);
252 std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);
253 void CheckAssociatedLoopConstraints(const parser::OpenMPLoopConstruct &x);
254 void CheckScanModifier(const parser::OmpClause::Reduction &x);
255 void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
256
257 // check-omp-metadirective.cpp
258 const std::list<parser::OmpTraitProperty> &GetTraitPropertyList(
260 std::optional<llvm::omp::Clause> GetClauseFromProperty(
262
263 void CheckTraitSelectorList(const std::list<parser::OmpTraitSelector> &);
264 void CheckTraitSetSelector(const parser::OmpTraitSetSelector &);
265 void CheckTraitScore(const parser::OmpTraitScore &);
266 bool VerifyTraitPropertyLists(
268 void CheckTraitSelector(
270 void CheckTraitADMO(
272 void CheckTraitCondition(
274 void CheckTraitDeviceNum(
276 void CheckTraitRequires(
278 void CheckTraitSimd(
280
281 // check-omp-structure.cpp
282 bool IsAllowedClause(llvm::omp::Clause clauseId);
283 bool CheckAllowedClause(llvm::omp::Clause clause);
284 void CheckVariableListItem(const SymbolSourceMap &symbols);
285 void CheckDirectiveSpelling(
286 parser::CharBlock spelling, llvm::omp::Directive id);
287 void CheckDirectiveDeprecation(const parser::OpenMPConstruct &x);
288 void AnalyzeObject(const parser::OmpObject &object);
289 void AnalyzeObjects(const parser::OmpObjectList &objects);
290
291 const parser::OpenMPConstruct *GetCurrentConstruct() const;
292 void CheckSourceLabel(const parser::Label &);
293 void CheckLabelContext(const parser::CharBlock, const parser::CharBlock,
295 void ClearLabels();
296 void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars,
297 const std::list<parser::Name> &nameList, const parser::CharBlock &item,
298 const std::string &clauseName);
299 void CheckMultListItems();
300 void CheckStructureComponent(
301 const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
302 bool HasInvalidWorksharingNesting(
303 const parser::OmpDirectiveName &name, const OmpDirectiveSet &);
304
305 bool IsCloselyNestedRegion(const OmpDirectiveSet &set);
306 bool IsNestedInDirective(llvm::omp::Directive directive);
307 bool IsCombinedParallelWorksharing(llvm::omp::Directive directive) const;
308 bool InTargetRegion();
309 void HasInvalidTeamsNesting(
310 const llvm::omp::Directive &dir, const parser::CharBlock &source);
311 bool HasRequires(llvm::omp::Clause req);
312 void CheckAllowedMapTypes(
313 parser::OmpMapType::Value, llvm::ArrayRef<parser::OmpMapType::Value>);
314
315 llvm::StringRef getClauseName(llvm::omp::Clause clause) override;
316 llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override;
317
318 template < //
319 typename LessTy, typename RangeTy,
320 typename IterTy = decltype(std::declval<RangeTy>().begin())>
321 std::optional<IterTy> FindDuplicate(RangeTy &&);
322
323 void CheckDependList(const parser::DataRef &);
324 void CheckDoacross(const parser::OmpDoacross &doa);
325 void CheckDimsModifier(parser::CharBlock source, size_t numValues,
326 const parser::OmpDimsModifier &x);
327 bool IsDataRefTypeParamInquiry(const parser::DataRef *dataRef);
328 void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,
329 const parser::OmpObject &obj, llvm::StringRef clause = "");
330 void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,
331 const parser::OmpObjectList &objList, llvm::StringRef clause = "");
332 void CheckThreadprivateOrDeclareTargetVar(const parser::Designator &);
333 void CheckThreadprivateOrDeclareTargetVar(const parser::Name &);
334 void CheckThreadprivateOrDeclareTargetVar(const parser::OmpObject &);
335 void CheckThreadprivateOrDeclareTargetVar(const parser::OmpObjectList &);
336 void CheckSymbolName(
337 const parser::CharBlock &source, const parser::OmpObject &object);
338 void CheckSymbolNames(
339 const parser::CharBlock &source, const parser::OmpObjectList &objList);
340 void CheckIntentInPointer(SymbolSourceMap &, const llvm::omp::Clause);
341 void CheckAssumedSizeArray(SymbolSourceMap &, const llvm::omp::Clause);
342 void CheckProcedurePointer(SymbolSourceMap &, const llvm::omp::Clause);
343 void CheckCrayPointee(const parser::OmpObjectList &objectList,
344 llvm::StringRef clause, bool suggestToUseCrayPointer = true);
345 void GetSymbolsInObjectList(const parser::OmpObjectList &, SymbolSourceMap &);
346 void CheckDefinableObjects(SymbolSourceMap &, const llvm::omp::Clause);
347 void CheckCopyingPolymorphicAllocatable(
348 SymbolSourceMap &, const llvm::omp::Clause);
349 void CheckPrivateSymbolsInOuterCxt(
350 SymbolSourceMap &, DirectivesClauseTriple &, const llvm::omp::Clause);
351 void CheckIsLoopIvPartOfClause(
352 llvm::omp::Clause clause, const parser::OmpObjectList &ompObjectList);
353 bool CheckTargetBlockOnlyTeams(const parser::Block &);
354 void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
355 void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
356 void CheckIndividualAllocateDirective(
357 const parser::OmpAllocateDirective &x, bool isExecutable);
358 void CheckExecutableAllocateDirective(const parser::OmpAllocateDirective &x);
359
360 void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
361 void CheckIteratorModifier(const parser::OmpIterator &x);
362
363 void CheckTargetNest(const parser::OpenMPConstruct &x);
364 void CheckTargetUpdate();
365 void CheckTaskgraph(const parser::OmpBlockConstruct &x);
366 void CheckDependenceType(const parser::OmpDependenceType::Value &x);
367 void CheckTaskDependenceType(const parser::OmpTaskDependenceType::Value &x);
368 std::optional<llvm::omp::Directive> GetCancelType(
369 llvm::omp::Directive cancelDir, const parser::CharBlock &cancelSource,
370 const std::optional<parser::OmpClauseList> &maybeClauses);
371 void CheckCancellationNest(
372 const parser::CharBlock &source, llvm::omp::Directive type);
373 void CheckReductionObjects(
374 const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
375 bool CheckReductionOperator(const parser::OmpReductionIdentifier &ident,
376 parser::CharBlock source, llvm::omp::Clause clauseId);
377 void CheckReductionObjectTypes(const parser::OmpObjectList &objects,
378 const parser::OmpReductionIdentifier &ident);
379 void CheckReductionModifier(const parser::OmpReductionModifier &);
380 void CheckLastprivateModifier(const parser::OmpLastprivateModifier &);
381 void CheckMasterNesting(const parser::OmpBlockConstruct &x);
382 void ChecksOnOrderedAsBlock();
383 void CheckBarrierNesting(const parser::OpenMPSimpleStandaloneConstruct &x);
384 void CheckScan(const parser::OpenMPSimpleStandaloneConstruct &x);
385 void ChecksOnOrderedAsStandalone();
386 void CheckOrderedDependClause(std::optional<std::int64_t> orderedValue);
387 void CheckReductionArraySection(
388 const parser::OmpObjectList &ompObjectList, llvm::omp::Clause clauseId);
389 void CheckArraySection(const parser::ArrayElement &arrayElement,
390 const parser::Name &name, const llvm::omp::Clause clause);
391 void CheckLastPartRefForArraySection(
392 const parser::Designator &designator, llvm::omp::Clause clauseId);
393 void CheckSharedBindingInOuterContext(
394 const parser::OmpObjectList &ompObjectList);
395 void CheckIfContiguous(const parser::OmpObject &object);
396 const parser::Name *GetObjectName(const parser::OmpObject &object);
397 void CheckInitOnDepobj(const parser::OpenMPDepobjConstruct &depobj,
398 const parser::OmpClause &initClause);
399 void CheckAllowedRequiresClause(llvm::omp::Clause clause);
400 void AddEndDirectiveClauses(const parser::OmpClauseList &clauses);
401
402 void EnterDirectiveNest(const int index) { directiveNest_[index]++; }
403 void ExitDirectiveNest(const int index) { directiveNest_[index]--; }
404 int GetDirectiveNest(const int index) { return directiveNest_[index]; }
405
406 bool deviceConstructFound_{false};
407 enum directiveNestType : int {
408 SIMDNest,
409 TargetBlockOnlyTeams,
410 TargetNest,
411 DeclarativeNest,
412 ContextSelectorNest,
413 MetadirectiveNest,
414 LastType = MetadirectiveNest,
415 };
416 int directiveNest_[LastType + 1] = {0};
417
418 int allocateDirectiveLevel_{0};
419 parser::CharBlock visitedAtomicSource_;
420 SymbolSourceMap deferredNonVariables_;
421
422 // Stack of nested DO loops and OpenMP constructs.
423 // This is used to verify DO loop nest for DOACROSS, and branches into
424 // and out of OpenMP constructs.
425 std::vector<LoopOrConstruct> constructStack_;
426 // Scopes for scoping units.
427 std::vector<const Scope *> scopeStack_;
428 // Stack of directive specifications (except for SECTION).
429 // This is to allow visitor functions to see all specified clauses, since
430 // they are only recorded in DirectiveContext as they are processed.
431 std::vector<const parser::OmpDirectiveSpecification *> dirStack_;
432
433 enum class PartKind : int {
434 // There are also other "parts", such as internal-subprogram-part, etc,
435 // but we're keeping track of these two for now.
436 SpecificationPart,
437 ExecutionPart,
438 };
439 std::vector<PartKind> partStack_;
440
441 std::multimap<const parser::Label,
442 std::pair<parser::CharBlock, const parser::OpenMPConstruct *>>
443 sourceLabels_;
444 std::map<const parser::Label,
445 std::pair<parser::CharBlock, const parser::OpenMPConstruct *>>
446 targetLabels_;
447 parser::CharBlock currentStatementSource_;
448};
449
450template <typename A>
451void OmpStructureChecker::Enter(const parser::Statement<A> &statement) {
452 currentStatementSource_ = statement.source;
453 // Keep track of the labels in all the labelled statements
454 if (statement.label) {
455 auto label{statement.label.value()};
456 // Get the context to check if the labelled statement is in an
457 // enclosing OpenMP construct
458 auto *thisConstruct{GetCurrentConstruct()};
459 targetLabels_.emplace(
460 label, std::make_pair(currentStatementSource_, thisConstruct));
461 // Check if a statement that causes a jump to the 'label'
462 // has already been encountered
463 auto range{sourceLabels_.equal_range(label)};
464 for (auto it{range.first}; it != range.second; ++it) {
465 // Check if both the statement with 'label' and the statement that
466 // causes a jump to the 'label' are in the same scope
467 CheckLabelContext(it->second.first, currentStatementSource_,
468 it->second.second, thisConstruct);
469 }
470 }
471}
472
475template <typename LessTy, typename RangeTy, typename IterTy>
476std::optional<IterTy> OmpStructureChecker::FindDuplicate(RangeTy &&range) {
477 // Deal with iterators, since the actual elements may be rvalues (i.e.
478 // have no addresses), for example with custom-constructed ranges that
479 // are not simple c.begin()..c.end().
480 std::set<IterTy, LessTy> uniq;
481 for (auto it{range.begin()}, end{range.end()}; it != end; ++it) {
482 if (!uniq.insert(it).second) {
483 return it;
484 }
485 }
486 return std::nullopt;
487}
488
489} // namespace Fortran::semantics
490#endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
Definition enum-set.h:28
Definition expression.h:921
Definition common.h:215
Definition char-block.h:28
Definition check-directive-structure.h:208
Definition semantics.h:67
Definition FIRType.h:103
Definition parse-tree.h:3492
Definition parse-tree.h:1898
Definition parse-tree.h:3502
Definition parse-tree.h:2182
Definition parse-tree.h:3020
Definition parse-tree.h:2510
Definition parse-tree.h:1808
Definition parse-tree.h:1847
Definition parse-tree.h:2325
Definition parse-tree.h:556
Definition parse-tree.h:3139
Definition parse-tree.h:465
Definition parse-tree.h:2929
Definition parse-tree.h:2943
Definition parse-tree.h:2964
Definition parse-tree.h:589
Definition parse-tree.h:5314
Definition parse-tree.h:5108
Definition parse-tree.h:5118
Definition parse-tree.h:5077
Definition parse-tree.h:5061
Definition parse-tree.h:3528
Definition parse-tree.h:5084
Definition parse-tree.h:4508
Definition parse-tree.h:5113
Definition parse-tree.h:5184
Definition parse-tree.h:5146
Definition parse-tree.h:5131
Definition parse-tree.h:5140
Definition parse-tree.h:3573
Definition parse-tree.h:3561
Definition parse-tree.h:3617
Definition parse-tree.h:5340
Definition parse-tree.h:5173
Definition parse-tree.h:5345
Definition parse-tree.h:5382
Definition parse-tree.h:5466
Definition parse-tree.h:5330
Definition parse-tree.h:5161
Definition parse-tree.h:5393
Definition parse-tree.h:5407
Definition parse-tree.h:5423
Definition parse-tree.h:5269
Definition parse-tree.h:5431
Definition parse-tree.h:5484
Definition parse-tree.h:5452
Definition parse-tree.h:5275
Definition parse-tree.h:5199
Definition parse-tree.h:5281
Definition parse-tree.h:573
Definition parse-tree.h:453
Definition parse-tree.h:361
Definition parse-tree.h:3006
Definition parse-tree.h:3158
Definition parse-tree.h:4010
Definition parse-tree.h:4076
Definition parse-tree.h:4086
Definition parse-tree.h:4094
Definition parse-tree.h:4235
Definition parse-tree.h:3763
Definition parse-tree.h:3729
Definition parse-tree.h:3802
Definition parse-tree.h:3824
Definition check-omp-atomic.cpp:242
Definition openmp-utils.h:254