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 std::size_t bytes{static_cast<std::size_t>(evaluate::ToInt64( 233 type.MeasureSizeInBytes(&exprAnalyzer_.GetFoldingContext())) 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 (auto converted{ConvertElement(*expr, *designatorType)}) { 333 // value non-pointer initialization 334 if (std::holds_alternative<evaluate::BOZLiteralConstant>(expr->u) && 335 designatorType->category() != TypeCategory::Integer) { // 8.6.7(11) 336 exprAnalyzer_.Say( 337 "BOZ literal should appear in a DATA statement only as a value for an integer object, but '%s' is '%s'"_en_US, 338 DescribeElement(), designatorType->AsFortran()); 339 } else if (converted->second) { 340 exprAnalyzer_.context().Say( 341 "DATA statement value initializes '%s' of type '%s' with CHARACTER"_en_US, 342 DescribeElement(), designatorType->AsFortran()); 343 } 344 auto folded{evaluate::Fold(context, std::move(converted->first))}; 345 switch ( 346 GetImage().Add(offsetSymbol.offset(), offsetSymbol.size(), folded)) { 347 case evaluate::InitialImage::Ok: 348 return true; 349 case evaluate::InitialImage::NotAConstant: 350 exprAnalyzer_.Say( 351 "DATA statement value '%s' for '%s' is not a constant"_err_en_US, 352 folded.AsFortran(), DescribeElement()); 353 break; 354 case evaluate::InitialImage::OutOfRange: 355 OutOfRangeError(); 356 break; 357 default: 358 CHECK(exprAnalyzer_.context().AnyFatalError()); 359 break; 360 } 361 } else { 362 exprAnalyzer_.context().Say( 363 "DATA statement value could not be converted to the type '%s' of the object '%s'"_err_en_US, 364 designatorType->AsFortran(), DescribeElement()); 365 } 366 } else { 367 CHECK(exprAnalyzer_.context().AnyFatalError()); 368 } 369 return false; 370 } 371 372 void AccumulateDataInitializations(DataInitializations &inits, 373 evaluate::ExpressionAnalyzer &exprAnalyzer, 374 const parser::DataStmtSet &set) { 375 DataInitializationCompiler scanner{inits, exprAnalyzer, set}; 376 for (const auto &object : 377 std::get<std::list<parser::DataStmtObject>>(set.t)) { 378 if (!scanner.Scan(object)) { 379 return; 380 } 381 } 382 if (scanner.HasSurplusValues()) { 383 exprAnalyzer.context().Say( 384 "DATA statement set has more values than objects"_err_en_US); 385 } 386 } 387 388 static bool CombineSomeEquivalencedInits( 389 DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) { 390 auto end{inits.end()}; 391 for (auto iter{inits.begin()}; iter != end; ++iter) { 392 const Symbol &symbol{*iter->first}; 393 Scope &scope{const_cast<Scope &>(symbol.owner())}; 394 if (scope.equivalenceSets().empty()) { 395 continue; // no problem to solve here 396 } 397 const auto *commonBlock{FindCommonBlockContaining(symbol)}; 398 // Sweep following DATA initializations in search of overlapping 399 // objects, accumulating into a vector; iterate to a fixed point. 400 std::vector<const Symbol *> conflicts; 401 auto minStart{symbol.offset()}; 402 auto maxEnd{symbol.offset() + symbol.size()}; 403 std::size_t minElementBytes{1}; 404 while (true) { 405 auto prevCount{conflicts.size()}; 406 conflicts.clear(); 407 for (auto scan{iter}; ++scan != end;) { 408 const Symbol &other{*scan->first}; 409 const Scope &otherScope{other.owner()}; 410 if (&otherScope == &scope && 411 FindCommonBlockContaining(other) == commonBlock && 412 maxEnd > other.offset() && 413 other.offset() + other.size() > minStart) { 414 // "other" conflicts with "symbol" or another conflict 415 conflicts.push_back(&other); 416 minStart = std::min(minStart, other.offset()); 417 maxEnd = std::max(maxEnd, other.offset() + other.size()); 418 } 419 } 420 if (conflicts.size() == prevCount) { 421 break; 422 } 423 } 424 if (conflicts.empty()) { 425 continue; 426 } 427 // Compute the minimum common granularity 428 if (auto dyType{evaluate::DynamicType::From(symbol)}) { 429 minElementBytes = evaluate::ToInt64( 430 dyType->MeasureSizeInBytes(&exprAnalyzer.GetFoldingContext())) 431 .value_or(1); 432 } 433 for (const Symbol *s : conflicts) { 434 if (auto dyType{evaluate::DynamicType::From(*s)}) { 435 minElementBytes = std::min(minElementBytes, 436 static_cast<std::size_t>(evaluate::ToInt64( 437 dyType->MeasureSizeInBytes(&exprAnalyzer.GetFoldingContext())) 438 .value_or(1))); 439 } else { 440 minElementBytes = 1; 441 } 442 } 443 CHECK(minElementBytes > 0); 444 CHECK((minElementBytes & (minElementBytes - 1)) == 0); 445 auto bytes{static_cast<common::ConstantSubscript>(maxEnd - minStart)}; 446 CHECK(bytes % minElementBytes == 0); 447 const DeclTypeSpec &typeSpec{scope.MakeNumericType( 448 TypeCategory::Integer, KindExpr{minElementBytes})}; 449 // Combine "symbol" and "conflicts[]" into a compiler array temp 450 // that overlaps all of them, and merge their initial values into 451 // the temp's initializer. 452 SourceName name{exprAnalyzer.context().GetTempName(scope)}; 453 auto emplaced{ 454 scope.try_emplace(name, Attrs{Attr::SAVE}, ObjectEntityDetails{})}; 455 CHECK(emplaced.second); 456 Symbol &combinedSymbol{*emplaced.first->second}; 457 auto &details{combinedSymbol.get<ObjectEntityDetails>()}; 458 combinedSymbol.set_offset(minStart); 459 combinedSymbol.set_size(bytes); 460 details.set_type(typeSpec); 461 ArraySpec arraySpec; 462 arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{ 463 bytes / static_cast<common::ConstantSubscript>(minElementBytes)})); 464 details.set_shape(arraySpec); 465 if (commonBlock) { 466 details.set_commonBlock(*commonBlock); 467 } 468 // Merge these EQUIVALENCE'd DATA initializations, and remove the 469 // original initializations from the map. 470 auto combinedInit{ 471 inits.emplace(&combinedSymbol, static_cast<std::size_t>(bytes))}; 472 evaluate::InitialImage &combined{combinedInit.first->second.image}; 473 combined.Incorporate(symbol.offset() - minStart, iter->second.image); 474 inits.erase(iter); 475 for (const Symbol *s : conflicts) { 476 auto sIter{inits.find(s)}; 477 CHECK(sIter != inits.end()); 478 combined.Incorporate(s->offset() - minStart, sIter->second.image); 479 inits.erase(sIter); 480 } 481 return true; // got one 482 } 483 return false; // no remaining EQUIVALENCE'd DATA initializations 484 } 485 486 // Converts the initialization image for all the DATA statement appearances of 487 // a single symbol into an init() expression in the symbol table entry. 488 void ConstructInitializer(const Symbol &symbol, 489 SymbolDataInitialization &initialization, 490 evaluate::ExpressionAnalyzer &exprAnalyzer) { 491 auto &context{exprAnalyzer.GetFoldingContext()}; 492 initialization.inits.sort(); 493 ConstantSubscript next{0}; 494 for (const auto &init : initialization.inits) { 495 if (init.start() < next) { 496 auto badDesignator{evaluate::OffsetToDesignator( 497 context, symbol, init.start(), init.size())}; 498 CHECK(badDesignator); 499 exprAnalyzer.Say(symbol.name(), 500 "DATA statement initializations affect '%s' more than once"_err_en_US, 501 badDesignator->AsFortran()); 502 } 503 next = init.start() + init.size(); 504 CHECK(next <= static_cast<ConstantSubscript>(initialization.image.size())); 505 } 506 if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { 507 CHECK(IsProcedurePointer(symbol)); 508 const auto &procDesignator{initialization.image.AsConstantProcPointer()}; 509 CHECK(!procDesignator.GetComponent()); 510 auto &mutableProc{const_cast<ProcEntityDetails &>(*proc)}; 511 mutableProc.set_init(DEREF(procDesignator.GetSymbol())); 512 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 513 if (auto symbolType{evaluate::DynamicType::From(symbol)}) { 514 auto &mutableObject{const_cast<ObjectEntityDetails &>(*object)}; 515 if (IsPointer(symbol)) { 516 mutableObject.set_init( 517 initialization.image.AsConstantDataPointer(*symbolType)); 518 mutableObject.set_initWasValidated(); 519 } else { 520 if (auto extents{evaluate::GetConstantExtents(context, symbol)}) { 521 mutableObject.set_init( 522 initialization.image.AsConstant(context, *symbolType, *extents)); 523 mutableObject.set_initWasValidated(); 524 } else { 525 exprAnalyzer.Say(symbol.name(), 526 "internal: unknown shape for '%s' while constructing initializer from DATA"_err_en_US, 527 symbol.name()); 528 return; 529 } 530 } 531 } else { 532 exprAnalyzer.Say(symbol.name(), 533 "internal: no type for '%s' while constructing initializer from DATA"_err_en_US, 534 symbol.name()); 535 return; 536 } 537 if (!object->init()) { 538 exprAnalyzer.Say(symbol.name(), 539 "internal: could not construct an initializer from DATA statements for '%s'"_err_en_US, 540 symbol.name()); 541 } 542 } else { 543 CHECK(exprAnalyzer.context().AnyFatalError()); 544 } 545 } 546 547 void ConvertToInitializers( 548 DataInitializations &inits, evaluate::ExpressionAnalyzer &exprAnalyzer) { 549 while (CombineSomeEquivalencedInits(inits, exprAnalyzer)) { 550 } 551 for (auto &[symbolPtr, initialization] : inits) { 552 ConstructInitializer(*symbolPtr, initialization, exprAnalyzer); 553 } 554 } 555 } // namespace Fortran::semantics 556