1 //===-- lib/Semantics/check-io.cpp ----------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "check-io.h" 10 #include "flang/Common/format.h" 11 #include "flang/Parser/tools.h" 12 #include "flang/Semantics/expression.h" 13 #include "flang/Semantics/tools.h" 14 #include <unordered_map> 15 16 namespace Fortran::semantics { 17 18 // TODO: C1234, C1235 -- defined I/O constraints 19 20 class FormatErrorReporter { 21 public: 22 FormatErrorReporter(SemanticsContext &context, 23 const parser::CharBlock &formatCharBlock, int errorAllowance = 3) 24 : context_{context}, formatCharBlock_{formatCharBlock}, 25 errorAllowance_{errorAllowance} {} 26 27 bool Say(const common::FormatMessage &); 28 29 private: 30 SemanticsContext &context_; 31 const parser::CharBlock &formatCharBlock_; 32 int errorAllowance_; // initialized to maximum number of errors to report 33 }; 34 35 bool FormatErrorReporter::Say(const common::FormatMessage &msg) { 36 if (!msg.isError && !context_.warnOnNonstandardUsage()) { 37 return false; 38 } 39 parser::MessageFormattedText text{ 40 parser::MessageFixedText(msg.text, strlen(msg.text), msg.isError), 41 msg.arg}; 42 if (formatCharBlock_.size()) { 43 // The input format is a folded expression. Error markers span the full 44 // original unfolded expression in formatCharBlock_. 45 context_.Say(formatCharBlock_, text); 46 } else { 47 // The input format is a source expression. Error markers have an offset 48 // and length relative to the beginning of formatCharBlock_. 49 parser::CharBlock messageCharBlock{ 50 parser::CharBlock(formatCharBlock_.begin() + msg.offset, msg.length)}; 51 context_.Say(messageCharBlock, text); 52 } 53 return msg.isError && --errorAllowance_ <= 0; 54 } 55 56 void IoChecker::Enter( 57 const parser::Statement<common::Indirection<parser::FormatStmt>> &stmt) { 58 if (!stmt.label) { 59 context_.Say("Format statement must be labeled"_err_en_US); // C1301 60 } 61 const char *formatStart{static_cast<const char *>( 62 std::memchr(stmt.source.begin(), '(', stmt.source.size()))}; 63 parser::CharBlock reporterCharBlock{formatStart, static_cast<std::size_t>(0)}; 64 FormatErrorReporter reporter{context_, reporterCharBlock}; 65 auto reporterWrapper{[&](const auto &msg) { return reporter.Say(msg); }}; 66 switch (context_.GetDefaultKind(TypeCategory::Character)) { 67 case 1: { 68 common::FormatValidator<char> validator{formatStart, 69 stmt.source.size() - (formatStart - stmt.source.begin()), 70 reporterWrapper}; 71 validator.Check(); 72 break; 73 } 74 case 2: { // TODO: Get this to work. 75 common::FormatValidator<char16_t> validator{ 76 /*???*/ nullptr, /*???*/ 0, reporterWrapper}; 77 validator.Check(); 78 break; 79 } 80 case 4: { // TODO: Get this to work. 81 common::FormatValidator<char32_t> validator{ 82 /*???*/ nullptr, /*???*/ 0, reporterWrapper}; 83 validator.Check(); 84 break; 85 } 86 default: 87 CRASH_NO_CASE; 88 } 89 } 90 91 void IoChecker::Enter(const parser::ConnectSpec &spec) { 92 // ConnectSpec context FileNameExpr 93 if (std::get_if<parser::FileNameExpr>(&spec.u)) { 94 SetSpecifier(IoSpecKind::File); 95 } 96 } 97 98 void IoChecker::Enter(const parser::ConnectSpec::CharExpr &spec) { 99 IoSpecKind specKind{}; 100 using ParseKind = parser::ConnectSpec::CharExpr::Kind; 101 switch (std::get<ParseKind>(spec.t)) { 102 case ParseKind::Access: 103 specKind = IoSpecKind::Access; 104 break; 105 case ParseKind::Action: 106 specKind = IoSpecKind::Action; 107 break; 108 case ParseKind::Asynchronous: 109 specKind = IoSpecKind::Asynchronous; 110 break; 111 case ParseKind::Blank: 112 specKind = IoSpecKind::Blank; 113 break; 114 case ParseKind::Decimal: 115 specKind = IoSpecKind::Decimal; 116 break; 117 case ParseKind::Delim: 118 specKind = IoSpecKind::Delim; 119 break; 120 case ParseKind::Encoding: 121 specKind = IoSpecKind::Encoding; 122 break; 123 case ParseKind::Form: 124 specKind = IoSpecKind::Form; 125 break; 126 case ParseKind::Pad: 127 specKind = IoSpecKind::Pad; 128 break; 129 case ParseKind::Position: 130 specKind = IoSpecKind::Position; 131 break; 132 case ParseKind::Round: 133 specKind = IoSpecKind::Round; 134 break; 135 case ParseKind::Sign: 136 specKind = IoSpecKind::Sign; 137 break; 138 case ParseKind::Carriagecontrol: 139 specKind = IoSpecKind::Carriagecontrol; 140 break; 141 case ParseKind::Convert: 142 specKind = IoSpecKind::Convert; 143 break; 144 case ParseKind::Dispose: 145 specKind = IoSpecKind::Dispose; 146 break; 147 } 148 SetSpecifier(specKind); 149 if (const std::optional<std::string> charConst{GetConstExpr<std::string>( 150 std::get<parser::ScalarDefaultCharExpr>(spec.t))}) { 151 std::string s{parser::ToUpperCaseLetters(*charConst)}; 152 if (specKind == IoSpecKind::Access) { 153 flags_.set(Flag::KnownAccess); 154 flags_.set(Flag::AccessDirect, s == "DIRECT"); 155 flags_.set(Flag::AccessStream, s == "STREAM"); 156 } 157 CheckStringValue(specKind, *charConst, parser::FindSourceLocation(spec)); 158 if (specKind == IoSpecKind::Carriagecontrol && 159 (s == "FORTRAN" || s == "NONE")) { 160 context_.Say(parser::FindSourceLocation(spec), 161 "Unimplemented %s value '%s'"_err_en_US, 162 parser::ToUpperCaseLetters(common::EnumToString(specKind)), 163 *charConst); 164 } 165 } 166 } 167 168 void IoChecker::Enter(const parser::ConnectSpec::Newunit &var) { 169 CheckForDefinableVariable(var, "NEWUNIT"); 170 SetSpecifier(IoSpecKind::Newunit); 171 } 172 173 void IoChecker::Enter(const parser::ConnectSpec::Recl &spec) { 174 SetSpecifier(IoSpecKind::Recl); 175 if (const std::optional<std::int64_t> recl{ 176 GetConstExpr<std::int64_t>(spec)}) { 177 if (*recl <= 0) { 178 context_.Say(parser::FindSourceLocation(spec), 179 "RECL value (%jd) must be positive"_err_en_US, 180 *recl); // 12.5.6.15 181 } 182 } 183 } 184 185 void IoChecker::Enter(const parser::EndLabel &) { 186 SetSpecifier(IoSpecKind::End); 187 } 188 189 void IoChecker::Enter(const parser::EorLabel &) { 190 SetSpecifier(IoSpecKind::Eor); 191 } 192 193 void IoChecker::Enter(const parser::ErrLabel &) { 194 SetSpecifier(IoSpecKind::Err); 195 } 196 197 void IoChecker::Enter(const parser::FileUnitNumber &) { 198 SetSpecifier(IoSpecKind::Unit); 199 flags_.set(Flag::NumberUnit); 200 } 201 202 void IoChecker::Enter(const parser::Format &spec) { 203 SetSpecifier(IoSpecKind::Fmt); 204 flags_.set(Flag::FmtOrNml); 205 std::visit( 206 common::visitors{ 207 [&](const parser::Label &) { flags_.set(Flag::LabelFmt); }, 208 [&](const parser::Star &) { flags_.set(Flag::StarFmt); }, 209 [&](const parser::Expr &format) { 210 const SomeExpr *expr{GetExpr(format)}; 211 if (!expr) { 212 return; 213 } 214 auto type{expr->GetType()}; 215 if (!type || 216 (type->category() != TypeCategory::Integer && 217 type->category() != TypeCategory::Character) || 218 type->kind() != 219 context_.defaultKinds().GetDefaultKind(type->category())) { 220 context_.Say(format.source, 221 "Format expression must be default character or integer"_err_en_US); 222 return; 223 } 224 if (type->category() == TypeCategory::Integer) { 225 flags_.set(Flag::AssignFmt); 226 if (expr->Rank() != 0 || !IsVariable(*expr)) { 227 context_.Say(format.source, 228 "Assigned format label must be a scalar variable"_err_en_US); 229 } 230 return; 231 } 232 flags_.set(Flag::CharFmt); 233 const std::optional<std::string> constantFormat{ 234 GetConstExpr<std::string>(format)}; 235 if (!constantFormat) { 236 return; 237 } 238 // validate constant format -- 12.6.2.2 239 bool isFolded{constantFormat->size() != format.source.size() - 2}; 240 parser::CharBlock reporterCharBlock{isFolded 241 ? parser::CharBlock{format.source} 242 : parser::CharBlock{format.source.begin() + 1, 243 static_cast<std::size_t>(0)}}; 244 FormatErrorReporter reporter{context_, reporterCharBlock}; 245 auto reporterWrapper{ 246 [&](const auto &msg) { return reporter.Say(msg); }}; 247 switch (context_.GetDefaultKind(TypeCategory::Character)) { 248 case 1: { 249 common::FormatValidator<char> validator{constantFormat->c_str(), 250 constantFormat->length(), reporterWrapper, stmt_}; 251 validator.Check(); 252 break; 253 } 254 case 2: { 255 // TODO: Get this to work. (Maybe combine with earlier instance?) 256 common::FormatValidator<char16_t> validator{ 257 /*???*/ nullptr, /*???*/ 0, reporterWrapper, stmt_}; 258 validator.Check(); 259 break; 260 } 261 case 4: { 262 // TODO: Get this to work. (Maybe combine with earlier instance?) 263 common::FormatValidator<char32_t> validator{ 264 /*???*/ nullptr, /*???*/ 0, reporterWrapper, stmt_}; 265 validator.Check(); 266 break; 267 } 268 default: 269 CRASH_NO_CASE; 270 } 271 }, 272 }, 273 spec.u); 274 } 275 276 void IoChecker::Enter(const parser::IdExpr &) { SetSpecifier(IoSpecKind::Id); } 277 278 void IoChecker::Enter(const parser::IdVariable &spec) { 279 SetSpecifier(IoSpecKind::Id); 280 const auto *expr{GetExpr(spec)}; 281 if (!expr || !expr->GetType()) { 282 return; 283 } 284 CheckForDefinableVariable(spec, "ID"); 285 int kind{expr->GetType()->kind()}; 286 int defaultKind{context_.GetDefaultKind(TypeCategory::Integer)}; 287 if (kind < defaultKind) { 288 context_.Say( 289 "ID kind (%d) is smaller than default INTEGER kind (%d)"_err_en_US, 290 std::move(kind), std::move(defaultKind)); // C1229 291 } 292 } 293 294 void IoChecker::Enter(const parser::InputItem &spec) { 295 flags_.set(Flag::DataList); 296 const parser::Variable *var{std::get_if<parser::Variable>(&spec.u)}; 297 if (!var) { 298 return; 299 } 300 CheckForDefinableVariable(*var, "Input"); 301 const auto &name{GetLastName(*var)}; 302 const auto *expr{GetExpr(*var)}; 303 if (name.symbol && IsAssumedSizeArray(*name.symbol) && expr && 304 !evaluate::IsArrayElement(*GetExpr(*var))) { 305 context_.Say(name.source, 306 "Whole assumed size array '%s' may not be an input item"_err_en_US, 307 name.source); // C1231 308 } 309 } 310 311 void IoChecker::Enter(const parser::InquireSpec &spec) { 312 // InquireSpec context FileNameExpr 313 if (std::get_if<parser::FileNameExpr>(&spec.u)) { 314 SetSpecifier(IoSpecKind::File); 315 } 316 } 317 318 void IoChecker::Enter(const parser::InquireSpec::CharVar &spec) { 319 IoSpecKind specKind{}; 320 using ParseKind = parser::InquireSpec::CharVar::Kind; 321 switch (std::get<ParseKind>(spec.t)) { 322 case ParseKind::Access: 323 specKind = IoSpecKind::Access; 324 break; 325 case ParseKind::Action: 326 specKind = IoSpecKind::Action; 327 break; 328 case ParseKind::Asynchronous: 329 specKind = IoSpecKind::Asynchronous; 330 break; 331 case ParseKind::Blank: 332 specKind = IoSpecKind::Blank; 333 break; 334 case ParseKind::Decimal: 335 specKind = IoSpecKind::Decimal; 336 break; 337 case ParseKind::Delim: 338 specKind = IoSpecKind::Delim; 339 break; 340 case ParseKind::Direct: 341 specKind = IoSpecKind::Direct; 342 break; 343 case ParseKind::Encoding: 344 specKind = IoSpecKind::Encoding; 345 break; 346 case ParseKind::Form: 347 specKind = IoSpecKind::Form; 348 break; 349 case ParseKind::Formatted: 350 specKind = IoSpecKind::Formatted; 351 break; 352 case ParseKind::Iomsg: 353 specKind = IoSpecKind::Iomsg; 354 break; 355 case ParseKind::Name: 356 specKind = IoSpecKind::Name; 357 break; 358 case ParseKind::Pad: 359 specKind = IoSpecKind::Pad; 360 break; 361 case ParseKind::Position: 362 specKind = IoSpecKind::Position; 363 break; 364 case ParseKind::Read: 365 specKind = IoSpecKind::Read; 366 break; 367 case ParseKind::Readwrite: 368 specKind = IoSpecKind::Readwrite; 369 break; 370 case ParseKind::Round: 371 specKind = IoSpecKind::Round; 372 break; 373 case ParseKind::Sequential: 374 specKind = IoSpecKind::Sequential; 375 break; 376 case ParseKind::Sign: 377 specKind = IoSpecKind::Sign; 378 break; 379 case ParseKind::Status: 380 specKind = IoSpecKind::Status; 381 break; 382 case ParseKind::Stream: 383 specKind = IoSpecKind::Stream; 384 break; 385 case ParseKind::Unformatted: 386 specKind = IoSpecKind::Unformatted; 387 break; 388 case ParseKind::Write: 389 specKind = IoSpecKind::Write; 390 break; 391 case ParseKind::Carriagecontrol: 392 specKind = IoSpecKind::Carriagecontrol; 393 break; 394 case ParseKind::Convert: 395 specKind = IoSpecKind::Convert; 396 break; 397 case ParseKind::Dispose: 398 specKind = IoSpecKind::Dispose; 399 break; 400 } 401 CheckForDefinableVariable(std::get<parser::ScalarDefaultCharVariable>(spec.t), 402 parser::ToUpperCaseLetters(common::EnumToString(specKind))); 403 SetSpecifier(specKind); 404 } 405 406 void IoChecker::Enter(const parser::InquireSpec::IntVar &spec) { 407 IoSpecKind specKind{}; 408 using ParseKind = parser::InquireSpec::IntVar::Kind; 409 switch (std::get<parser::InquireSpec::IntVar::Kind>(spec.t)) { 410 case ParseKind::Iostat: 411 specKind = IoSpecKind::Iostat; 412 break; 413 case ParseKind::Nextrec: 414 specKind = IoSpecKind::Nextrec; 415 break; 416 case ParseKind::Number: 417 specKind = IoSpecKind::Number; 418 break; 419 case ParseKind::Pos: 420 specKind = IoSpecKind::Pos; 421 break; 422 case ParseKind::Recl: 423 specKind = IoSpecKind::Recl; 424 break; 425 case ParseKind::Size: 426 specKind = IoSpecKind::Size; 427 break; 428 } 429 CheckForDefinableVariable(std::get<parser::ScalarIntVariable>(spec.t), 430 parser::ToUpperCaseLetters(common::EnumToString(specKind))); 431 SetSpecifier(specKind); 432 } 433 434 void IoChecker::Enter(const parser::InquireSpec::LogVar &spec) { 435 IoSpecKind specKind{}; 436 using ParseKind = parser::InquireSpec::LogVar::Kind; 437 switch (std::get<parser::InquireSpec::LogVar::Kind>(spec.t)) { 438 case ParseKind::Exist: 439 specKind = IoSpecKind::Exist; 440 break; 441 case ParseKind::Named: 442 specKind = IoSpecKind::Named; 443 break; 444 case ParseKind::Opened: 445 specKind = IoSpecKind::Opened; 446 break; 447 case ParseKind::Pending: 448 specKind = IoSpecKind::Pending; 449 break; 450 } 451 SetSpecifier(specKind); 452 } 453 454 void IoChecker::Enter(const parser::IoControlSpec &spec) { 455 // IoControlSpec context Name 456 flags_.set(Flag::IoControlList); 457 if (std::holds_alternative<parser::Name>(spec.u)) { 458 SetSpecifier(IoSpecKind::Nml); 459 flags_.set(Flag::FmtOrNml); 460 } 461 } 462 463 void IoChecker::Enter(const parser::IoControlSpec::Asynchronous &spec) { 464 SetSpecifier(IoSpecKind::Asynchronous); 465 if (const std::optional<std::string> charConst{ 466 GetConstExpr<std::string>(spec)}) { 467 flags_.set( 468 Flag::AsynchronousYes, parser::ToUpperCaseLetters(*charConst) == "YES"); 469 CheckStringValue(IoSpecKind::Asynchronous, *charConst, 470 parser::FindSourceLocation(spec)); // C1223 471 } 472 } 473 474 void IoChecker::Enter(const parser::IoControlSpec::CharExpr &spec) { 475 IoSpecKind specKind{}; 476 using ParseKind = parser::IoControlSpec::CharExpr::Kind; 477 switch (std::get<ParseKind>(spec.t)) { 478 case ParseKind::Advance: 479 specKind = IoSpecKind::Advance; 480 break; 481 case ParseKind::Blank: 482 specKind = IoSpecKind::Blank; 483 break; 484 case ParseKind::Decimal: 485 specKind = IoSpecKind::Decimal; 486 break; 487 case ParseKind::Delim: 488 specKind = IoSpecKind::Delim; 489 break; 490 case ParseKind::Pad: 491 specKind = IoSpecKind::Pad; 492 break; 493 case ParseKind::Round: 494 specKind = IoSpecKind::Round; 495 break; 496 case ParseKind::Sign: 497 specKind = IoSpecKind::Sign; 498 break; 499 } 500 SetSpecifier(specKind); 501 if (const std::optional<std::string> charConst{GetConstExpr<std::string>( 502 std::get<parser::ScalarDefaultCharExpr>(spec.t))}) { 503 if (specKind == IoSpecKind::Advance) { 504 flags_.set( 505 Flag::AdvanceYes, parser::ToUpperCaseLetters(*charConst) == "YES"); 506 } 507 CheckStringValue(specKind, *charConst, parser::FindSourceLocation(spec)); 508 } 509 } 510 511 void IoChecker::Enter(const parser::IoControlSpec::Pos &) { 512 SetSpecifier(IoSpecKind::Pos); 513 } 514 515 void IoChecker::Enter(const parser::IoControlSpec::Rec &) { 516 SetSpecifier(IoSpecKind::Rec); 517 } 518 519 void IoChecker::Enter(const parser::IoControlSpec::Size &var) { 520 CheckForDefinableVariable(var, "SIZE"); 521 SetSpecifier(IoSpecKind::Size); 522 } 523 524 void IoChecker::Enter(const parser::IoUnit &spec) { 525 if (const parser::Variable * var{std::get_if<parser::Variable>(&spec.u)}) { 526 if (stmt_ == IoStmtKind::Write) { 527 CheckForDefinableVariable(*var, "Internal file"); 528 } 529 if (const auto *expr{GetExpr(*var)}) { 530 if (HasVectorSubscript(*expr)) { 531 context_.Say(parser::FindSourceLocation(*var), // C1201 532 "Internal file must not have a vector subscript"_err_en_US); 533 } else if (!ExprTypeKindIsDefault(*expr, context_)) { 534 // This may be too restrictive; other kinds may be valid. 535 context_.Say(parser::FindSourceLocation(*var), // C1202 536 "Invalid character kind for an internal file variable"_err_en_US); 537 } 538 } 539 SetSpecifier(IoSpecKind::Unit); 540 flags_.set(Flag::InternalUnit); 541 } else if (std::get_if<parser::Star>(&spec.u)) { 542 SetSpecifier(IoSpecKind::Unit); 543 flags_.set(Flag::StarUnit); 544 } 545 } 546 547 void IoChecker::Enter(const parser::MsgVariable &var) { 548 if (stmt_ == IoStmtKind::None) { 549 // allocate, deallocate, image control 550 CheckForDefinableVariable(var, "ERRMSG"); 551 return; 552 } 553 CheckForDefinableVariable(var, "IOMSG"); 554 SetSpecifier(IoSpecKind::Iomsg); 555 } 556 557 void IoChecker::Enter(const parser::OutputItem &item) { 558 flags_.set(Flag::DataList); 559 if (const auto *x{std::get_if<parser::Expr>(&item.u)}) { 560 if (const auto *expr{GetExpr(*x)}) { 561 if (IsProcedurePointer(*expr)) { 562 context_.Say(parser::FindSourceLocation(*x), 563 "Output item must not be a procedure pointer"_err_en_US); // C1233 564 } 565 } 566 } 567 } 568 569 void IoChecker::Enter(const parser::StatusExpr &spec) { 570 SetSpecifier(IoSpecKind::Status); 571 if (const std::optional<std::string> charConst{ 572 GetConstExpr<std::string>(spec)}) { 573 // Status values for Open and Close are different. 574 std::string s{parser::ToUpperCaseLetters(*charConst)}; 575 if (stmt_ == IoStmtKind::Open) { 576 flags_.set(Flag::KnownStatus); 577 flags_.set(Flag::StatusNew, s == "NEW"); 578 flags_.set(Flag::StatusReplace, s == "REPLACE"); 579 flags_.set(Flag::StatusScratch, s == "SCRATCH"); 580 // CheckStringValue compares for OPEN Status string values. 581 CheckStringValue( 582 IoSpecKind::Status, *charConst, parser::FindSourceLocation(spec)); 583 return; 584 } 585 CHECK(stmt_ == IoStmtKind::Close); 586 if (s != "DELETE" && s != "KEEP") { 587 context_.Say(parser::FindSourceLocation(spec), 588 "Invalid STATUS value '%s'"_err_en_US, *charConst); 589 } 590 } 591 } 592 593 void IoChecker::Enter(const parser::StatVariable &var) { 594 if (stmt_ == IoStmtKind::None) { 595 // allocate, deallocate, image control 596 CheckForDefinableVariable(var, "STAT"); 597 return; 598 } 599 CheckForDefinableVariable(var, "IOSTAT"); 600 SetSpecifier(IoSpecKind::Iostat); 601 } 602 603 void IoChecker::Leave(const parser::BackspaceStmt &) { 604 CheckForPureSubprogram(); 605 CheckForRequiredSpecifier( 606 flags_.test(Flag::NumberUnit), "UNIT number"); // C1240 607 Done(); 608 } 609 610 void IoChecker::Leave(const parser::CloseStmt &) { 611 CheckForPureSubprogram(); 612 CheckForRequiredSpecifier( 613 flags_.test(Flag::NumberUnit), "UNIT number"); // C1208 614 Done(); 615 } 616 617 void IoChecker::Leave(const parser::EndfileStmt &) { 618 CheckForPureSubprogram(); 619 CheckForRequiredSpecifier( 620 flags_.test(Flag::NumberUnit), "UNIT number"); // C1240 621 Done(); 622 } 623 624 void IoChecker::Leave(const parser::FlushStmt &) { 625 CheckForPureSubprogram(); 626 CheckForRequiredSpecifier( 627 flags_.test(Flag::NumberUnit), "UNIT number"); // C1243 628 Done(); 629 } 630 631 void IoChecker::Leave(const parser::InquireStmt &stmt) { 632 if (std::get_if<std::list<parser::InquireSpec>>(&stmt.u)) { 633 CheckForPureSubprogram(); 634 // Inquire by unit or by file (vs. by output list). 635 CheckForRequiredSpecifier( 636 flags_.test(Flag::NumberUnit) || specifierSet_.test(IoSpecKind::File), 637 "UNIT number or FILE"); // C1246 638 CheckForProhibitedSpecifier(IoSpecKind::File, IoSpecKind::Unit); // C1246 639 CheckForRequiredSpecifier(IoSpecKind::Id, IoSpecKind::Pending); // C1248 640 } 641 Done(); 642 } 643 644 void IoChecker::Leave(const parser::OpenStmt &) { 645 CheckForPureSubprogram(); 646 CheckForRequiredSpecifier(specifierSet_.test(IoSpecKind::Unit) || 647 specifierSet_.test(IoSpecKind::Newunit), 648 "UNIT or NEWUNIT"); // C1204, C1205 649 CheckForProhibitedSpecifier( 650 IoSpecKind::Newunit, IoSpecKind::Unit); // C1204, C1205 651 CheckForRequiredSpecifier(flags_.test(Flag::StatusNew), "STATUS='NEW'", 652 IoSpecKind::File); // 12.5.6.10 653 CheckForRequiredSpecifier(flags_.test(Flag::StatusReplace), 654 "STATUS='REPLACE'", IoSpecKind::File); // 12.5.6.10 655 CheckForProhibitedSpecifier(flags_.test(Flag::StatusScratch), 656 "STATUS='SCRATCH'", IoSpecKind::File); // 12.5.6.10 657 if (flags_.test(Flag::KnownStatus)) { 658 CheckForRequiredSpecifier(IoSpecKind::Newunit, 659 specifierSet_.test(IoSpecKind::File) || 660 flags_.test(Flag::StatusScratch), 661 "FILE or STATUS='SCRATCH'"); // 12.5.6.12 662 } else { 663 CheckForRequiredSpecifier(IoSpecKind::Newunit, 664 specifierSet_.test(IoSpecKind::File) || 665 specifierSet_.test(IoSpecKind::Status), 666 "FILE or STATUS"); // 12.5.6.12 667 } 668 if (flags_.test(Flag::KnownAccess)) { 669 CheckForRequiredSpecifier(flags_.test(Flag::AccessDirect), 670 "ACCESS='DIRECT'", IoSpecKind::Recl); // 12.5.6.15 671 CheckForProhibitedSpecifier(flags_.test(Flag::AccessStream), 672 "STATUS='STREAM'", IoSpecKind::Recl); // 12.5.6.15 673 } 674 Done(); 675 } 676 677 void IoChecker::Leave(const parser::PrintStmt &) { 678 CheckForPureSubprogram(); 679 Done(); 680 } 681 682 static void CheckForDoVariableInNamelist(const Symbol &namelist, 683 SemanticsContext &context, parser::CharBlock namelistLocation) { 684 const auto &details{namelist.GetUltimate().get<NamelistDetails>()}; 685 for (const Symbol &object : details.objects()) { 686 context.CheckIndexVarRedefine(namelistLocation, object); 687 } 688 } 689 690 static void CheckForDoVariableInNamelistSpec( 691 const parser::ReadStmt &readStmt, SemanticsContext &context) { 692 const std::list<parser::IoControlSpec> &controls{readStmt.controls}; 693 for (const auto &control : controls) { 694 if (const auto *namelist{std::get_if<parser::Name>(&control.u)}) { 695 if (const Symbol * symbol{namelist->symbol}) { 696 CheckForDoVariableInNamelist(*symbol, context, namelist->source); 697 } 698 } 699 } 700 } 701 702 static void CheckForDoVariable( 703 const parser::ReadStmt &readStmt, SemanticsContext &context) { 704 CheckForDoVariableInNamelistSpec(readStmt, context); 705 const std::list<parser::InputItem> &items{readStmt.items}; 706 for (const auto &item : items) { 707 if (const parser::Variable * 708 variable{std::get_if<parser::Variable>(&item.u)}) { 709 context.CheckIndexVarRedefine(*variable); 710 } 711 } 712 } 713 714 void IoChecker::Leave(const parser::ReadStmt &readStmt) { 715 if (!flags_.test(Flag::InternalUnit)) { 716 CheckForPureSubprogram(); 717 } 718 CheckForDoVariable(readStmt, context_); 719 if (!flags_.test(Flag::IoControlList)) { 720 Done(); 721 return; 722 } 723 LeaveReadWrite(); 724 CheckForProhibitedSpecifier(IoSpecKind::Delim); // C1212 725 CheckForProhibitedSpecifier(IoSpecKind::Sign); // C1212 726 CheckForProhibitedSpecifier(IoSpecKind::Rec, IoSpecKind::End); // C1220 727 CheckForRequiredSpecifier(IoSpecKind::Eor, 728 specifierSet_.test(IoSpecKind::Advance) && !flags_.test(Flag::AdvanceYes), 729 "ADVANCE with value 'NO'"); // C1222 + 12.6.2.1p2 730 CheckForRequiredSpecifier(IoSpecKind::Blank, flags_.test(Flag::FmtOrNml), 731 "FMT or NML"); // C1227 732 CheckForRequiredSpecifier( 733 IoSpecKind::Pad, flags_.test(Flag::FmtOrNml), "FMT or NML"); // C1227 734 Done(); 735 } 736 737 void IoChecker::Leave(const parser::RewindStmt &) { 738 CheckForRequiredSpecifier( 739 flags_.test(Flag::NumberUnit), "UNIT number"); // C1240 740 CheckForPureSubprogram(); 741 Done(); 742 } 743 744 void IoChecker::Leave(const parser::WaitStmt &) { 745 CheckForRequiredSpecifier( 746 flags_.test(Flag::NumberUnit), "UNIT number"); // C1237 747 CheckForPureSubprogram(); 748 Done(); 749 } 750 751 void IoChecker::Leave(const parser::WriteStmt &) { 752 if (!flags_.test(Flag::InternalUnit)) { 753 CheckForPureSubprogram(); 754 } 755 LeaveReadWrite(); 756 CheckForProhibitedSpecifier(IoSpecKind::Blank); // C1213 757 CheckForProhibitedSpecifier(IoSpecKind::End); // C1213 758 CheckForProhibitedSpecifier(IoSpecKind::Eor); // C1213 759 CheckForProhibitedSpecifier(IoSpecKind::Pad); // C1213 760 CheckForProhibitedSpecifier(IoSpecKind::Size); // C1213 761 CheckForRequiredSpecifier( 762 IoSpecKind::Sign, flags_.test(Flag::FmtOrNml), "FMT or NML"); // C1227 763 CheckForRequiredSpecifier(IoSpecKind::Delim, 764 flags_.test(Flag::StarFmt) || specifierSet_.test(IoSpecKind::Nml), 765 "FMT=* or NML"); // C1228 766 Done(); 767 } 768 769 void IoChecker::LeaveReadWrite() const { 770 CheckForRequiredSpecifier(IoSpecKind::Unit); // C1211 771 CheckForProhibitedSpecifier(IoSpecKind::Nml, IoSpecKind::Rec); // C1216 772 CheckForProhibitedSpecifier(IoSpecKind::Nml, IoSpecKind::Fmt); // C1216 773 CheckForProhibitedSpecifier( 774 IoSpecKind::Nml, flags_.test(Flag::DataList), "a data list"); // C1216 775 CheckForProhibitedSpecifier(flags_.test(Flag::InternalUnit), 776 "UNIT=internal-file", IoSpecKind::Pos); // C1219 777 CheckForProhibitedSpecifier(flags_.test(Flag::InternalUnit), 778 "UNIT=internal-file", IoSpecKind::Rec); // C1219 779 CheckForProhibitedSpecifier( 780 flags_.test(Flag::StarUnit), "UNIT=*", IoSpecKind::Pos); // C1219 781 CheckForProhibitedSpecifier( 782 flags_.test(Flag::StarUnit), "UNIT=*", IoSpecKind::Rec); // C1219 783 CheckForProhibitedSpecifier( 784 IoSpecKind::Rec, flags_.test(Flag::StarFmt), "FMT=*"); // C1220 785 CheckForRequiredSpecifier(IoSpecKind::Advance, 786 flags_.test(Flag::CharFmt) || flags_.test(Flag::LabelFmt) || 787 flags_.test(Flag::AssignFmt), 788 "an explicit format"); // C1221 789 CheckForProhibitedSpecifier(IoSpecKind::Advance, 790 flags_.test(Flag::InternalUnit), "UNIT=internal-file"); // C1221 791 CheckForRequiredSpecifier(flags_.test(Flag::AsynchronousYes), 792 "ASYNCHRONOUS='YES'", flags_.test(Flag::NumberUnit), 793 "UNIT=number"); // C1224 794 CheckForRequiredSpecifier(IoSpecKind::Id, flags_.test(Flag::AsynchronousYes), 795 "ASYNCHRONOUS='YES'"); // C1225 796 CheckForProhibitedSpecifier(IoSpecKind::Pos, IoSpecKind::Rec); // C1226 797 CheckForRequiredSpecifier(IoSpecKind::Decimal, flags_.test(Flag::FmtOrNml), 798 "FMT or NML"); // C1227 799 CheckForRequiredSpecifier(IoSpecKind::Round, flags_.test(Flag::FmtOrNml), 800 "FMT or NML"); // C1227 801 } 802 803 void IoChecker::SetSpecifier(IoSpecKind specKind) { 804 if (stmt_ == IoStmtKind::None) { 805 // FMT may appear on PRINT statements, which don't have any checks. 806 // [IO]MSG and [IO]STAT parse symbols are shared with non-I/O statements. 807 return; 808 } 809 // C1203, C1207, C1210, C1236, C1239, C1242, C1245 810 if (specifierSet_.test(specKind)) { 811 context_.Say("Duplicate %s specifier"_err_en_US, 812 parser::ToUpperCaseLetters(common::EnumToString(specKind))); 813 } 814 specifierSet_.set(specKind); 815 } 816 817 void IoChecker::CheckStringValue(IoSpecKind specKind, const std::string &value, 818 const parser::CharBlock &source) const { 819 static std::unordered_map<IoSpecKind, const std::set<std::string>> specValues{ 820 {IoSpecKind::Access, {"DIRECT", "SEQUENTIAL", "STREAM"}}, 821 {IoSpecKind::Action, {"READ", "READWRITE", "WRITE"}}, 822 {IoSpecKind::Advance, {"NO", "YES"}}, 823 {IoSpecKind::Asynchronous, {"NO", "YES"}}, 824 {IoSpecKind::Blank, {"NULL", "ZERO"}}, 825 {IoSpecKind::Decimal, {"COMMA", "POINT"}}, 826 {IoSpecKind::Delim, {"APOSTROPHE", "NONE", "QUOTE"}}, 827 {IoSpecKind::Encoding, {"DEFAULT", "UTF-8"}}, 828 {IoSpecKind::Form, {"FORMATTED", "UNFORMATTED"}}, 829 {IoSpecKind::Pad, {"NO", "YES"}}, 830 {IoSpecKind::Position, {"APPEND", "ASIS", "REWIND"}}, 831 {IoSpecKind::Round, 832 {"COMPATIBLE", "DOWN", "NEAREST", "PROCESSOR_DEFINED", "UP", "ZERO"}}, 833 {IoSpecKind::Sign, {"PLUS", "PROCESSOR_DEFINED", "SUPPRESS"}}, 834 {IoSpecKind::Status, 835 // Open values; Close values are {"DELETE", "KEEP"}. 836 {"NEW", "OLD", "REPLACE", "SCRATCH", "UNKNOWN"}}, 837 {IoSpecKind::Carriagecontrol, {"LIST", "FORTRAN", "NONE"}}, 838 {IoSpecKind::Convert, {"BIG_ENDIAN", "LITTLE_ENDIAN", "NATIVE"}}, 839 {IoSpecKind::Dispose, {"DELETE", "KEEP"}}, 840 }; 841 if (!specValues.at(specKind).count(parser::ToUpperCaseLetters(value))) { 842 context_.Say(source, "Invalid %s value '%s'"_err_en_US, 843 parser::ToUpperCaseLetters(common::EnumToString(specKind)), value); 844 } 845 } 846 847 // CheckForRequiredSpecifier and CheckForProhibitedSpecifier functions 848 // need conditions to check, and string arguments to insert into a message. 849 // An IoSpecKind provides both an absence/presence condition and a string 850 // argument (its name). A (condition, string) pair provides an arbitrary 851 // condition and an arbitrary string. 852 853 void IoChecker::CheckForRequiredSpecifier(IoSpecKind specKind) const { 854 if (!specifierSet_.test(specKind)) { 855 context_.Say("%s statement must have a %s specifier"_err_en_US, 856 parser::ToUpperCaseLetters(common::EnumToString(stmt_)), 857 parser::ToUpperCaseLetters(common::EnumToString(specKind))); 858 } 859 } 860 861 void IoChecker::CheckForRequiredSpecifier( 862 bool condition, const std::string &s) const { 863 if (!condition) { 864 context_.Say("%s statement must have a %s specifier"_err_en_US, 865 parser::ToUpperCaseLetters(common::EnumToString(stmt_)), s); 866 } 867 } 868 869 void IoChecker::CheckForRequiredSpecifier( 870 IoSpecKind specKind1, IoSpecKind specKind2) const { 871 if (specifierSet_.test(specKind1) && !specifierSet_.test(specKind2)) { 872 context_.Say("If %s appears, %s must also appear"_err_en_US, 873 parser::ToUpperCaseLetters(common::EnumToString(specKind1)), 874 parser::ToUpperCaseLetters(common::EnumToString(specKind2))); 875 } 876 } 877 878 void IoChecker::CheckForRequiredSpecifier( 879 IoSpecKind specKind, bool condition, const std::string &s) const { 880 if (specifierSet_.test(specKind) && !condition) { 881 context_.Say("If %s appears, %s must also appear"_err_en_US, 882 parser::ToUpperCaseLetters(common::EnumToString(specKind)), s); 883 } 884 } 885 886 void IoChecker::CheckForRequiredSpecifier( 887 bool condition, const std::string &s, IoSpecKind specKind) const { 888 if (condition && !specifierSet_.test(specKind)) { 889 context_.Say("If %s appears, %s must also appear"_err_en_US, s, 890 parser::ToUpperCaseLetters(common::EnumToString(specKind))); 891 } 892 } 893 894 void IoChecker::CheckForRequiredSpecifier(bool condition1, 895 const std::string &s1, bool condition2, const std::string &s2) const { 896 if (condition1 && !condition2) { 897 context_.Say("If %s appears, %s must also appear"_err_en_US, s1, s2); 898 } 899 } 900 901 void IoChecker::CheckForProhibitedSpecifier(IoSpecKind specKind) const { 902 if (specifierSet_.test(specKind)) { 903 context_.Say("%s statement must not have a %s specifier"_err_en_US, 904 parser::ToUpperCaseLetters(common::EnumToString(stmt_)), 905 parser::ToUpperCaseLetters(common::EnumToString(specKind))); 906 } 907 } 908 909 void IoChecker::CheckForProhibitedSpecifier( 910 IoSpecKind specKind1, IoSpecKind specKind2) const { 911 if (specifierSet_.test(specKind1) && specifierSet_.test(specKind2)) { 912 context_.Say("If %s appears, %s must not appear"_err_en_US, 913 parser::ToUpperCaseLetters(common::EnumToString(specKind1)), 914 parser::ToUpperCaseLetters(common::EnumToString(specKind2))); 915 } 916 } 917 918 void IoChecker::CheckForProhibitedSpecifier( 919 IoSpecKind specKind, bool condition, const std::string &s) const { 920 if (specifierSet_.test(specKind) && condition) { 921 context_.Say("If %s appears, %s must not appear"_err_en_US, 922 parser::ToUpperCaseLetters(common::EnumToString(specKind)), s); 923 } 924 } 925 926 void IoChecker::CheckForProhibitedSpecifier( 927 bool condition, const std::string &s, IoSpecKind specKind) const { 928 if (condition && specifierSet_.test(specKind)) { 929 context_.Say("If %s appears, %s must not appear"_err_en_US, s, 930 parser::ToUpperCaseLetters(common::EnumToString(specKind))); 931 } 932 } 933 934 template <typename A> 935 void IoChecker::CheckForDefinableVariable( 936 const A &var, const std::string &s) const { 937 const Symbol *sym{ 938 GetFirstName(*parser::Unwrap<parser::Variable>(var)).symbol}; 939 if (WhyNotModifiable(*sym, context_.FindScope(*context_.location()))) { 940 context_.Say(parser::FindSourceLocation(var), 941 "%s variable '%s' must be definable"_err_en_US, s, sym->name()); 942 } 943 } 944 945 void IoChecker::CheckForPureSubprogram() const { // C1597 946 CHECK(context_.location()); 947 if (FindPureProcedureContaining(context_.FindScope(*context_.location()))) { 948 context_.Say("External I/O is not allowed in a pure subprogram"_err_en_US); 949 } 950 } 951 952 } // namespace Fortran::semantics 953