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/Semantics/tools.h" 21 22 namespace Fortran::semantics { 23 24 // Steps through a list of values in a DATA statement set; implements 25 // repetition. 26 class ValueListIterator { 27 public: 28 explicit ValueListIterator(const parser::DataStmtSet &set) 29 : end_{std::get<std::list<parser::DataStmtValue>>(set.t).end()}, 30 at_{std::get<std::list<parser::DataStmtValue>>(set.t).begin()} { 31 SetRepetitionCount(); 32 } 33 bool hasFatalError() const { return hasFatalError_; } 34 bool IsAtEnd() const { return at_ == end_; } 35 const SomeExpr *operator*() const { return GetExpr(GetConstant()); } 36 parser::CharBlock LocateSource() const { return GetConstant().source; } 37 ValueListIterator &operator++() { 38 if (repetitionsRemaining_ > 0) { 39 --repetitionsRemaining_; 40 } else if (at_ != end_) { 41 ++at_; 42 SetRepetitionCount(); 43 } 44 return *this; 45 } 46 47 private: 48 using listIterator = std::list<parser::DataStmtValue>::const_iterator; 49 void SetRepetitionCount(); 50 const parser::DataStmtConstant &GetConstant() const { 51 return std::get<parser::DataStmtConstant>(at_->t); 52 } 53 54 listIterator end_; 55 listIterator at_; 56 ConstantSubscript repetitionsRemaining_{0}; 57 bool hasFatalError_{false}; 58 }; 59 60 void ValueListIterator::SetRepetitionCount() { 61 for (repetitionsRemaining_ = 1; at_ != end_; ++at_) { 62 if (at_->repetitions < 0) { 63 hasFatalError_ = true; 64 } 65 if (at_->repetitions > 0) { 66 repetitionsRemaining_ = at_->repetitions - 1; 67 return; 68 } 69 } 70 repetitionsRemaining_ = 0; 71 } 72 73 // Collects all of the elemental initializations from DATA statements 74 // into a single image for each symbol that appears in any DATA. 75 // Expands the implied DO loops and array references. 76 // Applies checks that validate each distinct elemental initialization 77 // of the variables in a data-stmt-set, as well as those that apply 78 // to the corresponding values being use to initialize each element. 79 class DataInitializationCompiler { 80 public: 81 DataInitializationCompiler(DataInitializations &inits, 82 evaluate::ExpressionAnalyzer &a, const parser::DataStmtSet &set) 83 : inits_{inits}, exprAnalyzer_{a}, values_{set} {} 84 const DataInitializations &inits() const { return inits_; } 85 bool HasSurplusValues() const { return !values_.IsAtEnd(); } 86 bool Scan(const parser::DataStmtObject &); 87 88 private: 89 bool Scan(const parser::Variable &); 90 bool Scan(const parser::Designator &); 91 bool Scan(const parser::DataImpliedDo &); 92 bool Scan(const parser::DataIDoObject &); 93 94 // Initializes all elements of a designator, which can be an array or section. 95 bool InitDesignator(const SomeExpr &); 96 // Initializes a single object. 97 bool InitElement(const evaluate::OffsetSymbol &, const SomeExpr &designator); 98 // If the returned flag is true, emit a warning about CHARACTER misusage. 99 std::optional<std::pair<SomeExpr, bool>> ConvertElement( 100 const SomeExpr &, const evaluate::DynamicType &); 101 102 DataInitializations &inits_; 103 evaluate::ExpressionAnalyzer &exprAnalyzer_; 104 ValueListIterator values_; 105 }; 106 107 bool DataInitializationCompiler::Scan(const parser::DataStmtObject &object) { 108 return std::visit( 109 common::visitors{ 110 [&](const common::Indirection<parser::Variable> &var) { 111 return Scan(var.value()); 112 }, 113 [&](const parser::DataImpliedDo &ido) { return Scan(ido); }, 114 }, 115 object.u); 116 } 117 118 bool DataInitializationCompiler::Scan(const parser::Variable &var) { 119 if (const auto *expr{GetExpr(var)}) { 120 exprAnalyzer_.GetFoldingContext().messages().SetLocation(var.GetSource()); 121 if (InitDesignator(*expr)) { 122 return true; 123 } 124 } 125 return false; 126 } 127 128 bool DataInitializationCompiler::Scan(const parser::Designator &designator) { 129 if (auto expr{exprAnalyzer_.Analyze(designator)}) { 130 exprAnalyzer_.GetFoldingContext().messages().SetLocation( 131 parser::FindSourceLocation(designator)); 132 if (InitDesignator(*expr)) { 133 return true; 134 } 135 } 136 return false; 137 } 138 139 bool DataInitializationCompiler::Scan(const parser::DataImpliedDo &ido) { 140 const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)}; 141 auto name{bounds.name.thing.thing}; 142 const auto *lowerExpr{GetExpr(bounds.lower.thing.thing)}; 143 const auto *upperExpr{GetExpr(bounds.upper.thing.thing)}; 144 const auto *stepExpr{ 145 bounds.step ? GetExpr(bounds.step->thing.thing) : nullptr}; 146 if (lowerExpr && upperExpr) { 147 auto lower{ToInt64(*lowerExpr)}; 148 auto upper{ToInt64(*upperExpr)}; 149 auto step{stepExpr ? ToInt64(*stepExpr) : std::nullopt}; 150 auto stepVal{step.value_or(1)}; 151 if (stepVal == 0) { 152 exprAnalyzer_.Say(name.source, 153 "DATA statement implied DO loop has a step value of zero"_err_en_US); 154 } else if (lower && upper) { 155 int kind{evaluate::ResultType<evaluate::ImpliedDoIndex>::kind}; 156 if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) { 157 if (dynamicType->category() == TypeCategory::Integer) { 158 kind = dynamicType->kind(); 159 } 160 } 161 if (exprAnalyzer_.AddImpliedDo(name.source, kind)) { 162 auto &value{exprAnalyzer_.GetFoldingContext().StartImpliedDo( 163 name.source, *lower)}; 164 bool result{true}; 165 for (auto n{(*upper - value + stepVal) / stepVal}; n > 0; 166 --n, value += stepVal) { 167 for (const auto &object : 168 std::get<std::list<parser::DataIDoObject>>(ido.t)) { 169 if (!Scan(object)) { 170 result = false; 171 break; 172 } 173 } 174 } 175 exprAnalyzer_.GetFoldingContext().EndImpliedDo(name.source); 176 exprAnalyzer_.RemoveImpliedDo(name.source); 177 return result; 178 } 179 } 180 } 181 return false; 182 } 183 184 bool DataInitializationCompiler::Scan(const parser::DataIDoObject &object) { 185 return std::visit( 186 common::visitors{ 187 [&](const parser::Scalar<common::Indirection<parser::Designator>> 188 &var) { return Scan(var.thing.value()); }, 189 [&](const common::Indirection<parser::DataImpliedDo> &ido) { 190 return Scan(ido.value()); 191 }, 192 }, 193 object.u); 194 } 195 196 bool DataInitializationCompiler::InitDesignator(const SomeExpr &designator) { 197 evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; 198 evaluate::DesignatorFolder folder{context}; 199 while (auto offsetSymbol{folder.FoldDesignator(designator)}) { 200 if (folder.isOutOfRange()) { 201 if (auto bad{evaluate::OffsetToDesignator(context, *offsetSymbol)}) { 202 exprAnalyzer_.context().Say( 203 "DATA statement designator '%s' is out of range"_err_en_US, 204 bad->AsFortran()); 205 } else { 206 exprAnalyzer_.context().Say( 207 "DATA statement designator '%s' is out of range"_err_en_US, 208 designator.AsFortran()); 209 } 210 return false; 211 } else if (!InitElement(*offsetSymbol, designator)) { 212 return false; 213 } else { 214 ++values_; 215 } 216 } 217 return folder.isEmpty(); 218 } 219 220 std::optional<std::pair<SomeExpr, bool>> 221 DataInitializationCompiler::ConvertElement( 222 const SomeExpr &expr, const evaluate::DynamicType &type) { 223 if (auto converted{evaluate::ConvertToType(type, SomeExpr{expr})}) { 224 return {std::make_pair(std::move(*converted), false)}; 225 } 226 if (std::optional<std::string> chValue{evaluate::GetScalarConstantValue< 227 evaluate::Type<TypeCategory::Character, 1>>(expr)}) { 228 // Allow DATA initialization with Hollerith and kind=1 CHARACTER like 229 // (most) other Fortran compilers do. Pad on the right with spaces 230 // when short, truncate the right if long. 231 // TODO: big-endian targets 232 auto bytes{static_cast<std::size_t>(evaluate::ToInt64( 233 type.MeasureSizeInBytes(exprAnalyzer_.GetFoldingContext(), false)) 234 .value())}; 235 evaluate::BOZLiteralConstant bits{0}; 236 for (std::size_t j{0}; j < bytes; ++j) { 237 char ch{j >= chValue->size() ? ' ' : chValue->at(j)}; 238 evaluate::BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)}; 239 bits = bits.IOR(chBOZ.SHIFTL(8 * j)); 240 } 241 if (auto converted{evaluate::ConvertToType(type, SomeExpr{bits})}) { 242 return {std::make_pair(std::move(*converted), true)}; 243 } 244 } 245 return std::nullopt; 246 } 247 248 bool DataInitializationCompiler::InitElement( 249 const evaluate::OffsetSymbol &offsetSymbol, const SomeExpr &designator) { 250 const Symbol &symbol{offsetSymbol.symbol()}; 251 const Symbol *lastSymbol{GetLastSymbol(designator)}; 252 bool isPointer{lastSymbol && IsPointer(*lastSymbol)}; 253 bool isProcPointer{lastSymbol && IsProcedurePointer(*lastSymbol)}; 254 evaluate::FoldingContext &context{exprAnalyzer_.GetFoldingContext()}; 255 auto restorer{context.messages().SetLocation(values_.LocateSource())}; 256 257 const auto DescribeElement{[&]() { 258 if (auto badDesignator{ 259 evaluate::OffsetToDesignator(context, offsetSymbol)}) { 260 return badDesignator->AsFortran(); 261 } else { 262 // Error recovery 263 std::string buf; 264 llvm::raw_string_ostream ss{buf}; 265 ss << offsetSymbol.symbol().name() << " offset " << offsetSymbol.offset() 266 << " bytes for " << offsetSymbol.size() << " bytes"; 267 return ss.str(); 268 } 269 }}; 270 const auto GetImage{[&]() -> evaluate::InitialImage & { 271 auto &symbolInit{inits_.emplace(&symbol, symbol.size()).first->second}; 272 symbolInit.inits.emplace_back(offsetSymbol.offset(), offsetSymbol.size()); 273 return symbolInit.image; 274 }}; 275 const auto OutOfRangeError{[&]() { 276 evaluate::AttachDeclaration( 277 exprAnalyzer_.context().Say( 278 "DATA statement designator '%s' is out of range for its variable '%s'"_err_en_US, 279 DescribeElement(), symbol.name()), 280 symbol); 281 }}; 282 283 if (values_.hasFatalError()) { 284 return false; 285 } else if (values_.IsAtEnd()) { 286 exprAnalyzer_.context().Say( 287 "DATA statement set has no value for '%s'"_err_en_US, 288 DescribeElement()); 289 return false; 290 } else if (static_cast<std::size_t>( 291 offsetSymbol.offset() + offsetSymbol.size()) > symbol.size()) { 292 OutOfRangeError(); 293 return false; 294 } 295 296 const SomeExpr *expr{*values_}; 297 if (!expr) { 298 CHECK(exprAnalyzer_.context().AnyFatalError()); 299 } else if (isPointer) { 300 if (static_cast<std::size_t>(offsetSymbol.offset() + offsetSymbol.size()) > 301 symbol.size()) { 302 OutOfRangeError(); 303 } else if (evaluate::IsNullPointer(*expr)) { 304 // nothing to do; rely on zero initialization 305 return true; 306 } else if (isProcPointer) { 307 if (evaluate::IsProcedure(*expr)) { 308 if (CheckPointerAssignment(context, designator, *expr)) { 309 GetImage().AddPointer(offsetSymbol.offset(), *expr); 310 return true; 311 } 312 } else { 313 exprAnalyzer_.Say( 314 "Data object '%s' may not be used to initialize '%s', which is a procedure pointer"_err_en_US, 315 expr->AsFortran(), DescribeElement()); 316 } 317 } else if (evaluate::IsProcedure(*expr)) { 318 exprAnalyzer_.Say( 319 "Procedure '%s' may not be used to initialize '%s', which is not a procedure pointer"_err_en_US, 320 expr->AsFortran(), DescribeElement()); 321 } else if (CheckInitialTarget(context, designator, *expr)) { 322 GetImage().AddPointer(offsetSymbol.offset(), *expr); 323 return true; 324 } 325 } else if (evaluate::IsNullPointer(*expr)) { 326 exprAnalyzer_.Say("Initializer for '%s' must not be a pointer"_err_en_US, 327 DescribeElement()); 328 } else if (evaluate::IsProcedure(*expr)) { 329 exprAnalyzer_.Say("Initializer for '%s' must not be a procedure"_err_en_US, 330 DescribeElement()); 331 } else if (auto designatorType{designator.GetType()}) { 332 if (expr->Rank() > 0) { 333 // Because initial-data-target is ambiguous with scalar-constant and 334 // scalar-constant-subobject at parse time, enforcement of scalar-* 335 // must be deferred to here. 336 exprAnalyzer_.Say( 337 "DATA statement value initializes '%s' with an array"_err_en_US, 338 DescribeElement()); 339 } else if (auto converted{ConvertElement(*expr, *designatorType)}) { 340 // value non-pointer initialization 341 if (std::holds_alternative<evaluate::BOZLiteralConstant>(expr->u) && 342 designatorType->category() != TypeCategory::Integer) { // 8.6.7(11) 343 exprAnalyzer_.Say( 344 "BOZ literal should appear in a DATA statement only as a value for an integer object, but '%s' is '%s'"_en_US, 345 DescribeElement(), designatorType->AsFortran()); 346 } else if (converted->second) { 347 exprAnalyzer_.context().Say( 348 "DATA statement value initializes '%s' of type '%s' with CHARACTER"_en_US, 349 DescribeElement(), designatorType->AsFortran()); 350 } 351 auto folded{evaluate::Fold(context, std::move(converted->first))}; 352 switch (GetImage().Add( 353 offsetSymbol.offset(), offsetSymbol.size(), folded, context)) { 354 case evaluate::InitialImage::Ok: 355 return true; 356 case evaluate::InitialImage::NotAConstant: 357 exprAnalyzer_.Say( 358 "DATA statement value '%s' for '%s' is not a constant"_err_en_US, 359 folded.AsFortran(), DescribeElement()); 360 break; 361 case evaluate::InitialImage::OutOfRange: 362 OutOfRangeError(); 363 break; 364 default: 365 CHECK(exprAnalyzer_.context().AnyFatalError()); 366 break; 367 } 368 } else { 369 exprAnalyzer_.context().Say( 370 "DATA statement value could not be converted to the type '%s' of the object '%s'"_err_en_US, 371 designatorType->AsFortran(), DescribeElement()); 372 } 373 } else { 374 CHECK(exprAnalyzer_.context().AnyFatalError()); 375 } 376 return false; 377 } 378 379 void AccumulateDataInitializations(DataInitializations &inits, 380 evaluate::ExpressionAnalyzer &exprAnalyzer, 381 const parser::DataStmtSet &set) { 382 DataInitializationCompiler scanner{inits, exprAnalyzer, set}; 383 for (const auto &object : 384 std::get<std::list<parser::DataStmtObject>>(set.t)) { 385 if (!scanner.Scan(object)) { 386 return; 387 } 388 } 389 if (scanner.HasSurplusValues()) { 390 exprAnalyzer.context().Say( 391 "DATA statement set has more values than objects"_err_en_US); 392 } 393 } 394 395 static bool CombineSomeEquivalencedInits( 396 DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) { 397 auto end{inits.end()}; 398 for (auto iter{inits.begin()}; iter != end; ++iter) { 399 const Symbol &symbol{*iter->first}; 400 Scope &scope{const_cast<Scope &>(symbol.owner())}; 401 if (scope.equivalenceSets().empty()) { 402 continue; // no problem to solve here 403 } 404 const auto *commonBlock{FindCommonBlockContaining(symbol)}; 405 // Sweep following DATA initializations in search of overlapping 406 // objects, accumulating into a vector; iterate to a fixed point. 407 std::vector<const Symbol *> conflicts; 408 auto minStart{symbol.offset()}; 409 auto maxEnd{symbol.offset() + symbol.size()}; 410 std::size_t minElementBytes{1}; 411 while (true) { 412 auto prevCount{conflicts.size()}; 413 conflicts.clear(); 414 for (auto scan{iter}; ++scan != end;) { 415 const Symbol &other{*scan->first}; 416 const Scope &otherScope{other.owner()}; 417 if (&otherScope == &scope && 418 FindCommonBlockContaining(other) == commonBlock && 419 maxEnd > other.offset() && 420 other.offset() + other.size() > minStart) { 421 // "other" conflicts with "symbol" or another conflict 422 conflicts.push_back(&other); 423 minStart = std::min(minStart, other.offset()); 424 maxEnd = std::max(maxEnd, other.offset() + other.size()); 425 } 426 } 427 if (conflicts.size() == prevCount) { 428 break; 429 } 430 } 431 if (conflicts.empty()) { 432 continue; 433 } 434 // Compute the minimum common granularity 435 if (auto dyType{evaluate::DynamicType::From(symbol)}) { 436 minElementBytes = evaluate::ToInt64( 437 dyType->MeasureSizeInBytes(exprAnalyzer.GetFoldingContext(), true)) 438 .value_or(1); 439 } 440 for (const Symbol *s : conflicts) { 441 if (auto dyType{evaluate::DynamicType::From(*s)}) { 442 minElementBytes = std::min<std::size_t>(minElementBytes, 443 evaluate::ToInt64(dyType->MeasureSizeInBytes( 444 exprAnalyzer.GetFoldingContext(), true)) 445 .value_or(1)); 446 } else { 447 minElementBytes = 1; 448 } 449 } 450 CHECK(minElementBytes > 0); 451 CHECK((minElementBytes & (minElementBytes - 1)) == 0); 452 auto bytes{static_cast<common::ConstantSubscript>(maxEnd - minStart)}; 453 CHECK(bytes % minElementBytes == 0); 454 const DeclTypeSpec &typeSpec{scope.MakeNumericType( 455 TypeCategory::Integer, KindExpr{minElementBytes})}; 456 // Combine "symbol" and "conflicts[]" into a compiler array temp 457 // that overlaps all of them, and merge their initial values into 458 // the temp's initializer. 459 SourceName name{exprAnalyzer.context().GetTempName(scope)}; 460 auto emplaced{ 461 scope.try_emplace(name, Attrs{Attr::SAVE}, ObjectEntityDetails{})}; 462 CHECK(emplaced.second); 463 Symbol &combinedSymbol{*emplaced.first->second}; 464 auto &details{combinedSymbol.get<ObjectEntityDetails>()}; 465 combinedSymbol.set_offset(minStart); 466 combinedSymbol.set_size(bytes); 467 details.set_type(typeSpec); 468 ArraySpec arraySpec; 469 arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{ 470 bytes / static_cast<common::ConstantSubscript>(minElementBytes)})); 471 details.set_shape(arraySpec); 472 if (commonBlock) { 473 details.set_commonBlock(*commonBlock); 474 } 475 // Merge these EQUIVALENCE'd DATA initializations, and remove the 476 // original initializations from the map. 477 auto combinedInit{ 478 inits.emplace(&combinedSymbol, static_cast<std::size_t>(bytes))}; 479 evaluate::InitialImage &combined{combinedInit.first->second.image}; 480 combined.Incorporate(symbol.offset() - minStart, iter->second.image); 481 inits.erase(iter); 482 for (const Symbol *s : conflicts) { 483 auto sIter{inits.find(s)}; 484 CHECK(sIter != inits.end()); 485 combined.Incorporate(s->offset() - minStart, sIter->second.image); 486 inits.erase(sIter); 487 } 488 return true; // got one 489 } 490 return false; // no remaining EQUIVALENCE'd DATA initializations 491 } 492 493 // Converts the initialization image for all the DATA statement appearances of 494 // a single symbol into an init() expression in the symbol table entry. 495 void ConstructInitializer(const Symbol &symbol, 496 SymbolDataInitialization &initialization, 497 evaluate::ExpressionAnalyzer &exprAnalyzer) { 498 auto &context{exprAnalyzer.GetFoldingContext()}; 499 initialization.inits.sort(); 500 ConstantSubscript next{0}; 501 for (const auto &init : initialization.inits) { 502 if (init.start() < next) { 503 auto badDesignator{evaluate::OffsetToDesignator( 504 context, symbol, init.start(), init.size())}; 505 CHECK(badDesignator); 506 exprAnalyzer.Say(symbol.name(), 507 "DATA statement initializations affect '%s' more than once"_err_en_US, 508 badDesignator->AsFortran()); 509 } 510 next = init.start() + init.size(); 511 CHECK(next <= static_cast<ConstantSubscript>(initialization.image.size())); 512 } 513 if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { 514 CHECK(IsProcedurePointer(symbol)); 515 const auto &procDesignator{initialization.image.AsConstantProcPointer()}; 516 CHECK(!procDesignator.GetComponent()); 517 auto &mutableProc{const_cast<ProcEntityDetails &>(*proc)}; 518 mutableProc.set_init(DEREF(procDesignator.GetSymbol())); 519 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 520 if (auto symbolType{evaluate::DynamicType::From(symbol)}) { 521 auto &mutableObject{const_cast<ObjectEntityDetails &>(*object)}; 522 if (IsPointer(symbol)) { 523 mutableObject.set_init( 524 initialization.image.AsConstantDataPointer(*symbolType)); 525 } else { 526 if (auto extents{evaluate::GetConstantExtents(context, symbol)}) { 527 mutableObject.set_init( 528 initialization.image.AsConstant(context, *symbolType, *extents)); 529 } else { 530 exprAnalyzer.Say(symbol.name(), 531 "internal: unknown shape for '%s' while constructing initializer from DATA"_err_en_US, 532 symbol.name()); 533 return; 534 } 535 } 536 } else { 537 exprAnalyzer.Say(symbol.name(), 538 "internal: no type for '%s' while constructing initializer from DATA"_err_en_US, 539 symbol.name()); 540 return; 541 } 542 if (!object->init()) { 543 exprAnalyzer.Say(symbol.name(), 544 "internal: could not construct an initializer from DATA statements for '%s'"_err_en_US, 545 symbol.name()); 546 } 547 } else { 548 CHECK(exprAnalyzer.context().AnyFatalError()); 549 } 550 } 551 552 void ConvertToInitializers( 553 DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) { 554 while (CombineSomeEquivalencedInits(inits, exprAnalyzer)) { 555 } 556 for (auto &[symbolPtr, initialization] : inits) { 557 ConstructInitializer(*symbolPtr, initialization, exprAnalyzer); 558 } 559 } 560 } // namespace Fortran::semantics 561