1 //===-- lib/Semantics/check-io.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 //===----------------------------------------------------------------------===//
10 #include "definable.h"
11 #include "flang/Common/format.h"
12 #include "flang/Evaluate/tools.h"
13 #include "flang/Parser/tools.h"
14 #include "flang/Semantics/expression.h"
15 #include "flang/Semantics/tools.h"
16 #include <unordered_map>
18 namespace Fortran::semantics
{
20 // TODO: C1234, C1235 -- defined I/O constraints
22 class FormatErrorReporter
{
24 FormatErrorReporter(SemanticsContext
&context
,
25 const parser::CharBlock
&formatCharBlock
, int errorAllowance
= 3)
26 : context_
{context
}, formatCharBlock_
{formatCharBlock
},
27 errorAllowance_
{errorAllowance
} {}
29 bool Say(const common::FormatMessage
&);
32 SemanticsContext
&context_
;
33 const parser::CharBlock
&formatCharBlock_
;
34 int errorAllowance_
; // initialized to maximum number of errors to report
37 bool FormatErrorReporter::Say(const common::FormatMessage
&msg
) {
38 if (!msg
.isError
&& !context_
.warnOnNonstandardUsage()) {
41 parser::MessageFormattedText text
{
42 parser::MessageFixedText
{msg
.text
, strlen(msg
.text
),
43 msg
.isError
? parser::Severity::Error
: parser::Severity::Warning
},
45 if (formatCharBlock_
.size()) {
46 // The input format is a folded expression. Error markers span the full
47 // original unfolded expression in formatCharBlock_.
48 context_
.Say(formatCharBlock_
, text
);
50 // The input format is a source expression. Error markers have an offset
51 // and length relative to the beginning of formatCharBlock_.
52 parser::CharBlock messageCharBlock
{
53 parser::CharBlock(formatCharBlock_
.begin() + msg
.offset
, msg
.length
)};
54 context_
.Say(messageCharBlock
, text
);
56 return msg
.isError
&& --errorAllowance_
<= 0;
59 void IoChecker::Enter(
60 const parser::Statement
<common::Indirection
<parser::FormatStmt
>> &stmt
) {
62 context_
.Say("Format statement must be labeled"_err_en_US
); // C1301
64 const char *formatStart
{static_cast<const char *>(
65 std::memchr(stmt
.source
.begin(), '(', stmt
.source
.size()))};
66 parser::CharBlock reporterCharBlock
{formatStart
, static_cast<std::size_t>(0)};
67 FormatErrorReporter reporter
{context_
, reporterCharBlock
};
68 auto reporterWrapper
{[&](const auto &msg
) { return reporter
.Say(msg
); }};
69 switch (context_
.GetDefaultKind(TypeCategory::Character
)) {
71 common::FormatValidator
<char> validator
{formatStart
,
72 stmt
.source
.size() - (formatStart
- stmt
.source
.begin()),
77 case 2: { // TODO: Get this to work.
78 common::FormatValidator
<char16_t
> validator
{
79 /*???*/ nullptr, /*???*/ 0, reporterWrapper
};
83 case 4: { // TODO: Get this to work.
84 common::FormatValidator
<char32_t
> validator
{
85 /*???*/ nullptr, /*???*/ 0, reporterWrapper
};
94 void IoChecker::Enter(const parser::ConnectSpec
&spec
) {
95 // ConnectSpec context FileNameExpr
96 if (std::get_if
<parser::FileNameExpr
>(&spec
.u
)) {
97 SetSpecifier(IoSpecKind::File
);
101 // Ignore trailing spaces (12.5.6.2 p1) and convert to upper case
102 static std::string
Normalize(const std::string
&value
) {
103 auto upper
{parser::ToUpperCaseLetters(value
)};
104 std::size_t lastNonBlank
{upper
.find_last_not_of(" ")};
105 upper
.resize(lastNonBlank
== std::string::npos
? 0 : lastNonBlank
+ 1);
109 void IoChecker::Enter(const parser::ConnectSpec::CharExpr
&spec
) {
110 IoSpecKind specKind
{};
111 using ParseKind
= parser::ConnectSpec::CharExpr::Kind
;
112 switch (std::get
<ParseKind
>(spec
.t
)) {
113 case ParseKind::Access
:
114 specKind
= IoSpecKind::Access
;
116 case ParseKind::Action
:
117 specKind
= IoSpecKind::Action
;
119 case ParseKind::Asynchronous
:
120 specKind
= IoSpecKind::Asynchronous
;
122 case ParseKind::Blank
:
123 specKind
= IoSpecKind::Blank
;
125 case ParseKind::Decimal
:
126 specKind
= IoSpecKind::Decimal
;
128 case ParseKind::Delim
:
129 specKind
= IoSpecKind::Delim
;
131 case ParseKind::Encoding
:
132 specKind
= IoSpecKind::Encoding
;
134 case ParseKind::Form
:
135 specKind
= IoSpecKind::Form
;
138 specKind
= IoSpecKind::Pad
;
140 case ParseKind::Position
:
141 specKind
= IoSpecKind::Position
;
143 case ParseKind::Round
:
144 specKind
= IoSpecKind::Round
;
146 case ParseKind::Sign
:
147 specKind
= IoSpecKind::Sign
;
149 case ParseKind::Carriagecontrol
:
150 specKind
= IoSpecKind::Carriagecontrol
;
152 case ParseKind::Convert
:
153 specKind
= IoSpecKind::Convert
;
155 case ParseKind::Dispose
:
156 specKind
= IoSpecKind::Dispose
;
159 SetSpecifier(specKind
);
160 if (const std::optional
<std::string
> charConst
{GetConstExpr
<std::string
>(
161 std::get
<parser::ScalarDefaultCharExpr
>(spec
.t
))}) {
162 std::string s
{Normalize(*charConst
)};
163 if (specKind
== IoSpecKind::Access
) {
164 flags_
.set(Flag::KnownAccess
);
165 flags_
.set(Flag::AccessDirect
, s
== "DIRECT");
166 flags_
.set(Flag::AccessStream
, s
== "STREAM");
168 CheckStringValue(specKind
, *charConst
, parser::FindSourceLocation(spec
));
169 if (specKind
== IoSpecKind::Carriagecontrol
&&
170 (s
== "FORTRAN" || s
== "NONE")) {
171 context_
.Say(parser::FindSourceLocation(spec
),
172 "Unimplemented %s value '%s'"_err_en_US
,
173 parser::ToUpperCaseLetters(common::EnumToString(specKind
)),
179 void IoChecker::Enter(const parser::ConnectSpec::Newunit
&var
) {
180 CheckForDefinableVariable(var
, "NEWUNIT");
181 SetSpecifier(IoSpecKind::Newunit
);
184 void IoChecker::Enter(const parser::ConnectSpec::Recl
&spec
) {
185 SetSpecifier(IoSpecKind::Recl
);
186 if (const std::optional
<std::int64_t> recl
{
187 GetConstExpr
<std::int64_t>(spec
)}) {
189 context_
.Say(parser::FindSourceLocation(spec
),
190 "RECL value (%jd) must be positive"_err_en_US
,
196 void IoChecker::Enter(const parser::EndLabel
&) {
197 SetSpecifier(IoSpecKind::End
);
200 void IoChecker::Enter(const parser::EorLabel
&) {
201 SetSpecifier(IoSpecKind::Eor
);
204 void IoChecker::Enter(const parser::ErrLabel
&) {
205 SetSpecifier(IoSpecKind::Err
);
208 void IoChecker::Enter(const parser::FileUnitNumber
&) {
209 SetSpecifier(IoSpecKind::Unit
);
210 flags_
.set(Flag::NumberUnit
);
213 void IoChecker::Enter(const parser::Format
&spec
) {
214 SetSpecifier(IoSpecKind::Fmt
);
215 flags_
.set(Flag::FmtOrNml
);
218 [&](const parser::Label
&) { flags_
.set(Flag::LabelFmt
); },
219 [&](const parser::Star
&) { flags_
.set(Flag::StarFmt
); },
220 [&](const parser::Expr
&format
) {
221 const SomeExpr
*expr
{GetExpr(context_
, format
)};
225 auto type
{expr
->GetType()};
226 if (type
&& type
->category() == TypeCategory::Integer
&&
228 context_
.defaultKinds().GetDefaultKind(type
->category()) &&
230 flags_
.set(Flag::AssignFmt
);
231 if (!IsVariable(*expr
)) {
232 context_
.Say(format
.source
,
233 "Assigned format label must be a scalar variable"_err_en_US
);
234 } else if (context_
.ShouldWarn(common::LanguageFeature::Assign
)) {
235 context_
.Say(format
.source
,
236 "Assigned format labels are deprecated"_port_en_US
);
240 if (type
&& type
->category() != TypeCategory::Character
&&
241 (type
->category() != TypeCategory::Integer
||
244 common::LanguageFeature::NonCharacterFormat
)) {
245 // Legacy extension: using non-character variables, typically
246 // DATA-initialized with Hollerith, as format expressions.
247 if (context_
.ShouldWarn(
248 common::LanguageFeature::NonCharacterFormat
)) {
249 context_
.Say(format
.source
,
250 "Non-character format expression is not standard"_port_en_US
);
254 context_
.defaultKinds().GetDefaultKind(type
->category())) {
255 context_
.Say(format
.source
,
256 "Format expression must be default character or default scalar integer"_err_en_US
);
259 flags_
.set(Flag::CharFmt
);
260 const std::optional
<std::string
> constantFormat
{
261 GetConstExpr
<std::string
>(format
)};
262 if (!constantFormat
) {
265 // validate constant format -- 12.6.2.2
266 bool isFolded
{constantFormat
->size() != format
.source
.size() - 2};
267 parser::CharBlock reporterCharBlock
{isFolded
268 ? parser::CharBlock
{format
.source
}
269 : parser::CharBlock
{format
.source
.begin() + 1,
270 static_cast<std::size_t>(0)}};
271 FormatErrorReporter reporter
{context_
, reporterCharBlock
};
272 auto reporterWrapper
{
273 [&](const auto &msg
) { return reporter
.Say(msg
); }};
274 switch (context_
.GetDefaultKind(TypeCategory::Character
)) {
276 common::FormatValidator
<char> validator
{constantFormat
->c_str(),
277 constantFormat
->length(), reporterWrapper
, stmt_
};
282 // TODO: Get this to work. (Maybe combine with earlier instance?)
283 common::FormatValidator
<char16_t
> validator
{
284 /*???*/ nullptr, /*???*/ 0, reporterWrapper
, stmt_
};
289 // TODO: Get this to work. (Maybe combine with earlier instance?)
290 common::FormatValidator
<char32_t
> validator
{
291 /*???*/ nullptr, /*???*/ 0, reporterWrapper
, stmt_
};
303 void IoChecker::Enter(const parser::IdExpr
&) { SetSpecifier(IoSpecKind::Id
); }
305 void IoChecker::Enter(const parser::IdVariable
&spec
) {
306 SetSpecifier(IoSpecKind::Id
);
307 const auto *expr
{GetExpr(context_
, spec
)};
308 if (!expr
|| !expr
->GetType()) {
311 CheckForDefinableVariable(spec
, "ID");
312 int kind
{expr
->GetType()->kind()};
313 int defaultKind
{context_
.GetDefaultKind(TypeCategory::Integer
)};
314 if (kind
< defaultKind
) {
316 "ID kind (%d) is smaller than default INTEGER kind (%d)"_err_en_US
,
317 std::move(kind
), std::move(defaultKind
)); // C1229
321 void IoChecker::Enter(const parser::InputItem
&spec
) {
322 flags_
.set(Flag::DataList
);
323 const parser::Variable
*var
{std::get_if
<parser::Variable
>(&spec
.u
)};
327 CheckForDefinableVariable(*var
, "Input");
328 if (auto expr
{AnalyzeExpr(context_
, *var
)}) {
329 CheckForBadIoType(*expr
,
330 flags_
.test(Flag::FmtOrNml
) ? GenericKind::DefinedIo::ReadFormatted
331 : GenericKind::DefinedIo::ReadUnformatted
,
336 void IoChecker::Enter(const parser::InquireSpec
&spec
) {
337 // InquireSpec context FileNameExpr
338 if (std::get_if
<parser::FileNameExpr
>(&spec
.u
)) {
339 SetSpecifier(IoSpecKind::File
);
343 void IoChecker::Enter(const parser::InquireSpec::CharVar
&spec
) {
344 IoSpecKind specKind
{};
345 using ParseKind
= parser::InquireSpec::CharVar::Kind
;
346 switch (std::get
<ParseKind
>(spec
.t
)) {
347 case ParseKind::Access
:
348 specKind
= IoSpecKind::Access
;
350 case ParseKind::Action
:
351 specKind
= IoSpecKind::Action
;
353 case ParseKind::Asynchronous
:
354 specKind
= IoSpecKind::Asynchronous
;
356 case ParseKind::Blank
:
357 specKind
= IoSpecKind::Blank
;
359 case ParseKind::Decimal
:
360 specKind
= IoSpecKind::Decimal
;
362 case ParseKind::Delim
:
363 specKind
= IoSpecKind::Delim
;
365 case ParseKind::Direct
:
366 specKind
= IoSpecKind::Direct
;
368 case ParseKind::Encoding
:
369 specKind
= IoSpecKind::Encoding
;
371 case ParseKind::Form
:
372 specKind
= IoSpecKind::Form
;
374 case ParseKind::Formatted
:
375 specKind
= IoSpecKind::Formatted
;
377 case ParseKind::Iomsg
:
378 specKind
= IoSpecKind::Iomsg
;
380 case ParseKind::Name
:
381 specKind
= IoSpecKind::Name
;
384 specKind
= IoSpecKind::Pad
;
386 case ParseKind::Position
:
387 specKind
= IoSpecKind::Position
;
389 case ParseKind::Read
:
390 specKind
= IoSpecKind::Read
;
392 case ParseKind::Readwrite
:
393 specKind
= IoSpecKind::Readwrite
;
395 case ParseKind::Round
:
396 specKind
= IoSpecKind::Round
;
398 case ParseKind::Sequential
:
399 specKind
= IoSpecKind::Sequential
;
401 case ParseKind::Sign
:
402 specKind
= IoSpecKind::Sign
;
404 case ParseKind::Status
:
405 specKind
= IoSpecKind::Status
;
407 case ParseKind::Stream
:
408 specKind
= IoSpecKind::Stream
;
410 case ParseKind::Unformatted
:
411 specKind
= IoSpecKind::Unformatted
;
413 case ParseKind::Write
:
414 specKind
= IoSpecKind::Write
;
416 case ParseKind::Carriagecontrol
:
417 specKind
= IoSpecKind::Carriagecontrol
;
419 case ParseKind::Convert
:
420 specKind
= IoSpecKind::Convert
;
422 case ParseKind::Dispose
:
423 specKind
= IoSpecKind::Dispose
;
426 CheckForDefinableVariable(std::get
<parser::ScalarDefaultCharVariable
>(spec
.t
),
427 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
428 SetSpecifier(specKind
);
431 void IoChecker::Enter(const parser::InquireSpec::IntVar
&spec
) {
432 IoSpecKind specKind
{};
433 using ParseKind
= parser::InquireSpec::IntVar::Kind
;
434 switch (std::get
<parser::InquireSpec::IntVar::Kind
>(spec
.t
)) {
435 case ParseKind::Iostat
:
436 specKind
= IoSpecKind::Iostat
;
438 case ParseKind::Nextrec
:
439 specKind
= IoSpecKind::Nextrec
;
441 case ParseKind::Number
:
442 specKind
= IoSpecKind::Number
;
445 specKind
= IoSpecKind::Pos
;
447 case ParseKind::Recl
:
448 specKind
= IoSpecKind::Recl
;
450 case ParseKind::Size
:
451 specKind
= IoSpecKind::Size
;
454 CheckForDefinableVariable(std::get
<parser::ScalarIntVariable
>(spec
.t
),
455 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
456 SetSpecifier(specKind
);
459 void IoChecker::Enter(const parser::InquireSpec::LogVar
&spec
) {
460 IoSpecKind specKind
{};
461 using ParseKind
= parser::InquireSpec::LogVar::Kind
;
462 switch (std::get
<parser::InquireSpec::LogVar::Kind
>(spec
.t
)) {
463 case ParseKind::Exist
:
464 specKind
= IoSpecKind::Exist
;
466 case ParseKind::Named
:
467 specKind
= IoSpecKind::Named
;
469 case ParseKind::Opened
:
470 specKind
= IoSpecKind::Opened
;
472 case ParseKind::Pending
:
473 specKind
= IoSpecKind::Pending
;
476 SetSpecifier(specKind
);
479 void IoChecker::Enter(const parser::IoControlSpec
&spec
) {
480 // IoControlSpec context Name
481 flags_
.set(Flag::IoControlList
);
482 if (std::holds_alternative
<parser::Name
>(spec
.u
)) {
483 SetSpecifier(IoSpecKind::Nml
);
484 flags_
.set(Flag::FmtOrNml
);
488 void IoChecker::Enter(const parser::IoControlSpec::Asynchronous
&spec
) {
489 SetSpecifier(IoSpecKind::Asynchronous
);
490 if (const std::optional
<std::string
> charConst
{
491 GetConstExpr
<std::string
>(spec
)}) {
492 flags_
.set(Flag::AsynchronousYes
, Normalize(*charConst
) == "YES");
493 CheckStringValue(IoSpecKind::Asynchronous
, *charConst
,
494 parser::FindSourceLocation(spec
)); // C1223
498 void IoChecker::Enter(const parser::IoControlSpec::CharExpr
&spec
) {
499 IoSpecKind specKind
{};
500 using ParseKind
= parser::IoControlSpec::CharExpr::Kind
;
501 switch (std::get
<ParseKind
>(spec
.t
)) {
502 case ParseKind::Advance
:
503 specKind
= IoSpecKind::Advance
;
505 case ParseKind::Blank
:
506 specKind
= IoSpecKind::Blank
;
508 case ParseKind::Decimal
:
509 specKind
= IoSpecKind::Decimal
;
511 case ParseKind::Delim
:
512 specKind
= IoSpecKind::Delim
;
515 specKind
= IoSpecKind::Pad
;
517 case ParseKind::Round
:
518 specKind
= IoSpecKind::Round
;
520 case ParseKind::Sign
:
521 specKind
= IoSpecKind::Sign
;
524 SetSpecifier(specKind
);
525 if (const std::optional
<std::string
> charConst
{GetConstExpr
<std::string
>(
526 std::get
<parser::ScalarDefaultCharExpr
>(spec
.t
))}) {
527 if (specKind
== IoSpecKind::Advance
) {
528 flags_
.set(Flag::AdvanceYes
, Normalize(*charConst
) == "YES");
530 CheckStringValue(specKind
, *charConst
, parser::FindSourceLocation(spec
));
534 void IoChecker::Enter(const parser::IoControlSpec::Pos
&) {
535 SetSpecifier(IoSpecKind::Pos
);
538 void IoChecker::Enter(const parser::IoControlSpec::Rec
&) {
539 SetSpecifier(IoSpecKind::Rec
);
542 void IoChecker::Enter(const parser::IoControlSpec::Size
&var
) {
543 CheckForDefinableVariable(var
, "SIZE");
544 SetSpecifier(IoSpecKind::Size
);
547 void IoChecker::Enter(const parser::IoUnit
&spec
) {
548 if (const parser::Variable
* var
{std::get_if
<parser::Variable
>(&spec
.u
)}) {
549 // Only now after generic resolution can it be known whether a function
550 // call appearing as UNIT=f() is an integer scalar external unit number
551 // or a character pointer for internal I/O.
552 const auto *expr
{GetExpr(context_
, *var
)};
553 std::optional
<evaluate::DynamicType
> dyType
;
555 dyType
= expr
->GetType();
557 if (dyType
&& dyType
->category() == TypeCategory::Integer
) {
558 if (expr
->Rank() != 0) {
559 context_
.Say(parser::FindSourceLocation(*var
),
560 "I/O unit number must be scalar"_err_en_US
);
562 // In the case of an integer unit number variable, rewrite the parse
563 // tree as if the unit had been parsed as a FileUnitNumber in order
565 auto &mutableSpec
{const_cast<parser::IoUnit
&>(spec
)};
566 auto &mutableVar
{std::get
<parser::Variable
>(mutableSpec
.u
)};
567 auto source
{mutableVar
.GetSource()};
568 auto typedExpr
{std::move(mutableVar
.typedExpr
)};
569 auto newExpr
{common::visit(
570 [](auto &&indirection
) {
571 return parser::Expr
{std::move(indirection
)};
573 std::move(mutableVar
.u
))};
574 newExpr
.source
= source
;
575 newExpr
.typedExpr
= std::move(typedExpr
);
576 mutableSpec
.u
= parser::FileUnitNumber
{
577 parser::ScalarIntExpr
{parser::IntExpr
{std::move(newExpr
)}}};
578 } else if (!dyType
|| dyType
->category() != TypeCategory::Character
) {
579 SetSpecifier(IoSpecKind::Unit
);
580 context_
.Say(parser::FindSourceLocation(*var
),
581 "I/O unit must be a character variable or a scalar integer expression"_err_en_US
);
582 } else { // CHARACTER variable (internal I/O)
583 if (stmt_
== IoStmtKind::Write
) {
584 CheckForDefinableVariable(*var
, "Internal file");
586 if (HasVectorSubscript(*expr
)) {
587 context_
.Say(parser::FindSourceLocation(*var
), // C1201
588 "Internal file must not have a vector subscript"_err_en_US
);
590 SetSpecifier(IoSpecKind::Unit
);
591 flags_
.set(Flag::InternalUnit
);
593 } else if (std::get_if
<parser::Star
>(&spec
.u
)) {
594 SetSpecifier(IoSpecKind::Unit
);
595 flags_
.set(Flag::StarUnit
);
599 void IoChecker::Enter(const parser::MsgVariable
&var
) {
600 if (stmt_
== IoStmtKind::None
) {
601 // allocate, deallocate, image control
602 CheckForDefinableVariable(var
, "ERRMSG");
605 CheckForDefinableVariable(var
, "IOMSG");
606 SetSpecifier(IoSpecKind::Iomsg
);
609 void IoChecker::Enter(const parser::OutputItem
&item
) {
610 flags_
.set(Flag::DataList
);
611 if (const auto *x
{std::get_if
<parser::Expr
>(&item
.u
)}) {
612 if (const auto *expr
{GetExpr(context_
, *x
)}) {
613 if (evaluate::IsBOZLiteral(*expr
)) {
614 context_
.Say(parser::FindSourceLocation(*x
), // C7109
615 "Output item must not be a BOZ literal constant"_err_en_US
);
616 } else if (IsProcedure(*expr
)) {
617 context_
.Say(parser::FindSourceLocation(*x
),
618 "Output item must not be a procedure"_err_en_US
); // C1233
620 CheckForBadIoType(*expr
,
621 flags_
.test(Flag::FmtOrNml
)
622 ? GenericKind::DefinedIo::WriteFormatted
623 : GenericKind::DefinedIo::WriteUnformatted
,
624 parser::FindSourceLocation(item
));
629 void IoChecker::Enter(const parser::StatusExpr
&spec
) {
630 SetSpecifier(IoSpecKind::Status
);
631 if (const std::optional
<std::string
> charConst
{
632 GetConstExpr
<std::string
>(spec
)}) {
633 // Status values for Open and Close are different.
634 std::string s
{Normalize(*charConst
)};
635 if (stmt_
== IoStmtKind::Open
) {
636 flags_
.set(Flag::KnownStatus
);
637 flags_
.set(Flag::StatusNew
, s
== "NEW");
638 flags_
.set(Flag::StatusReplace
, s
== "REPLACE");
639 flags_
.set(Flag::StatusScratch
, s
== "SCRATCH");
640 // CheckStringValue compares for OPEN Status string values.
642 IoSpecKind::Status
, *charConst
, parser::FindSourceLocation(spec
));
645 CHECK(stmt_
== IoStmtKind::Close
);
646 if (s
!= "DELETE" && s
!= "KEEP") {
647 context_
.Say(parser::FindSourceLocation(spec
),
648 "Invalid STATUS value '%s'"_err_en_US
, *charConst
);
653 void IoChecker::Enter(const parser::StatVariable
&var
) {
654 if (stmt_
== IoStmtKind::None
) {
655 // allocate, deallocate, image control
656 CheckForDefinableVariable(var
, "STAT");
659 CheckForDefinableVariable(var
, "IOSTAT");
660 SetSpecifier(IoSpecKind::Iostat
);
663 void IoChecker::Leave(const parser::BackspaceStmt
&) {
664 CheckForPureSubprogram();
665 CheckForRequiredSpecifier(
666 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1240
670 void IoChecker::Leave(const parser::CloseStmt
&) {
671 CheckForPureSubprogram();
672 CheckForRequiredSpecifier(
673 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1208
677 void IoChecker::Leave(const parser::EndfileStmt
&) {
678 CheckForPureSubprogram();
679 CheckForRequiredSpecifier(
680 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1240
684 void IoChecker::Leave(const parser::FlushStmt
&) {
685 CheckForPureSubprogram();
686 CheckForRequiredSpecifier(
687 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1243
691 void IoChecker::Leave(const parser::InquireStmt
&stmt
) {
692 if (std::get_if
<std::list
<parser::InquireSpec
>>(&stmt
.u
)) {
693 CheckForPureSubprogram();
694 // Inquire by unit or by file (vs. by output list).
695 CheckForRequiredSpecifier(
696 flags_
.test(Flag::NumberUnit
) || specifierSet_
.test(IoSpecKind::File
),
697 "UNIT number or FILE"); // C1246
698 CheckForProhibitedSpecifier(IoSpecKind::File
, IoSpecKind::Unit
); // C1246
699 CheckForRequiredSpecifier(IoSpecKind::Id
, IoSpecKind::Pending
); // C1248
704 void IoChecker::Leave(const parser::OpenStmt
&) {
705 CheckForPureSubprogram();
706 CheckForRequiredSpecifier(specifierSet_
.test(IoSpecKind::Unit
) ||
707 specifierSet_
.test(IoSpecKind::Newunit
),
708 "UNIT or NEWUNIT"); // C1204, C1205
709 CheckForProhibitedSpecifier(
710 IoSpecKind::Newunit
, IoSpecKind::Unit
); // C1204, C1205
711 CheckForRequiredSpecifier(flags_
.test(Flag::StatusNew
), "STATUS='NEW'",
712 IoSpecKind::File
); // 12.5.6.10
713 CheckForRequiredSpecifier(flags_
.test(Flag::StatusReplace
),
714 "STATUS='REPLACE'", IoSpecKind::File
); // 12.5.6.10
715 CheckForProhibitedSpecifier(flags_
.test(Flag::StatusScratch
),
716 "STATUS='SCRATCH'", IoSpecKind::File
); // 12.5.6.10
717 if (flags_
.test(Flag::KnownStatus
)) {
718 CheckForRequiredSpecifier(IoSpecKind::Newunit
,
719 specifierSet_
.test(IoSpecKind::File
) ||
720 flags_
.test(Flag::StatusScratch
),
721 "FILE or STATUS='SCRATCH'"); // 12.5.6.12
723 CheckForRequiredSpecifier(IoSpecKind::Newunit
,
724 specifierSet_
.test(IoSpecKind::File
) ||
725 specifierSet_
.test(IoSpecKind::Status
),
726 "FILE or STATUS"); // 12.5.6.12
728 if (flags_
.test(Flag::KnownAccess
)) {
729 CheckForRequiredSpecifier(flags_
.test(Flag::AccessDirect
),
730 "ACCESS='DIRECT'", IoSpecKind::Recl
); // 12.5.6.15
731 CheckForProhibitedSpecifier(flags_
.test(Flag::AccessStream
),
732 "STATUS='STREAM'", IoSpecKind::Recl
); // 12.5.6.15
737 void IoChecker::Leave(const parser::PrintStmt
&) {
738 CheckForPureSubprogram();
742 static const parser::Name
*FindNamelist(
743 const std::list
<parser::IoControlSpec
> &controls
) {
744 for (const auto &control
: controls
) {
745 if (const parser::Name
* namelist
{std::get_if
<parser::Name
>(&control
.u
)}) {
746 if (namelist
->symbol
&&
747 namelist
->symbol
->GetUltimate().has
<NamelistDetails
>()) {
755 static void CheckForDoVariable(
756 const parser::ReadStmt
&readStmt
, SemanticsContext
&context
) {
757 const std::list
<parser::InputItem
> &items
{readStmt
.items
};
758 for (const auto &item
: items
) {
759 if (const parser::Variable
*
760 variable
{std::get_if
<parser::Variable
>(&item
.u
)}) {
761 context
.CheckIndexVarRedefine(*variable
);
766 void IoChecker::Leave(const parser::ReadStmt
&readStmt
) {
767 if (!flags_
.test(Flag::InternalUnit
)) {
768 CheckForPureSubprogram();
770 if (const parser::Name
* namelist
{FindNamelist(readStmt
.controls
)}) {
771 if (namelist
->symbol
) {
772 CheckNamelist(*namelist
->symbol
, GenericKind::DefinedIo::ReadFormatted
,
776 CheckForDoVariable(readStmt
, context_
);
777 if (!flags_
.test(Flag::IoControlList
)) {
782 CheckForProhibitedSpecifier(IoSpecKind::Delim
); // C1212
783 CheckForProhibitedSpecifier(IoSpecKind::Sign
); // C1212
784 CheckForProhibitedSpecifier(IoSpecKind::Rec
, IoSpecKind::End
); // C1220
785 CheckForRequiredSpecifier(IoSpecKind::Eor
,
786 specifierSet_
.test(IoSpecKind::Advance
) && !flags_
.test(Flag::AdvanceYes
),
787 "ADVANCE with value 'NO'"); // C1222 + 12.6.2.1p2
788 CheckForRequiredSpecifier(IoSpecKind::Blank
, flags_
.test(Flag::FmtOrNml
),
789 "FMT or NML"); // C1227
790 CheckForRequiredSpecifier(
791 IoSpecKind::Pad
, flags_
.test(Flag::FmtOrNml
), "FMT or NML"); // C1227
795 void IoChecker::Leave(const parser::RewindStmt
&) {
796 CheckForRequiredSpecifier(
797 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1240
798 CheckForPureSubprogram();
802 void IoChecker::Leave(const parser::WaitStmt
&) {
803 CheckForRequiredSpecifier(
804 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1237
805 CheckForPureSubprogram();
809 void IoChecker::Leave(const parser::WriteStmt
&writeStmt
) {
810 if (!flags_
.test(Flag::InternalUnit
)) {
811 CheckForPureSubprogram();
813 if (const parser::Name
* namelist
{FindNamelist(writeStmt
.controls
)}) {
814 if (namelist
->symbol
) {
815 CheckNamelist(*namelist
->symbol
, GenericKind::DefinedIo::WriteFormatted
,
820 CheckForProhibitedSpecifier(IoSpecKind::Blank
); // C1213
821 CheckForProhibitedSpecifier(IoSpecKind::End
); // C1213
822 CheckForProhibitedSpecifier(IoSpecKind::Eor
); // C1213
823 CheckForProhibitedSpecifier(IoSpecKind::Pad
); // C1213
824 CheckForProhibitedSpecifier(IoSpecKind::Size
); // C1213
825 CheckForRequiredSpecifier(
826 IoSpecKind::Sign
, flags_
.test(Flag::FmtOrNml
), "FMT or NML"); // C1227
827 CheckForRequiredSpecifier(IoSpecKind::Delim
,
828 flags_
.test(Flag::StarFmt
) || specifierSet_
.test(IoSpecKind::Nml
),
829 "FMT=* or NML"); // C1228
833 void IoChecker::LeaveReadWrite() const {
834 CheckForRequiredSpecifier(IoSpecKind::Unit
); // C1211
835 CheckForProhibitedSpecifier(IoSpecKind::Nml
, IoSpecKind::Rec
); // C1216
836 CheckForProhibitedSpecifier(IoSpecKind::Nml
, IoSpecKind::Fmt
); // C1216
837 CheckForProhibitedSpecifier(
838 IoSpecKind::Nml
, flags_
.test(Flag::DataList
), "a data list"); // C1216
839 CheckForProhibitedSpecifier(flags_
.test(Flag::InternalUnit
),
840 "UNIT=internal-file", IoSpecKind::Pos
); // C1219
841 CheckForProhibitedSpecifier(flags_
.test(Flag::InternalUnit
),
842 "UNIT=internal-file", IoSpecKind::Rec
); // C1219
843 CheckForProhibitedSpecifier(
844 flags_
.test(Flag::StarUnit
), "UNIT=*", IoSpecKind::Pos
); // C1219
845 CheckForProhibitedSpecifier(
846 flags_
.test(Flag::StarUnit
), "UNIT=*", IoSpecKind::Rec
); // C1219
847 CheckForProhibitedSpecifier(
848 IoSpecKind::Rec
, flags_
.test(Flag::StarFmt
), "FMT=*"); // C1220
849 CheckForRequiredSpecifier(IoSpecKind::Advance
,
850 flags_
.test(Flag::CharFmt
) || flags_
.test(Flag::LabelFmt
) ||
851 flags_
.test(Flag::AssignFmt
),
852 "an explicit format"); // C1221
853 CheckForProhibitedSpecifier(IoSpecKind::Advance
,
854 flags_
.test(Flag::InternalUnit
), "UNIT=internal-file"); // C1221
855 CheckForRequiredSpecifier(flags_
.test(Flag::AsynchronousYes
),
856 "ASYNCHRONOUS='YES'", flags_
.test(Flag::NumberUnit
),
857 "UNIT=number"); // C1224
858 CheckForRequiredSpecifier(IoSpecKind::Id
, flags_
.test(Flag::AsynchronousYes
),
859 "ASYNCHRONOUS='YES'"); // C1225
860 CheckForProhibitedSpecifier(IoSpecKind::Pos
, IoSpecKind::Rec
); // C1226
861 CheckForRequiredSpecifier(IoSpecKind::Decimal
, flags_
.test(Flag::FmtOrNml
),
862 "FMT or NML"); // C1227
863 CheckForRequiredSpecifier(IoSpecKind::Round
, flags_
.test(Flag::FmtOrNml
),
864 "FMT or NML"); // C1227
867 void IoChecker::SetSpecifier(IoSpecKind specKind
) {
868 if (stmt_
== IoStmtKind::None
) {
869 // FMT may appear on PRINT statements, which don't have any checks.
870 // [IO]MSG and [IO]STAT parse symbols are shared with non-I/O statements.
873 // C1203, C1207, C1210, C1236, C1239, C1242, C1245
874 if (specifierSet_
.test(specKind
)) {
875 context_
.Say("Duplicate %s specifier"_err_en_US
,
876 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
878 specifierSet_
.set(specKind
);
881 void IoChecker::CheckStringValue(IoSpecKind specKind
, const std::string
&value
,
882 const parser::CharBlock
&source
) const {
883 static std::unordered_map
<IoSpecKind
, const std::set
<std::string
>> specValues
{
884 {IoSpecKind::Access
, {"DIRECT", "SEQUENTIAL", "STREAM"}},
885 {IoSpecKind::Action
, {"READ", "READWRITE", "WRITE"}},
886 {IoSpecKind::Advance
, {"NO", "YES"}},
887 {IoSpecKind::Asynchronous
, {"NO", "YES"}},
888 {IoSpecKind::Blank
, {"NULL", "ZERO"}},
889 {IoSpecKind::Decimal
, {"COMMA", "POINT"}},
890 {IoSpecKind::Delim
, {"APOSTROPHE", "NONE", "QUOTE"}},
891 {IoSpecKind::Encoding
, {"DEFAULT", "UTF-8"}},
892 {IoSpecKind::Form
, {"FORMATTED", "UNFORMATTED"}},
893 {IoSpecKind::Pad
, {"NO", "YES"}},
894 {IoSpecKind::Position
, {"APPEND", "ASIS", "REWIND"}},
896 {"COMPATIBLE", "DOWN", "NEAREST", "PROCESSOR_DEFINED", "UP", "ZERO"}},
897 {IoSpecKind::Sign
, {"PLUS", "PROCESSOR_DEFINED", "SUPPRESS"}},
899 // Open values; Close values are {"DELETE", "KEEP"}.
900 {"NEW", "OLD", "REPLACE", "SCRATCH", "UNKNOWN"}},
901 {IoSpecKind::Carriagecontrol
, {"LIST", "FORTRAN", "NONE"}},
902 {IoSpecKind::Convert
, {"BIG_ENDIAN", "LITTLE_ENDIAN", "NATIVE"}},
903 {IoSpecKind::Dispose
, {"DELETE", "KEEP"}},
905 auto upper
{Normalize(value
)};
906 if (specValues
.at(specKind
).count(upper
) == 0) {
907 if (specKind
== IoSpecKind::Access
&& upper
== "APPEND") {
908 if (context_
.languageFeatures().ShouldWarn(
909 common::LanguageFeature::OpenAccessAppend
)) {
911 "ACCESS='%s' interpreted as POSITION='%s'"_port_en_US
, value
,
915 context_
.Say(source
, "Invalid %s value '%s'"_err_en_US
,
916 parser::ToUpperCaseLetters(common::EnumToString(specKind
)), value
);
921 // CheckForRequiredSpecifier and CheckForProhibitedSpecifier functions
922 // need conditions to check, and string arguments to insert into a message.
923 // An IoSpecKind provides both an absence/presence condition and a string
924 // argument (its name). A (condition, string) pair provides an arbitrary
925 // condition and an arbitrary string.
927 void IoChecker::CheckForRequiredSpecifier(IoSpecKind specKind
) const {
928 if (!specifierSet_
.test(specKind
)) {
929 context_
.Say("%s statement must have a %s specifier"_err_en_US
,
930 parser::ToUpperCaseLetters(common::EnumToString(stmt_
)),
931 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
935 void IoChecker::CheckForRequiredSpecifier(
936 bool condition
, const std::string
&s
) const {
938 context_
.Say("%s statement must have a %s specifier"_err_en_US
,
939 parser::ToUpperCaseLetters(common::EnumToString(stmt_
)), s
);
943 void IoChecker::CheckForRequiredSpecifier(
944 IoSpecKind specKind1
, IoSpecKind specKind2
) const {
945 if (specifierSet_
.test(specKind1
) && !specifierSet_
.test(specKind2
)) {
946 context_
.Say("If %s appears, %s must also appear"_err_en_US
,
947 parser::ToUpperCaseLetters(common::EnumToString(specKind1
)),
948 parser::ToUpperCaseLetters(common::EnumToString(specKind2
)));
952 void IoChecker::CheckForRequiredSpecifier(
953 IoSpecKind specKind
, bool condition
, const std::string
&s
) const {
954 if (specifierSet_
.test(specKind
) && !condition
) {
955 context_
.Say("If %s appears, %s must also appear"_err_en_US
,
956 parser::ToUpperCaseLetters(common::EnumToString(specKind
)), s
);
960 void IoChecker::CheckForRequiredSpecifier(
961 bool condition
, const std::string
&s
, IoSpecKind specKind
) const {
962 if (condition
&& !specifierSet_
.test(specKind
)) {
963 context_
.Say("If %s appears, %s must also appear"_err_en_US
, s
,
964 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
968 void IoChecker::CheckForRequiredSpecifier(bool condition1
,
969 const std::string
&s1
, bool condition2
, const std::string
&s2
) const {
970 if (condition1
&& !condition2
) {
971 context_
.Say("If %s appears, %s must also appear"_err_en_US
, s1
, s2
);
975 void IoChecker::CheckForProhibitedSpecifier(IoSpecKind specKind
) const {
976 if (specifierSet_
.test(specKind
)) {
977 context_
.Say("%s statement must not have a %s specifier"_err_en_US
,
978 parser::ToUpperCaseLetters(common::EnumToString(stmt_
)),
979 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
983 void IoChecker::CheckForProhibitedSpecifier(
984 IoSpecKind specKind1
, IoSpecKind specKind2
) const {
985 if (specifierSet_
.test(specKind1
) && specifierSet_
.test(specKind2
)) {
986 context_
.Say("If %s appears, %s must not appear"_err_en_US
,
987 parser::ToUpperCaseLetters(common::EnumToString(specKind1
)),
988 parser::ToUpperCaseLetters(common::EnumToString(specKind2
)));
992 void IoChecker::CheckForProhibitedSpecifier(
993 IoSpecKind specKind
, bool condition
, const std::string
&s
) const {
994 if (specifierSet_
.test(specKind
) && condition
) {
995 context_
.Say("If %s appears, %s must not appear"_err_en_US
,
996 parser::ToUpperCaseLetters(common::EnumToString(specKind
)), s
);
1000 void IoChecker::CheckForProhibitedSpecifier(
1001 bool condition
, const std::string
&s
, IoSpecKind specKind
) const {
1002 if (condition
&& specifierSet_
.test(specKind
)) {
1003 context_
.Say("If %s appears, %s must not appear"_err_en_US
, s
,
1004 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
1008 template <typename A
>
1009 void IoChecker::CheckForDefinableVariable(
1010 const A
&variable
, const std::string
&s
) const {
1011 if (const auto *var
{parser::Unwrap
<parser::Variable
>(variable
)}) {
1012 if (auto expr
{AnalyzeExpr(context_
, *var
)}) {
1013 auto at
{var
->GetSource()};
1014 if (auto whyNot
{WhyNotDefinable(at
, context_
.FindScope(at
),
1015 DefinabilityFlags
{DefinabilityFlag::VectorSubscriptIsOk
},
1017 const Symbol
*base
{GetFirstSymbol(*expr
)};
1019 .Say(at
, "%s variable '%s' is not definable"_err_en_US
, s
,
1020 (base
? base
->name() : at
).ToString())
1021 .Attach(std::move(*whyNot
));
1027 void IoChecker::CheckForPureSubprogram() const { // C1597
1028 CHECK(context_
.location());
1030 scope
{context_
.globalScope().FindScope(*context_
.location())}) {
1031 if (FindPureProcedureContaining(*scope
)) {
1033 "External I/O is not allowed in a pure subprogram"_err_en_US
);
1038 // Seeks out an allocatable or pointer ultimate component that is not
1039 // nested in a nonallocatable/nonpointer component with a specific
1040 // defined I/O procedure.
1041 static const Symbol
*FindUnsafeIoDirectComponent(GenericKind::DefinedIo which
,
1042 const DerivedTypeSpec
&derived
, const Scope
&scope
) {
1043 if (HasDefinedIo(which
, derived
, &scope
)) {
1046 if (const Scope
* dtScope
{derived
.scope()}) {
1047 for (const auto &pair
: *dtScope
) {
1048 const Symbol
&symbol
{*pair
.second
};
1049 if (IsAllocatableOrPointer(symbol
)) {
1052 if (const auto *details
{symbol
.detailsIf
<ObjectEntityDetails
>()}) {
1053 if (const DeclTypeSpec
* type
{details
->type()}) {
1054 if (type
->category() == DeclTypeSpec::Category::TypeDerived
) {
1055 const DerivedTypeSpec
&componentDerived
{type
->derivedTypeSpec()};
1057 bad
{FindUnsafeIoDirectComponent(
1058 which
, componentDerived
, scope
)}) {
1069 // For a type that does not have a defined I/O subroutine, finds a direct
1070 // component that is a witness to an accessibility violation outside the module
1071 // in which the type was defined.
1072 static const Symbol
*FindInaccessibleComponent(GenericKind::DefinedIo which
,
1073 const DerivedTypeSpec
&derived
, const Scope
&scope
) {
1074 if (const Scope
* dtScope
{derived
.scope()}) {
1075 if (const Scope
* module
{FindModuleContaining(*dtScope
)}) {
1076 for (const auto &pair
: *dtScope
) {
1077 const Symbol
&symbol
{*pair
.second
};
1078 if (IsAllocatableOrPointer(symbol
)) {
1079 continue; // already an error
1081 if (const auto *details
{symbol
.detailsIf
<ObjectEntityDetails
>()}) {
1082 const DerivedTypeSpec
*componentDerived
{nullptr};
1083 if (const DeclTypeSpec
* type
{details
->type()}) {
1084 if (type
->category() == DeclTypeSpec::Category::TypeDerived
) {
1085 componentDerived
= &type
->derivedTypeSpec();
1088 if (componentDerived
&&
1089 HasDefinedIo(which
, *componentDerived
, &scope
)) {
1090 continue; // this component and its descendents are fine
1092 if (symbol
.attrs().test(Attr::PRIVATE
) &&
1093 !symbol
.test(Symbol::Flag::ParentComp
)) {
1094 if (!DoesScopeContain(module
, scope
)) {
1098 if (componentDerived
) {
1100 bad
{FindInaccessibleComponent(
1101 which
, *componentDerived
, scope
)}) {
1112 // Fortran 2018, 12.6.3 paragraphs 5 & 7
1113 parser::Message
*IoChecker::CheckForBadIoType(const evaluate::DynamicType
&type
,
1114 GenericKind::DefinedIo which
, parser::CharBlock where
) const {
1115 if (type
.IsUnlimitedPolymorphic()) {
1116 return &context_
.Say(
1117 where
, "I/O list item may not be unlimited polymorphic"_err_en_US
);
1118 } else if (type
.category() == TypeCategory::Derived
) {
1119 const auto &derived
{type
.GetDerivedTypeSpec()};
1120 const Scope
&scope
{context_
.FindScope(where
)};
1122 bad
{FindUnsafeIoDirectComponent(which
, derived
, scope
)}) {
1123 return &context_
.SayWithDecl(*bad
, where
,
1124 "Derived type '%s' in I/O cannot have an allocatable or pointer direct component '%s' unless using defined I/O"_err_en_US
,
1125 derived
.name(), bad
->name());
1127 if (!HasDefinedIo(which
, derived
, &scope
)) {
1128 if (type
.IsPolymorphic()) {
1129 return &context_
.Say(where
,
1130 "Derived type '%s' in I/O may not be polymorphic unless using defined I/O"_err_en_US
,
1134 bad
{FindInaccessibleComponent(which
, derived
, scope
)}) {
1135 return &context_
.Say(where
,
1136 "I/O of the derived type '%s' may not be performed without defined I/O in a scope in which a direct component like '%s' is inaccessible"_err_en_US
,
1137 derived
.name(), bad
->name());
1144 void IoChecker::CheckForBadIoType(const SomeExpr
&expr
,
1145 GenericKind::DefinedIo which
, parser::CharBlock where
) const {
1146 if (auto type
{expr
.GetType()}) {
1147 CheckForBadIoType(*type
, which
, where
);
1151 parser::Message
*IoChecker::CheckForBadIoType(const Symbol
&symbol
,
1152 GenericKind::DefinedIo which
, parser::CharBlock where
) const {
1153 if (auto type
{evaluate::DynamicType::From(symbol
)}) {
1154 if (auto *msg
{CheckForBadIoType(*type
, which
, where
)}) {
1155 evaluate::AttachDeclaration(*msg
, symbol
);
1162 void IoChecker::CheckNamelist(const Symbol
&namelist
,
1163 GenericKind::DefinedIo which
, parser::CharBlock namelistLocation
) const {
1164 const auto &details
{namelist
.GetUltimate().get
<NamelistDetails
>()};
1165 for (const Symbol
&object
: details
.objects()) {
1166 context_
.CheckIndexVarRedefine(namelistLocation
, object
);
1167 if (auto *msg
{CheckForBadIoType(object
, which
, namelistLocation
)}) {
1168 evaluate::AttachDeclaration(*msg
, namelist
);
1173 } // namespace Fortran::semantics