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