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
) {
39 !context_
.ShouldWarn(common::LanguageFeature::AdditionalFormats
)) {
42 parser::MessageFormattedText text
{
43 parser::MessageFixedText
{msg
.text
, strlen(msg
.text
),
44 msg
.isError
? parser::Severity::Error
: parser::Severity::Warning
},
46 if (formatCharBlock_
.size()) {
47 // The input format is a folded expression. Error markers span the full
48 // original unfolded expression in formatCharBlock_.
49 context_
.Say(formatCharBlock_
, text
);
51 // The input format is a source expression. Error markers have an offset
52 // and length relative to the beginning of formatCharBlock_.
53 parser::CharBlock messageCharBlock
{
54 parser::CharBlock(formatCharBlock_
.begin() + msg
.offset
, msg
.length
)};
55 context_
.Say(messageCharBlock
, text
);
57 return msg
.isError
&& --errorAllowance_
<= 0;
60 void IoChecker::Enter(
61 const parser::Statement
<common::Indirection
<parser::FormatStmt
>> &stmt
) {
63 context_
.Say("Format statement must be labeled"_err_en_US
); // C1301
65 const char *formatStart
{static_cast<const char *>(
66 std::memchr(stmt
.source
.begin(), '(', stmt
.source
.size()))};
67 parser::CharBlock reporterCharBlock
{formatStart
, static_cast<std::size_t>(0)};
68 FormatErrorReporter reporter
{context_
, reporterCharBlock
};
69 auto reporterWrapper
{[&](const auto &msg
) { return reporter
.Say(msg
); }};
70 switch (context_
.GetDefaultKind(TypeCategory::Character
)) {
72 common::FormatValidator
<char> validator
{formatStart
,
73 stmt
.source
.size() - (formatStart
- stmt
.source
.begin()),
78 case 2: { // TODO: Get this to work.
79 common::FormatValidator
<char16_t
> validator
{
80 /*???*/ nullptr, /*???*/ 0, reporterWrapper
};
84 case 4: { // TODO: Get this to work.
85 common::FormatValidator
<char32_t
> validator
{
86 /*???*/ nullptr, /*???*/ 0, reporterWrapper
};
95 void IoChecker::Enter(const parser::ConnectSpec
&spec
) {
96 // ConnectSpec context FileNameExpr
97 if (std::get_if
<parser::FileNameExpr
>(&spec
.u
)) {
98 SetSpecifier(IoSpecKind::File
);
102 // Ignore trailing spaces (12.5.6.2 p1) and convert to upper case
103 static std::string
Normalize(const std::string
&value
) {
104 auto upper
{parser::ToUpperCaseLetters(value
)};
105 std::size_t lastNonBlank
{upper
.find_last_not_of(' ')};
106 upper
.resize(lastNonBlank
== std::string::npos
? 0 : lastNonBlank
+ 1);
110 void IoChecker::Enter(const parser::ConnectSpec::CharExpr
&spec
) {
111 IoSpecKind specKind
{};
112 using ParseKind
= parser::ConnectSpec::CharExpr::Kind
;
113 switch (std::get
<ParseKind
>(spec
.t
)) {
114 case ParseKind::Access
:
115 specKind
= IoSpecKind::Access
;
117 case ParseKind::Action
:
118 specKind
= IoSpecKind::Action
;
120 case ParseKind::Asynchronous
:
121 specKind
= IoSpecKind::Asynchronous
;
123 case ParseKind::Blank
:
124 specKind
= IoSpecKind::Blank
;
126 case ParseKind::Decimal
:
127 specKind
= IoSpecKind::Decimal
;
129 case ParseKind::Delim
:
130 specKind
= IoSpecKind::Delim
;
132 case ParseKind::Encoding
:
133 specKind
= IoSpecKind::Encoding
;
135 case ParseKind::Form
:
136 specKind
= IoSpecKind::Form
;
139 specKind
= IoSpecKind::Pad
;
141 case ParseKind::Position
:
142 specKind
= IoSpecKind::Position
;
144 case ParseKind::Round
:
145 specKind
= IoSpecKind::Round
;
147 case ParseKind::Sign
:
148 specKind
= IoSpecKind::Sign
;
150 case ParseKind::Carriagecontrol
:
151 specKind
= IoSpecKind::Carriagecontrol
;
153 case ParseKind::Convert
:
154 specKind
= IoSpecKind::Convert
;
156 case ParseKind::Dispose
:
157 specKind
= IoSpecKind::Dispose
;
160 SetSpecifier(specKind
);
161 if (const std::optional
<std::string
> charConst
{GetConstExpr
<std::string
>(
162 std::get
<parser::ScalarDefaultCharExpr
>(spec
.t
))}) {
163 std::string s
{Normalize(*charConst
)};
164 if (specKind
== IoSpecKind::Access
) {
165 flags_
.set(Flag::KnownAccess
);
166 flags_
.set(Flag::AccessDirect
, s
== "DIRECT");
167 flags_
.set(Flag::AccessStream
, s
== "STREAM");
169 CheckStringValue(specKind
, *charConst
, parser::FindSourceLocation(spec
));
170 if (specKind
== IoSpecKind::Carriagecontrol
&&
171 (s
== "FORTRAN" || s
== "NONE")) {
172 context_
.Say(parser::FindSourceLocation(spec
),
173 "Unimplemented %s value '%s'"_err_en_US
,
174 parser::ToUpperCaseLetters(common::EnumToString(specKind
)),
180 void IoChecker::Enter(const parser::ConnectSpec::Newunit
&var
) {
181 CheckForDefinableVariable(var
, "NEWUNIT");
182 SetSpecifier(IoSpecKind::Newunit
);
185 void IoChecker::Enter(const parser::ConnectSpec::Recl
&spec
) {
186 SetSpecifier(IoSpecKind::Recl
);
187 if (const std::optional
<std::int64_t> recl
{
188 GetConstExpr
<std::int64_t>(spec
)}) {
190 context_
.Say(parser::FindSourceLocation(spec
),
191 "RECL value (%jd) must be positive"_err_en_US
,
197 void IoChecker::Enter(const parser::EndLabel
&) {
198 SetSpecifier(IoSpecKind::End
);
201 void IoChecker::Enter(const parser::EorLabel
&) {
202 SetSpecifier(IoSpecKind::Eor
);
205 void IoChecker::Enter(const parser::ErrLabel
&) {
206 SetSpecifier(IoSpecKind::Err
);
209 void IoChecker::Enter(const parser::FileUnitNumber
&) {
210 SetSpecifier(IoSpecKind::Unit
);
211 flags_
.set(Flag::NumberUnit
);
214 void IoChecker::Enter(const parser::Format
&spec
) {
215 SetSpecifier(IoSpecKind::Fmt
);
216 flags_
.set(Flag::FmtOrNml
);
219 [&](const parser::Label
&) { flags_
.set(Flag::LabelFmt
); },
220 [&](const parser::Star
&) { flags_
.set(Flag::StarFmt
); },
221 [&](const parser::Expr
&format
) {
222 const SomeExpr
*expr
{GetExpr(context_
, format
)};
226 auto type
{expr
->GetType()};
227 if (type
&& type
->category() == TypeCategory::Integer
&&
229 context_
.defaultKinds().GetDefaultKind(type
->category()) &&
231 flags_
.set(Flag::AssignFmt
);
232 if (!IsVariable(*expr
)) {
233 context_
.Say(format
.source
,
234 "Assigned format label must be a scalar variable"_err_en_US
);
236 context_
.Warn(common::LanguageFeature::Assign
, format
.source
,
237 "Assigned format labels are deprecated"_port_en_US
);
241 if (type
&& type
->category() != TypeCategory::Character
&&
242 (type
->category() != TypeCategory::Integer
||
245 common::LanguageFeature::NonCharacterFormat
)) {
246 // Legacy extension: using non-character variables, typically
247 // DATA-initialized with Hollerith, as format expressions.
248 context_
.Warn(common::LanguageFeature::NonCharacterFormat
,
250 "Non-character format expression is not standard"_port_en_US
);
253 context_
.defaultKinds().GetDefaultKind(type
->category())) {
254 context_
.Say(format
.source
,
255 "Format expression must be default character or default scalar integer"_err_en_US
);
258 flags_
.set(Flag::CharFmt
);
259 const std::optional
<std::string
> constantFormat
{
260 GetConstExpr
<std::string
>(format
)};
261 if (!constantFormat
) {
264 // validate constant format -- 12.6.2.2
265 bool isFolded
{constantFormat
->size() != format
.source
.size() - 2};
266 parser::CharBlock reporterCharBlock
{isFolded
267 ? parser::CharBlock
{format
.source
}
268 : parser::CharBlock
{format
.source
.begin() + 1,
269 static_cast<std::size_t>(0)}};
270 FormatErrorReporter reporter
{context_
, reporterCharBlock
};
271 auto reporterWrapper
{
272 [&](const auto &msg
) { return reporter
.Say(msg
); }};
273 switch (context_
.GetDefaultKind(TypeCategory::Character
)) {
275 common::FormatValidator
<char> validator
{constantFormat
->c_str(),
276 constantFormat
->length(), reporterWrapper
, stmt_
};
281 // TODO: Get this to work. (Maybe combine with earlier instance?)
282 common::FormatValidator
<char16_t
> validator
{
283 /*???*/ nullptr, /*???*/ 0, reporterWrapper
, stmt_
};
288 // TODO: Get this to work. (Maybe combine with earlier instance?)
289 common::FormatValidator
<char32_t
> validator
{
290 /*???*/ nullptr, /*???*/ 0, reporterWrapper
, stmt_
};
302 void IoChecker::Enter(const parser::IdExpr
&) { SetSpecifier(IoSpecKind::Id
); }
304 void IoChecker::Enter(const parser::IdVariable
&spec
) {
305 SetSpecifier(IoSpecKind::Id
);
306 const auto *expr
{GetExpr(context_
, spec
)};
307 if (!expr
|| !expr
->GetType()) {
310 CheckForDefinableVariable(spec
, "ID");
311 int kind
{expr
->GetType()->kind()};
312 int defaultKind
{context_
.GetDefaultKind(TypeCategory::Integer
)};
313 if (kind
< defaultKind
) {
315 "ID kind (%d) is smaller than default INTEGER kind (%d)"_err_en_US
,
316 std::move(kind
), std::move(defaultKind
)); // C1229
320 void IoChecker::Enter(const parser::InputItem
&spec
) {
321 flags_
.set(Flag::DataList
);
322 const parser::Variable
*var
{std::get_if
<parser::Variable
>(&spec
.u
)};
326 CheckForDefinableVariable(*var
, "Input");
327 if (auto expr
{AnalyzeExpr(context_
, *var
)}) {
328 CheckForBadIoType(*expr
,
329 flags_
.test(Flag::FmtOrNml
) ? common::DefinedIo::ReadFormatted
330 : common::DefinedIo::ReadUnformatted
,
335 void IoChecker::Enter(const parser::InquireSpec
&spec
) {
336 // InquireSpec context FileNameExpr
337 if (std::get_if
<parser::FileNameExpr
>(&spec
.u
)) {
338 SetSpecifier(IoSpecKind::File
);
342 void IoChecker::Enter(const parser::InquireSpec::CharVar
&spec
) {
343 IoSpecKind specKind
{};
344 using ParseKind
= parser::InquireSpec::CharVar::Kind
;
345 switch (std::get
<ParseKind
>(spec
.t
)) {
346 case ParseKind::Access
:
347 specKind
= IoSpecKind::Access
;
349 case ParseKind::Action
:
350 specKind
= IoSpecKind::Action
;
352 case ParseKind::Asynchronous
:
353 specKind
= IoSpecKind::Asynchronous
;
355 case ParseKind::Blank
:
356 specKind
= IoSpecKind::Blank
;
358 case ParseKind::Decimal
:
359 specKind
= IoSpecKind::Decimal
;
361 case ParseKind::Delim
:
362 specKind
= IoSpecKind::Delim
;
364 case ParseKind::Direct
:
365 specKind
= IoSpecKind::Direct
;
367 case ParseKind::Encoding
:
368 specKind
= IoSpecKind::Encoding
;
370 case ParseKind::Form
:
371 specKind
= IoSpecKind::Form
;
373 case ParseKind::Formatted
:
374 specKind
= IoSpecKind::Formatted
;
376 case ParseKind::Iomsg
:
377 specKind
= IoSpecKind::Iomsg
;
379 case ParseKind::Name
:
380 specKind
= IoSpecKind::Name
;
383 specKind
= IoSpecKind::Pad
;
385 case ParseKind::Position
:
386 specKind
= IoSpecKind::Position
;
388 case ParseKind::Read
:
389 specKind
= IoSpecKind::Read
;
391 case ParseKind::Readwrite
:
392 specKind
= IoSpecKind::Readwrite
;
394 case ParseKind::Round
:
395 specKind
= IoSpecKind::Round
;
397 case ParseKind::Sequential
:
398 specKind
= IoSpecKind::Sequential
;
400 case ParseKind::Sign
:
401 specKind
= IoSpecKind::Sign
;
403 case ParseKind::Status
:
404 specKind
= IoSpecKind::Status
;
406 case ParseKind::Stream
:
407 specKind
= IoSpecKind::Stream
;
409 case ParseKind::Unformatted
:
410 specKind
= IoSpecKind::Unformatted
;
412 case ParseKind::Write
:
413 specKind
= IoSpecKind::Write
;
415 case ParseKind::Carriagecontrol
:
416 specKind
= IoSpecKind::Carriagecontrol
;
418 case ParseKind::Convert
:
419 specKind
= IoSpecKind::Convert
;
421 case ParseKind::Dispose
:
422 specKind
= IoSpecKind::Dispose
;
425 const parser::Variable
&var
{
426 std::get
<parser::ScalarDefaultCharVariable
>(spec
.t
).thing
.thing
};
427 std::string what
{parser::ToUpperCaseLetters(common::EnumToString(specKind
))};
428 CheckForDefinableVariable(var
, what
);
429 WarnOnDeferredLengthCharacterScalar(
430 context_
, GetExpr(context_
, var
), var
.GetSource(), what
.c_str());
431 SetSpecifier(specKind
);
434 void IoChecker::Enter(const parser::InquireSpec::IntVar
&spec
) {
435 IoSpecKind specKind
{};
436 using ParseKind
= parser::InquireSpec::IntVar::Kind
;
437 switch (std::get
<parser::InquireSpec::IntVar::Kind
>(spec
.t
)) {
438 case ParseKind::Iostat
:
439 specKind
= IoSpecKind::Iostat
;
441 case ParseKind::Nextrec
:
442 specKind
= IoSpecKind::Nextrec
;
444 case ParseKind::Number
:
445 specKind
= IoSpecKind::Number
;
448 specKind
= IoSpecKind::Pos
;
450 case ParseKind::Recl
:
451 specKind
= IoSpecKind::Recl
;
453 case ParseKind::Size
:
454 specKind
= IoSpecKind::Size
;
457 CheckForDefinableVariable(std::get
<parser::ScalarIntVariable
>(spec
.t
),
458 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
459 SetSpecifier(specKind
);
462 void IoChecker::Enter(const parser::InquireSpec::LogVar
&spec
) {
463 IoSpecKind specKind
{};
464 using ParseKind
= parser::InquireSpec::LogVar::Kind
;
465 switch (std::get
<parser::InquireSpec::LogVar::Kind
>(spec
.t
)) {
466 case ParseKind::Exist
:
467 specKind
= IoSpecKind::Exist
;
469 case ParseKind::Named
:
470 specKind
= IoSpecKind::Named
;
472 case ParseKind::Opened
:
473 specKind
= IoSpecKind::Opened
;
475 case ParseKind::Pending
:
476 specKind
= IoSpecKind::Pending
;
479 SetSpecifier(specKind
);
482 void IoChecker::Enter(const parser::IoControlSpec
&spec
) {
483 // IoControlSpec context Name
484 flags_
.set(Flag::IoControlList
);
485 if (std::holds_alternative
<parser::Name
>(spec
.u
)) {
486 SetSpecifier(IoSpecKind::Nml
);
487 flags_
.set(Flag::FmtOrNml
);
491 void IoChecker::Enter(const parser::IoControlSpec::Asynchronous
&spec
) {
492 SetSpecifier(IoSpecKind::Asynchronous
);
493 if (const std::optional
<std::string
> charConst
{
494 GetConstExpr
<std::string
>(spec
)}) {
495 flags_
.set(Flag::AsynchronousYes
, Normalize(*charConst
) == "YES");
496 CheckStringValue(IoSpecKind::Asynchronous
, *charConst
,
497 parser::FindSourceLocation(spec
)); // C1223
501 void IoChecker::Enter(const parser::IoControlSpec::CharExpr
&spec
) {
502 IoSpecKind specKind
{};
503 using ParseKind
= parser::IoControlSpec::CharExpr::Kind
;
504 switch (std::get
<ParseKind
>(spec
.t
)) {
505 case ParseKind::Advance
:
506 specKind
= IoSpecKind::Advance
;
508 case ParseKind::Blank
:
509 specKind
= IoSpecKind::Blank
;
511 case ParseKind::Decimal
:
512 specKind
= IoSpecKind::Decimal
;
514 case ParseKind::Delim
:
515 specKind
= IoSpecKind::Delim
;
518 specKind
= IoSpecKind::Pad
;
520 case ParseKind::Round
:
521 specKind
= IoSpecKind::Round
;
523 case ParseKind::Sign
:
524 specKind
= IoSpecKind::Sign
;
527 SetSpecifier(specKind
);
528 if (const std::optional
<std::string
> charConst
{GetConstExpr
<std::string
>(
529 std::get
<parser::ScalarDefaultCharExpr
>(spec
.t
))}) {
530 if (specKind
== IoSpecKind::Advance
) {
531 flags_
.set(Flag::AdvanceYes
, Normalize(*charConst
) == "YES");
533 CheckStringValue(specKind
, *charConst
, parser::FindSourceLocation(spec
));
537 void IoChecker::Enter(const parser::IoControlSpec::Pos
&) {
538 SetSpecifier(IoSpecKind::Pos
);
541 void IoChecker::Enter(const parser::IoControlSpec::Rec
&) {
542 SetSpecifier(IoSpecKind::Rec
);
545 void IoChecker::Enter(const parser::IoControlSpec::Size
&var
) {
546 CheckForDefinableVariable(var
, "SIZE");
547 SetSpecifier(IoSpecKind::Size
);
550 void IoChecker::Enter(const parser::IoUnit
&spec
) {
551 if (const parser::Variable
* var
{std::get_if
<parser::Variable
>(&spec
.u
)}) {
552 // Only now after generic resolution can it be known whether a function
553 // call appearing as UNIT=f() is an integer scalar external unit number
554 // or a character pointer for internal I/O.
555 const auto *expr
{GetExpr(context_
, *var
)};
556 std::optional
<evaluate::DynamicType
> dyType
;
558 dyType
= expr
->GetType();
560 if (dyType
&& dyType
->category() == TypeCategory::Integer
) {
561 if (expr
->Rank() != 0) {
562 context_
.Say(parser::FindSourceLocation(*var
),
563 "I/O unit number must be scalar"_err_en_US
);
565 // In the case of an integer unit number variable, rewrite the parse
566 // tree as if the unit had been parsed as a FileUnitNumber in order
568 auto &mutableSpec
{const_cast<parser::IoUnit
&>(spec
)};
569 auto &mutableVar
{std::get
<parser::Variable
>(mutableSpec
.u
)};
570 auto source
{mutableVar
.GetSource()};
571 auto typedExpr
{std::move(mutableVar
.typedExpr
)};
572 auto newExpr
{common::visit(
573 [](auto &&indirection
) {
574 return parser::Expr
{std::move(indirection
)};
576 std::move(mutableVar
.u
))};
577 newExpr
.source
= source
;
578 newExpr
.typedExpr
= std::move(typedExpr
);
579 mutableSpec
.u
= parser::FileUnitNumber
{
580 parser::ScalarIntExpr
{parser::IntExpr
{std::move(newExpr
)}}};
581 } else if (!dyType
|| dyType
->category() != TypeCategory::Character
) {
582 SetSpecifier(IoSpecKind::Unit
);
583 context_
.Say(parser::FindSourceLocation(*var
),
584 "I/O unit must be a character variable or a scalar integer expression"_err_en_US
);
585 } else { // CHARACTER variable (internal I/O)
586 if (stmt_
== IoStmtKind::Write
) {
587 CheckForDefinableVariable(*var
, "Internal file");
588 WarnOnDeferredLengthCharacterScalar(
589 context_
, expr
, var
->GetSource(), "Internal file");
591 if (HasVectorSubscript(*expr
)) {
592 context_
.Say(parser::FindSourceLocation(*var
), // C1201
593 "Internal file must not have a vector subscript"_err_en_US
);
595 SetSpecifier(IoSpecKind::Unit
);
596 flags_
.set(Flag::InternalUnit
);
598 } else if (std::get_if
<parser::Star
>(&spec
.u
)) {
599 SetSpecifier(IoSpecKind::Unit
);
600 flags_
.set(Flag::StarUnit
);
604 void IoChecker::Enter(const parser::MsgVariable
&msgVar
) {
605 const parser::Variable
&var
{msgVar
.v
.thing
.thing
};
606 if (stmt_
== IoStmtKind::None
) {
607 // allocate, deallocate, image control
608 CheckForDefinableVariable(var
, "ERRMSG");
609 WarnOnDeferredLengthCharacterScalar(
610 context_
, GetExpr(context_
, var
), var
.GetSource(), "ERRMSG=");
612 CheckForDefinableVariable(var
, "IOMSG");
613 WarnOnDeferredLengthCharacterScalar(
614 context_
, GetExpr(context_
, var
), var
.GetSource(), "IOMSG=");
615 SetSpecifier(IoSpecKind::Iomsg
);
619 void IoChecker::Enter(const parser::OutputItem
&item
) {
620 flags_
.set(Flag::DataList
);
621 if (const auto *x
{std::get_if
<parser::Expr
>(&item
.u
)}) {
622 if (const auto *expr
{GetExpr(context_
, *x
)}) {
623 if (evaluate::IsBOZLiteral(*expr
)) {
624 context_
.Say(parser::FindSourceLocation(*x
), // C7109
625 "Output item must not be a BOZ literal constant"_err_en_US
);
626 } else if (IsProcedure(*expr
)) {
627 context_
.Say(parser::FindSourceLocation(*x
),
628 "Output item must not be a procedure"_err_en_US
); // C1233
630 CheckForBadIoType(*expr
,
631 flags_
.test(Flag::FmtOrNml
) ? common::DefinedIo::WriteFormatted
632 : common::DefinedIo::WriteUnformatted
,
633 parser::FindSourceLocation(item
));
638 void IoChecker::Enter(const parser::StatusExpr
&spec
) {
639 SetSpecifier(IoSpecKind::Status
);
640 if (const std::optional
<std::string
> charConst
{
641 GetConstExpr
<std::string
>(spec
)}) {
642 // Status values for Open and Close are different.
643 std::string s
{Normalize(*charConst
)};
644 if (stmt_
== IoStmtKind::Open
) {
645 flags_
.set(Flag::KnownStatus
);
646 flags_
.set(Flag::StatusNew
, s
== "NEW");
647 flags_
.set(Flag::StatusReplace
, s
== "REPLACE");
648 flags_
.set(Flag::StatusScratch
, s
== "SCRATCH");
649 // CheckStringValue compares for OPEN Status string values.
651 IoSpecKind::Status
, *charConst
, parser::FindSourceLocation(spec
));
654 CHECK(stmt_
== IoStmtKind::Close
);
655 if (s
!= "DELETE" && s
!= "KEEP") {
656 context_
.Say(parser::FindSourceLocation(spec
),
657 "Invalid STATUS value '%s'"_err_en_US
, *charConst
);
662 void IoChecker::Enter(const parser::StatVariable
&var
) {
663 if (stmt_
== IoStmtKind::None
) {
664 // allocate, deallocate, image control
665 CheckForDefinableVariable(var
, "STAT");
667 CheckForDefinableVariable(var
, "IOSTAT");
668 SetSpecifier(IoSpecKind::Iostat
);
672 void IoChecker::Leave(const parser::BackspaceStmt
&) {
673 CheckForPureSubprogram();
674 CheckForRequiredSpecifier(
675 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1240
676 CheckForUselessIomsg();
680 void IoChecker::Leave(const parser::CloseStmt
&) {
681 CheckForPureSubprogram();
682 CheckForRequiredSpecifier(
683 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1208
684 CheckForUselessIomsg();
688 void IoChecker::Leave(const parser::EndfileStmt
&) {
689 CheckForPureSubprogram();
690 CheckForRequiredSpecifier(
691 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1240
692 CheckForUselessIomsg();
696 void IoChecker::Leave(const parser::FlushStmt
&) {
697 CheckForPureSubprogram();
698 CheckForRequiredSpecifier(
699 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1243
700 CheckForUselessIomsg();
704 void IoChecker::Leave(const parser::InquireStmt
&stmt
) {
705 if (std::get_if
<std::list
<parser::InquireSpec
>>(&stmt
.u
)) {
706 CheckForPureSubprogram();
707 // Inquire by unit or by file (vs. by output list).
708 CheckForRequiredSpecifier(
709 flags_
.test(Flag::NumberUnit
) || specifierSet_
.test(IoSpecKind::File
),
710 "UNIT number or FILE"); // C1246
711 CheckForProhibitedSpecifier(IoSpecKind::File
, IoSpecKind::Unit
); // C1246
712 CheckForRequiredSpecifier(IoSpecKind::Id
, IoSpecKind::Pending
); // C1248
713 CheckForUselessIomsg();
718 void IoChecker::Leave(const parser::OpenStmt
&) {
719 CheckForPureSubprogram();
720 CheckForRequiredSpecifier(specifierSet_
.test(IoSpecKind::Unit
) ||
721 specifierSet_
.test(IoSpecKind::Newunit
),
722 "UNIT or NEWUNIT"); // C1204, C1205
723 CheckForProhibitedSpecifier(
724 IoSpecKind::Newunit
, IoSpecKind::Unit
); // C1204, C1205
725 CheckForRequiredSpecifier(flags_
.test(Flag::StatusNew
), "STATUS='NEW'",
726 IoSpecKind::File
); // 12.5.6.10
727 CheckForRequiredSpecifier(flags_
.test(Flag::StatusReplace
),
728 "STATUS='REPLACE'", IoSpecKind::File
); // 12.5.6.10
729 CheckForProhibitedSpecifier(flags_
.test(Flag::StatusScratch
),
730 "STATUS='SCRATCH'", IoSpecKind::File
); // 12.5.6.10
731 if (flags_
.test(Flag::KnownStatus
)) {
732 CheckForRequiredSpecifier(IoSpecKind::Newunit
,
733 specifierSet_
.test(IoSpecKind::File
) ||
734 flags_
.test(Flag::StatusScratch
),
735 "FILE or STATUS='SCRATCH'"); // 12.5.6.12
737 CheckForRequiredSpecifier(IoSpecKind::Newunit
,
738 specifierSet_
.test(IoSpecKind::File
) ||
739 specifierSet_
.test(IoSpecKind::Status
),
740 "FILE or STATUS"); // 12.5.6.12
742 if (flags_
.test(Flag::KnownAccess
)) {
743 CheckForRequiredSpecifier(flags_
.test(Flag::AccessDirect
),
744 "ACCESS='DIRECT'", IoSpecKind::Recl
); // 12.5.6.15
745 CheckForProhibitedSpecifier(flags_
.test(Flag::AccessStream
),
746 "STATUS='STREAM'", IoSpecKind::Recl
); // 12.5.6.15
748 CheckForUselessIomsg();
752 void IoChecker::Leave(const parser::PrintStmt
&) {
753 CheckForPureSubprogram();
754 CheckForUselessIomsg();
758 static const parser::Name
*FindNamelist(
759 const std::list
<parser::IoControlSpec
> &controls
) {
760 for (const auto &control
: controls
) {
761 if (const parser::Name
* namelist
{std::get_if
<parser::Name
>(&control
.u
)}) {
762 if (namelist
->symbol
&&
763 namelist
->symbol
->GetUltimate().has
<NamelistDetails
>()) {
771 static void CheckForDoVariable(
772 const parser::ReadStmt
&readStmt
, SemanticsContext
&context
) {
773 const std::list
<parser::InputItem
> &items
{readStmt
.items
};
774 for (const auto &item
: items
) {
775 if (const parser::Variable
*
776 variable
{std::get_if
<parser::Variable
>(&item
.u
)}) {
777 context
.CheckIndexVarRedefine(*variable
);
782 void IoChecker::Leave(const parser::ReadStmt
&readStmt
) {
783 if (!flags_
.test(Flag::InternalUnit
)) {
784 CheckForPureSubprogram();
786 if (const parser::Name
* namelist
{FindNamelist(readStmt
.controls
)}) {
787 if (namelist
->symbol
) {
788 CheckNamelist(*namelist
->symbol
, common::DefinedIo::ReadFormatted
,
792 CheckForDoVariable(readStmt
, context_
);
793 if (!flags_
.test(Flag::IoControlList
)) {
798 CheckForProhibitedSpecifier(IoSpecKind::Delim
); // C1212
799 CheckForProhibitedSpecifier(IoSpecKind::Sign
); // C1212
800 CheckForProhibitedSpecifier(IoSpecKind::Rec
, IoSpecKind::End
); // C1220
801 if (specifierSet_
.test(IoSpecKind::Size
)) {
802 // F'2023 C1214 - allow with a warning
803 if (context_
.ShouldWarn(common::LanguageFeature::ListDirectedSize
)) {
804 if (specifierSet_
.test(IoSpecKind::Nml
)) {
805 context_
.Say("If NML appears, SIZE should not appear"_port_en_US
);
806 } else if (flags_
.test(Flag::StarFmt
)) {
807 context_
.Say("If FMT=* appears, SIZE should not appear"_port_en_US
);
811 CheckForRequiredSpecifier(IoSpecKind::Eor
,
812 specifierSet_
.test(IoSpecKind::Advance
) && !flags_
.test(Flag::AdvanceYes
),
813 "ADVANCE with value 'NO'"); // C1222 + 12.6.2.1p2
814 CheckForRequiredSpecifier(IoSpecKind::Blank
, flags_
.test(Flag::FmtOrNml
),
815 "FMT or NML"); // C1227
816 CheckForRequiredSpecifier(
817 IoSpecKind::Pad
, flags_
.test(Flag::FmtOrNml
), "FMT or NML"); // C1227
821 void IoChecker::Leave(const parser::RewindStmt
&) {
822 CheckForRequiredSpecifier(
823 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1240
824 CheckForPureSubprogram();
825 CheckForUselessIomsg();
829 void IoChecker::Leave(const parser::WaitStmt
&) {
830 CheckForRequiredSpecifier(
831 flags_
.test(Flag::NumberUnit
), "UNIT number"); // C1237
832 CheckForPureSubprogram();
833 CheckForUselessIomsg();
837 void IoChecker::Leave(const parser::WriteStmt
&writeStmt
) {
838 if (!flags_
.test(Flag::InternalUnit
)) {
839 CheckForPureSubprogram();
841 if (const parser::Name
* namelist
{FindNamelist(writeStmt
.controls
)}) {
842 if (namelist
->symbol
) {
843 CheckNamelist(*namelist
->symbol
, common::DefinedIo::WriteFormatted
,
848 CheckForProhibitedSpecifier(IoSpecKind::Blank
); // C1213
849 CheckForProhibitedSpecifier(IoSpecKind::End
); // C1213
850 CheckForProhibitedSpecifier(IoSpecKind::Eor
); // C1213
851 CheckForProhibitedSpecifier(IoSpecKind::Pad
); // C1213
852 CheckForProhibitedSpecifier(IoSpecKind::Size
); // C1213
853 CheckForRequiredSpecifier(
854 IoSpecKind::Sign
, flags_
.test(Flag::FmtOrNml
), "FMT or NML"); // C1227
855 CheckForRequiredSpecifier(IoSpecKind::Delim
,
856 flags_
.test(Flag::StarFmt
) || specifierSet_
.test(IoSpecKind::Nml
),
857 "FMT=* or NML"); // C1228
861 void IoChecker::LeaveReadWrite() const {
862 CheckForRequiredSpecifier(IoSpecKind::Unit
); // C1211
863 CheckForRequiredSpecifier(flags_
.test(Flag::InternalUnit
),
864 "UNIT=internal-file", flags_
.test(Flag::FmtOrNml
), "FMT or NML");
865 CheckForProhibitedSpecifier(IoSpecKind::Nml
, IoSpecKind::Rec
); // C1216
866 CheckForProhibitedSpecifier(IoSpecKind::Nml
, IoSpecKind::Fmt
); // C1216
867 CheckForProhibitedSpecifier(
868 IoSpecKind::Nml
, flags_
.test(Flag::DataList
), "a data list"); // C1216
869 CheckForProhibitedSpecifier(flags_
.test(Flag::InternalUnit
),
870 "UNIT=internal-file", IoSpecKind::Pos
); // C1219
871 CheckForProhibitedSpecifier(flags_
.test(Flag::InternalUnit
),
872 "UNIT=internal-file", IoSpecKind::Rec
); // C1219
873 CheckForProhibitedSpecifier(
874 flags_
.test(Flag::StarUnit
), "UNIT=*", IoSpecKind::Pos
); // C1219
875 CheckForProhibitedSpecifier(
876 flags_
.test(Flag::StarUnit
), "UNIT=*", IoSpecKind::Rec
); // C1219
877 CheckForProhibitedSpecifier(
878 IoSpecKind::Rec
, flags_
.test(Flag::StarFmt
), "FMT=*"); // C1220
879 CheckForRequiredSpecifier(IoSpecKind::Advance
,
880 flags_
.test(Flag::CharFmt
) || flags_
.test(Flag::LabelFmt
) ||
881 flags_
.test(Flag::AssignFmt
),
882 "an explicit format"); // C1221
883 CheckForProhibitedSpecifier(IoSpecKind::Advance
,
884 flags_
.test(Flag::InternalUnit
), "UNIT=internal-file"); // C1221
885 CheckForRequiredSpecifier(flags_
.test(Flag::AsynchronousYes
),
886 "ASYNCHRONOUS='YES'", flags_
.test(Flag::NumberUnit
),
887 "UNIT=number"); // C1224
888 CheckForRequiredSpecifier(IoSpecKind::Id
, flags_
.test(Flag::AsynchronousYes
),
889 "ASYNCHRONOUS='YES'"); // C1225
890 CheckForProhibitedSpecifier(IoSpecKind::Pos
, IoSpecKind::Rec
); // C1226
891 CheckForRequiredSpecifier(IoSpecKind::Decimal
, flags_
.test(Flag::FmtOrNml
),
892 "FMT or NML"); // C1227
893 CheckForRequiredSpecifier(IoSpecKind::Round
, flags_
.test(Flag::FmtOrNml
),
894 "FMT or NML"); // C1227
895 CheckForUselessIomsg();
898 void IoChecker::SetSpecifier(IoSpecKind specKind
) {
899 if (stmt_
== IoStmtKind::None
) {
900 // FMT may appear on PRINT statements, which don't have any checks.
901 // [IO]MSG and [IO]STAT parse symbols are shared with non-I/O statements.
904 // C1203, C1207, C1210, C1236, C1239, C1242, C1245
905 if (specifierSet_
.test(specKind
)) {
906 context_
.Say("Duplicate %s specifier"_err_en_US
,
907 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
909 specifierSet_
.set(specKind
);
912 void IoChecker::CheckStringValue(IoSpecKind specKind
, const std::string
&value
,
913 const parser::CharBlock
&source
) const {
914 static std::unordered_map
<IoSpecKind
, const std::set
<std::string
>> specValues
{
915 {IoSpecKind::Access
, {"DIRECT", "SEQUENTIAL", "STREAM"}},
916 {IoSpecKind::Action
, {"READ", "READWRITE", "WRITE"}},
917 {IoSpecKind::Advance
, {"NO", "YES"}},
918 {IoSpecKind::Asynchronous
, {"NO", "YES"}},
919 {IoSpecKind::Blank
, {"NULL", "ZERO"}},
920 {IoSpecKind::Decimal
, {"COMMA", "POINT"}},
921 {IoSpecKind::Delim
, {"APOSTROPHE", "NONE", "QUOTE"}},
922 {IoSpecKind::Encoding
, {"DEFAULT", "UTF-8"}},
923 {IoSpecKind::Form
, {"FORMATTED", "UNFORMATTED", "BINARY"}},
924 {IoSpecKind::Pad
, {"NO", "YES"}},
925 {IoSpecKind::Position
, {"APPEND", "ASIS", "REWIND"}},
927 {"COMPATIBLE", "DOWN", "NEAREST", "PROCESSOR_DEFINED", "UP", "ZERO"}},
928 {IoSpecKind::Sign
, {"PLUS", "PROCESSOR_DEFINED", "SUPPRESS"}},
930 // Open values; Close values are {"DELETE", "KEEP"}.
931 {"NEW", "OLD", "REPLACE", "SCRATCH", "UNKNOWN"}},
932 {IoSpecKind::Carriagecontrol
, {"LIST", "FORTRAN", "NONE"}},
933 {IoSpecKind::Convert
, {"BIG_ENDIAN", "LITTLE_ENDIAN", "NATIVE", "SWAP"}},
934 {IoSpecKind::Dispose
, {"DELETE", "KEEP"}},
936 auto upper
{Normalize(value
)};
937 if (specValues
.at(specKind
).count(upper
) == 0) {
938 if (specKind
== IoSpecKind::Access
&& upper
== "APPEND") {
939 context_
.Warn(common::LanguageFeature::OpenAccessAppend
, source
,
940 "ACCESS='%s' interpreted as POSITION='%s'"_port_en_US
, value
, upper
);
942 context_
.Say(source
, "Invalid %s value '%s'"_err_en_US
,
943 parser::ToUpperCaseLetters(common::EnumToString(specKind
)), value
);
948 // CheckForRequiredSpecifier and CheckForProhibitedSpecifier functions
949 // need conditions to check, and string arguments to insert into a message.
950 // An IoSpecKind provides both an absence/presence condition and a string
951 // argument (its name). A (condition, string) pair provides an arbitrary
952 // condition and an arbitrary string.
954 void IoChecker::CheckForRequiredSpecifier(IoSpecKind specKind
) const {
955 if (!specifierSet_
.test(specKind
)) {
956 context_
.Say("%s statement must have a %s specifier"_err_en_US
,
957 parser::ToUpperCaseLetters(common::EnumToString(stmt_
)),
958 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
962 void IoChecker::CheckForRequiredSpecifier(
963 bool condition
, const std::string
&s
) const {
965 context_
.Say("%s statement must have a %s specifier"_err_en_US
,
966 parser::ToUpperCaseLetters(common::EnumToString(stmt_
)), s
);
970 void IoChecker::CheckForRequiredSpecifier(
971 IoSpecKind specKind1
, IoSpecKind specKind2
) const {
972 if (specifierSet_
.test(specKind1
) && !specifierSet_
.test(specKind2
)) {
973 context_
.Say("If %s appears, %s must also appear"_err_en_US
,
974 parser::ToUpperCaseLetters(common::EnumToString(specKind1
)),
975 parser::ToUpperCaseLetters(common::EnumToString(specKind2
)));
979 void IoChecker::CheckForRequiredSpecifier(
980 IoSpecKind specKind
, bool condition
, const std::string
&s
) const {
981 if (specifierSet_
.test(specKind
) && !condition
) {
982 context_
.Say("If %s appears, %s must also appear"_err_en_US
,
983 parser::ToUpperCaseLetters(common::EnumToString(specKind
)), s
);
987 void IoChecker::CheckForRequiredSpecifier(
988 bool condition
, const std::string
&s
, IoSpecKind specKind
) const {
989 if (condition
&& !specifierSet_
.test(specKind
)) {
990 context_
.Say("If %s appears, %s must also appear"_err_en_US
, s
,
991 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
995 void IoChecker::CheckForRequiredSpecifier(bool condition1
,
996 const std::string
&s1
, bool condition2
, const std::string
&s2
) const {
997 if (condition1
&& !condition2
) {
998 context_
.Say("If %s appears, %s must also appear"_err_en_US
, s1
, s2
);
1002 void IoChecker::CheckForProhibitedSpecifier(IoSpecKind specKind
) const {
1003 if (specifierSet_
.test(specKind
)) {
1004 context_
.Say("%s statement must not have a %s specifier"_err_en_US
,
1005 parser::ToUpperCaseLetters(common::EnumToString(stmt_
)),
1006 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
1010 void IoChecker::CheckForProhibitedSpecifier(
1011 IoSpecKind specKind1
, IoSpecKind specKind2
) const {
1012 if (specifierSet_
.test(specKind1
) && specifierSet_
.test(specKind2
)) {
1013 context_
.Say("If %s appears, %s must not appear"_err_en_US
,
1014 parser::ToUpperCaseLetters(common::EnumToString(specKind1
)),
1015 parser::ToUpperCaseLetters(common::EnumToString(specKind2
)));
1019 void IoChecker::CheckForProhibitedSpecifier(
1020 IoSpecKind specKind
, bool condition
, const std::string
&s
) const {
1021 if (specifierSet_
.test(specKind
) && condition
) {
1022 context_
.Say("If %s appears, %s must not appear"_err_en_US
,
1023 parser::ToUpperCaseLetters(common::EnumToString(specKind
)), s
);
1027 void IoChecker::CheckForProhibitedSpecifier(
1028 bool condition
, const std::string
&s
, IoSpecKind specKind
) const {
1029 if (condition
&& specifierSet_
.test(specKind
)) {
1030 context_
.Say("If %s appears, %s must not appear"_err_en_US
, s
,
1031 parser::ToUpperCaseLetters(common::EnumToString(specKind
)));
1035 template <typename A
>
1036 void IoChecker::CheckForDefinableVariable(
1037 const A
&variable
, const std::string
&s
) const {
1038 if (const auto *var
{parser::Unwrap
<parser::Variable
>(variable
)}) {
1039 if (auto expr
{AnalyzeExpr(context_
, *var
)}) {
1040 auto at
{var
->GetSource()};
1041 if (auto whyNot
{WhyNotDefinable(at
, context_
.FindScope(at
),
1042 DefinabilityFlags
{DefinabilityFlag::VectorSubscriptIsOk
},
1044 if (whyNot
->IsFatal()) {
1045 const Symbol
*base
{GetFirstSymbol(*expr
)};
1047 .Say(at
, "%s variable '%s' is not definable"_err_en_US
, s
,
1048 (base
? base
->name() : at
).ToString())
1050 std::move(whyNot
->set_severity(parser::Severity::Because
)));
1052 context_
.Say(std::move(*whyNot
));
1059 void IoChecker::CheckForPureSubprogram() const { // C1597
1060 CHECK(context_
.location());
1061 const Scope
&scope
{context_
.FindScope(*context_
.location())};
1062 if (FindPureProcedureContaining(scope
)) {
1063 context_
.Say("External I/O is not allowed in a pure subprogram"_err_en_US
);
1067 void IoChecker::CheckForUselessIomsg() const {
1068 if (specifierSet_
.test(IoSpecKind::Iomsg
) &&
1069 !specifierSet_
.test(IoSpecKind::Err
) &&
1070 !specifierSet_
.test(IoSpecKind::Iostat
) &&
1071 context_
.ShouldWarn(common::UsageWarning::UselessIomsg
)) {
1072 context_
.Say("IOMSG= is useless without either ERR= or IOSTAT="_warn_en_US
);
1076 // Seeks out an allocatable or pointer ultimate component that is not
1077 // nested in a nonallocatable/nonpointer component with a specific
1078 // defined I/O procedure.
1079 static const Symbol
*FindUnsafeIoDirectComponent(common::DefinedIo which
,
1080 const DerivedTypeSpec
&derived
, const Scope
&scope
) {
1081 if (HasDefinedIo(which
, derived
, &scope
)) {
1084 if (const Scope
* dtScope
{derived
.scope()}) {
1085 for (const auto &pair
: *dtScope
) {
1086 const Symbol
&symbol
{*pair
.second
};
1087 if (IsAllocatableOrPointer(symbol
)) {
1090 if (const auto *details
{symbol
.detailsIf
<ObjectEntityDetails
>()}) {
1091 if (const DeclTypeSpec
* type
{details
->type()}) {
1092 if (type
->category() == DeclTypeSpec::Category::TypeDerived
) {
1093 const DerivedTypeSpec
&componentDerived
{type
->derivedTypeSpec()};
1095 bad
{FindUnsafeIoDirectComponent(
1096 which
, componentDerived
, scope
)}) {
1107 // For a type that does not have a defined I/O subroutine, finds a direct
1108 // component that is a witness to an accessibility violation outside the module
1109 // in which the type was defined.
1110 static const Symbol
*FindInaccessibleComponent(common::DefinedIo which
,
1111 const DerivedTypeSpec
&derived
, const Scope
&scope
) {
1112 if (const Scope
* dtScope
{derived
.scope()}) {
1113 if (const Scope
* module
{FindModuleContaining(*dtScope
)}) {
1114 for (const auto &pair
: *dtScope
) {
1115 const Symbol
&symbol
{*pair
.second
};
1116 if (IsAllocatableOrPointer(symbol
)) {
1117 continue; // already an error
1119 if (const auto *details
{symbol
.detailsIf
<ObjectEntityDetails
>()}) {
1120 const DerivedTypeSpec
*componentDerived
{nullptr};
1121 if (const DeclTypeSpec
* type
{details
->type()}) {
1122 if (type
->category() == DeclTypeSpec::Category::TypeDerived
) {
1123 componentDerived
= &type
->derivedTypeSpec();
1126 if (componentDerived
&&
1127 HasDefinedIo(which
, *componentDerived
, &scope
)) {
1128 continue; // this component and its descendents are fine
1130 if (symbol
.attrs().test(Attr::PRIVATE
) &&
1131 !symbol
.test(Symbol::Flag::ParentComp
)) {
1132 if (!DoesScopeContain(module
, scope
)) {
1136 if (componentDerived
) {
1138 bad
{FindInaccessibleComponent(
1139 which
, *componentDerived
, scope
)}) {
1150 // Fortran 2018, 12.6.3 paragraphs 5 & 7
1151 parser::Message
*IoChecker::CheckForBadIoType(const evaluate::DynamicType
&type
,
1152 common::DefinedIo which
, parser::CharBlock where
) const {
1153 if (type
.IsUnlimitedPolymorphic()) {
1154 return &context_
.Say(
1155 where
, "I/O list item may not be unlimited polymorphic"_err_en_US
);
1156 } else if (type
.category() == TypeCategory::Derived
) {
1157 const auto &derived
{type
.GetDerivedTypeSpec()};
1158 const Scope
&scope
{context_
.FindScope(where
)};
1160 bad
{FindUnsafeIoDirectComponent(which
, derived
, scope
)}) {
1161 return &context_
.SayWithDecl(*bad
, where
,
1162 "Derived type '%s' in I/O cannot have an allocatable or pointer direct component '%s' unless using defined I/O"_err_en_US
,
1163 derived
.name(), bad
->name());
1165 if (!HasDefinedIo(which
, derived
, &scope
)) {
1166 if (type
.IsPolymorphic()) {
1167 return &context_
.Say(where
,
1168 "Derived type '%s' in I/O may not be polymorphic unless using defined I/O"_err_en_US
,
1171 if ((IsBuiltinDerivedType(&derived
, "c_ptr") ||
1172 IsBuiltinDerivedType(&derived
, "c_devptr")) &&
1173 !context_
.ShouldWarn(common::LanguageFeature::PrintCptr
)) {
1174 // Bypass the check below for c_ptr and c_devptr.
1178 bad
{FindInaccessibleComponent(which
, derived
, scope
)}) {
1179 return &context_
.Say(where
,
1180 "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
,
1181 derived
.name(), bad
->name());
1188 void IoChecker::CheckForBadIoType(const SomeExpr
&expr
, common::DefinedIo which
,
1189 parser::CharBlock where
) const {
1190 if (auto type
{expr
.GetType()}) {
1191 CheckForBadIoType(*type
, which
, where
);
1195 parser::Message
*IoChecker::CheckForBadIoType(const Symbol
&symbol
,
1196 common::DefinedIo which
, parser::CharBlock where
) const {
1197 if (auto type
{evaluate::DynamicType::From(symbol
)}) {
1198 if (auto *msg
{CheckForBadIoType(*type
, which
, where
)}) {
1199 evaluate::AttachDeclaration(*msg
, symbol
);
1206 void IoChecker::CheckNamelist(const Symbol
&namelist
, common::DefinedIo which
,
1207 parser::CharBlock namelistLocation
) const {
1208 if (!context_
.HasError(namelist
)) {
1209 const auto &details
{namelist
.GetUltimate().get
<NamelistDetails
>()};
1210 for (const Symbol
&object
: details
.objects()) {
1211 context_
.CheckIndexVarRedefine(namelistLocation
, object
);
1212 if (auto *msg
{CheckForBadIoType(object
, which
, namelistLocation
)}) {
1213 evaluate::AttachDeclaration(*msg
, namelist
);
1214 } else if (which
== common::DefinedIo::ReadFormatted
) {
1215 if (auto why
{WhyNotDefinable(namelistLocation
, namelist
.owner(),
1216 DefinabilityFlags
{}, object
)}) {
1218 .Say(namelistLocation
,
1219 "NAMELIST input group must not contain undefinable item '%s'"_err_en_US
,
1221 .Attach(std::move(why
->set_severity(parser::Severity::Because
)));
1222 context_
.SetError(namelist
);
1229 } // namespace Fortran::semantics