1 //===-- lib/Semantics/resolve-labels.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 "resolve-labels.h"
10 #include "flang/Common/enum-set.h"
11 #include "flang/Common/template.h"
12 #include "flang/Parser/parse-tree-visitor.h"
13 #include "flang/Semantics/semantics.h"
15 #include <type_traits>
17 namespace Fortran::semantics
{
19 using namespace parser::literals
;
22 TargetStatementEnum
, Do
, Branch
, Format
, CompatibleDo
, CompatibleBranch
)
23 using LabeledStmtClassificationSet
=
24 common::EnumSet
<TargetStatementEnum
, TargetStatementEnum_enumSize
>;
26 using IndexList
= std::vector
<std::pair
<parser::CharBlock
, parser::CharBlock
>>;
27 // A ProxyForScope is an integral proxy for a Fortran scope. This is required
28 // because the parse tree does not actually have the scopes required.
29 using ProxyForScope
= unsigned;
30 // Minimal scope information
32 ProxyForScope parent
{};
33 bool isExteriorGotoFatal
{false};
36 struct LabeledStatementInfoTuplePOD
{
37 ProxyForScope proxyForScope
;
38 parser::CharBlock parserCharBlock
;
39 LabeledStmtClassificationSet labeledStmtClassificationSet
;
40 bool isExecutableConstructEndStmt
;
42 using TargetStmtMap
= std::map
<parser::Label
, LabeledStatementInfoTuplePOD
>;
43 struct SourceStatementInfoTuplePOD
{
44 SourceStatementInfoTuplePOD(const parser::Label
&parserLabel
,
45 const ProxyForScope
&proxyForScope
,
46 const parser::CharBlock
&parserCharBlock
)
47 : parserLabel
{parserLabel
}, proxyForScope
{proxyForScope
},
48 parserCharBlock
{parserCharBlock
} {}
49 parser::Label parserLabel
;
50 ProxyForScope proxyForScope
;
51 parser::CharBlock parserCharBlock
;
53 using SourceStmtList
= std::vector
<SourceStatementInfoTuplePOD
>;
54 enum class Legality
{ never
, always
, formerly
};
56 bool HasScope(ProxyForScope scope
) { return scope
!= ProxyForScope
{0u}; }
60 constexpr Legality
IsLegalDoTerm(const parser::Statement
<A
> &) {
61 if (std::is_same_v
<A
, common::Indirection
<parser::EndDoStmt
>> ||
62 std::is_same_v
<A
, parser::EndDoStmt
>) {
63 return Legality::always
;
64 } else if (std::is_same_v
<A
, parser::EndForallStmt
> ||
65 std::is_same_v
<A
, parser::EndWhereStmt
>) {
66 // Executable construct end statements are also supported as
67 // an extension but they need special care because the associated
68 // construct create their own scope.
69 return Legality::formerly
;
71 return Legality::never
;
75 constexpr Legality
IsLegalDoTerm(
76 const parser::Statement
<parser::ActionStmt
> &actionStmt
) {
77 if (std::holds_alternative
<parser::ContinueStmt
>(actionStmt
.statement
.u
)) {
79 return Legality::always
;
80 } else if (!(std::holds_alternative
<
81 common::Indirection
<parser::ArithmeticIfStmt
>>(
82 actionStmt
.statement
.u
) ||
83 std::holds_alternative
<common::Indirection
<parser::CycleStmt
>>(
84 actionStmt
.statement
.u
) ||
85 std::holds_alternative
<common::Indirection
<parser::ExitStmt
>>(
86 actionStmt
.statement
.u
) ||
87 std::holds_alternative
<common::Indirection
<parser::StopStmt
>>(
88 actionStmt
.statement
.u
) ||
89 std::holds_alternative
<common::Indirection
<parser::GotoStmt
>>(
90 actionStmt
.statement
.u
) ||
91 std::holds_alternative
<
92 common::Indirection
<parser::ReturnStmt
>>(
93 actionStmt
.statement
.u
))) {
94 return Legality::formerly
;
96 return Legality::never
;
100 template <typename A
> constexpr bool IsFormat(const parser::Statement
<A
> &) {
101 return std::is_same_v
<A
, common::Indirection
<parser::FormatStmt
>>;
104 template <typename A
>
105 constexpr Legality
IsLegalBranchTarget(const parser::Statement
<A
> &) {
106 if (std::is_same_v
<A
, parser::ActionStmt
> ||
107 std::is_same_v
<A
, parser::AssociateStmt
> ||
108 std::is_same_v
<A
, parser::EndAssociateStmt
> ||
109 std::is_same_v
<A
, parser::IfThenStmt
> ||
110 std::is_same_v
<A
, parser::EndIfStmt
> ||
111 std::is_same_v
<A
, parser::SelectCaseStmt
> ||
112 std::is_same_v
<A
, parser::EndSelectStmt
> ||
113 std::is_same_v
<A
, parser::SelectRankStmt
> ||
114 std::is_same_v
<A
, parser::SelectTypeStmt
> ||
115 std::is_same_v
<A
, common::Indirection
<parser::LabelDoStmt
>> ||
116 std::is_same_v
<A
, parser::NonLabelDoStmt
> ||
117 std::is_same_v
<A
, parser::EndDoStmt
> ||
118 std::is_same_v
<A
, common::Indirection
<parser::EndDoStmt
>> ||
119 std::is_same_v
<A
, parser::BlockStmt
> ||
120 std::is_same_v
<A
, parser::EndBlockStmt
> ||
121 std::is_same_v
<A
, parser::CriticalStmt
> ||
122 std::is_same_v
<A
, parser::EndCriticalStmt
> ||
123 std::is_same_v
<A
, parser::ForallConstructStmt
> ||
124 std::is_same_v
<A
, parser::WhereConstructStmt
> ||
125 std::is_same_v
<A
, parser::EndFunctionStmt
> ||
126 std::is_same_v
<A
, parser::EndMpSubprogramStmt
> ||
127 std::is_same_v
<A
, parser::EndProgramStmt
> ||
128 std::is_same_v
<A
, parser::EndSubroutineStmt
>) {
129 return Legality::always
;
131 return Legality::never
;
135 template <typename A
>
136 constexpr LabeledStmtClassificationSet
ConstructBranchTargetFlags(
137 const parser::Statement
<A
> &statement
) {
138 LabeledStmtClassificationSet labeledStmtClassificationSet
{};
139 if (IsLegalDoTerm(statement
) == Legality::always
) {
140 labeledStmtClassificationSet
.set(TargetStatementEnum::Do
);
141 } else if (IsLegalDoTerm(statement
) == Legality::formerly
) {
142 labeledStmtClassificationSet
.set(TargetStatementEnum::CompatibleDo
);
144 if (IsLegalBranchTarget(statement
) == Legality::always
) {
145 labeledStmtClassificationSet
.set(TargetStatementEnum::Branch
);
146 } else if (IsLegalBranchTarget(statement
) == Legality::formerly
) {
147 labeledStmtClassificationSet
.set(TargetStatementEnum::CompatibleBranch
);
149 if (IsFormat(statement
)) {
150 labeledStmtClassificationSet
.set(TargetStatementEnum::Format
);
152 return labeledStmtClassificationSet
;
155 static unsigned SayLabel(parser::Label label
) {
156 return static_cast<unsigned>(label
);
159 struct UnitAnalysis
{
160 UnitAnalysis() { scopeModel
.emplace_back(); }
162 SourceStmtList doStmtSources
;
163 SourceStmtList formatStmtSources
;
164 SourceStmtList otherStmtSources
;
165 SourceStmtList assignStmtSources
;
166 TargetStmtMap targetStmts
;
167 std::vector
<ScopeInfo
> scopeModel
;
170 // Some parse tree record for statements simply wrap construct names;
171 // others include them as tuple components. Given a statement,
172 // return a pointer to its name if it has one.
173 template <typename A
>
174 const parser::CharBlock
*GetStmtName(const parser::Statement
<A
> &stmt
) {
175 const std::optional
<parser::Name
> *name
{nullptr};
176 if constexpr (WrapperTrait
<A
>) {
177 if constexpr (std::is_same_v
<decltype(A::v
), parser::Name
>) {
178 return &stmt
.statement
.v
.source
;
180 name
= &stmt
.statement
.v
;
182 } else if constexpr (std::is_same_v
<A
, parser::SelectRankStmt
> ||
183 std::is_same_v
<A
, parser::SelectTypeStmt
>) {
184 name
= &std::get
<0>(stmt
.statement
.t
);
185 } else if constexpr (common::HasMember
<parser::Name
,
186 decltype(stmt
.statement
.t
)>) {
187 return &std::get
<parser::Name
>(stmt
.statement
.t
).source
;
189 name
= &std::get
<std::optional
<parser::Name
>>(stmt
.statement
.t
);
192 return &(*name
)->source
;
197 class ParseTreeAnalyzer
{
199 ParseTreeAnalyzer(ParseTreeAnalyzer
&&that
) = default;
200 ParseTreeAnalyzer(SemanticsContext
&context
) : context_
{context
} {}
202 template <typename A
> constexpr bool Pre(const A
&x
) {
203 using LabeledProgramUnitStmts
=
204 std::tuple
<parser::MainProgram
, parser::FunctionSubprogram
,
205 parser::SubroutineSubprogram
, parser::SeparateModuleSubprogram
>;
206 if constexpr (common::HasMember
<A
, LabeledProgramUnitStmts
>) {
207 const auto &endStmt
{std::get
<std::tuple_size_v
<decltype(x
.t
)> - 1>(x
.t
)};
209 // The END statement for a subprogram appears after any internal
210 // subprograms. Visit that statement in advance so that results
211 // are placed in the correct programUnits_ slot.
212 auto targetFlags
{ConstructBranchTargetFlags(endStmt
)};
213 AddTargetLabelDefinition(
214 endStmt
.label
.value(), targetFlags
, currentScope_
);
219 template <typename A
> constexpr void Post(const A
&) {}
221 template <typename A
> bool Pre(const parser::Statement
<A
> &statement
) {
222 currentPosition_
= statement
.source
;
223 const auto &label
= statement
.label
;
227 using LabeledConstructStmts
= std::tuple
<parser::AssociateStmt
,
228 parser::BlockStmt
, parser::ChangeTeamStmt
, parser::CriticalStmt
,
229 parser::IfThenStmt
, parser::NonLabelDoStmt
, parser::SelectCaseStmt
,
230 parser::SelectRankStmt
, parser::SelectTypeStmt
,
231 parser::ForallConstructStmt
, parser::WhereConstructStmt
>;
232 using LabeledConstructEndStmts
= std::tuple
<parser::EndAssociateStmt
,
233 parser::EndBlockStmt
, parser::EndChangeTeamStmt
,
234 parser::EndCriticalStmt
, parser::EndDoStmt
, parser::EndForallStmt
,
235 parser::EndIfStmt
, parser::EndWhereStmt
>;
236 using LabeledProgramUnitEndStmts
=
237 std::tuple
<parser::EndFunctionStmt
, parser::EndMpSubprogramStmt
,
238 parser::EndProgramStmt
, parser::EndSubroutineStmt
>;
239 auto targetFlags
{ConstructBranchTargetFlags(statement
)};
240 if constexpr (common::HasMember
<A
, LabeledConstructStmts
>) {
241 AddTargetLabelDefinition(label
.value(), targetFlags
, ParentScope());
242 } else if constexpr (std::is_same_v
<A
, parser::EndIfStmt
> ||
243 std::is_same_v
<A
, parser::EndSelectStmt
>) {
244 // the label on an END IF/SELECT is not in the last part/case
245 AddTargetLabelDefinition(label
.value(), targetFlags
, ParentScope(), true);
246 } else if constexpr (common::HasMember
<A
, LabeledConstructEndStmts
>) {
247 constexpr bool isExecutableConstructEndStmt
{true};
248 AddTargetLabelDefinition(label
.value(), targetFlags
, currentScope_
,
249 isExecutableConstructEndStmt
);
250 } else if constexpr (!common::HasMember
<A
, LabeledProgramUnitEndStmts
>) {
251 // Program unit END statements have already been processed.
252 AddTargetLabelDefinition(label
.value(), targetFlags
, currentScope_
);
258 bool Pre(const parser::ProgramUnit
&) { return InitializeNewScopeContext(); }
259 bool Pre(const parser::InternalSubprogram
&) {
260 return InitializeNewScopeContext();
262 bool Pre(const parser::ModuleSubprogram
&) {
263 return InitializeNewScopeContext();
265 bool Pre(const parser::AssociateConstruct
&associateConstruct
) {
266 return PushConstructName(associateConstruct
);
268 bool Pre(const parser::BlockConstruct
&blockConstruct
) {
269 return PushConstructName(blockConstruct
);
271 bool Pre(const parser::ChangeTeamConstruct
&changeTeamConstruct
) {
272 return PushConstructName(changeTeamConstruct
);
274 bool Pre(const parser::CriticalConstruct
&criticalConstruct
) {
275 return PushConstructName(criticalConstruct
);
277 bool Pre(const parser::DoConstruct
&doConstruct
) {
278 const auto &optionalName
{std::get
<std::optional
<parser::Name
>>(
279 std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(doConstruct
.t
)
282 constructNames_
.emplace_back(optionalName
->ToString());
284 // Allow FORTRAN '66 extended DO ranges
286 // Process labels of the DO and END DO statements, but not the
287 // statements themselves, so that a non-construct END DO
288 // can be distinguished (below).
289 Pre(std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(doConstruct
.t
));
290 Walk(std::get
<parser::Block
>(doConstruct
.t
), *this);
291 Pre(std::get
<parser::Statement
<parser::EndDoStmt
>>(doConstruct
.t
));
292 PopConstructName(doConstruct
);
295 void Post(const parser::EndDoStmt
&endDoStmt
) {
296 // Visited only for non-construct labeled DO termination
297 if (const auto &name
{endDoStmt
.v
}) {
298 context_
.Say(name
->source
, "Unexpected DO construct name '%s'"_err_en_US
,
302 bool Pre(const parser::IfConstruct
&ifConstruct
) {
303 return PushConstructName(ifConstruct
);
305 void Post(const parser::IfThenStmt
&) { PushScope(false); }
306 bool Pre(const parser::IfConstruct::ElseIfBlock
&) {
307 return SwitchToNewScope();
309 bool Pre(const parser::IfConstruct::ElseBlock
&) {
310 return SwitchToNewScope();
312 bool Pre(const parser::EndIfStmt
&) {
316 bool Pre(const parser::CaseConstruct
&caseConstruct
) {
317 return PushConstructName(caseConstruct
);
319 void Post(const parser::SelectCaseStmt
&) { PushScope(false); }
320 bool Pre(const parser::CaseConstruct::Case
&) { return SwitchToNewScope(); }
321 bool Pre(const parser::SelectRankConstruct
&selectRankConstruct
) {
322 return PushConstructName(selectRankConstruct
);
324 void Post(const parser::SelectRankStmt
&) { PushScope(true); }
325 bool Pre(const parser::SelectRankConstruct::RankCase
&) {
326 return SwitchToNewScope();
328 bool Pre(const parser::SelectTypeConstruct
&selectTypeConstruct
) {
329 return PushConstructName(selectTypeConstruct
);
331 void Post(const parser::SelectTypeStmt
&) { PushScope(true); }
332 bool Pre(const parser::SelectTypeConstruct::TypeCase
&) {
333 return SwitchToNewScope();
335 void Post(const parser::EndSelectStmt
&) { PopScope(); }
336 bool Pre(const parser::WhereConstruct
&whereConstruct
) {
337 return PushConstructName(whereConstruct
);
339 bool Pre(const parser::ForallConstruct
&forallConstruct
) {
340 return PushConstructName(forallConstruct
);
343 void Post(const parser::AssociateConstruct
&associateConstruct
) {
344 PopConstructName(associateConstruct
);
346 void Post(const parser::BlockConstruct
&blockConstruct
) {
347 PopConstructName(blockConstruct
);
349 void Post(const parser::ChangeTeamConstruct
&changeTeamConstruct
) {
350 PopConstructName(changeTeamConstruct
);
352 void Post(const parser::CriticalConstruct
&criticalConstruct
) {
353 PopConstructName(criticalConstruct
);
355 void Post(const parser::IfConstruct
&ifConstruct
) {
356 PopConstructName(ifConstruct
);
358 void Post(const parser::CaseConstruct
&caseConstruct
) {
359 PopConstructName(caseConstruct
);
361 void Post(const parser::SelectRankConstruct
&selectRankConstruct
) {
362 PopConstructName(selectRankConstruct
);
364 void Post(const parser::SelectTypeConstruct
&selectTypeConstruct
) {
365 PopConstructName(selectTypeConstruct
);
367 void Post(const parser::WhereConstruct
&whereConstruct
) {
368 PopConstructName(whereConstruct
);
370 void Post(const parser::ForallConstruct
&forallConstruct
) {
371 PopConstructName(forallConstruct
);
374 // Checks for missing or mismatching names on various constructs (e.g., IF)
375 // and their intermediate or terminal statements that allow optional
376 // construct names(e.g., ELSE). When an optional construct name is present,
377 // the construct as a whole must have a name that matches.
378 template <typename FIRST
, typename CONSTRUCT
, typename STMT
>
379 void CheckOptionalName(const char *constructTag
, const CONSTRUCT
&a
,
380 const parser::Statement
<STMT
> &stmt
) {
381 if (const parser::CharBlock
* name
{GetStmtName(stmt
)}) {
382 const auto &firstStmt
{std::get
<parser::Statement
<FIRST
>>(a
.t
)};
383 if (const parser::CharBlock
* firstName
{GetStmtName(firstStmt
)}) {
384 if (*firstName
!= *name
) {
385 context_
.Say(*name
, "%s name mismatch"_err_en_US
, constructTag
)
386 .Attach(*firstName
, "should be"_en_US
);
389 context_
.Say(*name
, "%s name not allowed"_err_en_US
, constructTag
)
390 .Attach(firstStmt
.source
, "in unnamed %s"_en_US
, constructTag
);
396 void Post(const parser::BlockData
&blockData
) {
397 CheckOptionalName
<parser::BlockDataStmt
>("BLOCK DATA subprogram", blockData
,
398 std::get
<parser::Statement
<parser::EndBlockDataStmt
>>(blockData
.t
));
401 bool Pre(const parser::InterfaceBody
&) {
405 void Post(const parser::InterfaceBody
&) { PopDisposableMap(); }
408 void Post(const parser::InterfaceBody::Function
&func
) {
409 CheckOptionalName
<parser::FunctionStmt
>("FUNCTION", func
,
410 std::get
<parser::Statement
<parser::EndFunctionStmt
>>(func
.t
));
414 void Post(const parser::FunctionSubprogram
&functionSubprogram
) {
415 CheckOptionalName
<parser::FunctionStmt
>("FUNCTION", functionSubprogram
,
416 std::get
<parser::Statement
<parser::EndFunctionStmt
>>(
417 functionSubprogram
.t
));
421 void Post(const parser::InterfaceBlock
&interfaceBlock
) {
422 if (const auto &endGenericSpec
{
423 std::get
<parser::Statement
<parser::EndInterfaceStmt
>>(
426 const auto &interfaceStmt
{
427 std::get
<parser::Statement
<parser::InterfaceStmt
>>(interfaceBlock
.t
)};
428 if (std::holds_alternative
<parser::Abstract
>(interfaceStmt
.statement
.u
)) {
430 .Say(endGenericSpec
->source
,
431 "END INTERFACE generic name (%s) may not appear for ABSTRACT INTERFACE"_err_en_US
,
432 endGenericSpec
->source
)
434 interfaceStmt
.source
, "corresponding ABSTRACT INTERFACE"_en_US
);
435 } else if (const auto &genericSpec
{
436 std::get
<std::optional
<parser::GenericSpec
>>(
437 interfaceStmt
.statement
.u
)}) {
438 bool ok
{genericSpec
->source
== endGenericSpec
->source
};
440 // Accept variant spellings of .LT. &c.
442 std::get_if
<parser::DefinedOperator
>(&endGenericSpec
->u
)};
443 const auto *op
{std::get_if
<parser::DefinedOperator
>(&genericSpec
->u
)};
445 const auto *endIntrin
{
446 std::get_if
<parser::DefinedOperator::IntrinsicOperator
>(
449 std::get_if
<parser::DefinedOperator::IntrinsicOperator
>(
451 ok
= endIntrin
&& intrin
&& *endIntrin
== *intrin
;
456 .Say(endGenericSpec
->source
,
457 "END INTERFACE generic name (%s) does not match generic INTERFACE (%s)"_err_en_US
,
458 endGenericSpec
->source
, genericSpec
->source
)
459 .Attach(genericSpec
->source
, "corresponding INTERFACE"_en_US
);
463 .Say(endGenericSpec
->source
,
464 "END INTERFACE generic name (%s) may not appear for non-generic INTERFACE"_err_en_US
,
465 endGenericSpec
->source
)
466 .Attach(interfaceStmt
.source
, "corresponding INTERFACE"_en_US
);
472 void Post(const parser::Module
&module
) {
473 CheckOptionalName
<parser::ModuleStmt
>("MODULE", module
,
474 std::get
<parser::Statement
<parser::EndModuleStmt
>>(module
.t
));
478 void Post(const parser::SeparateModuleSubprogram
&separateModuleSubprogram
) {
479 CheckOptionalName
<parser::MpSubprogramStmt
>("MODULE PROCEDURE",
480 separateModuleSubprogram
,
481 std::get
<parser::Statement
<parser::EndMpSubprogramStmt
>>(
482 separateModuleSubprogram
.t
));
486 void Post(const parser::MainProgram
&mainProgram
) {
487 if (const parser::CharBlock
*
488 endName
{GetStmtName(std::get
<parser::Statement
<parser::EndProgramStmt
>>(
490 if (const auto &program
{
491 std::get
<std::optional
<parser::Statement
<parser::ProgramStmt
>>>(
493 if (*endName
!= program
->statement
.v
.source
) {
494 context_
.Say(*endName
, "END PROGRAM name mismatch"_err_en_US
)
495 .Attach(program
->statement
.v
.source
, "should be"_en_US
);
498 context_
.Say(*endName
,
499 "END PROGRAM has name without PROGRAM statement"_err_en_US
);
505 void Post(const parser::Submodule
&submodule
) {
506 CheckOptionalName
<parser::SubmoduleStmt
>("SUBMODULE", submodule
,
507 std::get
<parser::Statement
<parser::EndSubmoduleStmt
>>(submodule
.t
));
511 void Post(const parser::InterfaceBody::Subroutine
&sub
) {
512 CheckOptionalName
<parser::SubroutineStmt
>("SUBROUTINE", sub
,
513 std::get
<parser::Statement
<parser::EndSubroutineStmt
>>(sub
.t
));
517 void Post(const parser::SubroutineSubprogram
&subroutineSubprogram
) {
518 CheckOptionalName
<parser::SubroutineStmt
>("SUBROUTINE",
519 subroutineSubprogram
,
520 std::get
<parser::Statement
<parser::EndSubroutineStmt
>>(
521 subroutineSubprogram
.t
));
525 bool Pre(const parser::DerivedTypeDef
&) {
529 void Post(const parser::DerivedTypeDef
&derivedTypeDef
) {
530 CheckOptionalName
<parser::DerivedTypeStmt
>("derived type definition",
532 std::get
<parser::Statement
<parser::EndTypeStmt
>>(derivedTypeDef
.t
));
536 void Post(const parser::LabelDoStmt
&labelDoStmt
) {
537 AddLabelReferenceFromDoStmt(std::get
<parser::Label
>(labelDoStmt
.t
));
539 void Post(const parser::GotoStmt
&gotoStmt
) { AddLabelReference(gotoStmt
.v
); }
540 void Post(const parser::ComputedGotoStmt
&computedGotoStmt
) {
541 AddLabelReference(std::get
<std::list
<parser::Label
>>(computedGotoStmt
.t
));
543 void Post(const parser::ArithmeticIfStmt
&arithmeticIfStmt
) {
544 AddLabelReference(std::get
<1>(arithmeticIfStmt
.t
));
545 AddLabelReference(std::get
<2>(arithmeticIfStmt
.t
));
546 AddLabelReference(std::get
<3>(arithmeticIfStmt
.t
));
548 void Post(const parser::AssignStmt
&assignStmt
) {
549 AddLabelReferenceFromAssignStmt(std::get
<parser::Label
>(assignStmt
.t
));
551 void Post(const parser::AssignedGotoStmt
&assignedGotoStmt
) {
552 AddLabelReference(std::get
<std::list
<parser::Label
>>(assignedGotoStmt
.t
));
554 void Post(const parser::AltReturnSpec
&altReturnSpec
) {
555 AddLabelReference(altReturnSpec
.v
);
558 void Post(const parser::ErrLabel
&errLabel
) { AddLabelReference(errLabel
.v
); }
559 void Post(const parser::EndLabel
&endLabel
) { AddLabelReference(endLabel
.v
); }
560 void Post(const parser::EorLabel
&eorLabel
) { AddLabelReference(eorLabel
.v
); }
561 void Post(const parser::Format
&format
) {
562 if (const auto *labelPointer
{std::get_if
<parser::Label
>(&format
.u
)}) {
563 AddLabelReferenceToFormatStmt(*labelPointer
);
566 void Post(const parser::CycleStmt
&cycleStmt
) {
568 CheckLabelContext("CYCLE", cycleStmt
.v
->source
);
571 void Post(const parser::ExitStmt
&exitStmt
) {
573 CheckLabelContext("EXIT", exitStmt
.v
->source
);
577 const std::vector
<UnitAnalysis
> &ProgramUnits() const {
578 return programUnits_
;
580 SemanticsContext
&ErrorHandler() { return context_
; }
583 ScopeInfo
&PushScope(bool isExteriorGotoFatal
) {
584 auto &model
{programUnits_
.back().scopeModel
};
585 int newDepth
{model
.empty() ? 1 : model
[currentScope_
].depth
+ 1};
586 ScopeInfo
&result
{model
.emplace_back()};
587 result
.parent
= currentScope_
;
588 result
.depth
= newDepth
;
589 result
.isExteriorGotoFatal
= isExteriorGotoFatal
;
590 currentScope_
= model
.size() - 1;
593 bool InitializeNewScopeContext() {
594 programUnits_
.emplace_back(UnitAnalysis
{});
599 ScopeInfo
&PopScope() {
600 ScopeInfo
&result
{programUnits_
.back().scopeModel
[currentScope_
]};
601 currentScope_
= result
.parent
;
604 ProxyForScope
ParentScope() {
605 return programUnits_
.back().scopeModel
[currentScope_
].parent
;
607 bool SwitchToNewScope() {
608 PushScope(PopScope().isExteriorGotoFatal
);
612 template <typename A
> bool PushConstructName(const A
&a
) {
613 const auto &optionalName
{std::get
<0>(std::get
<0>(a
.t
).statement
.t
)};
615 constructNames_
.emplace_back(optionalName
->ToString());
617 // Gotos into this construct from outside it are diagnosed, and
618 // are fatal unless the construct is a DO, IF, or SELECT CASE.
619 PushScope(!(std::is_same_v
<A
, parser::DoConstruct
> ||
620 std::is_same_v
<A
, parser::IfConstruct
> ||
621 std::is_same_v
<A
, parser::CaseConstruct
>));
624 bool PushConstructName(const parser::BlockConstruct
&blockConstruct
) {
625 const auto &optionalName
{
626 std::get
<parser::Statement
<parser::BlockStmt
>>(blockConstruct
.t
)
629 constructNames_
.emplace_back(optionalName
->ToString());
634 template <typename A
> void PopConstructNameIfPresent(const A
&a
) {
635 const auto &optionalName
{std::get
<0>(std::get
<0>(a
.t
).statement
.t
)};
637 constructNames_
.pop_back();
640 void PopConstructNameIfPresent(const parser::BlockConstruct
&blockConstruct
) {
641 const auto &optionalName
{
642 std::get
<parser::Statement
<parser::BlockStmt
>>(blockConstruct
.t
)
645 constructNames_
.pop_back();
649 template <typename A
> void PopConstructName(const A
&a
) {
652 PopConstructNameIfPresent(a
);
655 template <typename FIRST
, typename CASEBLOCK
, typename CASE
,
657 void CheckSelectNames(const char *tag
, const CONSTRUCT
&construct
) {
658 CheckEndName
<FIRST
, parser::EndSelectStmt
>(tag
, construct
);
659 for (const auto &inner
: std::get
<std::list
<CASEBLOCK
>>(construct
.t
)) {
660 CheckOptionalName
<FIRST
>(
661 tag
, construct
, std::get
<parser::Statement
<CASE
>>(inner
.t
));
666 void PopConstructName(const parser::CaseConstruct
&caseConstruct
) {
667 CheckSelectNames
<parser::SelectCaseStmt
, parser::CaseConstruct::Case
,
668 parser::CaseStmt
>("SELECT CASE", caseConstruct
);
670 PopConstructNameIfPresent(caseConstruct
);
674 void PopConstructName(
675 const parser::SelectRankConstruct
&selectRankConstruct
) {
676 CheckSelectNames
<parser::SelectRankStmt
,
677 parser::SelectRankConstruct::RankCase
, parser::SelectRankCaseStmt
>(
678 "SELECT RANK", selectRankConstruct
);
680 PopConstructNameIfPresent(selectRankConstruct
);
684 void PopConstructName(
685 const parser::SelectTypeConstruct
&selectTypeConstruct
) {
686 CheckSelectNames
<parser::SelectTypeStmt
,
687 parser::SelectTypeConstruct::TypeCase
, parser::TypeGuardStmt
>(
688 "SELECT TYPE", selectTypeConstruct
);
690 PopConstructNameIfPresent(selectTypeConstruct
);
693 // Checks for missing or mismatching names on various constructs (e.g., BLOCK)
694 // and their END statements. Both names must be present if either one is.
695 template <typename FIRST
, typename END
, typename CONSTRUCT
>
696 void CheckEndName(const char *constructTag
, const CONSTRUCT
&a
) {
697 const auto &constructStmt
{std::get
<parser::Statement
<FIRST
>>(a
.t
)};
698 const auto &endStmt
{std::get
<parser::Statement
<END
>>(a
.t
)};
699 const parser::CharBlock
*endName
{GetStmtName(endStmt
)};
700 if (const parser::CharBlock
* constructName
{GetStmtName(constructStmt
)}) {
702 if (*constructName
!= *endName
) {
704 .Say(*endName
, "%s construct name mismatch"_err_en_US
,
706 .Attach(*constructName
, "should be"_en_US
);
711 "%s construct name required but missing"_err_en_US
,
713 .Attach(*constructName
, "should be"_en_US
);
715 } else if (endName
) {
717 .Say(*endName
, "%s construct name unexpected"_err_en_US
, constructTag
)
719 constructStmt
.source
, "unnamed %s statement"_en_US
, constructTag
);
724 void CheckName(const parser::AssociateConstruct
&associateConstruct
) {
725 CheckEndName
<parser::AssociateStmt
, parser::EndAssociateStmt
>(
726 "ASSOCIATE", associateConstruct
);
729 void CheckName(const parser::CriticalConstruct
&criticalConstruct
) {
730 CheckEndName
<parser::CriticalStmt
, parser::EndCriticalStmt
>(
731 "CRITICAL", criticalConstruct
);
734 void CheckName(const parser::DoConstruct
&doConstruct
) {
735 CheckEndName
<parser::NonLabelDoStmt
, parser::EndDoStmt
>("DO", doConstruct
);
736 if (auto label
{std::get
<std::optional
<parser::Label
>>(
737 std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(doConstruct
.t
)
739 const auto &endDoStmt
{
740 std::get
<parser::Statement
<parser::EndDoStmt
>>(doConstruct
.t
)};
741 if (!endDoStmt
.label
|| *endDoStmt
.label
!= *label
) {
743 .Say(endDoStmt
.source
,
744 "END DO statement must have the label '%d' matching its DO statement"_err_en_US
,
746 .Attach(std::get
<parser::Statement
<parser::NonLabelDoStmt
>>(
749 "corresponding DO statement"_en_US
);
754 void CheckName(const parser::ForallConstruct
&forallConstruct
) {
755 CheckEndName
<parser::ForallConstructStmt
, parser::EndForallStmt
>(
756 "FORALL", forallConstruct
);
760 void CheckName(const parser::BlockConstruct
&blockConstruct
) {
761 CheckEndName
<parser::BlockStmt
, parser::EndBlockStmt
>(
762 "BLOCK", blockConstruct
);
765 void CheckName(const parser::ChangeTeamConstruct
&changeTeamConstruct
) {
766 CheckEndName
<parser::ChangeTeamStmt
, parser::EndChangeTeamStmt
>(
767 "CHANGE TEAM", changeTeamConstruct
);
771 void CheckName(const parser::IfConstruct
&ifConstruct
) {
772 CheckEndName
<parser::IfThenStmt
, parser::EndIfStmt
>("IF", ifConstruct
);
773 for (const auto &elseIfBlock
:
774 std::get
<std::list
<parser::IfConstruct::ElseIfBlock
>>(ifConstruct
.t
)) {
775 CheckOptionalName
<parser::IfThenStmt
>("IF construct", ifConstruct
,
776 std::get
<parser::Statement
<parser::ElseIfStmt
>>(elseIfBlock
.t
));
778 if (const auto &elseBlock
{
779 std::get
<std::optional
<parser::IfConstruct::ElseBlock
>>(
781 CheckOptionalName
<parser::IfThenStmt
>("IF construct", ifConstruct
,
782 std::get
<parser::Statement
<parser::ElseStmt
>>(elseBlock
->t
));
787 void CheckName(const parser::WhereConstruct
&whereConstruct
) {
788 CheckEndName
<parser::WhereConstructStmt
, parser::EndWhereStmt
>(
789 "WHERE", whereConstruct
);
790 for (const auto &maskedElsewhere
:
791 std::get
<std::list
<parser::WhereConstruct::MaskedElsewhere
>>(
793 CheckOptionalName
<parser::WhereConstructStmt
>("WHERE construct",
795 std::get
<parser::Statement
<parser::MaskedElsewhereStmt
>>(
798 if (const auto &elsewhere
{
799 std::get
<std::optional
<parser::WhereConstruct::Elsewhere
>>(
800 whereConstruct
.t
)}) {
801 CheckOptionalName
<parser::WhereConstructStmt
>("WHERE construct",
803 std::get
<parser::Statement
<parser::ElsewhereStmt
>>(elsewhere
->t
));
808 void CheckLabelContext(
809 const char *const stmtString
, const parser::CharBlock
&constructName
) {
810 const auto iter
{std::find(constructNames_
.crbegin(),
811 constructNames_
.crend(), constructName
.ToString())};
812 if (iter
== constructNames_
.crend()) {
813 context_
.Say(constructName
, "%s construct-name is not in scope"_err_en_US
,
818 // 6.2.5, paragraph 2
819 void CheckLabelInRange(parser::Label label
) {
820 if (label
< 1 || label
> 99999) {
821 context_
.Say(currentPosition_
, "Label '%u' is out of range"_err_en_US
,
826 // 6.2.5., paragraph 2
827 void AddTargetLabelDefinition(parser::Label label
,
828 LabeledStmtClassificationSet labeledStmtClassificationSet
,
829 ProxyForScope scope
, bool isExecutableConstructEndStmt
= false) {
830 CheckLabelInRange(label
);
831 TargetStmtMap
&targetStmtMap
{disposableMaps_
.empty()
832 ? programUnits_
.back().targetStmts
833 : disposableMaps_
.back()};
834 const auto pair
{targetStmtMap
.emplace(label
,
835 LabeledStatementInfoTuplePOD
{scope
, currentPosition_
,
836 labeledStmtClassificationSet
, isExecutableConstructEndStmt
})};
838 context_
.Say(currentPosition_
, "Label '%u' is not distinct"_err_en_US
,
843 void AddLabelReferenceFromDoStmt(parser::Label label
) {
844 CheckLabelInRange(label
);
845 programUnits_
.back().doStmtSources
.emplace_back(
846 label
, currentScope_
, currentPosition_
);
849 void AddLabelReferenceToFormatStmt(parser::Label label
) {
850 CheckLabelInRange(label
);
851 programUnits_
.back().formatStmtSources
.emplace_back(
852 label
, currentScope_
, currentPosition_
);
855 void AddLabelReferenceFromAssignStmt(parser::Label label
) {
856 CheckLabelInRange(label
);
857 programUnits_
.back().assignStmtSources
.emplace_back(
858 label
, currentScope_
, currentPosition_
);
861 void AddLabelReference(parser::Label label
) {
862 CheckLabelInRange(label
);
863 programUnits_
.back().otherStmtSources
.emplace_back(
864 label
, currentScope_
, currentPosition_
);
867 void AddLabelReference(const std::list
<parser::Label
> &labels
) {
868 for (const parser::Label
&label
: labels
) {
869 AddLabelReference(label
);
873 void PushDisposableMap() { disposableMaps_
.emplace_back(); }
874 void PopDisposableMap() { disposableMaps_
.pop_back(); }
876 std::vector
<UnitAnalysis
> programUnits_
;
877 SemanticsContext
&context_
;
878 parser::CharBlock currentPosition_
;
879 ProxyForScope currentScope_
;
880 std::vector
<std::string
> constructNames_
;
881 // For labels in derived type definitions and procedure
882 // interfaces, which are their own inclusive scopes. None
883 // of these labels can be used as a branch target, but they
884 // should be pairwise distinct.
885 std::vector
<TargetStmtMap
> disposableMaps_
;
888 bool InInclusiveScope(const std::vector
<ScopeInfo
> &scopes
, ProxyForScope tail
,
889 ProxyForScope head
) {
890 for (; tail
!= head
; tail
= scopes
[tail
].parent
) {
891 if (!HasScope(tail
)) {
898 ParseTreeAnalyzer
LabelAnalysis(
899 SemanticsContext
&context
, const parser::Program
&program
) {
900 ParseTreeAnalyzer analysis
{context
};
901 Walk(program
, analysis
);
905 bool InBody(const parser::CharBlock
&position
,
906 const std::pair
<parser::CharBlock
, parser::CharBlock
> &pair
) {
907 if (position
.begin() >= pair
.first
.begin()) {
908 if (position
.begin() < pair
.second
.end()) {
915 LabeledStatementInfoTuplePOD
GetLabel(
916 const TargetStmtMap
&labels
, const parser::Label
&label
) {
917 const auto iter
{labels
.find(label
)};
918 if (iter
== labels
.cend()) {
919 return {0u, nullptr, LabeledStmtClassificationSet
{}, false};
926 void CheckBranchesIntoDoBody(const SourceStmtList
&branches
,
927 const TargetStmtMap
&labels
, const IndexList
&loopBodies
,
928 SemanticsContext
&context
) {
929 for (const auto &branch
: branches
) {
930 const auto &label
{branch
.parserLabel
};
931 auto branchTarget
{GetLabel(labels
, label
)};
932 if (HasScope(branchTarget
.proxyForScope
)) {
933 const auto &fromPosition
{branch
.parserCharBlock
};
934 const auto &toPosition
{branchTarget
.parserCharBlock
};
935 for (const auto &body
: loopBodies
) {
936 if (!InBody(fromPosition
, body
) && InBody(toPosition
, body
) &&
937 context
.ShouldWarn(common::LanguageFeature::BranchIntoConstruct
)) {
940 fromPosition
, "branch into loop body from outside"_warn_en_US
)
941 .Attach(body
.first
, "the loop branched into"_en_US
)
942 .set_languageFeature(
943 common::LanguageFeature::BranchIntoConstruct
);
950 void CheckDoNesting(const IndexList
&loopBodies
, SemanticsContext
&context
) {
951 for (auto i1
{loopBodies
.cbegin()}; i1
!= loopBodies
.cend(); ++i1
) {
953 for (auto i2
{i1
+ 1}; i2
!= loopBodies
.cend(); ++i2
) {
955 if (v2
.first
.begin() < v1
.second
.end() &&
956 v1
.second
.begin() < v2
.second
.begin()) {
957 context
.Say(v1
.first
, "DO loop doesn't properly nest"_err_en_US
)
958 .Attach(v2
.first
, "DO loop conflicts"_en_US
);
964 parser::CharBlock
SkipLabel(const parser::CharBlock
&position
) {
965 const std::size_t maxPosition
{position
.size()};
966 if (maxPosition
&& parser::IsDecimalDigit(position
[0])) {
968 for (; (i
< maxPosition
) && parser::IsDecimalDigit(position
[i
]); ++i
) {
970 for (; (i
< maxPosition
) && parser::IsWhiteSpace(position
[i
]); ++i
) {
972 return parser::CharBlock
{position
.begin() + i
, position
.end()};
977 ProxyForScope
ParentScope(
978 const std::vector
<ScopeInfo
> &scopes
, ProxyForScope scope
) {
979 return scopes
[scope
].parent
;
982 void CheckLabelDoConstraints(const SourceStmtList
&dos
,
983 const SourceStmtList
&branches
, const TargetStmtMap
&labels
,
984 const std::vector
<ScopeInfo
> &scopes
, SemanticsContext
&context
) {
985 IndexList loopBodies
;
986 for (const auto &stmt
: dos
) {
987 const auto &label
{stmt
.parserLabel
};
988 const auto &scope
{stmt
.proxyForScope
};
989 const auto &position
{stmt
.parserCharBlock
};
990 auto doTarget
{GetLabel(labels
, label
)};
991 if (!HasScope(doTarget
.proxyForScope
)) {
994 position
, "Label '%u' cannot be found"_err_en_US
, SayLabel(label
));
995 } else if (doTarget
.parserCharBlock
.begin() < position
.begin()) {
997 context
.Say(position
,
998 "Label '%u' doesn't lexically follow DO stmt"_err_en_US
,
1001 } else if ((InInclusiveScope(scopes
, scope
, doTarget
.proxyForScope
) &&
1002 doTarget
.labeledStmtClassificationSet
.test(
1003 TargetStatementEnum::CompatibleDo
)) ||
1004 (doTarget
.isExecutableConstructEndStmt
&&
1005 ParentScope(scopes
, doTarget
.proxyForScope
) == scope
)) {
1006 if (context
.ShouldWarn(
1007 common::LanguageFeature::OldLabelDoEndStatements
)) {
1010 "A DO loop should terminate with an END DO or CONTINUE"_port_en_US
)
1011 .Attach(doTarget
.parserCharBlock
,
1012 "DO loop currently ends at statement:"_en_US
)
1013 .set_languageFeature(
1014 common::LanguageFeature::OldLabelDoEndStatements
);
1016 } else if (!InInclusiveScope(scopes
, scope
, doTarget
.proxyForScope
)) {
1017 context
.Say(position
, "Label '%u' is not in DO loop scope"_err_en_US
,
1019 } else if (!doTarget
.labeledStmtClassificationSet
.test(
1020 TargetStatementEnum::Do
)) {
1021 context
.Say(doTarget
.parserCharBlock
,
1022 "A DO loop should terminate with an END DO or CONTINUE"_err_en_US
);
1024 loopBodies
.emplace_back(SkipLabel(position
), doTarget
.parserCharBlock
);
1028 CheckBranchesIntoDoBody(branches
, labels
, loopBodies
, context
);
1029 CheckDoNesting(loopBodies
, context
);
1033 void CheckScopeConstraints(const SourceStmtList
&stmts
,
1034 const TargetStmtMap
&labels
, const std::vector
<ScopeInfo
> &scopes
,
1035 SemanticsContext
&context
) {
1036 for (const auto &stmt
: stmts
) {
1037 const auto &label
{stmt
.parserLabel
};
1038 const auto &scope
{stmt
.proxyForScope
};
1039 const auto &position
{stmt
.parserCharBlock
};
1040 auto target
{GetLabel(labels
, label
)};
1041 if (!HasScope(target
.proxyForScope
)) {
1043 position
, "Label '%u' was not found"_err_en_US
, SayLabel(label
));
1044 } else if (!InInclusiveScope(scopes
, scope
, target
.proxyForScope
)) {
1045 // Clause 11.1.2.1 prohibits transfer of control to the interior of a
1046 // block from outside the block, but this does not apply to formats.
1047 // C1038 and C1034 forbid statements in FORALL and WHERE constructs
1048 // (resp.) from being branch targets.
1049 if (target
.labeledStmtClassificationSet
.test(
1050 TargetStatementEnum::Format
)) {
1053 bool isFatal
{false};
1054 ProxyForScope fromScope
{scope
};
1055 for (ProxyForScope toScope
{target
.proxyForScope
}; HasScope(toScope
);
1056 toScope
= scopes
[toScope
].parent
) {
1057 while (scopes
[fromScope
].depth
> scopes
[toScope
].depth
) {
1058 fromScope
= scopes
[fromScope
].parent
;
1060 if (toScope
== fromScope
) {
1063 if (scopes
[toScope
].isExteriorGotoFatal
) {
1069 context
.Say(position
,
1070 "Label '%u' is in a construct that prevents its use as a branch target here"_err_en_US
,
1072 } else if (context
.ShouldWarn(
1073 common::LanguageFeature::BranchIntoConstruct
)) {
1076 "Label '%u' is in a construct that should not be used as a branch target here"_warn_en_US
,
1078 .set_languageFeature(common::LanguageFeature::BranchIntoConstruct
);
1084 void CheckBranchTargetConstraints(const SourceStmtList
&stmts
,
1085 const TargetStmtMap
&labels
, SemanticsContext
&context
) {
1086 for (const auto &stmt
: stmts
) {
1087 const auto &label
{stmt
.parserLabel
};
1088 auto branchTarget
{GetLabel(labels
, label
)};
1089 if (HasScope(branchTarget
.proxyForScope
)) {
1090 if (!branchTarget
.labeledStmtClassificationSet
.test(
1091 TargetStatementEnum::Branch
) &&
1092 !branchTarget
.labeledStmtClassificationSet
.test(
1093 TargetStatementEnum::CompatibleBranch
)) { // error
1095 .Say(branchTarget
.parserCharBlock
,
1096 "Label '%u' is not a branch target"_err_en_US
, SayLabel(label
))
1097 .Attach(stmt
.parserCharBlock
, "Control flow use of '%u'"_en_US
,
1099 } else if (!branchTarget
.labeledStmtClassificationSet
.test(
1100 TargetStatementEnum::Branch
) &&
1101 context
.ShouldWarn(common::LanguageFeature::BadBranchTarget
)) {
1103 .Say(branchTarget
.parserCharBlock
,
1104 "Label '%u' is not a branch target"_warn_en_US
, SayLabel(label
))
1105 .Attach(stmt
.parserCharBlock
, "Control flow use of '%u'"_en_US
,
1107 .set_languageFeature(common::LanguageFeature::BadBranchTarget
);
1113 void CheckBranchConstraints(const SourceStmtList
&branches
,
1114 const TargetStmtMap
&labels
, const std::vector
<ScopeInfo
> &scopes
,
1115 SemanticsContext
&context
) {
1116 CheckScopeConstraints(branches
, labels
, scopes
, context
);
1117 CheckBranchTargetConstraints(branches
, labels
, context
);
1120 void CheckDataXferTargetConstraints(const SourceStmtList
&stmts
,
1121 const TargetStmtMap
&labels
, SemanticsContext
&context
) {
1122 for (const auto &stmt
: stmts
) {
1123 const auto &label
{stmt
.parserLabel
};
1124 auto ioTarget
{GetLabel(labels
, label
)};
1125 if (HasScope(ioTarget
.proxyForScope
)) {
1126 if (!ioTarget
.labeledStmtClassificationSet
.test(
1127 TargetStatementEnum::Format
)) {
1129 .Say(ioTarget
.parserCharBlock
, "'%u' not a FORMAT"_err_en_US
,
1131 .Attach(stmt
.parserCharBlock
, "data transfer use of '%u'"_en_US
,
1138 void CheckDataTransferConstraints(const SourceStmtList
&dataTransfers
,
1139 const TargetStmtMap
&labels
, const std::vector
<ScopeInfo
> &scopes
,
1140 SemanticsContext
&context
) {
1141 CheckScopeConstraints(dataTransfers
, labels
, scopes
, context
);
1142 CheckDataXferTargetConstraints(dataTransfers
, labels
, context
);
1145 void CheckAssignTargetConstraints(const SourceStmtList
&stmts
,
1146 const TargetStmtMap
&labels
, SemanticsContext
&context
) {
1147 for (const auto &stmt
: stmts
) {
1148 const auto &label
{stmt
.parserLabel
};
1149 auto target
{GetLabel(labels
, label
)};
1150 if (HasScope(target
.proxyForScope
) &&
1151 !target
.labeledStmtClassificationSet
.test(
1152 TargetStatementEnum::Branch
) &&
1153 !target
.labeledStmtClassificationSet
.test(
1154 TargetStatementEnum::Format
)) {
1155 parser::Message
*msg
{nullptr};
1156 if (!target
.labeledStmtClassificationSet
.test(
1157 TargetStatementEnum::CompatibleBranch
)) {
1158 msg
= &context
.Say(target
.parserCharBlock
,
1159 "Label '%u' is not a branch target or FORMAT"_err_en_US
,
1161 } else if (context
.ShouldWarn(common::LanguageFeature::BadBranchTarget
)) {
1164 .Say(target
.parserCharBlock
,
1165 "Label '%u' is not a branch target or FORMAT"_warn_en_US
,
1167 .set_languageFeature(common::LanguageFeature::BadBranchTarget
);
1170 msg
->Attach(stmt
.parserCharBlock
, "ASSIGN statement use of '%u'"_en_US
,
1177 void CheckAssignConstraints(const SourceStmtList
&assigns
,
1178 const TargetStmtMap
&labels
, const std::vector
<ScopeInfo
> &scopes
,
1179 SemanticsContext
&context
) {
1180 CheckScopeConstraints(assigns
, labels
, scopes
, context
);
1181 CheckAssignTargetConstraints(assigns
, labels
, context
);
1184 bool CheckConstraints(ParseTreeAnalyzer
&&parseTreeAnalysis
) {
1185 auto &context
{parseTreeAnalysis
.ErrorHandler()};
1186 for (const auto &programUnit
: parseTreeAnalysis
.ProgramUnits()) {
1187 const auto &dos
{programUnit
.doStmtSources
};
1188 const auto &branches
{programUnit
.otherStmtSources
};
1189 const auto &labels
{programUnit
.targetStmts
};
1190 const auto &scopes
{programUnit
.scopeModel
};
1191 CheckLabelDoConstraints(dos
, branches
, labels
, scopes
, context
);
1192 CheckBranchConstraints(branches
, labels
, scopes
, context
);
1193 const auto &dataTransfers
{programUnit
.formatStmtSources
};
1194 CheckDataTransferConstraints(dataTransfers
, labels
, scopes
, context
);
1195 const auto &assigns
{programUnit
.assignStmtSources
};
1196 CheckAssignConstraints(assigns
, labels
, scopes
, context
);
1198 return !context
.AnyFatalError();
1201 bool ValidateLabels(SemanticsContext
&context
, const parser::Program
&program
) {
1202 return CheckConstraints(LabelAnalysis(context
, program
));
1204 } // namespace Fortran::semantics