1 //===-- lib/Semantics/data-to-inits.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 object/value checking and conversion to static 10 // initializers 11 // - Applies specific checks to each scalar element initialization with a 12 // constant value or pointer target with class DataInitializationCompiler; 13 // - Collects the elemental initializations for each symbol and converts them 14 // into a single init() expression with member function 15 // DataChecker::ConstructInitializer(). 16 17 #include "data-to-inits.h" 18 #include "pointer-assignment.h" 19 #include "flang/Evaluate/fold-designator.h" 20 #include "flang/Evaluate/tools.h" 21 #include "flang/Semantics/tools.h" 22 23 // The job of generating explicit static initializers for objects that don't 24 // have them in order to implement default component initialization is now being 25 // done in lowering, so don't do it here in semantics; but the code remains here 26 // in case we change our minds. 27 static constexpr bool makeDefaultInitializationExplicit{false}; 28 29 // Whether to delete the original "init()" initializers from storage-associated 30 // objects and pointers. 31 static constexpr bool removeOriginalInits{false}; 32 33 namespace Fortran::semantics { 34 35 // Steps through a list of values in a DATA statement set; implements 36 // repetition. 37 class ValueListIterator { 38 public: 39 explicit ValueListIterator(const parser::DataStmtSet &set) 40 : end_{std::get<std::list<parser::DataStmtValue>>(set.t).end()}, 41 at_{std::get<std::list<parser::DataStmtValue>>(set.t).begin()} { 42 SetRepetitionCount(); 43 } 44 bool hasFatalError() const { return hasFatalError_; } 45 bool IsAtEnd() const { return at_ == end_; } 46 const SomeExpr *operator*() const { return GetExpr(GetConstant()); } 47 parser::CharBlock LocateSource() const { return GetConstant().source; } 48 ValueListIterator &operator++() { 49 if (repetitionsRemaining_ > 0) { 50 --repetitionsRemaining_; 51 } else if (at_ != end_) { 52 ++at_; 53 SetRepetitionCount(); 54 } 55 return *this; 56 } 57 58 private: 59 using listIterator = std::list<parser::DataStmtValue>::const_iterator; 60 void SetRepetitionCount(); 61 const parser::DataStmtConstant &GetConstant() const { 62 return std::get<parser::DataStmtConstant>(at_->t); 63 } 64 65 listIterator end_; 66 listIterator at_; 67 ConstantSubscript repetitionsRemaining_{0}; 68 bool hasFatalError_{false}; 69 }; 70 71 void ValueListIterator::SetRepetitionCount() { 72 for (repetitionsRemaining_ = 1; at_ != end_; ++at_) { 73 if (at_->repetitions < 0) { 74 hasFatalError_ = true; 75 } 76 if (at_->repetitions > 0) { 77 repetitionsRemaining_ = at_->repetitions - 1; 78 return; 79 } 80 } 81 repetitionsRemaining_ = 0; 82 } 83 84 // Collects all of the elemental initializations from DATA statements 85 // into a single image for each symbol that appears in any DATA. 86 // Expands the implied DO loops and array references. 87 // Applies checks that validate each distinct elemental initialization 88 // of the variables in a data-stmt-set, as well as those that apply 89 // to the corresponding values being use to initialize each element. 90 class DataInitializationCompiler { 91 public: 92 DataInitializationCompiler(DataInitializations &inits, 93 evaluate::ExpressionAnalyzer &a, const parser::DataStmtSet &set) 94 : inits_{inits}, exprAnalyzer_{a}, values_{set} {} 95 const DataInitializations &inits() const { return inits_; } 96 bool HasSurplusValues() const { return !values_.IsAtEnd(); } 97 bool Scan(const parser::DataStmtObject &); 98 99 private: 100 bool Scan(const parser::Variable &); 101 bool Scan(const parser::Designator &); 102 bool Scan(const parser::DataImpliedDo &); 103 bool Scan(const parser::DataIDoObject &); 104 105 // Initializes all elements of a designator, which can be an array or section. 106 bool InitDesignator(const SomeExpr &); 107 // Initializes a single object. 108 bool InitElement(const evaluate::OffsetSymbol &, const SomeExpr &designator); 109 // If the returned flag is true, emit a warning about CHARACTER misusage. 110 std::optional<std::pair<SomeExpr, bool>> ConvertElement( 111 const SomeExpr &, const evaluate::DynamicType &); 112 113 DataInitializations &inits_; 114 evaluate::ExpressionAnalyzer &exprAnalyzer_; 115 ValueListIterator values_; 116 }; 117 118 bool DataInitializationCompiler::Scan(const parser::DataStmtObject &object) { 119 return std::visit( 120 common::visitors{ 121 [&](const common::Indirection<parser::Variable> &var) { 122 return Scan(var.value()); 123 }, 124 [&](const parser::DataImpliedDo &ido) { return Scan(ido); }, 125 }, 126 object.u); 127 } 128 129 bool DataInitializationCompiler::Scan(const parser::Variable &var) { 130 if (const auto *expr{GetExpr(var)}) { 131 exprAnalyzer_.GetFoldingContext().messages().SetLocation(var.GetSource()); 132 if (InitDesignator(*expr)) { 133 return true; 134 } 135 } 136 return false; 137 } 138 139 bool DataInitializationCompiler::Scan(const parser::Designator &designator) { 140 if (auto expr{exprAnalyzer_.Analyze(designator)}) { 141 exprAnalyzer_.GetFoldingContext().messages().SetLocation( 142 parser::FindSourceLocation(designator)); 143 if (InitDesignator(*expr)) { 144 return true; 145 } 146 } 147 return false; 148 } 149 150 bool DataInitializationCompiler::Scan(const parser::DataImpliedDo &ido) { 151 const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)}; 152 auto name{bounds.name.thing.thing}; 153 const auto *lowerExpr{GetExpr(bounds.lower.thing.thing)}; 154 const auto *upperExpr{GetExpr(bounds.upper.thing.thing)}; 155 const auto *stepExpr{ 156 bounds.step ? GetExpr(bounds.step->thing.thing) : nullptr}; 157 if (lowerExpr && upperExpr) { 158 // Fold the bounds expressions (again) in case any of them depend 159 // on outer implied DO loops. 160 evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; 161 std::int64_t stepVal{1}; 162 if (stepExpr) { 163 auto foldedStep{evaluate::Fold(context, SomeExpr{*stepExpr})}; 164 stepVal = ToInt64(foldedStep).value_or(1); 165 if (stepVal == 0) { 166 exprAnalyzer_.Say(name.source, 167 "DATA statement implied DO loop has a step value of zero"_err_en_US); 168 return false; 169 } 170 } 171 auto foldedLower{evaluate::Fold(context, SomeExpr{*lowerExpr})}; 172 auto lower{ToInt64(foldedLower)}; 173 auto foldedUpper{evaluate::Fold(context, SomeExpr{*upperExpr})}; 174 auto upper{ToInt64(foldedUpper)}; 175 if (lower && upper) { 176 int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind}; 177 if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) { 178 if (dynamicType->category() == TypeCategory::Integer) { 179 kind = dynamicType->kind(); 180 } 181 } 182 if (exprAnalyzer_.AddImpliedDo(name.source, kind)) { 183 auto &value{context.StartImpliedDo(name.source, *lower)}; 184 bool result{true}; 185 for (auto n{(*upper - value + stepVal) / stepVal}; n > 0; 186 --n, value += stepVal) { 187 for (const auto &object : 188 std::get<std::list<parser::DataIDoObject>>(ido.t)) { 189 if (!Scan(object)) { 190 result = false; 191 break; 192 } 193 } 194 } 195 context.EndImpliedDo(name.source); 196 exprAnalyzer_.RemoveImpliedDo(name.source); 197 return result; 198 } 199 } 200 } 201 return false; 202 } 203 204 bool DataInitializationCompiler::Scan(const parser::DataIDoObject &object) { 205 return std::visit( 206 common::visitors{ 207 [&](const parser::Scalar<common::Indirection<parser::Designator>> 208 &var) { return Scan(var.thing.value()); }, 209 [&](const common::Indirection<parser::DataImpliedDo> &ido) { 210 return Scan(ido.value()); 211 }, 212 }, 213 object.u); 214 } 215 216 bool DataInitializationCompiler::InitDesignator(const SomeExpr &designator) { 217 evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; 218 evaluate::DesignatorFolder folder{context}; 219 while (auto offsetSymbol{folder.FoldDesignator(designator)}) { 220 if (folder.isOutOfRange()) { 221 if (auto bad{evaluate::OffsetToDesignator(context, *offsetSymbol)}) { 222 exprAnalyzer_.context().Say( 223 "DATA statement designator '%s' is out of range"_err_en_US, 224 bad->AsFortran()); 225 } else { 226 exprAnalyzer_.context().Say( 227 "DATA statement designator '%s' is out of range"_err_en_US, 228 designator.AsFortran()); 229 } 230 return false; 231 } else if (!InitElement(*offsetSymbol, designator)) { 232 return false; 233 } else { 234 ++values_; 235 } 236 } 237 return folder.isEmpty(); 238 } 239 240 std::optional<std::pair<SomeExpr, bool>> 241 DataInitializationCompiler::ConvertElement( 242 const SomeExpr &expr, const evaluate::DynamicType &type) { 243 if (auto converted{evaluate::ConvertToType(type, SomeExpr{expr})}) { 244 return {std::make_pair(std::move(*converted), false)}; 245 } 246 if (std::optional<std::string> chValue{ 247 evaluate::GetScalarConstantValue<evaluate::Ascii>(expr)}) { 248 // Allow DATA initialization with Hollerith and kind=1 CHARACTER like 249 // (most) other Fortran compilers do. Pad on the right with spaces 250 // when short, truncate the right if long. 251 // TODO: big-endian targets 252 auto bytes{static_cast<std::size_t>(evaluate::ToInt64( 253 type.MeasureSizeInBytes(exprAnalyzer_.GetFoldingContext(), false)) 254 .value())}; 255 evaluate::BOZLiteralConstant bits{0}; 256 for (std::size_t j{0}; j < bytes; ++j) { 257 char ch{j >= chValue->size() ? ' ' : chValue->at(j)}; 258 evaluate::BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)}; 259 bits = bits.IOR(chBOZ.SHIFTL(8 * j)); 260 } 261 if (auto converted{evaluate::ConvertToType(type, SomeExpr{bits})}) { 262 return {std::make_pair(std::move(*converted), true)}; 263 } 264 } 265 return std::nullopt; 266 } 267 268 bool DataInitializationCompiler::InitElement( 269 const evaluate::OffsetSymbol &offsetSymbol, const SomeExpr &designator) { 270 const Symbol &symbol{offsetSymbol.symbol()}; 271 const Symbol *lastSymbol{GetLastSymbol(designator)}; 272 bool isPointer{lastSymbol && IsPointer(*lastSymbol)}; 273 bool isProcPointer{lastSymbol && IsProcedurePointer(*lastSymbol)}; 274 evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; 275 auto restorer{context.messages().SetLocation(values_.LocateSource())}; 276 277 const auto DescribeElement{[&]() { 278 if (auto badDesignator{ 279 evaluate::OffsetToDesignator(context, offsetSymbol)}) { 280 return badDesignator->AsFortran(); 281 } else { 282 // Error recovery 283 std::string buf; 284 llvm::raw_string_ostream ss{buf}; 285 ss << offsetSymbol.symbol().name() << " offset " << offsetSymbol.offset() 286 << " bytes for " << offsetSymbol.size() << " bytes"; 287 return ss.str(); 288 } 289 }}; 290 const auto GetImage{[&]() -> evaluate::InitialImage & { 291 auto iter{inits_.emplace(&symbol, symbol.size())}; 292 auto &symbolInit{iter.first->second}; 293 symbolInit.initializedRanges.emplace_back( 294 offsetSymbol.offset(), offsetSymbol.size()); 295 return symbolInit.image; 296 }}; 297 const auto OutOfRangeError{[&]() { 298 evaluate::AttachDeclaration( 299 exprAnalyzer_.context().Say( 300 "DATA statement designator '%s' is out of range for its variable '%s'"_err_en_US, 301 DescribeElement(), symbol.name()), 302 symbol); 303 }}; 304 305 if (values_.hasFatalError()) { 306 return false; 307 } else if (values_.IsAtEnd()) { 308 exprAnalyzer_.context().Say( 309 "DATA statement set has no value for '%s'"_err_en_US, 310 DescribeElement()); 311 return false; 312 } else if (static_cast<std::size_t>( 313 offsetSymbol.offset() + offsetSymbol.size()) > symbol.size()) { 314 OutOfRangeError(); 315 return false; 316 } 317 318 const SomeExpr *expr{*values_}; 319 if (!expr) { 320 CHECK(exprAnalyzer_.context().AnyFatalError()); 321 } else if (isPointer) { 322 if (static_cast<std::size_t>(offsetSymbol.offset() + offsetSymbol.size()) > 323 symbol.size()) { 324 OutOfRangeError(); 325 } else if (evaluate::IsNullPointer(*expr)) { 326 // nothing to do; rely on zero initialization 327 return true; 328 } else if (isProcPointer) { 329 if (evaluate::IsProcedure(*expr)) { 330 if (CheckPointerAssignment(context, designator, *expr)) { 331 GetImage().AddPointer(offsetSymbol.offset(), *expr); 332 return true; 333 } 334 } else { 335 exprAnalyzer_.Say( 336 "Data object '%s' may not be used to initialize '%s', which is a procedure pointer"_err_en_US, 337 expr->AsFortran(), DescribeElement()); 338 } 339 } else if (evaluate::IsProcedure(*expr)) { 340 exprAnalyzer_.Say( 341 "Procedure '%s' may not be used to initialize '%s', which is not a procedure pointer"_err_en_US, 342 expr->AsFortran(), DescribeElement()); 343 } else if (CheckInitialTarget(context, designator, *expr)) { 344 GetImage().AddPointer(offsetSymbol.offset(), *expr); 345 return true; 346 } 347 } else if (evaluate::IsNullPointer(*expr)) { 348 exprAnalyzer_.Say("Initializer for '%s' must not be a pointer"_err_en_US, 349 DescribeElement()); 350 } else if (evaluate::IsProcedure(*expr)) { 351 exprAnalyzer_.Say("Initializer for '%s' must not be a procedure"_err_en_US, 352 DescribeElement()); 353 } else if (auto designatorType{designator.GetType()}) { 354 if (expr->Rank() > 0) { 355 // Because initial-data-target is ambiguous with scalar-constant and 356 // scalar-constant-subobject at parse time, enforcement of scalar-* 357 // must be deferred to here. 358 exprAnalyzer_.Say( 359 "DATA statement value initializes '%s' with an array"_err_en_US, 360 DescribeElement()); 361 } else if (auto converted{ConvertElement(*expr, *designatorType)}) { 362 // value non-pointer initialization 363 if (IsBOZLiteral(*expr) && 364 designatorType->category() != TypeCategory::Integer) { // 8.6.7(11) 365 exprAnalyzer_.Say( 366 "BOZ literal should appear in a DATA statement only as a value for an integer object, but '%s' is '%s'"_en_US, 367 DescribeElement(), designatorType->AsFortran()); 368 } else if (converted->second) { 369 exprAnalyzer_.context().Say( 370 "DATA statement value initializes '%s' of type '%s' with CHARACTER"_en_US, 371 DescribeElement(), designatorType->AsFortran()); 372 } 373 auto folded{evaluate::Fold(context, std::move(converted->first))}; 374 switch (GetImage().Add( 375 offsetSymbol.offset(), offsetSymbol.size(), folded, context)) { 376 case evaluate::InitialImage::Ok: 377 return true; 378 case evaluate::InitialImage::NotAConstant: 379 exprAnalyzer_.Say( 380 "DATA statement value '%s' for '%s' is not a constant"_err_en_US, 381 folded.AsFortran(), DescribeElement()); 382 break; 383 case evaluate::InitialImage::OutOfRange: 384 OutOfRangeError(); 385 break; 386 default: 387 CHECK(exprAnalyzer_.context().AnyFatalError()); 388 break; 389 } 390 } else { 391 exprAnalyzer_.context().Say( 392 "DATA statement value could not be converted to the type '%s' of the object '%s'"_err_en_US, 393 designatorType->AsFortran(), DescribeElement()); 394 } 395 } else { 396 CHECK(exprAnalyzer_.context().AnyFatalError()); 397 } 398 return false; 399 } 400 401 void AccumulateDataInitializations(DataInitializations &inits, 402 evaluate::ExpressionAnalyzer &exprAnalyzer, 403 const parser::DataStmtSet &set) { 404 DataInitializationCompiler scanner{inits, exprAnalyzer, set}; 405 for (const auto &object : 406 std::get<std::list<parser::DataStmtObject>>(set.t)) { 407 if (!scanner.Scan(object)) { 408 return; 409 } 410 } 411 if (scanner.HasSurplusValues()) { 412 exprAnalyzer.context().Say( 413 "DATA statement set has more values than objects"_err_en_US); 414 } 415 } 416 417 // Looks for default derived type component initialization -- but 418 // *not* allocatables. 419 static const DerivedTypeSpec *HasDefaultInitialization(const Symbol &symbol) { 420 if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 421 if (object->init().has_value()) { 422 return nullptr; // init is explicit, not default 423 } else if (!object->isDummy() && object->type()) { 424 if (const DerivedTypeSpec * derived{object->type()->AsDerived()}) { 425 DirectComponentIterator directs{*derived}; 426 if (std::find_if( 427 directs.begin(), directs.end(), [](const Symbol &component) { 428 return !IsAllocatable(component) && 429 HasDeclarationInitializer(component); 430 })) { 431 return derived; 432 } 433 } 434 } 435 } 436 return nullptr; 437 } 438 439 // PopulateWithComponentDefaults() adds initializations to an instance 440 // of SymbolDataInitialization containing all of the default component 441 // initializers 442 443 static void PopulateWithComponentDefaults(SymbolDataInitialization &init, 444 std::size_t offset, const DerivedTypeSpec &derived, 445 evaluate::FoldingContext &foldingContext); 446 447 static void PopulateWithComponentDefaults(SymbolDataInitialization &init, 448 std::size_t offset, const DerivedTypeSpec &derived, 449 evaluate::FoldingContext &foldingContext, const Symbol &symbol) { 450 if (auto extents{evaluate::GetConstantExtents(foldingContext, symbol)}) { 451 const Scope &scope{derived.scope() ? *derived.scope() 452 : DEREF(derived.typeSymbol().scope())}; 453 std::size_t stride{scope.size()}; 454 if (std::size_t alignment{scope.alignment().value_or(0)}) { 455 stride = ((stride + alignment - 1) / alignment) * alignment; 456 } 457 for (auto elements{evaluate::GetSize(*extents)}; elements-- > 0; 458 offset += stride) { 459 PopulateWithComponentDefaults(init, offset, derived, foldingContext); 460 } 461 } 462 } 463 464 // F'2018 19.5.3(10) allows storage-associated default component initialization 465 // when the values are identical. 466 static void PopulateWithComponentDefaults(SymbolDataInitialization &init, 467 std::size_t offset, const DerivedTypeSpec &derived, 468 evaluate::FoldingContext &foldingContext) { 469 const Scope &scope{ 470 derived.scope() ? *derived.scope() : DEREF(derived.typeSymbol().scope())}; 471 for (const auto &pair : scope) { 472 const Symbol &component{*pair.second}; 473 std::size_t componentOffset{offset + component.offset()}; 474 if (const auto *object{component.detailsIf<ObjectEntityDetails>()}) { 475 if (!IsAllocatable(component) && !IsAutomatic(component)) { 476 bool initialized{false}; 477 if (object->init()) { 478 initialized = true; 479 if (IsPointer(component)) { 480 if (auto extant{init.image.AsConstantPointer(componentOffset)}) { 481 initialized = !(*extant == *object->init()); 482 } 483 if (initialized) { 484 init.image.AddPointer(componentOffset, *object->init()); 485 } 486 } else { // data, not pointer 487 if (auto dyType{evaluate::DynamicType::From(component)}) { 488 if (auto extents{evaluate::GetConstantExtents( 489 foldingContext, component)}) { 490 if (auto extant{init.image.AsConstant( 491 foldingContext, *dyType, *extents, componentOffset)}) { 492 initialized = !(*extant == *object->init()); 493 } 494 } 495 } 496 if (initialized) { 497 init.image.Add(componentOffset, component.size(), *object->init(), 498 foldingContext); 499 } 500 } 501 } else if (const DeclTypeSpec * type{component.GetType()}) { 502 if (const DerivedTypeSpec * componentDerived{type->AsDerived()}) { 503 PopulateWithComponentDefaults(init, componentOffset, 504 *componentDerived, foldingContext, component); 505 } 506 } 507 if (initialized) { 508 init.initializedRanges.emplace_back( 509 componentOffset, component.size()); 510 } 511 } 512 } else if (const auto *proc{component.detailsIf<ProcEntityDetails>()}) { 513 if (proc->init() && *proc->init()) { 514 SomeExpr procPtrInit{evaluate::ProcedureDesignator{**proc->init()}}; 515 auto extant{init.image.AsConstantPointer(componentOffset)}; 516 if (!extant || !(*extant == procPtrInit)) { 517 init.initializedRanges.emplace_back( 518 componentOffset, component.size()); 519 init.image.AddPointer(componentOffset, std::move(procPtrInit)); 520 } 521 } 522 } 523 } 524 } 525 526 static bool CheckForOverlappingInitialization( 527 const std::list<SymbolRef> &symbols, 528 SymbolDataInitialization &initialization, 529 evaluate::ExpressionAnalyzer &exprAnalyzer, const std::string &what) { 530 bool result{true}; 531 auto &context{exprAnalyzer.GetFoldingContext()}; 532 initialization.initializedRanges.sort(); 533 ConstantSubscript next{0}; 534 for (const auto &range : initialization.initializedRanges) { 535 if (range.start() < next) { 536 result = false; // error: overlap 537 bool hit{false}; 538 for (const Symbol &symbol : symbols) { 539 auto offset{range.start() - 540 static_cast<ConstantSubscript>( 541 symbol.offset() - symbols.front()->offset())}; 542 if (offset >= 0) { 543 if (auto badDesignator{evaluate::OffsetToDesignator( 544 context, symbol, offset, range.size())}) { 545 hit = true; 546 exprAnalyzer.Say(symbol.name(), 547 "%s affect '%s' more than once"_err_en_US, what, 548 badDesignator->AsFortran()); 549 } 550 } 551 } 552 CHECK(hit); 553 } 554 next = range.start() + range.size(); 555 CHECK(next <= static_cast<ConstantSubscript>(initialization.image.size())); 556 } 557 return result; 558 } 559 560 static void IncorporateExplicitInitialization( 561 SymbolDataInitialization &combined, DataInitializations &inits, 562 const Symbol &symbol, ConstantSubscript firstOffset, 563 evaluate::FoldingContext &foldingContext) { 564 auto iter{inits.find(&symbol)}; 565 const auto offset{symbol.offset() - firstOffset}; 566 if (iter != inits.end()) { // DATA statement initialization 567 for (const auto &range : iter->second.initializedRanges) { 568 auto at{offset + range.start()}; 569 combined.initializedRanges.emplace_back(at, range.size()); 570 combined.image.Incorporate( 571 at, iter->second.image, range.start(), range.size()); 572 } 573 if (removeOriginalInits) { 574 inits.erase(iter); 575 } 576 } else { // Declaration initialization 577 Symbol &mutableSymbol{const_cast<Symbol &>(symbol)}; 578 if (IsPointer(mutableSymbol)) { 579 if (auto *object{mutableSymbol.detailsIf<ObjectEntityDetails>()}) { 580 if (object->init()) { 581 combined.initializedRanges.emplace_back(offset, mutableSymbol.size()); 582 combined.image.AddPointer(offset, *object->init()); 583 if (removeOriginalInits) { 584 object->init().reset(); 585 } 586 } 587 } else if (auto *proc{mutableSymbol.detailsIf<ProcEntityDetails>()}) { 588 if (proc->init() && *proc->init()) { 589 combined.initializedRanges.emplace_back(offset, mutableSymbol.size()); 590 combined.image.AddPointer( 591 offset, SomeExpr{evaluate::ProcedureDesignator{**proc->init()}}); 592 if (removeOriginalInits) { 593 proc->init().reset(); 594 } 595 } 596 } 597 } else if (auto *object{mutableSymbol.detailsIf<ObjectEntityDetails>()}) { 598 if (!IsNamedConstant(mutableSymbol) && object->init()) { 599 combined.initializedRanges.emplace_back(offset, mutableSymbol.size()); 600 combined.image.Add( 601 offset, mutableSymbol.size(), *object->init(), foldingContext); 602 if (removeOriginalInits) { 603 object->init().reset(); 604 } 605 } 606 } 607 } 608 } 609 610 // Finds the size of the smallest element type in a list of 611 // storage-associated objects. 612 static std::size_t ComputeMinElementBytes( 613 const std::list<SymbolRef> &associated, 614 evaluate::FoldingContext &foldingContext) { 615 std::size_t minElementBytes{1}; 616 const Symbol &first{*associated.front()}; 617 for (const Symbol &s : associated) { 618 if (auto dyType{evaluate::DynamicType::From(s)}) { 619 auto size{static_cast<std::size_t>( 620 evaluate::ToInt64(dyType->MeasureSizeInBytes(foldingContext, true)) 621 .value_or(1))}; 622 if (std::size_t alignment{dyType->GetAlignment(foldingContext)}) { 623 size = ((size + alignment - 1) / alignment) * alignment; 624 } 625 if (&s == &first) { 626 minElementBytes = size; 627 } else { 628 minElementBytes = std::min(minElementBytes, size); 629 } 630 } else { 631 minElementBytes = 1; 632 } 633 } 634 return minElementBytes; 635 } 636 637 // Checks for overlapping initialization errors in a list of 638 // storage-associated objects. Default component initializations 639 // are allowed to be overridden by explicit initializations. 640 // If the objects are static, save the combined initializer as 641 // a compiler-created object that covers all of them. 642 static bool CombineEquivalencedInitialization( 643 const std::list<SymbolRef> &associated, 644 evaluate::ExpressionAnalyzer &exprAnalyzer, DataInitializations &inits) { 645 // Compute the minimum common granularity and total size 646 const Symbol &first{*associated.front()}; 647 std::size_t maxLimit{0}; 648 for (const Symbol &s : associated) { 649 CHECK(s.offset() >= first.offset()); 650 auto limit{s.offset() + s.size()}; 651 if (limit > maxLimit) { 652 maxLimit = limit; 653 } 654 } 655 auto bytes{static_cast<common::ConstantSubscript>(maxLimit - first.offset())}; 656 Scope &scope{const_cast<Scope &>(first.owner())}; 657 // Combine the initializations of the associated objects. 658 // Apply all default initializations first. 659 SymbolDataInitialization combined{static_cast<std::size_t>(bytes)}; 660 auto &foldingContext{exprAnalyzer.GetFoldingContext()}; 661 for (const Symbol &s : associated) { 662 if (!IsNamedConstant(s)) { 663 if (const auto *derived{HasDefaultInitialization(s)}) { 664 PopulateWithComponentDefaults( 665 combined, s.offset() - first.offset(), *derived, foldingContext, s); 666 } 667 } 668 } 669 if (!CheckForOverlappingInitialization(associated, combined, exprAnalyzer, 670 "Distinct default component initializations of equivalenced objects"s)) { 671 return false; 672 } 673 // Don't complain about overlap between explicit initializations and 674 // default initializations. 675 combined.initializedRanges.clear(); 676 // Now overlay all explicit initializations from DATA statements and 677 // from initializers in declarations. 678 for (const Symbol &symbol : associated) { 679 IncorporateExplicitInitialization( 680 combined, inits, symbol, first.offset(), foldingContext); 681 } 682 if (!CheckForOverlappingInitialization(associated, combined, exprAnalyzer, 683 "Explicit initializations of equivalenced objects"s)) { 684 return false; 685 } 686 // If the items are in static storage, save the final initialization. 687 if (std::find_if(associated.begin(), associated.end(), 688 [](SymbolRef ref) { return IsSaved(*ref); }) != associated.end()) { 689 // Create a compiler array temp that overlaps all the items. 690 SourceName name{exprAnalyzer.context().GetTempName(scope)}; 691 auto emplaced{ 692 scope.try_emplace(name, Attrs{Attr::SAVE}, ObjectEntityDetails{})}; 693 CHECK(emplaced.second); 694 Symbol &combinedSymbol{*emplaced.first->second}; 695 combinedSymbol.set(Symbol::Flag::CompilerCreated); 696 inits.emplace(&combinedSymbol, std::move(combined)); 697 auto &details{combinedSymbol.get<ObjectEntityDetails>()}; 698 combinedSymbol.set_offset(first.offset()); 699 combinedSymbol.set_size(bytes); 700 std::size_t minElementBytes{ 701 ComputeMinElementBytes(associated, foldingContext)}; 702 if (!evaluate::IsValidKindOfIntrinsicType( 703 TypeCategory::Integer, minElementBytes) || 704 (bytes % minElementBytes) != 0) { 705 minElementBytes = 1; 706 } 707 const DeclTypeSpec &typeSpec{scope.MakeNumericType( 708 TypeCategory::Integer, KindExpr{minElementBytes})}; 709 details.set_type(typeSpec); 710 ArraySpec arraySpec; 711 arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{ 712 bytes / static_cast<common::ConstantSubscript>(minElementBytes)})); 713 details.set_shape(arraySpec); 714 if (const auto *commonBlock{FindCommonBlockContaining(first)}) { 715 details.set_commonBlock(*commonBlock); 716 } 717 // Add an EQUIVALENCE set to the scope so that the new object appears in 718 // the results of GetStorageAssociations(). 719 auto &newSet{scope.equivalenceSets().emplace_back()}; 720 newSet.emplace_back(combinedSymbol); 721 newSet.emplace_back(const_cast<Symbol &>(first)); 722 } 723 return true; 724 } 725 726 // When a statically-allocated derived type variable has no explicit 727 // initialization, but its type has at least one nonallocatable ultimate 728 // component with default initialization, make its initialization explicit. 729 [[maybe_unused]] static void MakeDefaultInitializationExplicit( 730 const Scope &scope, const std::list<std::list<SymbolRef>> &associations, 731 evaluate::FoldingContext &foldingContext, DataInitializations &inits) { 732 UnorderedSymbolSet equivalenced; 733 for (const std::list<SymbolRef> &association : associations) { 734 for (const Symbol &symbol : association) { 735 equivalenced.emplace(symbol); 736 } 737 } 738 for (const auto &pair : scope) { 739 const Symbol &symbol{*pair.second}; 740 if (!symbol.test(Symbol::Flag::InDataStmt) && 741 !HasDeclarationInitializer(symbol) && IsSaved(symbol) && 742 equivalenced.find(symbol) == equivalenced.end()) { 743 // Static object, no local storage association, no explicit initialization 744 if (const DerivedTypeSpec * derived{HasDefaultInitialization(symbol)}) { 745 auto newInitIter{inits.emplace(&symbol, symbol.size())}; 746 CHECK(newInitIter.second); 747 auto &newInit{newInitIter.first->second}; 748 PopulateWithComponentDefaults( 749 newInit, 0, *derived, foldingContext, symbol); 750 } 751 } 752 } 753 } 754 755 // Traverses the Scopes to: 756 // 1) combine initialization of equivalenced objects, & 757 // 2) optionally make initialization explicit for otherwise uninitialized static 758 // objects of derived types with default component initialization 759 // Returns false on error. 760 static bool ProcessScopes(const Scope &scope, 761 evaluate::ExpressionAnalyzer &exprAnalyzer, DataInitializations &inits) { 762 bool result{true}; // no error 763 switch (scope.kind()) { 764 case Scope::Kind::Global: 765 case Scope::Kind::Module: 766 case Scope::Kind::MainProgram: 767 case Scope::Kind::Subprogram: 768 case Scope::Kind::BlockData: 769 case Scope::Kind::Block: { 770 std::list<std::list<SymbolRef>> associations{GetStorageAssociations(scope)}; 771 for (const std::list<SymbolRef> &associated : associations) { 772 if (std::find_if(associated.begin(), associated.end(), [](SymbolRef ref) { 773 return IsInitialized(*ref); 774 }) != associated.end()) { 775 result &= 776 CombineEquivalencedInitialization(associated, exprAnalyzer, inits); 777 } 778 } 779 if constexpr (makeDefaultInitializationExplicit) { 780 MakeDefaultInitializationExplicit( 781 scope, associations, exprAnalyzer.GetFoldingContext(), inits); 782 } 783 for (const Scope &child : scope.children()) { 784 result &= ProcessScopes(child, exprAnalyzer, inits); 785 } 786 } break; 787 default:; 788 } 789 return result; 790 } 791 792 // Converts the static initialization image for a single symbol with 793 // one or more DATA statement appearances. 794 void ConstructInitializer(const Symbol &symbol, 795 SymbolDataInitialization &initialization, 796 evaluate::ExpressionAnalyzer &exprAnalyzer) { 797 std::list<SymbolRef> symbols{symbol}; 798 CheckForOverlappingInitialization( 799 symbols, initialization, exprAnalyzer, "DATA statement initializations"s); 800 auto &context{exprAnalyzer.GetFoldingContext()}; 801 if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { 802 CHECK(IsProcedurePointer(symbol)); 803 auto &mutableProc{const_cast<ProcEntityDetails &>(*proc)}; 804 if (MaybeExpr expr{initialization.image.AsConstantPointer()}) { 805 if (const auto *procDesignator{ 806 std::get_if<evaluate::ProcedureDesignator>(&expr->u)}) { 807 CHECK(!procDesignator->GetComponent()); 808 mutableProc.set_init(DEREF(procDesignator->GetSymbol())); 809 } else { 810 CHECK(evaluate::IsNullPointer(*expr)); 811 mutableProc.set_init(nullptr); 812 } 813 } else { 814 mutableProc.set_init(nullptr); 815 } 816 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 817 auto &mutableObject{const_cast<ObjectEntityDetails &>(*object)}; 818 if (IsPointer(symbol)) { 819 if (auto ptr{initialization.image.AsConstantPointer()}) { 820 mutableObject.set_init(*ptr); 821 } else { 822 mutableObject.set_init(SomeExpr{evaluate::NullPointer{}}); 823 } 824 } else if (auto symbolType{evaluate::DynamicType::From(symbol)}) { 825 if (auto extents{evaluate::GetConstantExtents(context, symbol)}) { 826 mutableObject.set_init( 827 initialization.image.AsConstant(context, *symbolType, *extents)); 828 } else { 829 exprAnalyzer.Say(symbol.name(), 830 "internal: unknown shape for '%s' while constructing initializer from DATA"_err_en_US, 831 symbol.name()); 832 return; 833 } 834 } else { 835 exprAnalyzer.Say(symbol.name(), 836 "internal: no type for '%s' while constructing initializer from DATA"_err_en_US, 837 symbol.name()); 838 return; 839 } 840 if (!object->init()) { 841 exprAnalyzer.Say(symbol.name(), 842 "internal: could not construct an initializer from DATA statements for '%s'"_err_en_US, 843 symbol.name()); 844 } 845 } else { 846 CHECK(exprAnalyzer.context().AnyFatalError()); 847 } 848 } 849 850 void ConvertToInitializers( 851 DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) { 852 if (ProcessScopes( 853 exprAnalyzer.context().globalScope(), exprAnalyzer, inits)) { 854 for (auto &[symbolPtr, initialization] : inits) { 855 ConstructInitializer(*symbolPtr, initialization, exprAnalyzer); 856 } 857 } 858 } 859 } // namespace Fortran::semantics 860