1 //===-- lib/Semantics/check-data.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 // DATA statement semantic analysis. 10 // - Applies static semantic checks to the variables in each data-stmt-set with 11 // class DataVarChecker; 12 // - Applies specific checks to each scalar element initialization with a 13 // constant value or pointer tareg with class DataInitializationCompiler; 14 // - Collects the elemental initializations for each symbol and converts them 15 // into a single init() expression with member function 16 // DataChecker::ConstructInitializer(). 17 18 #include "check-data.h" 19 #include "pointer-assignment.h" 20 #include "flang/Evaluate/fold-designator.h" 21 #include "flang/Evaluate/traverse.h" 22 #include "flang/Parser/parse-tree.h" 23 #include "flang/Parser/tools.h" 24 #include "flang/Semantics/tools.h" 25 26 namespace Fortran::semantics { 27 28 // Ensures that references to an implied DO loop control variable are 29 // represented as such in the "body" of the implied DO loop. 30 void DataChecker::Enter(const parser::DataImpliedDo &x) { 31 auto name{std::get<parser::DataImpliedDo::Bounds>(x.t).name.thing.thing}; 32 int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind}; 33 if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) { 34 if (dynamicType->category() == TypeCategory::Integer) { 35 kind = dynamicType->kind(); 36 } 37 } 38 exprAnalyzer_.AddImpliedDo(name.source, kind); 39 } 40 41 void DataChecker::Leave(const parser::DataImpliedDo &x) { 42 auto name{std::get<parser::DataImpliedDo::Bounds>(x.t).name.thing.thing}; 43 exprAnalyzer_.RemoveImpliedDo(name.source); 44 } 45 46 // DataVarChecker applies static checks once to each variable that appears 47 // in a data-stmt-set. These checks are independent of the values that 48 // correspond to the variables. 49 class DataVarChecker : public evaluate::AllTraverse<DataVarChecker, true> { 50 public: 51 using Base = evaluate::AllTraverse<DataVarChecker, true>; 52 DataVarChecker(SemanticsContext &c, parser::CharBlock src) 53 : Base{*this}, context_{c}, source_{src} {} 54 using Base::operator(); 55 bool HasComponentWithoutSubscripts() const { 56 return hasComponent_ && !hasSubscript_; 57 } 58 bool operator()(const Symbol &symbol) { // C876 59 // 8.6.7p(2) - precludes non-pointers of derived types with 60 // default component values 61 const Scope &scope{context_.FindScope(source_)}; 62 bool isFirstSymbol{isFirstSymbol_}; 63 isFirstSymbol_ = false; 64 if (const char *whyNot{IsAutomatic(symbol) ? "Automatic variable" 65 : IsDummy(symbol) ? "Dummy argument" 66 : IsFunctionResult(symbol) ? "Function result" 67 : IsAllocatable(symbol) ? "Allocatable" 68 : IsInitialized(symbol, true) ? "Default-initialized" 69 : IsInBlankCommon(symbol) ? "Blank COMMON object" 70 : IsProcedure(symbol) && !IsPointer(symbol) ? "Procedure" 71 // remaining checks don't apply to components 72 : !isFirstSymbol ? nullptr 73 : IsHostAssociated(symbol, scope) ? "Host-associated object" 74 : IsUseAssociated(symbol, scope) ? "USE-associated object" 75 : nullptr}) { 76 context_.Say(source_, 77 "%s '%s' must not be initialized in a DATA statement"_err_en_US, 78 whyNot, symbol.name()); 79 return false; 80 } else if (IsProcedurePointer(symbol)) { 81 context_.Say(source_, 82 "Procedure pointer '%s' in a DATA statement is not standard"_en_US, 83 symbol.name()); 84 } 85 return true; 86 } 87 bool operator()(const evaluate::Component &component) { 88 hasComponent_ = true; 89 const Symbol &lastSymbol{component.GetLastSymbol()}; 90 if (isPointerAllowed_) { 91 if (IsPointer(lastSymbol) && hasSubscript_) { // C877 92 context_.Say(source_, 93 "Rightmost data object pointer '%s' must not be subscripted"_err_en_US, 94 lastSymbol.name().ToString()); 95 return false; 96 } 97 RestrictPointer(); 98 } else { 99 if (IsPointer(lastSymbol)) { // C877 100 context_.Say(source_, 101 "Data object must not contain pointer '%s' as a non-rightmost part"_err_en_US, 102 lastSymbol.name().ToString()); 103 return false; 104 } 105 } 106 return (*this)(component.base()) && (*this)(lastSymbol); 107 } 108 bool operator()(const evaluate::ArrayRef &arrayRef) { 109 hasSubscript_ = true; 110 return (*this)(arrayRef.base()) && (*this)(arrayRef.subscript()); 111 } 112 bool operator()(const evaluate::Substring &substring) { 113 hasSubscript_ = true; 114 return (*this)(substring.parent()) && (*this)(substring.lower()) && 115 (*this)(substring.upper()); 116 } 117 bool operator()(const evaluate::CoarrayRef &) { // C874 118 context_.Say( 119 source_, "Data object must not be a coindexed variable"_err_en_US); 120 return false; 121 } 122 bool operator()(const evaluate::Subscript &subs) { 123 DataVarChecker subscriptChecker{context_, source_}; 124 subscriptChecker.RestrictPointer(); 125 return std::visit( 126 common::visitors{ 127 [&](const evaluate::IndirectSubscriptIntegerExpr &expr) { 128 return CheckSubscriptExpr(expr); 129 }, 130 [&](const evaluate::Triplet &triplet) { 131 return CheckSubscriptExpr(triplet.lower()) && 132 CheckSubscriptExpr(triplet.upper()) && 133 CheckSubscriptExpr(triplet.stride()); 134 }, 135 }, 136 subs.u) && 137 subscriptChecker(subs.u); 138 } 139 template <typename T> 140 bool operator()(const evaluate::FunctionRef<T> &) const { // C875 141 context_.Say(source_, 142 "Data object variable must not be a function reference"_err_en_US); 143 return false; 144 } 145 void RestrictPointer() { isPointerAllowed_ = false; } 146 147 private: 148 bool CheckSubscriptExpr( 149 const std::optional<evaluate::IndirectSubscriptIntegerExpr> &x) const { 150 return !x || CheckSubscriptExpr(*x); 151 } 152 bool CheckSubscriptExpr( 153 const evaluate::IndirectSubscriptIntegerExpr &expr) const { 154 return CheckSubscriptExpr(expr.value()); 155 } 156 bool CheckSubscriptExpr( 157 const evaluate::Expr<evaluate::SubscriptInteger> &expr) const { 158 if (!evaluate::IsConstantExpr(expr)) { // C875,C881 159 context_.Say( 160 source_, "Data object must have constant subscripts"_err_en_US); 161 return false; 162 } else { 163 return true; 164 } 165 } 166 167 SemanticsContext &context_; 168 parser::CharBlock source_; 169 bool hasComponent_{false}; 170 bool hasSubscript_{false}; 171 bool isPointerAllowed_{true}; 172 bool isFirstSymbol_{true}; 173 }; 174 175 void DataChecker::Leave(const parser::DataIDoObject &object) { 176 if (const auto *designator{ 177 std::get_if<parser::Scalar<common::Indirection<parser::Designator>>>( 178 &object.u)}) { 179 if (MaybeExpr expr{exprAnalyzer_.Analyze(*designator)}) { 180 auto source{designator->thing.value().source}; 181 if (evaluate::IsConstantExpr(*expr)) { // C878,C879 182 exprAnalyzer_.context().Say( 183 source, "Data implied do object must be a variable"_err_en_US); 184 } else { 185 DataVarChecker checker{exprAnalyzer_.context(), source}; 186 if (checker(*expr)) { 187 if (checker.HasComponentWithoutSubscripts()) { // C880 188 exprAnalyzer_.context().Say(source, 189 "Data implied do structure component must be subscripted"_err_en_US); 190 } else { 191 return; 192 } 193 } 194 } 195 } 196 } 197 currentSetHasFatalErrors_ = true; 198 } 199 200 void DataChecker::Leave(const parser::DataStmtObject &dataObject) { 201 std::visit(common::visitors{ 202 [](const parser::DataImpliedDo &) { // has own Enter()/Leave() 203 }, 204 [&](const auto &var) { 205 auto expr{exprAnalyzer_.Analyze(var)}; 206 if (!expr || 207 !DataVarChecker{exprAnalyzer_.context(), 208 parser::FindSourceLocation(dataObject)}(*expr)) { 209 currentSetHasFatalErrors_ = true; 210 } 211 }, 212 }, 213 dataObject.u); 214 } 215 216 // Steps through a list of values in a DATA statement set; implements 217 // repetition. 218 class ValueListIterator { 219 public: 220 explicit ValueListIterator(const parser::DataStmtSet &set) 221 : end_{std::get<std::list<parser::DataStmtValue>>(set.t).end()}, 222 at_{std::get<std::list<parser::DataStmtValue>>(set.t).begin()} { 223 SetRepetitionCount(); 224 } 225 bool hasFatalError() const { return hasFatalError_; } 226 bool IsAtEnd() const { return at_ == end_; } 227 const SomeExpr *operator*() const { return GetExpr(GetConstant()); } 228 parser::CharBlock LocateSource() const { return GetConstant().source; } 229 ValueListIterator &operator++() { 230 if (repetitionsRemaining_ > 0) { 231 --repetitionsRemaining_; 232 } else if (at_ != end_) { 233 ++at_; 234 SetRepetitionCount(); 235 } 236 return *this; 237 } 238 239 private: 240 using listIterator = std::list<parser::DataStmtValue>::const_iterator; 241 void SetRepetitionCount(); 242 const parser::DataStmtConstant &GetConstant() const { 243 return std::get<parser::DataStmtConstant>(at_->t); 244 } 245 246 listIterator end_; 247 listIterator at_; 248 ConstantSubscript repetitionsRemaining_{0}; 249 bool hasFatalError_{false}; 250 }; 251 252 void ValueListIterator::SetRepetitionCount() { 253 for (repetitionsRemaining_ = 1; at_ != end_; ++at_) { 254 if (at_->repetitions < 0) { 255 hasFatalError_ = true; 256 } 257 if (at_->repetitions > 0) { 258 repetitionsRemaining_ = at_->repetitions - 1; 259 return; 260 } 261 } 262 repetitionsRemaining_ = 0; 263 } 264 265 // Collects all of the elemental initializations from DATA statements 266 // into a single image for each symbol that appears in any DATA. 267 // Expands the implied DO loops and array references. 268 // Applies checks that validate each distinct elemental initialization 269 // of the variables in a data-stmt-set, as well as those that apply 270 // to the corresponding values being use to initialize each element. 271 class DataInitializationCompiler { 272 public: 273 DataInitializationCompiler(DataInitializations &inits, 274 evaluate::ExpressionAnalyzer &a, const parser::DataStmtSet &set) 275 : inits_{inits}, exprAnalyzer_{a}, values_{set} {} 276 const DataInitializations &inits() const { return inits_; } 277 bool HasSurplusValues() const { return !values_.IsAtEnd(); } 278 bool Scan(const parser::DataStmtObject &); 279 280 private: 281 bool Scan(const parser::Variable &); 282 bool Scan(const parser::Designator &); 283 bool Scan(const parser::DataImpliedDo &); 284 bool Scan(const parser::DataIDoObject &); 285 286 // Initializes all elements of a designator, which can be an array or section. 287 bool InitDesignator(const SomeExpr &); 288 // Initializes a single object. 289 bool InitElement(const evaluate::OffsetSymbol &, const SomeExpr &designator); 290 291 DataInitializations &inits_; 292 evaluate::ExpressionAnalyzer &exprAnalyzer_; 293 ValueListIterator values_; 294 }; 295 296 bool DataInitializationCompiler::Scan(const parser::DataStmtObject &object) { 297 return std::visit( 298 common::visitors{ 299 [&](const common::Indirection<parser::Variable> &var) { 300 return Scan(var.value()); 301 }, 302 [&](const parser::DataImpliedDo &ido) { return Scan(ido); }, 303 }, 304 object.u); 305 } 306 307 bool DataInitializationCompiler::Scan(const parser::Variable &var) { 308 if (const auto *expr{GetExpr(var)}) { 309 exprAnalyzer_.GetFoldingContext().messages().SetLocation(var.GetSource()); 310 if (InitDesignator(*expr)) { 311 return true; 312 } 313 } 314 return false; 315 } 316 317 bool DataInitializationCompiler::Scan(const parser::Designator &designator) { 318 if (auto expr{exprAnalyzer_.Analyze(designator)}) { 319 exprAnalyzer_.GetFoldingContext().messages().SetLocation( 320 parser::FindSourceLocation(designator)); 321 if (InitDesignator(*expr)) { 322 return true; 323 } 324 } 325 return false; 326 } 327 328 bool DataInitializationCompiler::Scan(const parser::DataImpliedDo &ido) { 329 const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)}; 330 auto name{bounds.name.thing.thing}; 331 const auto *lowerExpr{GetExpr(bounds.lower.thing.thing)}; 332 const auto *upperExpr{GetExpr(bounds.upper.thing.thing)}; 333 const auto *stepExpr{ 334 bounds.step ? GetExpr(bounds.step->thing.thing) : nullptr}; 335 if (lowerExpr && upperExpr) { 336 auto lower{ToInt64(*lowerExpr)}; 337 auto upper{ToInt64(*upperExpr)}; 338 auto step{stepExpr ? ToInt64(*stepExpr) : std::nullopt}; 339 auto stepVal{step.value_or(1)}; 340 if (stepVal == 0) { 341 exprAnalyzer_.Say(name.source, 342 "DATA statement implied DO loop has a step value of zero"_err_en_US); 343 } else if (lower && upper) { 344 int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind}; 345 if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) { 346 if (dynamicType->category() == TypeCategory::Integer) { 347 kind = dynamicType->kind(); 348 } 349 } 350 if (exprAnalyzer_.AddImpliedDo(name.source, kind)) { 351 auto &value{exprAnalyzer_.GetFoldingContext().StartImpliedDo( 352 name.source, *lower)}; 353 bool result{true}; 354 for (auto n{(*upper - value + stepVal) / stepVal}; n > 0; 355 --n, value += stepVal) { 356 for (const auto &object : 357 std::get<std::list<parser::DataIDoObject>>(ido.t)) { 358 if (!Scan(object)) { 359 result = false; 360 break; 361 } 362 } 363 } 364 exprAnalyzer_.GetFoldingContext().EndImpliedDo(name.source); 365 exprAnalyzer_.RemoveImpliedDo(name.source); 366 return result; 367 } 368 } 369 } 370 return false; 371 } 372 373 bool DataInitializationCompiler::Scan(const parser::DataIDoObject &object) { 374 return std::visit( 375 common::visitors{ 376 [&](const parser::Scalar<common::Indirection<parser::Designator>> 377 &var) { return Scan(var.thing.value()); }, 378 [&](const common::Indirection<parser::DataImpliedDo> &ido) { 379 return Scan(ido.value()); 380 }, 381 }, 382 object.u); 383 } 384 385 bool DataInitializationCompiler::InitDesignator(const SomeExpr &designator) { 386 evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; 387 evaluate::DesignatorFolder folder{context}; 388 while (auto offsetSymbol{folder.FoldDesignator(designator)}) { 389 if (folder.isOutOfRange()) { 390 if (auto bad{evaluate::OffsetToDesignator(context, *offsetSymbol)}) { 391 exprAnalyzer_.context().Say( 392 "DATA statement designator '%s' is out of range"_err_en_US, 393 bad->AsFortran()); 394 } else { 395 exprAnalyzer_.context().Say( 396 "DATA statement designator '%s' is out of range"_err_en_US, 397 designator.AsFortran()); 398 } 399 return false; 400 } else if (!InitElement(*offsetSymbol, designator)) { 401 return false; 402 } else { 403 ++values_; 404 } 405 } 406 return folder.isEmpty(); 407 } 408 409 bool DataInitializationCompiler::InitElement( 410 const evaluate::OffsetSymbol &offsetSymbol, const SomeExpr &designator) { 411 const Symbol &symbol{offsetSymbol.symbol()}; 412 const Symbol *lastSymbol{GetLastSymbol(designator)}; 413 bool isPointer{lastSymbol && IsPointer(*lastSymbol)}; 414 bool isProcPointer{lastSymbol && IsProcedurePointer(*lastSymbol)}; 415 evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; 416 417 const auto DescribeElement{[&]() { 418 if (auto badDesignator{ 419 evaluate::OffsetToDesignator(context, offsetSymbol)}) { 420 return badDesignator->AsFortran(); 421 } else { 422 // Error recovery 423 std::string buf; 424 llvm::raw_string_ostream ss{buf}; 425 ss << offsetSymbol.symbol().name() << " offset " << offsetSymbol.offset() 426 << " bytes for " << offsetSymbol.size() << " bytes"; 427 return ss.str(); 428 } 429 }}; 430 const auto GetImage{[&]() -> evaluate::InitialImage & { 431 auto &symbolInit{inits_.emplace(symbol, symbol.size()).first->second}; 432 symbolInit.inits.emplace_back(offsetSymbol.offset(), offsetSymbol.size()); 433 return symbolInit.image; 434 }}; 435 const auto OutOfRangeError{[&]() { 436 evaluate::AttachDeclaration( 437 exprAnalyzer_.context().Say( 438 "DATA statement designator '%s' is out of range for its variable '%s'"_err_en_US, 439 DescribeElement(), symbol.name()), 440 symbol); 441 }}; 442 443 if (values_.hasFatalError()) { 444 return false; 445 } else if (values_.IsAtEnd()) { 446 exprAnalyzer_.context().Say( 447 "DATA statement set has no value for '%s'"_err_en_US, 448 DescribeElement()); 449 return false; 450 } else if (static_cast<std::size_t>( 451 offsetSymbol.offset() + offsetSymbol.size()) > symbol.size()) { 452 OutOfRangeError(); 453 return false; 454 } 455 456 const SomeExpr *expr{*values_}; 457 if (!expr) { 458 CHECK(exprAnalyzer_.context().AnyFatalError()); 459 } else if (isPointer) { 460 if (static_cast<std::size_t>(offsetSymbol.offset() + offsetSymbol.size()) > 461 symbol.size()) { 462 OutOfRangeError(); 463 } else if (evaluate::IsNullPointer(*expr)) { 464 // nothing to do; rely on zero initialization 465 return true; 466 } else if (evaluate::IsProcedure(*expr)) { 467 if (isProcPointer) { 468 if (CheckPointerAssignment(context, designator, *expr)) { 469 GetImage().AddPointer(offsetSymbol.offset(), *expr); 470 return true; 471 } 472 } else { 473 exprAnalyzer_.Say(values_.LocateSource(), 474 "Procedure '%s' may not be used to initialize '%s', which is not a procedure pointer"_err_en_US, 475 expr->AsFortran(), DescribeElement()); 476 } 477 } else if (isProcPointer) { 478 exprAnalyzer_.Say(values_.LocateSource(), 479 "Data object '%s' may not be used to initialize '%s', which is a procedure pointer"_err_en_US, 480 expr->AsFortran(), DescribeElement()); 481 } else if (CheckInitialTarget(context, designator, *expr)) { 482 GetImage().AddPointer(offsetSymbol.offset(), *expr); 483 return true; 484 } 485 } else if (evaluate::IsNullPointer(*expr)) { 486 exprAnalyzer_.Say(values_.LocateSource(), 487 "Initializer for '%s' must not be a pointer"_err_en_US, 488 DescribeElement()); 489 } else if (evaluate::IsProcedure(*expr)) { 490 exprAnalyzer_.Say(values_.LocateSource(), 491 "Initializer for '%s' must not be a procedure"_err_en_US, 492 DescribeElement()); 493 } else if (auto designatorType{designator.GetType()}) { 494 if (auto converted{ 495 evaluate::ConvertToType(*designatorType, SomeExpr{*expr})}) { 496 // value non-pointer initialization 497 if (std::holds_alternative<evaluate::BOZLiteralConstant>(expr->u) && 498 designatorType->category() != TypeCategory::Integer) { // 8.6.7(11) 499 exprAnalyzer_.Say(values_.LocateSource(), 500 "BOZ literal should appear in a DATA statement only as a value for an integer object, but '%s' is '%s'"_en_US, 501 DescribeElement(), designatorType->AsFortran()); 502 } 503 auto folded{evaluate::Fold(context, std::move(*converted))}; 504 switch ( 505 GetImage().Add(offsetSymbol.offset(), offsetSymbol.size(), folded)) { 506 case evaluate::InitialImage::Ok: 507 return true; 508 case evaluate::InitialImage::NotAConstant: 509 exprAnalyzer_.Say(values_.LocateSource(), 510 "DATA statement value '%s' for '%s' is not a constant"_err_en_US, 511 folded.AsFortran(), DescribeElement()); 512 break; 513 case evaluate::InitialImage::OutOfRange: 514 OutOfRangeError(); 515 break; 516 default: 517 CHECK(exprAnalyzer_.context().AnyFatalError()); 518 break; 519 } 520 } else { 521 exprAnalyzer_.context().Say( 522 "DATA statement value could not be converted to the type '%s' of the object '%s'"_err_en_US, 523 designatorType->AsFortran(), DescribeElement()); 524 } 525 } else { 526 CHECK(exprAnalyzer_.context().AnyFatalError()); 527 } 528 return false; 529 } 530 531 void DataChecker::Leave(const parser::DataStmtSet &set) { 532 if (!currentSetHasFatalErrors_) { 533 DataInitializationCompiler scanner{inits_, exprAnalyzer_, set}; 534 for (const auto &object : 535 std::get<std::list<parser::DataStmtObject>>(set.t)) { 536 if (!scanner.Scan(object)) { 537 return; 538 } 539 } 540 if (scanner.HasSurplusValues()) { 541 exprAnalyzer_.context().Say( 542 "DATA statement set has more values than objects"_err_en_US); 543 } 544 } 545 currentSetHasFatalErrors_ = false; 546 } 547 548 // Converts the initialization image for all the DATA statement appearances of 549 // a single symbol into an init() expression in the symbol table entry. 550 void DataChecker::ConstructInitializer( 551 const Symbol &symbol, SymbolDataInitialization &initialization) { 552 auto &context{exprAnalyzer_.GetFoldingContext()}; 553 initialization.inits.sort(); 554 ConstantSubscript next{0}; 555 for (const auto &init : initialization.inits) { 556 if (init.start() < next) { 557 auto badDesignator{evaluate::OffsetToDesignator( 558 context, symbol, init.start(), init.size())}; 559 CHECK(badDesignator); 560 exprAnalyzer_.Say(symbol.name(), 561 "DATA statement initializations affect '%s' more than once"_err_en_US, 562 badDesignator->AsFortran()); 563 } 564 next = init.start() + init.size(); 565 CHECK(next <= static_cast<ConstantSubscript>(initialization.image.size())); 566 } 567 if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { 568 CHECK(IsProcedurePointer(symbol)); 569 const auto &procDesignator{initialization.image.AsConstantProcPointer()}; 570 CHECK(!procDesignator.GetComponent()); 571 auto &mutableProc{const_cast<ProcEntityDetails &>(*proc)}; 572 mutableProc.set_init(DEREF(procDesignator.GetSymbol())); 573 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 574 if (auto symbolType{evaluate::DynamicType::From(symbol)}) { 575 auto &mutableObject{const_cast<ObjectEntityDetails &>(*object)}; 576 if (IsPointer(symbol)) { 577 mutableObject.set_init( 578 initialization.image.AsConstantDataPointer(*symbolType)); 579 mutableObject.set_initWasValidated(); 580 } else { 581 if (auto extents{evaluate::GetConstantExtents(context, symbol)}) { 582 mutableObject.set_init( 583 initialization.image.AsConstant(context, *symbolType, *extents)); 584 mutableObject.set_initWasValidated(); 585 } else { 586 exprAnalyzer_.Say(symbol.name(), 587 "internal: unknown shape for '%s' while constructing initializer from DATA"_err_en_US, 588 symbol.name()); 589 return; 590 } 591 } 592 } else { 593 exprAnalyzer_.Say(symbol.name(), 594 "internal: no type for '%s' while constructing initializer from DATA"_err_en_US, 595 symbol.name()); 596 return; 597 } 598 if (!object->init()) { 599 exprAnalyzer_.Say(symbol.name(), 600 "internal: could not construct an initializer from DATA statements for '%s'"_err_en_US, 601 symbol.name()); 602 } 603 } else { 604 CHECK(exprAnalyzer_.context().AnyFatalError()); 605 } 606 } 607 608 void DataChecker::CompileDataInitializationsIntoInitializers() { 609 for (auto &[symbolRef, initialization] : inits_) { 610 ConstructInitializer(*symbolRef, initialization); 611 } 612 } 613 614 } // namespace Fortran::semantics 615