1 //===-- lib/Semantics/tools.cpp -------------------------------------------===//
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 #include "flang/Parser/tools.h"
10 #include "flang/Common/Fortran.h"
11 #include "flang/Common/indirection.h"
12 #include "flang/Parser/dump-parse-tree.h"
13 #include "flang/Parser/message.h"
14 #include "flang/Parser/parse-tree.h"
15 #include "flang/Semantics/scope.h"
16 #include "flang/Semantics/semantics.h"
17 #include "flang/Semantics/symbol.h"
18 #include "flang/Semantics/tools.h"
19 #include "flang/Semantics/type.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include <algorithm>
22 #include <set>
23 #include <variant>
24
25 namespace Fortran::semantics {
26
27 // Find this or containing scope that matches predicate
FindScopeContaining(const Scope & start,std::function<bool (const Scope &)> predicate)28 static const Scope *FindScopeContaining(
29 const Scope &start, std::function<bool(const Scope &)> predicate) {
30 for (const Scope *scope{&start};; scope = &scope->parent()) {
31 if (predicate(*scope)) {
32 return scope;
33 }
34 if (scope->IsTopLevel()) {
35 return nullptr;
36 }
37 }
38 }
39
GetTopLevelUnitContaining(const Scope & start)40 const Scope &GetTopLevelUnitContaining(const Scope &start) {
41 CHECK(!start.IsTopLevel());
42 return DEREF(FindScopeContaining(
43 start, [](const Scope &scope) { return scope.parent().IsTopLevel(); }));
44 }
45
GetTopLevelUnitContaining(const Symbol & symbol)46 const Scope &GetTopLevelUnitContaining(const Symbol &symbol) {
47 return GetTopLevelUnitContaining(symbol.owner());
48 }
49
FindModuleContaining(const Scope & start)50 const Scope *FindModuleContaining(const Scope &start) {
51 return FindScopeContaining(
52 start, [](const Scope &scope) { return scope.IsModule(); });
53 }
54
FindModuleFileContaining(const Scope & start)55 const Scope *FindModuleFileContaining(const Scope &start) {
56 return FindScopeContaining(
57 start, [](const Scope &scope) { return scope.IsModuleFile(); });
58 }
59
GetProgramUnitContaining(const Scope & start)60 const Scope &GetProgramUnitContaining(const Scope &start) {
61 CHECK(!start.IsTopLevel());
62 return DEREF(FindScopeContaining(start, [](const Scope &scope) {
63 switch (scope.kind()) {
64 case Scope::Kind::Module:
65 case Scope::Kind::MainProgram:
66 case Scope::Kind::Subprogram:
67 case Scope::Kind::BlockData:
68 return true;
69 default:
70 return false;
71 }
72 }));
73 }
74
GetProgramUnitContaining(const Symbol & symbol)75 const Scope &GetProgramUnitContaining(const Symbol &symbol) {
76 return GetProgramUnitContaining(symbol.owner());
77 }
78
GetProgramUnitOrBlockConstructContaining(const Scope & start)79 const Scope &GetProgramUnitOrBlockConstructContaining(const Scope &start) {
80 CHECK(!start.IsTopLevel());
81 return DEREF(FindScopeContaining(start, [](const Scope &scope) {
82 switch (scope.kind()) {
83 case Scope::Kind::Module:
84 case Scope::Kind::MainProgram:
85 case Scope::Kind::Subprogram:
86 case Scope::Kind::BlockData:
87 case Scope::Kind::BlockConstruct:
88 return true;
89 default:
90 return false;
91 }
92 }));
93 }
94
GetProgramUnitOrBlockConstructContaining(const Symbol & symbol)95 const Scope &GetProgramUnitOrBlockConstructContaining(const Symbol &symbol) {
96 return GetProgramUnitOrBlockConstructContaining(symbol.owner());
97 }
98
FindPureProcedureContaining(const Scope & start)99 const Scope *FindPureProcedureContaining(const Scope &start) {
100 // N.B. We only need to examine the innermost containing program unit
101 // because an internal subprogram of a pure subprogram must also
102 // be pure (C1592).
103 if (start.IsTopLevel()) {
104 return nullptr;
105 } else {
106 const Scope &scope{GetProgramUnitContaining(start)};
107 return IsPureProcedure(scope) ? &scope : nullptr;
108 }
109 }
110
111 // 7.5.2.4 "same derived type" test -- rely on IsTkCompatibleWith() and its
112 // infrastructure to detect and handle comparisons on distinct (but "same")
113 // sequence/bind(C) derived types
MightBeSameDerivedType(const std::optional<evaluate::DynamicType> & lhsType,const std::optional<evaluate::DynamicType> & rhsType)114 static bool MightBeSameDerivedType(
115 const std::optional<evaluate::DynamicType> &lhsType,
116 const std::optional<evaluate::DynamicType> &rhsType) {
117 return lhsType && rhsType && lhsType->IsTkCompatibleWith(*rhsType);
118 }
119
IsDefinedAssignment(const std::optional<evaluate::DynamicType> & lhsType,int lhsRank,const std::optional<evaluate::DynamicType> & rhsType,int rhsRank)120 Tristate IsDefinedAssignment(
121 const std::optional<evaluate::DynamicType> &lhsType, int lhsRank,
122 const std::optional<evaluate::DynamicType> &rhsType, int rhsRank) {
123 if (!lhsType || !rhsType) {
124 return Tristate::No; // error or rhs is untyped
125 }
126 if (lhsType->IsUnlimitedPolymorphic()) {
127 return Tristate::No;
128 }
129 if (rhsType->IsUnlimitedPolymorphic()) {
130 return Tristate::Maybe;
131 }
132 TypeCategory lhsCat{lhsType->category()};
133 TypeCategory rhsCat{rhsType->category()};
134 if (rhsRank > 0 && lhsRank != rhsRank) {
135 return Tristate::Yes;
136 } else if (lhsCat != TypeCategory::Derived) {
137 return ToTristate(lhsCat != rhsCat &&
138 (!IsNumericTypeCategory(lhsCat) || !IsNumericTypeCategory(rhsCat)));
139 } else if (MightBeSameDerivedType(lhsType, rhsType)) {
140 return Tristate::Maybe; // TYPE(t) = TYPE(t) can be defined or intrinsic
141 } else {
142 return Tristate::Yes;
143 }
144 }
145
IsIntrinsicRelational(common::RelationalOperator opr,const evaluate::DynamicType & type0,int rank0,const evaluate::DynamicType & type1,int rank1)146 bool IsIntrinsicRelational(common::RelationalOperator opr,
147 const evaluate::DynamicType &type0, int rank0,
148 const evaluate::DynamicType &type1, int rank1) {
149 if (!evaluate::AreConformable(rank0, rank1)) {
150 return false;
151 } else {
152 auto cat0{type0.category()};
153 auto cat1{type1.category()};
154 if (IsNumericTypeCategory(cat0) && IsNumericTypeCategory(cat1)) {
155 // numeric types: EQ/NE always ok, others ok for non-complex
156 return opr == common::RelationalOperator::EQ ||
157 opr == common::RelationalOperator::NE ||
158 (cat0 != TypeCategory::Complex && cat1 != TypeCategory::Complex);
159 } else {
160 // not both numeric: only Character is ok
161 return cat0 == TypeCategory::Character && cat1 == TypeCategory::Character;
162 }
163 }
164 }
165
IsIntrinsicNumeric(const evaluate::DynamicType & type0)166 bool IsIntrinsicNumeric(const evaluate::DynamicType &type0) {
167 return IsNumericTypeCategory(type0.category());
168 }
IsIntrinsicNumeric(const evaluate::DynamicType & type0,int rank0,const evaluate::DynamicType & type1,int rank1)169 bool IsIntrinsicNumeric(const evaluate::DynamicType &type0, int rank0,
170 const evaluate::DynamicType &type1, int rank1) {
171 return evaluate::AreConformable(rank0, rank1) &&
172 IsNumericTypeCategory(type0.category()) &&
173 IsNumericTypeCategory(type1.category());
174 }
175
IsIntrinsicLogical(const evaluate::DynamicType & type0)176 bool IsIntrinsicLogical(const evaluate::DynamicType &type0) {
177 return type0.category() == TypeCategory::Logical;
178 }
IsIntrinsicLogical(const evaluate::DynamicType & type0,int rank0,const evaluate::DynamicType & type1,int rank1)179 bool IsIntrinsicLogical(const evaluate::DynamicType &type0, int rank0,
180 const evaluate::DynamicType &type1, int rank1) {
181 return evaluate::AreConformable(rank0, rank1) &&
182 type0.category() == TypeCategory::Logical &&
183 type1.category() == TypeCategory::Logical;
184 }
185
IsIntrinsicConcat(const evaluate::DynamicType & type0,int rank0,const evaluate::DynamicType & type1,int rank1)186 bool IsIntrinsicConcat(const evaluate::DynamicType &type0, int rank0,
187 const evaluate::DynamicType &type1, int rank1) {
188 return evaluate::AreConformable(rank0, rank1) &&
189 type0.category() == TypeCategory::Character &&
190 type1.category() == TypeCategory::Character &&
191 type0.kind() == type1.kind();
192 }
193
IsGenericDefinedOp(const Symbol & symbol)194 bool IsGenericDefinedOp(const Symbol &symbol) {
195 const Symbol &ultimate{symbol.GetUltimate()};
196 if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
197 return generic->kind().IsDefinedOperator();
198 } else if (const auto *misc{ultimate.detailsIf<MiscDetails>()}) {
199 return misc->kind() == MiscDetails::Kind::TypeBoundDefinedOp;
200 } else {
201 return false;
202 }
203 }
204
IsDefinedOperator(SourceName name)205 bool IsDefinedOperator(SourceName name) {
206 const char *begin{name.begin()};
207 const char *end{name.end()};
208 return begin != end && begin[0] == '.' && end[-1] == '.';
209 }
210
MakeOpName(SourceName name)211 std::string MakeOpName(SourceName name) {
212 std::string result{name.ToString()};
213 return IsDefinedOperator(name) ? "OPERATOR(" + result + ")"
214 : result.find("operator(", 0) == 0 ? parser::ToUpperCaseLetters(result)
215 : result;
216 }
217
IsCommonBlockContaining(const Symbol & block,const Symbol & object)218 bool IsCommonBlockContaining(const Symbol &block, const Symbol &object) {
219 const auto &objects{block.get<CommonBlockDetails>().objects()};
220 auto found{std::find(objects.begin(), objects.end(), object)};
221 return found != objects.end();
222 }
223
IsUseAssociated(const Symbol & symbol,const Scope & scope)224 bool IsUseAssociated(const Symbol &symbol, const Scope &scope) {
225 const Scope &owner{
226 GetProgramUnitOrBlockConstructContaining(symbol.GetUltimate().owner())};
227 return owner.kind() == Scope::Kind::Module &&
228 owner != GetProgramUnitOrBlockConstructContaining(scope);
229 }
230
DoesScopeContain(const Scope * maybeAncestor,const Scope & maybeDescendent)231 bool DoesScopeContain(
232 const Scope *maybeAncestor, const Scope &maybeDescendent) {
233 return maybeAncestor && !maybeDescendent.IsTopLevel() &&
234 FindScopeContaining(maybeDescendent.parent(),
235 [&](const Scope &scope) { return &scope == maybeAncestor; });
236 }
237
DoesScopeContain(const Scope * maybeAncestor,const Symbol & symbol)238 bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) {
239 return DoesScopeContain(maybeAncestor, symbol.owner());
240 }
241
FollowHostAssoc(const Symbol & symbol)242 static const Symbol &FollowHostAssoc(const Symbol &symbol) {
243 for (const Symbol *s{&symbol};;) {
244 const auto *details{s->detailsIf<HostAssocDetails>()};
245 if (!details) {
246 return *s;
247 }
248 s = &details->symbol();
249 }
250 }
251
IsHostAssociated(const Symbol & symbol,const Scope & scope)252 bool IsHostAssociated(const Symbol &symbol, const Scope &scope) {
253 return DoesScopeContain(
254 &GetProgramUnitOrBlockConstructContaining(FollowHostAssoc(symbol)),
255 GetProgramUnitOrBlockConstructContaining(scope));
256 }
257
IsInStmtFunction(const Symbol & symbol)258 bool IsInStmtFunction(const Symbol &symbol) {
259 if (const Symbol * function{symbol.owner().symbol()}) {
260 return IsStmtFunction(*function);
261 }
262 return false;
263 }
264
IsStmtFunctionDummy(const Symbol & symbol)265 bool IsStmtFunctionDummy(const Symbol &symbol) {
266 return IsDummy(symbol) && IsInStmtFunction(symbol);
267 }
268
IsStmtFunctionResult(const Symbol & symbol)269 bool IsStmtFunctionResult(const Symbol &symbol) {
270 return IsFunctionResult(symbol) && IsInStmtFunction(symbol);
271 }
272
IsPointerDummy(const Symbol & symbol)273 bool IsPointerDummy(const Symbol &symbol) {
274 return IsPointer(symbol) && IsDummy(symbol);
275 }
276
IsBindCProcedure(const Symbol & symbol)277 bool IsBindCProcedure(const Symbol &symbol) {
278 if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
279 if (const Symbol * procInterface{procDetails->interface().symbol()}) {
280 // procedure component with a BIND(C) interface
281 return IsBindCProcedure(*procInterface);
282 }
283 }
284 return symbol.attrs().test(Attr::BIND_C) && IsProcedure(symbol);
285 }
286
IsBindCProcedure(const Scope & scope)287 bool IsBindCProcedure(const Scope &scope) {
288 if (const Symbol * symbol{scope.GetSymbol()}) {
289 return IsBindCProcedure(*symbol);
290 } else {
291 return false;
292 }
293 }
294
FindPointerComponent(const Scope & scope,std::set<const Scope * > & visited)295 static const Symbol *FindPointerComponent(
296 const Scope &scope, std::set<const Scope *> &visited) {
297 if (!scope.IsDerivedType()) {
298 return nullptr;
299 }
300 if (!visited.insert(&scope).second) {
301 return nullptr;
302 }
303 // If there's a top-level pointer component, return it for clearer error
304 // messaging.
305 for (const auto &pair : scope) {
306 const Symbol &symbol{*pair.second};
307 if (IsPointer(symbol)) {
308 return &symbol;
309 }
310 }
311 for (const auto &pair : scope) {
312 const Symbol &symbol{*pair.second};
313 if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
314 if (const DeclTypeSpec * type{details->type()}) {
315 if (const DerivedTypeSpec * derived{type->AsDerived()}) {
316 if (const Scope * nested{derived->scope()}) {
317 if (const Symbol *
318 pointer{FindPointerComponent(*nested, visited)}) {
319 return pointer;
320 }
321 }
322 }
323 }
324 }
325 }
326 return nullptr;
327 }
328
FindPointerComponent(const Scope & scope)329 const Symbol *FindPointerComponent(const Scope &scope) {
330 std::set<const Scope *> visited;
331 return FindPointerComponent(scope, visited);
332 }
333
FindPointerComponent(const DerivedTypeSpec & derived)334 const Symbol *FindPointerComponent(const DerivedTypeSpec &derived) {
335 if (const Scope * scope{derived.scope()}) {
336 return FindPointerComponent(*scope);
337 } else {
338 return nullptr;
339 }
340 }
341
FindPointerComponent(const DeclTypeSpec & type)342 const Symbol *FindPointerComponent(const DeclTypeSpec &type) {
343 if (const DerivedTypeSpec * derived{type.AsDerived()}) {
344 return FindPointerComponent(*derived);
345 } else {
346 return nullptr;
347 }
348 }
349
FindPointerComponent(const DeclTypeSpec * type)350 const Symbol *FindPointerComponent(const DeclTypeSpec *type) {
351 return type ? FindPointerComponent(*type) : nullptr;
352 }
353
FindPointerComponent(const Symbol & symbol)354 const Symbol *FindPointerComponent(const Symbol &symbol) {
355 return IsPointer(symbol) ? &symbol : FindPointerComponent(symbol.GetType());
356 }
357
358 // C1594 specifies several ways by which an object might be globally visible.
FindExternallyVisibleObject(const Symbol & object,const Scope & scope)359 const Symbol *FindExternallyVisibleObject(
360 const Symbol &object, const Scope &scope) {
361 // TODO: Storage association with any object for which this predicate holds,
362 // once EQUIVALENCE is supported.
363 const Symbol &ultimate{GetAssociationRoot(object)};
364 if (IsDummy(ultimate)) {
365 if (IsIntentIn(ultimate)) {
366 return &ultimate;
367 }
368 if (IsPointer(ultimate) && IsPureProcedure(ultimate.owner()) &&
369 IsFunction(ultimate.owner())) {
370 return &ultimate;
371 }
372 } else if (&GetProgramUnitContaining(ultimate) !=
373 &GetProgramUnitContaining(scope)) {
374 return &object;
375 } else if (const Symbol * block{FindCommonBlockContaining(ultimate)}) {
376 return block;
377 }
378 return nullptr;
379 }
380
BypassGeneric(const Symbol & symbol)381 const Symbol &BypassGeneric(const Symbol &symbol) {
382 const Symbol &ultimate{symbol.GetUltimate()};
383 if (const auto *generic{ultimate.detailsIf<GenericDetails>()}) {
384 if (const Symbol * specific{generic->specific()}) {
385 return *specific;
386 }
387 }
388 return symbol;
389 }
390
ExprHasTypeCategory(const SomeExpr & expr,const common::TypeCategory & type)391 bool ExprHasTypeCategory(
392 const SomeExpr &expr, const common::TypeCategory &type) {
393 auto dynamicType{expr.GetType()};
394 return dynamicType && dynamicType->category() == type;
395 }
396
ExprTypeKindIsDefault(const SomeExpr & expr,const SemanticsContext & context)397 bool ExprTypeKindIsDefault(
398 const SomeExpr &expr, const SemanticsContext &context) {
399 auto dynamicType{expr.GetType()};
400 return dynamicType &&
401 dynamicType->category() != common::TypeCategory::Derived &&
402 dynamicType->kind() == context.GetDefaultKind(dynamicType->category());
403 }
404
405 // If an analyzed expr or assignment is missing, dump the node and die.
406 template <typename T>
CheckMissingAnalysis(bool crash,SemanticsContext * context,const T & x)407 static void CheckMissingAnalysis(
408 bool crash, SemanticsContext *context, const T &x) {
409 if (crash && !(context && context->AnyFatalError())) {
410 std::string buf;
411 llvm::raw_string_ostream ss{buf};
412 ss << "node has not been analyzed:\n";
413 parser::DumpTree(ss, x);
414 common::die(ss.str().c_str());
415 }
416 }
417
Get(const parser::Expr & x)418 const SomeExpr *GetExprHelper::Get(const parser::Expr &x) {
419 CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
420 return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
421 }
Get(const parser::Variable & x)422 const SomeExpr *GetExprHelper::Get(const parser::Variable &x) {
423 CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
424 return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
425 }
Get(const parser::DataStmtConstant & x)426 const SomeExpr *GetExprHelper::Get(const parser::DataStmtConstant &x) {
427 CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
428 return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
429 }
Get(const parser::AllocateObject & x)430 const SomeExpr *GetExprHelper::Get(const parser::AllocateObject &x) {
431 CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
432 return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
433 }
Get(const parser::PointerObject & x)434 const SomeExpr *GetExprHelper::Get(const parser::PointerObject &x) {
435 CheckMissingAnalysis(crashIfNoExpr_ && !x.typedExpr, context_, x);
436 return x.typedExpr ? common::GetPtrFromOptional(x.typedExpr->v) : nullptr;
437 }
438
GetAssignment(const parser::AssignmentStmt & x)439 const evaluate::Assignment *GetAssignment(const parser::AssignmentStmt &x) {
440 return x.typedAssignment ? common::GetPtrFromOptional(x.typedAssignment->v)
441 : nullptr;
442 }
GetAssignment(const parser::PointerAssignmentStmt & x)443 const evaluate::Assignment *GetAssignment(
444 const parser::PointerAssignmentStmt &x) {
445 return x.typedAssignment ? common::GetPtrFromOptional(x.typedAssignment->v)
446 : nullptr;
447 }
448
FindInterface(const Symbol & symbol)449 const Symbol *FindInterface(const Symbol &symbol) {
450 return common::visit(
451 common::visitors{
452 [](const ProcEntityDetails &details) {
453 return details.interface().symbol();
454 },
455 [](const ProcBindingDetails &details) { return &details.symbol(); },
456 [](const auto &) -> const Symbol * { return nullptr; },
457 },
458 symbol.details());
459 }
460
FindSubprogram(const Symbol & symbol)461 const Symbol *FindSubprogram(const Symbol &symbol) {
462 return common::visit(
463 common::visitors{
464 [&](const ProcEntityDetails &details) -> const Symbol * {
465 if (const Symbol * interface{details.interface().symbol()}) {
466 return FindSubprogram(*interface);
467 } else {
468 return &symbol;
469 }
470 },
471 [](const ProcBindingDetails &details) {
472 return FindSubprogram(details.symbol());
473 },
474 [&](const SubprogramDetails &) { return &symbol; },
475 [](const UseDetails &details) {
476 return FindSubprogram(details.symbol());
477 },
478 [](const HostAssocDetails &details) {
479 return FindSubprogram(details.symbol());
480 },
481 [](const auto &) -> const Symbol * { return nullptr; },
482 },
483 symbol.details());
484 }
485
FindOverriddenBinding(const Symbol & symbol)486 const Symbol *FindOverriddenBinding(const Symbol &symbol) {
487 if (symbol.has<ProcBindingDetails>()) {
488 if (const DeclTypeSpec * parentType{FindParentTypeSpec(symbol.owner())}) {
489 if (const DerivedTypeSpec * parentDerived{parentType->AsDerived()}) {
490 if (const Scope * parentScope{parentDerived->typeSymbol().scope()}) {
491 return parentScope->FindComponent(symbol.name());
492 }
493 }
494 }
495 }
496 return nullptr;
497 }
498
FindParentTypeSpec(const DerivedTypeSpec & derived)499 const DeclTypeSpec *FindParentTypeSpec(const DerivedTypeSpec &derived) {
500 return FindParentTypeSpec(derived.typeSymbol());
501 }
502
FindParentTypeSpec(const DeclTypeSpec & decl)503 const DeclTypeSpec *FindParentTypeSpec(const DeclTypeSpec &decl) {
504 if (const DerivedTypeSpec * derived{decl.AsDerived()}) {
505 return FindParentTypeSpec(*derived);
506 } else {
507 return nullptr;
508 }
509 }
510
FindParentTypeSpec(const Scope & scope)511 const DeclTypeSpec *FindParentTypeSpec(const Scope &scope) {
512 if (scope.kind() == Scope::Kind::DerivedType) {
513 if (const auto *symbol{scope.symbol()}) {
514 return FindParentTypeSpec(*symbol);
515 }
516 }
517 return nullptr;
518 }
519
FindParentTypeSpec(const Symbol & symbol)520 const DeclTypeSpec *FindParentTypeSpec(const Symbol &symbol) {
521 if (const Scope * scope{symbol.scope()}) {
522 if (const auto *details{symbol.detailsIf<DerivedTypeDetails>()}) {
523 if (const Symbol * parent{details->GetParentComponent(*scope)}) {
524 return parent->GetType();
525 }
526 }
527 }
528 return nullptr;
529 }
530
FindEquivalenceSet(const Symbol & symbol)531 const EquivalenceSet *FindEquivalenceSet(const Symbol &symbol) {
532 const Symbol &ultimate{symbol.GetUltimate()};
533 for (const EquivalenceSet &set : ultimate.owner().equivalenceSets()) {
534 for (const EquivalenceObject &object : set) {
535 if (object.symbol == ultimate) {
536 return &set;
537 }
538 }
539 }
540 return nullptr;
541 }
542
IsOrContainsEventOrLockComponent(const Symbol & original)543 bool IsOrContainsEventOrLockComponent(const Symbol &original) {
544 const Symbol &symbol{ResolveAssociations(original)};
545 if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
546 if (const DeclTypeSpec * type{details->type()}) {
547 if (const DerivedTypeSpec * derived{type->AsDerived()}) {
548 return IsEventTypeOrLockType(derived) ||
549 FindEventOrLockPotentialComponent(*derived);
550 }
551 }
552 }
553 return false;
554 }
555
556 // Check this symbol suitable as a type-bound procedure - C769
CanBeTypeBoundProc(const Symbol * symbol)557 bool CanBeTypeBoundProc(const Symbol *symbol) {
558 if (!symbol || IsDummy(*symbol) || IsProcedurePointer(*symbol)) {
559 return false;
560 } else if (symbol->has<SubprogramNameDetails>()) {
561 return symbol->owner().kind() == Scope::Kind::Module;
562 } else if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
563 return symbol->owner().kind() == Scope::Kind::Module ||
564 details->isInterface();
565 } else if (const auto *proc{symbol->detailsIf<ProcEntityDetails>()}) {
566 return !symbol->attrs().test(Attr::INTRINSIC) &&
567 proc->HasExplicitInterface();
568 } else {
569 return false;
570 }
571 }
572
HasDeclarationInitializer(const Symbol & symbol)573 bool HasDeclarationInitializer(const Symbol &symbol) {
574 if (IsNamedConstant(symbol)) {
575 return false;
576 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
577 return object->init().has_value();
578 } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
579 return proc->init().has_value();
580 } else {
581 return false;
582 }
583 }
584
IsInitialized(const Symbol & symbol,bool ignoreDataStatements,bool ignoreAllocatable)585 bool IsInitialized(
586 const Symbol &symbol, bool ignoreDataStatements, bool ignoreAllocatable) {
587 if (!ignoreAllocatable && IsAllocatable(symbol)) {
588 return true;
589 } else if (!ignoreDataStatements && symbol.test(Symbol::Flag::InDataStmt)) {
590 return true;
591 } else if (HasDeclarationInitializer(symbol)) {
592 return true;
593 } else if (IsNamedConstant(symbol) || IsFunctionResult(symbol) ||
594 IsPointer(symbol)) {
595 return false;
596 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
597 if (!object->isDummy() && object->type()) {
598 if (const auto *derived{object->type()->AsDerived()}) {
599 return derived->HasDefaultInitialization(ignoreAllocatable);
600 }
601 }
602 }
603 return false;
604 }
605
IsDestructible(const Symbol & symbol,const Symbol * derivedTypeSymbol)606 bool IsDestructible(const Symbol &symbol, const Symbol *derivedTypeSymbol) {
607 if (IsAllocatable(symbol) || IsAutomatic(symbol)) {
608 return true;
609 } else if (IsNamedConstant(symbol) || IsFunctionResult(symbol) ||
610 IsPointer(symbol)) {
611 return false;
612 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
613 if (!object->isDummy() && object->type()) {
614 if (const auto *derived{object->type()->AsDerived()}) {
615 return &derived->typeSymbol() != derivedTypeSymbol &&
616 derived->HasDestruction();
617 }
618 }
619 }
620 return false;
621 }
622
HasIntrinsicTypeName(const Symbol & symbol)623 bool HasIntrinsicTypeName(const Symbol &symbol) {
624 std::string name{symbol.name().ToString()};
625 if (name == "doubleprecision") {
626 return true;
627 } else if (name == "derived") {
628 return false;
629 } else {
630 for (int i{0}; i != common::TypeCategory_enumSize; ++i) {
631 if (name == parser::ToLowerCaseLetters(EnumToString(TypeCategory{i}))) {
632 return true;
633 }
634 }
635 return false;
636 }
637 }
638
IsSeparateModuleProcedureInterface(const Symbol * symbol)639 bool IsSeparateModuleProcedureInterface(const Symbol *symbol) {
640 if (symbol && symbol->attrs().test(Attr::MODULE)) {
641 if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
642 return details->isInterface();
643 }
644 }
645 return false;
646 }
647
IsFinalizable(const Symbol & symbol,std::set<const DerivedTypeSpec * > * inProgress)648 bool IsFinalizable(
649 const Symbol &symbol, std::set<const DerivedTypeSpec *> *inProgress) {
650 if (IsPointer(symbol)) {
651 return false;
652 }
653 if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
654 if (object->isDummy() && !IsIntentOut(symbol)) {
655 return false;
656 }
657 const DeclTypeSpec *type{object->type()};
658 const DerivedTypeSpec *typeSpec{type ? type->AsDerived() : nullptr};
659 return typeSpec && IsFinalizable(*typeSpec, inProgress);
660 }
661 return false;
662 }
663
IsFinalizable(const DerivedTypeSpec & derived,std::set<const DerivedTypeSpec * > * inProgress)664 bool IsFinalizable(const DerivedTypeSpec &derived,
665 std::set<const DerivedTypeSpec *> *inProgress) {
666 if (!derived.typeSymbol().get<DerivedTypeDetails>().finals().empty()) {
667 return true;
668 }
669 std::set<const DerivedTypeSpec *> basis;
670 if (inProgress) {
671 if (inProgress->find(&derived) != inProgress->end()) {
672 return false; // don't loop on recursive type
673 }
674 } else {
675 inProgress = &basis;
676 }
677 auto iterator{inProgress->insert(&derived).first};
678 PotentialComponentIterator components{derived};
679 bool result{bool{std::find_if(
680 components.begin(), components.end(), [=](const Symbol &component) {
681 return IsFinalizable(component, inProgress);
682 })}};
683 inProgress->erase(iterator);
684 return result;
685 }
686
HasImpureFinal(const DerivedTypeSpec & derived)687 bool HasImpureFinal(const DerivedTypeSpec &derived) {
688 if (const auto *details{
689 derived.typeSymbol().detailsIf<DerivedTypeDetails>()}) {
690 const auto &finals{details->finals()};
691 return std::any_of(finals.begin(), finals.end(),
692 [](const auto &x) { return !IsPureProcedure(*x.second); });
693 } else {
694 return false;
695 }
696 }
697
IsAssumedLengthCharacter(const Symbol & symbol)698 bool IsAssumedLengthCharacter(const Symbol &symbol) {
699 if (const DeclTypeSpec * type{symbol.GetType()}) {
700 return type->category() == DeclTypeSpec::Character &&
701 type->characterTypeSpec().length().isAssumed();
702 } else {
703 return false;
704 }
705 }
706
IsInBlankCommon(const Symbol & symbol)707 bool IsInBlankCommon(const Symbol &symbol) {
708 const Symbol *block{FindCommonBlockContaining(symbol)};
709 return block && block->name().empty();
710 }
711
712 // C722 and C723: For a function to be assumed length, it must be external and
713 // of CHARACTER type
IsExternal(const Symbol & symbol)714 bool IsExternal(const Symbol &symbol) {
715 return ClassifyProcedure(symbol) == ProcedureDefinitionClass::External;
716 }
717
718 // Most scopes have no EQUIVALENCE, and this function is a fast no-op for them.
GetStorageAssociations(const Scope & scope)719 std::list<std::list<SymbolRef>> GetStorageAssociations(const Scope &scope) {
720 UnorderedSymbolSet distinct;
721 for (const EquivalenceSet &set : scope.equivalenceSets()) {
722 for (const EquivalenceObject &object : set) {
723 distinct.emplace(object.symbol);
724 }
725 }
726 // This set is ordered by ascending offsets, with ties broken by greatest
727 // size. A multiset is used here because multiple symbols may have the
728 // same offset and size; the symbols in the set, however, are distinct.
729 std::multiset<SymbolRef, SymbolOffsetCompare> associated;
730 for (SymbolRef ref : distinct) {
731 associated.emplace(*ref);
732 }
733 std::list<std::list<SymbolRef>> result;
734 std::size_t limit{0};
735 const Symbol *currentCommon{nullptr};
736 for (const Symbol &symbol : associated) {
737 const Symbol *thisCommon{FindCommonBlockContaining(symbol)};
738 if (result.empty() || symbol.offset() >= limit ||
739 thisCommon != currentCommon) {
740 // Start a new group
741 result.emplace_back(std::list<SymbolRef>{});
742 limit = 0;
743 currentCommon = thisCommon;
744 }
745 result.back().emplace_back(symbol);
746 limit = std::max(limit, symbol.offset() + symbol.size());
747 }
748 return result;
749 }
750
IsModuleProcedure(const Symbol & symbol)751 bool IsModuleProcedure(const Symbol &symbol) {
752 return ClassifyProcedure(symbol) == ProcedureDefinitionClass::Module;
753 }
IsExternalInPureContext(const Symbol & symbol,const Scope & scope)754 const Symbol *IsExternalInPureContext(
755 const Symbol &symbol, const Scope &scope) {
756 if (const auto *pureProc{FindPureProcedureContaining(scope)}) {
757 return FindExternallyVisibleObject(symbol.GetUltimate(), *pureProc);
758 }
759 return nullptr;
760 }
761
FindPolymorphicPotentialComponent(const DerivedTypeSpec & derived)762 PotentialComponentIterator::const_iterator FindPolymorphicPotentialComponent(
763 const DerivedTypeSpec &derived) {
764 PotentialComponentIterator potentials{derived};
765 return std::find_if(
766 potentials.begin(), potentials.end(), [](const Symbol &component) {
767 if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
768 const DeclTypeSpec *type{details->type()};
769 return type && type->IsPolymorphic();
770 }
771 return false;
772 });
773 }
774
IsOrContainsPolymorphicComponent(const Symbol & original)775 bool IsOrContainsPolymorphicComponent(const Symbol &original) {
776 const Symbol &symbol{ResolveAssociations(original)};
777 if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
778 if (const DeclTypeSpec * type{details->type()}) {
779 if (type->IsPolymorphic()) {
780 return true;
781 }
782 if (const DerivedTypeSpec * derived{type->AsDerived()}) {
783 return (bool)FindPolymorphicPotentialComponent(*derived);
784 }
785 }
786 }
787 return false;
788 }
789
InProtectedContext(const Symbol & symbol,const Scope & currentScope)790 bool InProtectedContext(const Symbol &symbol, const Scope ¤tScope) {
791 return IsProtected(symbol) && !IsHostAssociated(symbol, currentScope);
792 }
793
794 // C1101 and C1158
795 // Modifiability checks on the leftmost symbol ("base object")
796 // of a data-ref
WhyNotModifiableFirst(parser::CharBlock at,const Symbol & symbol,const Scope & scope)797 static std::optional<parser::Message> WhyNotModifiableFirst(
798 parser::CharBlock at, const Symbol &symbol, const Scope &scope) {
799 if (const auto *assoc{symbol.detailsIf<AssocEntityDetails>()}) {
800 if (assoc->rank().has_value()) {
801 return std::nullopt; // SELECT RANK always modifiable variable
802 } else if (IsVariable(assoc->expr())) {
803 if (evaluate::HasVectorSubscript(assoc->expr().value())) {
804 return parser::Message{
805 at, "Construct association has a vector subscript"_en_US};
806 } else {
807 return WhyNotModifiable(at, *assoc->expr(), scope);
808 }
809 } else {
810 return parser::Message{at,
811 "'%s' is construct associated with an expression"_en_US,
812 symbol.name()};
813 }
814 } else if (IsExternalInPureContext(symbol, scope)) {
815 return parser::Message{at,
816 "'%s' is externally visible and referenced in a pure"
817 " procedure"_en_US,
818 symbol.name()};
819 } else if (!IsVariableName(symbol)) {
820 return parser::Message{at, "'%s' is not a variable"_en_US, symbol.name()};
821 } else {
822 return std::nullopt;
823 }
824 }
825
826 // Modifiability checks on the rightmost symbol of a data-ref
WhyNotModifiableLast(parser::CharBlock at,const Symbol & symbol,const Scope & scope)827 static std::optional<parser::Message> WhyNotModifiableLast(
828 parser::CharBlock at, const Symbol &symbol, const Scope &scope) {
829 if (IsOrContainsEventOrLockComponent(symbol)) {
830 return parser::Message{at,
831 "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US,
832 symbol.name()};
833 } else {
834 return std::nullopt;
835 }
836 }
837
838 // Modifiability checks on the leftmost (base) symbol of a data-ref
839 // that apply only when there are no pointer components or a base
840 // that is a pointer.
WhyNotModifiableIfNoPtr(parser::CharBlock at,const Symbol & symbol,const Scope & scope)841 static std::optional<parser::Message> WhyNotModifiableIfNoPtr(
842 parser::CharBlock at, const Symbol &symbol, const Scope &scope) {
843 if (InProtectedContext(symbol, scope)) {
844 return parser::Message{
845 at, "'%s' is protected in this scope"_en_US, symbol.name()};
846 } else if (IsIntentIn(symbol)) {
847 return parser::Message{
848 at, "'%s' is an INTENT(IN) dummy argument"_en_US, symbol.name()};
849 } else {
850 return std::nullopt;
851 }
852 }
853
854 // Apply all modifiability checks to a single symbol
WhyNotModifiable(const Symbol & original,const Scope & scope)855 std::optional<parser::Message> WhyNotModifiable(
856 const Symbol &original, const Scope &scope) {
857 const Symbol &symbol{GetAssociationRoot(original)};
858 if (auto first{WhyNotModifiableFirst(symbol.name(), symbol, scope)}) {
859 return first;
860 } else if (auto last{WhyNotModifiableLast(symbol.name(), symbol, scope)}) {
861 return last;
862 } else if (!IsPointer(symbol)) {
863 return WhyNotModifiableIfNoPtr(symbol.name(), symbol, scope);
864 } else {
865 return std::nullopt;
866 }
867 }
868
869 // Modifiability checks for a data-ref
WhyNotModifiable(parser::CharBlock at,const SomeExpr & expr,const Scope & scope,bool vectorSubscriptIsOk)870 std::optional<parser::Message> WhyNotModifiable(parser::CharBlock at,
871 const SomeExpr &expr, const Scope &scope, bool vectorSubscriptIsOk) {
872 if (auto dataRef{evaluate::ExtractDataRef(expr, true)}) {
873 if (!vectorSubscriptIsOk && evaluate::HasVectorSubscript(expr)) {
874 return parser::Message{at, "Variable has a vector subscript"_en_US};
875 }
876 const Symbol &first{GetAssociationRoot(dataRef->GetFirstSymbol())};
877 if (auto maybeWhyFirst{WhyNotModifiableFirst(at, first, scope)}) {
878 return maybeWhyFirst;
879 }
880 const Symbol &last{dataRef->GetLastSymbol()};
881 if (auto maybeWhyLast{WhyNotModifiableLast(at, last, scope)}) {
882 return maybeWhyLast;
883 }
884 if (!GetLastPointerSymbol(*dataRef)) {
885 if (auto maybeWhyFirst{WhyNotModifiableIfNoPtr(at, first, scope)}) {
886 return maybeWhyFirst;
887 }
888 }
889 } else if (!evaluate::IsVariable(expr)) {
890 return parser::Message{
891 at, "'%s' is not a variable"_en_US, expr.AsFortran()};
892 } else {
893 // reference to function returning POINTER
894 }
895 return std::nullopt;
896 }
897
898 class ImageControlStmtHelper {
899 using ImageControlStmts =
900 std::variant<parser::ChangeTeamConstruct, parser::CriticalConstruct,
901 parser::EventPostStmt, parser::EventWaitStmt, parser::FormTeamStmt,
902 parser::LockStmt, parser::SyncAllStmt, parser::SyncImagesStmt,
903 parser::SyncMemoryStmt, parser::SyncTeamStmt, parser::UnlockStmt>;
904
905 public:
operator ()(const T &)906 template <typename T> bool operator()(const T &) {
907 return common::HasMember<T, ImageControlStmts>;
908 }
operator ()(const common::Indirection<T> & x)909 template <typename T> bool operator()(const common::Indirection<T> &x) {
910 return (*this)(x.value());
911 }
operator ()(const parser::AllocateStmt & stmt)912 bool operator()(const parser::AllocateStmt &stmt) {
913 const auto &allocationList{std::get<std::list<parser::Allocation>>(stmt.t)};
914 for (const auto &allocation : allocationList) {
915 const auto &allocateObject{
916 std::get<parser::AllocateObject>(allocation.t)};
917 if (IsCoarrayObject(allocateObject)) {
918 return true;
919 }
920 }
921 return false;
922 }
operator ()(const parser::DeallocateStmt & stmt)923 bool operator()(const parser::DeallocateStmt &stmt) {
924 const auto &allocateObjectList{
925 std::get<std::list<parser::AllocateObject>>(stmt.t)};
926 for (const auto &allocateObject : allocateObjectList) {
927 if (IsCoarrayObject(allocateObject)) {
928 return true;
929 }
930 }
931 return false;
932 }
operator ()(const parser::CallStmt & stmt)933 bool operator()(const parser::CallStmt &stmt) {
934 const auto &procedureDesignator{
935 std::get<parser::ProcedureDesignator>(stmt.v.t)};
936 if (auto *name{std::get_if<parser::Name>(&procedureDesignator.u)}) {
937 // TODO: also ensure that the procedure is, in fact, an intrinsic
938 if (name->source == "move_alloc") {
939 const auto &args{std::get<std::list<parser::ActualArgSpec>>(stmt.v.t)};
940 if (!args.empty()) {
941 const parser::ActualArg &actualArg{
942 std::get<parser::ActualArg>(args.front().t)};
943 if (const auto *argExpr{
944 std::get_if<common::Indirection<parser::Expr>>(
945 &actualArg.u)}) {
946 return HasCoarray(argExpr->value());
947 }
948 }
949 }
950 }
951 return false;
952 }
operator ()(const parser::StopStmt & stmt)953 bool operator()(const parser::StopStmt &stmt) {
954 // STOP is an image control statement; ERROR STOP is not
955 return std::get<parser::StopStmt::Kind>(stmt.t) ==
956 parser::StopStmt::Kind::Stop;
957 }
operator ()(const parser::Statement<parser::ActionStmt> & stmt)958 bool operator()(const parser::Statement<parser::ActionStmt> &stmt) {
959 return common::visit(*this, stmt.statement.u);
960 }
961
962 private:
IsCoarrayObject(const parser::AllocateObject & allocateObject)963 bool IsCoarrayObject(const parser::AllocateObject &allocateObject) {
964 const parser::Name &name{GetLastName(allocateObject)};
965 return name.symbol && evaluate::IsCoarray(*name.symbol);
966 }
967 };
968
IsImageControlStmt(const parser::ExecutableConstruct & construct)969 bool IsImageControlStmt(const parser::ExecutableConstruct &construct) {
970 return common::visit(ImageControlStmtHelper{}, construct.u);
971 }
972
GetImageControlStmtCoarrayMsg(const parser::ExecutableConstruct & construct)973 std::optional<parser::MessageFixedText> GetImageControlStmtCoarrayMsg(
974 const parser::ExecutableConstruct &construct) {
975 if (const auto *actionStmt{
976 std::get_if<parser::Statement<parser::ActionStmt>>(&construct.u)}) {
977 return common::visit(
978 common::visitors{
979 [](const common::Indirection<parser::AllocateStmt> &)
980 -> std::optional<parser::MessageFixedText> {
981 return "ALLOCATE of a coarray is an image control"
982 " statement"_en_US;
983 },
984 [](const common::Indirection<parser::DeallocateStmt> &)
985 -> std::optional<parser::MessageFixedText> {
986 return "DEALLOCATE of a coarray is an image control"
987 " statement"_en_US;
988 },
989 [](const common::Indirection<parser::CallStmt> &)
990 -> std::optional<parser::MessageFixedText> {
991 return "MOVE_ALLOC of a coarray is an image control"
992 " statement "_en_US;
993 },
994 [](const auto &) -> std::optional<parser::MessageFixedText> {
995 return std::nullopt;
996 },
997 },
998 actionStmt->statement.u);
999 }
1000 return std::nullopt;
1001 }
1002
GetImageControlStmtLocation(const parser::ExecutableConstruct & executableConstruct)1003 parser::CharBlock GetImageControlStmtLocation(
1004 const parser::ExecutableConstruct &executableConstruct) {
1005 return common::visit(
1006 common::visitors{
1007 [](const common::Indirection<parser::ChangeTeamConstruct>
1008 &construct) {
1009 return std::get<parser::Statement<parser::ChangeTeamStmt>>(
1010 construct.value().t)
1011 .source;
1012 },
1013 [](const common::Indirection<parser::CriticalConstruct> &construct) {
1014 return std::get<parser::Statement<parser::CriticalStmt>>(
1015 construct.value().t)
1016 .source;
1017 },
1018 [](const parser::Statement<parser::ActionStmt> &actionStmt) {
1019 return actionStmt.source;
1020 },
1021 [](const auto &) { return parser::CharBlock{}; },
1022 },
1023 executableConstruct.u);
1024 }
1025
HasCoarray(const parser::Expr & expression)1026 bool HasCoarray(const parser::Expr &expression) {
1027 if (const auto *expr{GetExpr(nullptr, expression)}) {
1028 for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
1029 if (evaluate::IsCoarray(symbol)) {
1030 return true;
1031 }
1032 }
1033 }
1034 return false;
1035 }
1036
IsPolymorphic(const Symbol & symbol)1037 bool IsPolymorphic(const Symbol &symbol) {
1038 if (const DeclTypeSpec * type{symbol.GetType()}) {
1039 return type->IsPolymorphic();
1040 }
1041 return false;
1042 }
1043
IsPolymorphicAllocatable(const Symbol & symbol)1044 bool IsPolymorphicAllocatable(const Symbol &symbol) {
1045 return IsAllocatable(symbol) && IsPolymorphic(symbol);
1046 }
1047
CheckAccessibleComponent(const Scope & scope,const Symbol & symbol)1048 std::optional<parser::MessageFormattedText> CheckAccessibleComponent(
1049 const Scope &scope, const Symbol &symbol) {
1050 CHECK(symbol.owner().IsDerivedType()); // symbol must be a component
1051 if (symbol.attrs().test(Attr::PRIVATE)) {
1052 if (FindModuleFileContaining(scope)) {
1053 // Don't enforce component accessibility checks in module files;
1054 // there may be forward-substituted named constants of derived type
1055 // whose structure constructors reference private components.
1056 } else if (const Scope *
1057 moduleScope{FindModuleContaining(symbol.owner())}) {
1058 if (!moduleScope->Contains(scope)) {
1059 return parser::MessageFormattedText{
1060 "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US,
1061 symbol.name(), moduleScope->GetName().value()};
1062 }
1063 }
1064 }
1065 return std::nullopt;
1066 }
1067
OrderParameterNames(const Symbol & typeSymbol)1068 std::list<SourceName> OrderParameterNames(const Symbol &typeSymbol) {
1069 std::list<SourceName> result;
1070 if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1071 result = OrderParameterNames(spec->typeSymbol());
1072 }
1073 const auto ¶mNames{typeSymbol.get<DerivedTypeDetails>().paramNames()};
1074 result.insert(result.end(), paramNames.begin(), paramNames.end());
1075 return result;
1076 }
1077
OrderParameterDeclarations(const Symbol & typeSymbol)1078 SymbolVector OrderParameterDeclarations(const Symbol &typeSymbol) {
1079 SymbolVector result;
1080 if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
1081 result = OrderParameterDeclarations(spec->typeSymbol());
1082 }
1083 const auto ¶mDecls{typeSymbol.get<DerivedTypeDetails>().paramDecls()};
1084 result.insert(result.end(), paramDecls.begin(), paramDecls.end());
1085 return result;
1086 }
1087
FindOrInstantiateDerivedType(Scope & scope,DerivedTypeSpec && spec,DeclTypeSpec::Category category)1088 const DeclTypeSpec &FindOrInstantiateDerivedType(
1089 Scope &scope, DerivedTypeSpec &&spec, DeclTypeSpec::Category category) {
1090 spec.EvaluateParameters(scope.context());
1091 if (const DeclTypeSpec *
1092 type{scope.FindInstantiatedDerivedType(spec, category)}) {
1093 return *type;
1094 }
1095 // Create a new instantiation of this parameterized derived type
1096 // for this particular distinct set of actual parameter values.
1097 DeclTypeSpec &type{scope.MakeDerivedType(category, std::move(spec))};
1098 type.derivedTypeSpec().Instantiate(scope);
1099 return type;
1100 }
1101
FindSeparateModuleSubprogramInterface(const Symbol * proc)1102 const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) {
1103 if (proc) {
1104 if (const auto *subprogram{proc->detailsIf<SubprogramDetails>()}) {
1105 if (const Symbol * iface{subprogram->moduleInterface()}) {
1106 return iface;
1107 }
1108 }
1109 }
1110 return nullptr;
1111 }
1112
ClassifyProcedure(const Symbol & symbol)1113 ProcedureDefinitionClass ClassifyProcedure(const Symbol &symbol) { // 15.2.2
1114 const Symbol &ultimate{symbol.GetUltimate()};
1115 if (!IsProcedure(ultimate)) {
1116 return ProcedureDefinitionClass::None;
1117 } else if (ultimate.attrs().test(Attr::INTRINSIC)) {
1118 return ProcedureDefinitionClass::Intrinsic;
1119 } else if (ultimate.attrs().test(Attr::EXTERNAL)) {
1120 return ProcedureDefinitionClass::External;
1121 } else if (const auto *procDetails{ultimate.detailsIf<ProcEntityDetails>()}) {
1122 if (procDetails->isDummy()) {
1123 return ProcedureDefinitionClass::Dummy;
1124 } else if (IsPointer(ultimate)) {
1125 return ProcedureDefinitionClass::Pointer;
1126 }
1127 } else if (const Symbol * subp{FindSubprogram(symbol)}) {
1128 if (const auto *subpDetails{subp->detailsIf<SubprogramDetails>()}) {
1129 if (subpDetails->stmtFunction()) {
1130 return ProcedureDefinitionClass::StatementFunction;
1131 }
1132 }
1133 switch (ultimate.owner().kind()) {
1134 case Scope::Kind::Global:
1135 case Scope::Kind::IntrinsicModules:
1136 return ProcedureDefinitionClass::External;
1137 case Scope::Kind::Module:
1138 return ProcedureDefinitionClass::Module;
1139 case Scope::Kind::MainProgram:
1140 case Scope::Kind::Subprogram:
1141 return ProcedureDefinitionClass::Internal;
1142 default:
1143 break;
1144 }
1145 }
1146 return ProcedureDefinitionClass::None;
1147 }
1148
1149 // ComponentIterator implementation
1150
1151 template <ComponentKind componentKind>
1152 typename ComponentIterator<componentKind>::const_iterator
Create(const DerivedTypeSpec & derived)1153 ComponentIterator<componentKind>::const_iterator::Create(
1154 const DerivedTypeSpec &derived) {
1155 const_iterator it{};
1156 it.componentPath_.emplace_back(derived);
1157 it.Increment(); // cue up first relevant component, if any
1158 return it;
1159 }
1160
1161 template <ComponentKind componentKind>
1162 const DerivedTypeSpec *
PlanComponentTraversal(const Symbol & component) const1163 ComponentIterator<componentKind>::const_iterator::PlanComponentTraversal(
1164 const Symbol &component) const {
1165 if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1166 if (const DeclTypeSpec * type{details->type()}) {
1167 if (const auto *derived{type->AsDerived()}) {
1168 bool traverse{false};
1169 if constexpr (componentKind == ComponentKind::Ordered) {
1170 // Order Component (only visit parents)
1171 traverse = component.test(Symbol::Flag::ParentComp);
1172 } else if constexpr (componentKind == ComponentKind::Direct) {
1173 traverse = !IsAllocatableOrPointer(component);
1174 } else if constexpr (componentKind == ComponentKind::Ultimate) {
1175 traverse = !IsAllocatableOrPointer(component);
1176 } else if constexpr (componentKind == ComponentKind::Potential) {
1177 traverse = !IsPointer(component);
1178 } else if constexpr (componentKind == ComponentKind::Scope) {
1179 traverse = !IsAllocatableOrPointer(component);
1180 }
1181 if (traverse) {
1182 const Symbol &newTypeSymbol{derived->typeSymbol()};
1183 // Avoid infinite loop if the type is already part of the types
1184 // being visited. It is possible to have "loops in type" because
1185 // C744 does not forbid to use not yet declared type for
1186 // ALLOCATABLE or POINTER components.
1187 for (const auto &node : componentPath_) {
1188 if (&newTypeSymbol == &node.GetTypeSymbol()) {
1189 return nullptr;
1190 }
1191 }
1192 return derived;
1193 }
1194 }
1195 } // intrinsic & unlimited polymorphic not traversable
1196 }
1197 return nullptr;
1198 }
1199
1200 template <ComponentKind componentKind>
StopAtComponentPre(const Symbol & component)1201 static bool StopAtComponentPre(const Symbol &component) {
1202 if constexpr (componentKind == ComponentKind::Ordered) {
1203 // Parent components need to be iterated upon after their
1204 // sub-components in structure constructor analysis.
1205 return !component.test(Symbol::Flag::ParentComp);
1206 } else if constexpr (componentKind == ComponentKind::Direct) {
1207 return true;
1208 } else if constexpr (componentKind == ComponentKind::Ultimate) {
1209 return component.has<ProcEntityDetails>() ||
1210 IsAllocatableOrPointer(component) ||
1211 (component.get<ObjectEntityDetails>().type() &&
1212 component.get<ObjectEntityDetails>().type()->AsIntrinsic());
1213 } else if constexpr (componentKind == ComponentKind::Potential) {
1214 return !IsPointer(component);
1215 }
1216 }
1217
1218 template <ComponentKind componentKind>
StopAtComponentPost(const Symbol & component)1219 static bool StopAtComponentPost(const Symbol &component) {
1220 return componentKind == ComponentKind::Ordered &&
1221 component.test(Symbol::Flag::ParentComp);
1222 }
1223
1224 template <ComponentKind componentKind>
Increment()1225 void ComponentIterator<componentKind>::const_iterator::Increment() {
1226 while (!componentPath_.empty()) {
1227 ComponentPathNode &deepest{componentPath_.back()};
1228 if (deepest.component()) {
1229 if (!deepest.descended()) {
1230 deepest.set_descended(true);
1231 if (const DerivedTypeSpec *
1232 derived{PlanComponentTraversal(*deepest.component())}) {
1233 componentPath_.emplace_back(*derived);
1234 continue;
1235 }
1236 } else if (!deepest.visited()) {
1237 deepest.set_visited(true);
1238 return; // this is the next component to visit, after descending
1239 }
1240 }
1241 auto &nameIterator{deepest.nameIterator()};
1242 if (nameIterator == deepest.nameEnd()) {
1243 componentPath_.pop_back();
1244 } else if constexpr (componentKind == ComponentKind::Scope) {
1245 deepest.set_component(*nameIterator++->second);
1246 deepest.set_descended(false);
1247 deepest.set_visited(true);
1248 return; // this is the next component to visit, before descending
1249 } else {
1250 const Scope &scope{deepest.GetScope()};
1251 auto scopeIter{scope.find(*nameIterator++)};
1252 if (scopeIter != scope.cend()) {
1253 const Symbol &component{*scopeIter->second};
1254 deepest.set_component(component);
1255 deepest.set_descended(false);
1256 if (StopAtComponentPre<componentKind>(component)) {
1257 deepest.set_visited(true);
1258 return; // this is the next component to visit, before descending
1259 } else {
1260 deepest.set_visited(!StopAtComponentPost<componentKind>(component));
1261 }
1262 }
1263 }
1264 }
1265 }
1266
1267 template <ComponentKind componentKind>
1268 std::string
BuildResultDesignatorName() const1269 ComponentIterator<componentKind>::const_iterator::BuildResultDesignatorName()
1270 const {
1271 std::string designator{""};
1272 for (const auto &node : componentPath_) {
1273 designator += "%" + DEREF(node.component()).name().ToString();
1274 }
1275 return designator;
1276 }
1277
1278 template class ComponentIterator<ComponentKind::Ordered>;
1279 template class ComponentIterator<ComponentKind::Direct>;
1280 template class ComponentIterator<ComponentKind::Ultimate>;
1281 template class ComponentIterator<ComponentKind::Potential>;
1282 template class ComponentIterator<ComponentKind::Scope>;
1283
FindCoarrayUltimateComponent(const DerivedTypeSpec & derived)1284 UltimateComponentIterator::const_iterator FindCoarrayUltimateComponent(
1285 const DerivedTypeSpec &derived) {
1286 UltimateComponentIterator ultimates{derived};
1287 return std::find_if(ultimates.begin(), ultimates.end(),
1288 [](const Symbol &symbol) { return evaluate::IsCoarray(symbol); });
1289 }
1290
FindPointerUltimateComponent(const DerivedTypeSpec & derived)1291 UltimateComponentIterator::const_iterator FindPointerUltimateComponent(
1292 const DerivedTypeSpec &derived) {
1293 UltimateComponentIterator ultimates{derived};
1294 return std::find_if(ultimates.begin(), ultimates.end(), IsPointer);
1295 }
1296
FindEventOrLockPotentialComponent(const DerivedTypeSpec & derived)1297 PotentialComponentIterator::const_iterator FindEventOrLockPotentialComponent(
1298 const DerivedTypeSpec &derived) {
1299 PotentialComponentIterator potentials{derived};
1300 return std::find_if(
1301 potentials.begin(), potentials.end(), [](const Symbol &component) {
1302 if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
1303 const DeclTypeSpec *type{details->type()};
1304 return type && IsEventTypeOrLockType(type->AsDerived());
1305 }
1306 return false;
1307 });
1308 }
1309
FindAllocatableUltimateComponent(const DerivedTypeSpec & derived)1310 UltimateComponentIterator::const_iterator FindAllocatableUltimateComponent(
1311 const DerivedTypeSpec &derived) {
1312 UltimateComponentIterator ultimates{derived};
1313 return std::find_if(ultimates.begin(), ultimates.end(), IsAllocatable);
1314 }
1315
FindAllocatableOrPointerDirectComponent(const DerivedTypeSpec & derived)1316 DirectComponentIterator::const_iterator FindAllocatableOrPointerDirectComponent(
1317 const DerivedTypeSpec &derived) {
1318 DirectComponentIterator directs{derived};
1319 return std::find_if(directs.begin(), directs.end(), IsAllocatableOrPointer);
1320 }
1321
1322 UltimateComponentIterator::const_iterator
FindPolymorphicAllocatableUltimateComponent(const DerivedTypeSpec & derived)1323 FindPolymorphicAllocatableUltimateComponent(const DerivedTypeSpec &derived) {
1324 UltimateComponentIterator ultimates{derived};
1325 return std::find_if(
1326 ultimates.begin(), ultimates.end(), IsPolymorphicAllocatable);
1327 }
1328
1329 UltimateComponentIterator::const_iterator
FindPolymorphicAllocatableNonCoarrayUltimateComponent(const DerivedTypeSpec & derived)1330 FindPolymorphicAllocatableNonCoarrayUltimateComponent(
1331 const DerivedTypeSpec &derived) {
1332 UltimateComponentIterator ultimates{derived};
1333 return std::find_if(ultimates.begin(), ultimates.end(), [](const Symbol &x) {
1334 return IsPolymorphicAllocatable(x) && !evaluate::IsCoarray(x);
1335 });
1336 }
1337
FindUltimateComponent(const DerivedTypeSpec & derived,const std::function<bool (const Symbol &)> & predicate)1338 const Symbol *FindUltimateComponent(const DerivedTypeSpec &derived,
1339 const std::function<bool(const Symbol &)> &predicate) {
1340 UltimateComponentIterator ultimates{derived};
1341 if (auto it{std::find_if(ultimates.begin(), ultimates.end(),
1342 [&predicate](const Symbol &component) -> bool {
1343 return predicate(component);
1344 })}) {
1345 return &*it;
1346 }
1347 return nullptr;
1348 }
1349
FindUltimateComponent(const Symbol & symbol,const std::function<bool (const Symbol &)> & predicate)1350 const Symbol *FindUltimateComponent(const Symbol &symbol,
1351 const std::function<bool(const Symbol &)> &predicate) {
1352 if (predicate(symbol)) {
1353 return &symbol;
1354 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1355 if (const auto *type{object->type()}) {
1356 if (const auto *derived{type->AsDerived()}) {
1357 return FindUltimateComponent(*derived, predicate);
1358 }
1359 }
1360 }
1361 return nullptr;
1362 }
1363
FindImmediateComponent(const DerivedTypeSpec & type,const std::function<bool (const Symbol &)> & predicate)1364 const Symbol *FindImmediateComponent(const DerivedTypeSpec &type,
1365 const std::function<bool(const Symbol &)> &predicate) {
1366 if (const Scope * scope{type.scope()}) {
1367 const Symbol *parent{nullptr};
1368 for (const auto &pair : *scope) {
1369 const Symbol *symbol{&*pair.second};
1370 if (predicate(*symbol)) {
1371 return symbol;
1372 }
1373 if (symbol->test(Symbol::Flag::ParentComp)) {
1374 parent = symbol;
1375 }
1376 }
1377 if (parent) {
1378 if (const auto *object{parent->detailsIf<ObjectEntityDetails>()}) {
1379 if (const auto *type{object->type()}) {
1380 if (const auto *derived{type->AsDerived()}) {
1381 return FindImmediateComponent(*derived, predicate);
1382 }
1383 }
1384 }
1385 }
1386 }
1387 return nullptr;
1388 }
1389
IsFunctionResultWithSameNameAsFunction(const Symbol & symbol)1390 const Symbol *IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) {
1391 if (IsFunctionResult(symbol)) {
1392 if (const Symbol * function{symbol.owner().symbol()}) {
1393 if (symbol.name() == function->name()) {
1394 return function;
1395 }
1396 }
1397 // Check ENTRY result symbols too
1398 const Scope &outer{symbol.owner().parent()};
1399 auto iter{outer.find(symbol.name())};
1400 if (iter != outer.end()) {
1401 const Symbol &outerSym{*iter->second};
1402 if (const auto *subp{outerSym.detailsIf<SubprogramDetails>()}) {
1403 if (subp->entryScope() == &symbol.owner() &&
1404 symbol.name() == outerSym.name()) {
1405 return &outerSym;
1406 }
1407 }
1408 }
1409 }
1410 return nullptr;
1411 }
1412
Post(const parser::GotoStmt & gotoStmt)1413 void LabelEnforce::Post(const parser::GotoStmt &gotoStmt) {
1414 checkLabelUse(gotoStmt.v);
1415 }
Post(const parser::ComputedGotoStmt & computedGotoStmt)1416 void LabelEnforce::Post(const parser::ComputedGotoStmt &computedGotoStmt) {
1417 for (auto &i : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
1418 checkLabelUse(i);
1419 }
1420 }
1421
Post(const parser::ArithmeticIfStmt & arithmeticIfStmt)1422 void LabelEnforce::Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
1423 checkLabelUse(std::get<1>(arithmeticIfStmt.t));
1424 checkLabelUse(std::get<2>(arithmeticIfStmt.t));
1425 checkLabelUse(std::get<3>(arithmeticIfStmt.t));
1426 }
1427
Post(const parser::AssignStmt & assignStmt)1428 void LabelEnforce::Post(const parser::AssignStmt &assignStmt) {
1429 checkLabelUse(std::get<parser::Label>(assignStmt.t));
1430 }
1431
Post(const parser::AssignedGotoStmt & assignedGotoStmt)1432 void LabelEnforce::Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
1433 for (auto &i : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
1434 checkLabelUse(i);
1435 }
1436 }
1437
Post(const parser::AltReturnSpec & altReturnSpec)1438 void LabelEnforce::Post(const parser::AltReturnSpec &altReturnSpec) {
1439 checkLabelUse(altReturnSpec.v);
1440 }
1441
Post(const parser::ErrLabel & errLabel)1442 void LabelEnforce::Post(const parser::ErrLabel &errLabel) {
1443 checkLabelUse(errLabel.v);
1444 }
Post(const parser::EndLabel & endLabel)1445 void LabelEnforce::Post(const parser::EndLabel &endLabel) {
1446 checkLabelUse(endLabel.v);
1447 }
Post(const parser::EorLabel & eorLabel)1448 void LabelEnforce::Post(const parser::EorLabel &eorLabel) {
1449 checkLabelUse(eorLabel.v);
1450 }
1451
checkLabelUse(const parser::Label & labelUsed)1452 void LabelEnforce::checkLabelUse(const parser::Label &labelUsed) {
1453 if (labels_.find(labelUsed) == labels_.end()) {
1454 SayWithConstruct(context_, currentStatementSourcePosition_,
1455 parser::MessageFormattedText{
1456 "Control flow escapes from %s"_err_en_US, construct_},
1457 constructSourcePosition_);
1458 }
1459 }
1460
GetEnclosingConstructMsg()1461 parser::MessageFormattedText LabelEnforce::GetEnclosingConstructMsg() {
1462 return {"Enclosing %s statement"_en_US, construct_};
1463 }
1464
SayWithConstruct(SemanticsContext & context,parser::CharBlock stmtLocation,parser::MessageFormattedText && message,parser::CharBlock constructLocation)1465 void LabelEnforce::SayWithConstruct(SemanticsContext &context,
1466 parser::CharBlock stmtLocation, parser::MessageFormattedText &&message,
1467 parser::CharBlock constructLocation) {
1468 context.Say(stmtLocation, message)
1469 .Attach(constructLocation, GetEnclosingConstructMsg());
1470 }
1471
HasAlternateReturns(const Symbol & subprogram)1472 bool HasAlternateReturns(const Symbol &subprogram) {
1473 for (const auto *dummyArg : subprogram.get<SubprogramDetails>().dummyArgs()) {
1474 if (!dummyArg) {
1475 return true;
1476 }
1477 }
1478 return false;
1479 }
1480
InCommonBlock(const Symbol & symbol)1481 bool InCommonBlock(const Symbol &symbol) {
1482 const auto *details{symbol.detailsIf<ObjectEntityDetails>()};
1483 return details && details->commonBlock();
1484 }
1485
MaybeGetNodeName(const ConstructNode & construct)1486 const std::optional<parser::Name> &MaybeGetNodeName(
1487 const ConstructNode &construct) {
1488 return common::visit(
1489 common::visitors{
1490 [&](const parser::BlockConstruct *blockConstruct)
1491 -> const std::optional<parser::Name> & {
1492 return std::get<0>(blockConstruct->t).statement.v;
1493 },
1494 [&](const auto *a) -> const std::optional<parser::Name> & {
1495 return std::get<0>(std::get<0>(a->t).statement.t);
1496 },
1497 },
1498 construct);
1499 }
1500
ToArraySpec(evaluate::FoldingContext & context,const evaluate::Shape & shape)1501 std::optional<ArraySpec> ToArraySpec(
1502 evaluate::FoldingContext &context, const evaluate::Shape &shape) {
1503 if (auto extents{evaluate::AsConstantExtents(context, shape)}) {
1504 ArraySpec result;
1505 for (const auto &extent : *extents) {
1506 result.emplace_back(ShapeSpec::MakeExplicit(Bound{extent}));
1507 }
1508 return {std::move(result)};
1509 } else {
1510 return std::nullopt;
1511 }
1512 }
1513
ToArraySpec(evaluate::FoldingContext & context,const std::optional<evaluate::Shape> & shape)1514 std::optional<ArraySpec> ToArraySpec(evaluate::FoldingContext &context,
1515 const std::optional<evaluate::Shape> &shape) {
1516 return shape ? ToArraySpec(context, *shape) : std::nullopt;
1517 }
1518
HasDefinedIo(GenericKind::DefinedIo which,const DerivedTypeSpec & derived,const Scope * scope)1519 bool HasDefinedIo(GenericKind::DefinedIo which, const DerivedTypeSpec &derived,
1520 const Scope *scope) {
1521 if (const Scope * dtScope{derived.scope()}) {
1522 for (const auto &pair : *dtScope) {
1523 const Symbol &symbol{*pair.second};
1524 if (const auto *generic{symbol.detailsIf<GenericDetails>()}) {
1525 GenericKind kind{generic->kind()};
1526 if (const auto *io{std::get_if<GenericKind::DefinedIo>(&kind.u)}) {
1527 if (*io == which) {
1528 return true; // type-bound GENERIC exists
1529 }
1530 }
1531 }
1532 }
1533 }
1534 if (scope) {
1535 SourceName name{GenericKind::AsFortran(which)};
1536 evaluate::DynamicType dyDerived{derived};
1537 for (; scope && !scope->IsGlobal(); scope = &scope->parent()) {
1538 auto iter{scope->find(name)};
1539 if (iter != scope->end()) {
1540 const auto &generic{iter->second->GetUltimate().get<GenericDetails>()};
1541 for (auto ref : generic.specificProcs()) {
1542 const Symbol &procSym{ref->GetUltimate()};
1543 if (const auto *subp{procSym.detailsIf<SubprogramDetails>()}) {
1544 if (!subp->dummyArgs().empty()) {
1545 if (const Symbol * first{subp->dummyArgs().at(0)}) {
1546 if (const DeclTypeSpec * dtSpec{first->GetType()}) {
1547 if (auto dyDummy{evaluate::DynamicType::From(*dtSpec)}) {
1548 if (dyDummy->IsTkCompatibleWith(dyDerived)) {
1549 return true; // GENERIC or INTERFACE not in type
1550 }
1551 }
1552 }
1553 }
1554 }
1555 }
1556 }
1557 }
1558 }
1559 }
1560 return false;
1561 }
1562
FindUnsafeIoDirectComponent(GenericKind::DefinedIo which,const DerivedTypeSpec & derived,const Scope * scope)1563 const Symbol *FindUnsafeIoDirectComponent(GenericKind::DefinedIo which,
1564 const DerivedTypeSpec &derived, const Scope *scope) {
1565 if (HasDefinedIo(which, derived, scope)) {
1566 return nullptr;
1567 }
1568 if (const Scope * dtScope{derived.scope()}) {
1569 for (const auto &pair : *dtScope) {
1570 const Symbol &symbol{*pair.second};
1571 if (IsAllocatableOrPointer(symbol)) {
1572 return &symbol;
1573 }
1574 if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
1575 if (const DeclTypeSpec * type{details->type()}) {
1576 if (type->category() == DeclTypeSpec::Category::TypeDerived) {
1577 if (const Symbol *
1578 bad{FindUnsafeIoDirectComponent(
1579 which, type->derivedTypeSpec(), scope)}) {
1580 return bad;
1581 }
1582 }
1583 }
1584 }
1585 }
1586 }
1587 return nullptr;
1588 }
1589
1590 } // namespace Fortran::semantics
1591