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