| 1 | //===-- lib/Evaluate/designate.cpp ------------------------------*- C++ -*-===// |
| 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 | #include "flang/Evaluate/fold-designator.h" |
| 10 | #include "flang/Semantics/tools.h" |
| 11 | |
| 12 | namespace Fortran::evaluate { |
| 13 | |
| 14 | DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(OffsetSymbol) |
| 15 | |
| 16 | std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator( |
| 17 | const Symbol &symbol, ConstantSubscript which) { |
| 18 | if (!getLastComponent_ && IsAllocatableOrPointer(symbol)) { |
| 19 | // A pointer may appear as a DATA statement object if it is the |
| 20 | // rightmost symbol in a designator and has no subscripts. |
| 21 | // An allocatable may appear if its initializer is NULL(). |
| 22 | if (which > 0) { |
| 23 | isEmpty_ = true; |
| 24 | } else { |
| 25 | return OffsetSymbol{symbol, symbol.size()}; |
| 26 | } |
| 27 | } else if (symbol.has<semantics::ObjectEntityDetails>() && |
| 28 | !IsNamedConstant(symbol)) { |
| 29 | if (auto type{DynamicType::From(symbol)}) { |
| 30 | if (auto extents{GetConstantExtents(context_, symbol)}) { |
| 31 | if (auto bytes{ToInt64( |
| 32 | type->MeasureSizeInBytes(context_, GetRank(*extents) > 0))}) { |
| 33 | OffsetSymbol result{symbol, static_cast<std::size_t>(*bytes)}; |
| 34 | if (which < GetSize(*extents)) { |
| 35 | result.Augment(*bytes * which); |
| 36 | return result; |
| 37 | } else { |
| 38 | isEmpty_ = true; |
| 39 | } |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | } |
| 44 | return std::nullopt; |
| 45 | } |
| 46 | |
| 47 | std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator( |
| 48 | const ArrayRef &x, ConstantSubscript which) { |
| 49 | const Symbol &array{x.base().GetLastSymbol()}; |
| 50 | if (auto type{DynamicType::From(array)}) { |
| 51 | if (auto extents{GetConstantExtents(context_, array)}) { |
| 52 | if (auto bytes{ToInt64(type->MeasureSizeInBytes(context_, true))}) { |
| 53 | Shape lbs{GetLBOUNDs(context_, x.base())}; |
| 54 | if (auto lowerBounds{AsConstantExtents(context_, lbs)}) { |
| 55 | std::optional<OffsetSymbol> result; |
| 56 | if (!x.base().IsSymbol() && |
| 57 | x.base().GetComponent().base().Rank() > 0) { |
| 58 | // A(:)%B(1) - apply elementNumber_ to base |
| 59 | result = FoldDesignator(x.base(), which); |
| 60 | which = 0; |
| 61 | } else { // A(1)%B(:) - apply elementNumber_ to subscripts |
| 62 | result = FoldDesignator(x.base(), 0); |
| 63 | } |
| 64 | if (!result) { |
| 65 | return std::nullopt; |
| 66 | } |
| 67 | auto stride{*bytes}; |
| 68 | int dim{0}; |
| 69 | for (const Subscript &subscript : x.subscript()) { |
| 70 | ConstantSubscript lower{lowerBounds->at(dim)}; |
| 71 | ConstantSubscript extent{extents->at(dim)}; |
| 72 | ConstantSubscript upper{lower + extent - 1}; |
| 73 | if (!common::visit( |
| 74 | common::visitors{ |
| 75 | [&](const IndirectSubscriptIntegerExpr &expr) { |
| 76 | auto folded{ |
| 77 | Fold(context_, common::Clone(expr.value()))}; |
| 78 | if (auto value{UnwrapConstantValue<SubscriptInteger>( |
| 79 | folded)}) { |
| 80 | CHECK(value->Rank() <= 1); |
| 81 | if (value->size() != 0) { |
| 82 | // Apply subscript, possibly vector-valued |
| 83 | auto quotient{which / value->size()}; |
| 84 | auto remainder{which - value->size() * quotient}; |
| 85 | ConstantSubscript at{ |
| 86 | value->values().at(remainder).ToInt64()}; |
| 87 | if (at < lower || at > upper) { |
| 88 | isOutOfRange_ = true; |
| 89 | } |
| 90 | result->Augment((at - lower) * stride); |
| 91 | which = quotient; |
| 92 | return true; |
| 93 | } else { |
| 94 | isEmpty_ = true; |
| 95 | } |
| 96 | } |
| 97 | return false; |
| 98 | }, |
| 99 | [&](const Triplet &triplet) { |
| 100 | auto start{ToInt64(Fold(context_, |
| 101 | triplet.lower().value_or(ExtentExpr{lower})))}; |
| 102 | auto end{ToInt64(Fold(context_, |
| 103 | triplet.upper().value_or(ExtentExpr{upper})))}; |
| 104 | auto step{ToInt64(Fold(context_, triplet.stride()))}; |
| 105 | if (start && end && step) { |
| 106 | if (*step != 0) { |
| 107 | ConstantSubscript range{ |
| 108 | (*end - *start + *step) / *step}; |
| 109 | if (range > 0) { |
| 110 | auto quotient{which / range}; |
| 111 | auto remainder{which - range * quotient}; |
| 112 | auto j{*start + remainder * *step}; |
| 113 | result->Augment((j - lower) * stride); |
| 114 | which = quotient; |
| 115 | return true; |
| 116 | } else { |
| 117 | isEmpty_ = true; |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | return false; |
| 122 | }, |
| 123 | }, |
| 124 | subscript.u)) { |
| 125 | return std::nullopt; |
| 126 | } |
| 127 | ++dim; |
| 128 | stride *= extent; |
| 129 | } |
| 130 | if (which > 0) { |
| 131 | isEmpty_ = true; |
| 132 | } else { |
| 133 | return result; |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | return std::nullopt; |
| 140 | } |
| 141 | |
| 142 | std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator( |
| 143 | const Component &component, ConstantSubscript which) { |
| 144 | const Symbol &comp{component.GetLastSymbol()}; |
| 145 | if (getLastComponent_) { |
| 146 | return FoldDesignator(comp, which); |
| 147 | } else { |
| 148 | const DataRef &base{component.base()}; |
| 149 | std::optional<OffsetSymbol> baseResult, compResult; |
| 150 | if (base.Rank() == 0) { // A%X(:) - apply "which" to component |
| 151 | baseResult = FoldDesignator(base, 0); |
| 152 | compResult = FoldDesignator(comp, which); |
| 153 | } else { // A(:)%X - apply "which" to base |
| 154 | baseResult = FoldDesignator(base, which); |
| 155 | compResult = FoldDesignator(comp, 0); |
| 156 | } |
| 157 | if (baseResult && compResult) { |
| 158 | OffsetSymbol result{baseResult->symbol(), compResult->size()}; |
| 159 | result.Augment( |
| 160 | baseResult->offset() + compResult->offset() + comp.offset()); |
| 161 | return {std::move(result)}; |
| 162 | } else { |
| 163 | return std::nullopt; |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator( |
| 169 | const ComplexPart &z, ConstantSubscript which) { |
| 170 | if (auto result{FoldDesignator(z.complex(), which)}) { |
| 171 | result->set_size(result->size() >> 1); |
| 172 | if (z.part() == ComplexPart::Part::IM) { |
| 173 | result->Augment(result->size()); |
| 174 | } |
| 175 | return result; |
| 176 | } else { |
| 177 | return std::nullopt; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator( |
| 182 | const DataRef &dataRef, ConstantSubscript which) { |
| 183 | return common::visit( |
| 184 | [&](const auto &x) { return FoldDesignator(x, which); }, dataRef.u); |
| 185 | } |
| 186 | |
| 187 | std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator( |
| 188 | const NamedEntity &entity, ConstantSubscript which) { |
| 189 | return entity.IsSymbol() ? FoldDesignator(entity.GetLastSymbol(), which) |
| 190 | : FoldDesignator(entity.GetComponent(), which); |
| 191 | } |
| 192 | |
| 193 | std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator( |
| 194 | const CoarrayRef &, ConstantSubscript) { |
| 195 | return std::nullopt; |
| 196 | } |
| 197 | |
| 198 | std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator( |
| 199 | const ProcedureDesignator &proc, ConstantSubscript which) { |
| 200 | if (const Symbol * symbol{proc.GetSymbol()}) { |
| 201 | if (const Component * component{proc.GetComponent()}) { |
| 202 | return FoldDesignator(*component, which); |
| 203 | } else if (which > 0) { |
| 204 | isEmpty_ = true; |
| 205 | } else { |
| 206 | return FoldDesignator(*symbol, 0); |
| 207 | } |
| 208 | } |
| 209 | return std::nullopt; |
| 210 | } |
| 211 | |
| 212 | // Conversions of offset symbols (back) to Designators |
| 213 | |
| 214 | // Reconstructs subscripts. |
| 215 | // "offset" is decremented in place to hold remaining component offset. |
| 216 | static std::optional<ArrayRef> OffsetToArrayRef(FoldingContext &context, |
| 217 | NamedEntity &&entity, const Shape &shape, const DynamicType &elementType, |
| 218 | ConstantSubscript &offset) { |
| 219 | auto extents{AsConstantExtents(context, shape)}; |
| 220 | Shape lbs{GetRawLowerBounds(context, entity)}; |
| 221 | auto lower{AsConstantExtents(context, lbs)}; |
| 222 | auto elementBytes{ToInt64(elementType.MeasureSizeInBytes(context, true))}; |
| 223 | if (!extents || HasNegativeExtent(*extents) || !lower || !elementBytes || |
| 224 | *elementBytes <= 0) { |
| 225 | return std::nullopt; |
| 226 | } |
| 227 | int rank{GetRank(shape)}; |
| 228 | CHECK(extents->size() == static_cast<std::size_t>(rank) && |
| 229 | lower->size() == extents->size()); |
| 230 | auto element{offset / static_cast<std::size_t>(*elementBytes)}; |
| 231 | std::vector<Subscript> subscripts; |
| 232 | auto at{element}; |
| 233 | for (int dim{0}; dim + 1 < rank; ++dim) { |
| 234 | auto extent{(*extents)[dim]}; |
| 235 | if (extent <= 0) { |
| 236 | return std::nullopt; |
| 237 | } |
| 238 | auto quotient{at / extent}; |
| 239 | auto remainder{at - quotient * extent}; |
| 240 | subscripts.emplace_back(ExtentExpr{(*lower)[dim] + remainder}); |
| 241 | at = quotient; |
| 242 | } |
| 243 | // This final subscript might be out of range for use in error reporting. |
| 244 | subscripts.emplace_back(ExtentExpr{(*lower)[rank - 1] + at}); |
| 245 | offset -= element * static_cast<std::size_t>(*elementBytes); |
| 246 | return ArrayRef{std::move(entity), std::move(subscripts)}; |
| 247 | } |
| 248 | |
| 249 | // Maps an offset back to a component, when unambiguous. |
| 250 | static const Symbol *OffsetToUniqueComponent( |
| 251 | const semantics::DerivedTypeSpec &spec, ConstantSubscript offset) { |
| 252 | const Symbol *result{nullptr}; |
| 253 | if (const semantics::Scope * scope{spec.scope()}) { |
| 254 | for (const auto &pair : *scope) { |
| 255 | const Symbol &component{*pair.second}; |
| 256 | if (offset >= static_cast<ConstantSubscript>(component.offset()) && |
| 257 | offset < static_cast<ConstantSubscript>( |
| 258 | component.offset() + component.size())) { |
| 259 | if (result) { |
| 260 | return nullptr; // MAP overlap or error recovery |
| 261 | } |
| 262 | result = &component; |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | return result; |
| 267 | } |
| 268 | |
| 269 | // Converts an offset into subscripts &/or component references. Recursive. |
| 270 | // Any remaining offset is left in place in the "offset" reference argument. |
| 271 | static std::optional<DataRef> OffsetToDataRef(FoldingContext &context, |
| 272 | NamedEntity &&entity, ConstantSubscript &offset, std::size_t size) { |
| 273 | const Symbol &symbol{entity.GetLastSymbol()}; |
| 274 | if (IsAllocatableOrPointer(symbol)) { |
| 275 | return entity.IsSymbol() ? DataRef{symbol} |
| 276 | : DataRef{std::move(entity.GetComponent())}; |
| 277 | } else if (std::optional<DynamicType> type{DynamicType::From(symbol)}) { |
| 278 | std::optional<DataRef> result; |
| 279 | if (!type->IsUnlimitedPolymorphic()) { |
| 280 | if (std::optional<Shape> shape{GetShape(context, symbol)}) { |
| 281 | if (GetRank(*shape) > 0) { |
| 282 | if (auto aref{OffsetToArrayRef( |
| 283 | context, std::move(entity), *shape, *type, offset)}) { |
| 284 | result = DataRef{std::move(*aref)}; |
| 285 | } |
| 286 | } else { |
| 287 | result = entity.IsSymbol() |
| 288 | ? DataRef{symbol} |
| 289 | : DataRef{std::move(entity.GetComponent())}; |
| 290 | } |
| 291 | if (result && type->category() == TypeCategory::Derived && |
| 292 | size <= result->GetLastSymbol().size()) { |
| 293 | if (const Symbol * |
| 294 | component{OffsetToUniqueComponent( |
| 295 | type->GetDerivedTypeSpec(), offset)}) { |
| 296 | offset -= component->offset(); |
| 297 | return OffsetToDataRef(context, |
| 298 | NamedEntity{Component{std::move(*result), *component}}, offset, |
| 299 | size); |
| 300 | } |
| 301 | } |
| 302 | } |
| 303 | } |
| 304 | return result; |
| 305 | } else { |
| 306 | return std::nullopt; |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | // Reconstructs a Designator from a symbol, an offset, and a size. |
| 311 | // Returns a ProcedureDesignator in the case of a whole procedure pointer. |
| 312 | std::optional<Expr<SomeType>> OffsetToDesignator(FoldingContext &context, |
| 313 | const Symbol &baseSymbol, ConstantSubscript offset, std::size_t size) { |
| 314 | if (offset < 0) { |
| 315 | return std::nullopt; |
| 316 | } else if (std::optional<DataRef> dataRef{OffsetToDataRef( |
| 317 | context, NamedEntity{baseSymbol}, offset, size)}) { |
| 318 | const Symbol &symbol{dataRef->GetLastSymbol()}; |
| 319 | if (IsProcedurePointer(symbol)) { |
| 320 | if (std::holds_alternative<SymbolRef>(dataRef->u)) { |
| 321 | return Expr<SomeType>{ProcedureDesignator{symbol}}; |
| 322 | } else if (auto *component{std::get_if<Component>(&dataRef->u)}) { |
| 323 | return Expr<SomeType>{ProcedureDesignator{std::move(*component)}}; |
| 324 | } |
| 325 | } else if (std::optional<Expr<SomeType>> result{ |
| 326 | AsGenericExpr(std::move(*dataRef))}) { |
| 327 | if (IsAllocatableOrPointer(symbol)) { |
| 328 | } else if (auto type{DynamicType::From(symbol)}) { |
| 329 | if (auto elementBytes{ |
| 330 | ToInt64(type->MeasureSizeInBytes(context, true))}) { |
| 331 | if (auto *zExpr{std::get_if<Expr<SomeComplex>>(&result->u)}) { |
| 332 | if (size * 2 > static_cast<std::size_t>(*elementBytes)) { |
| 333 | return result; |
| 334 | } else if (offset == 0 || offset * 2 == *elementBytes) { |
| 335 | // Pick a COMPLEX component |
| 336 | auto part{ |
| 337 | offset == 0 ? ComplexPart::Part::RE : ComplexPart::Part::IM}; |
| 338 | return common::visit( |
| 339 | [&](const auto &z) -> std::optional<Expr<SomeType>> { |
| 340 | using PartType = typename ResultType<decltype(z)>::Part; |
| 341 | return AsGenericExpr(Designator<PartType>{ComplexPart{ |
| 342 | ExtractDataRef(std::move(*zExpr)).value(), part}}); |
| 343 | }, |
| 344 | zExpr->u); |
| 345 | } |
| 346 | } else if (auto *cExpr{ |
| 347 | std::get_if<Expr<SomeCharacter>>(&result->u)}) { |
| 348 | if (offset > 0 || size != static_cast<std::size_t>(*elementBytes)) { |
| 349 | // Select a substring |
| 350 | return common::visit( |
| 351 | [&](const auto &x) -> std::optional<Expr<SomeType>> { |
| 352 | using T = typename std::decay_t<decltype(x)>::Result; |
| 353 | return AsGenericExpr(Designator<T>{ |
| 354 | Substring{ExtractDataRef(std::move(*cExpr)).value(), |
| 355 | std::optional<Expr<SubscriptInteger>>{ |
| 356 | 1 + (offset / T::kind)}, |
| 357 | std::optional<Expr<SubscriptInteger>>{ |
| 358 | 1 + ((offset + size - 1) / T::kind)}}}); |
| 359 | }, |
| 360 | cExpr->u); |
| 361 | } |
| 362 | } |
| 363 | } |
| 364 | } |
| 365 | if (offset == 0) { |
| 366 | return result; |
| 367 | } |
| 368 | } |
| 369 | } |
| 370 | return std::nullopt; |
| 371 | } |
| 372 | |
| 373 | std::optional<Expr<SomeType>> OffsetToDesignator( |
| 374 | FoldingContext &context, const OffsetSymbol &offsetSymbol) { |
| 375 | return OffsetToDesignator(context, offsetSymbol.symbol(), |
| 376 | offsetSymbol.offset(), offsetSymbol.size()); |
| 377 | } |
| 378 | |
| 379 | ConstantObjectPointer ConstantObjectPointer::From( |
| 380 | FoldingContext &context, const Expr<SomeType> &expr) { |
| 381 | auto extents{GetConstantExtents(context, expr)}; |
| 382 | CHECK(extents); |
| 383 | std::optional<uint64_t> optElements{TotalElementCount(*extents)}; |
| 384 | CHECK(optElements); |
| 385 | uint64_t elements{*optElements}; |
| 386 | CHECK(elements > 0); |
| 387 | int rank{GetRank(*extents)}; |
| 388 | ConstantSubscripts at(rank, 1); |
| 389 | ConstantObjectPointer::Dimensions dimensions(rank); |
| 390 | for (int j{0}; j < rank; ++j) { |
| 391 | dimensions[j].extent = (*extents)[j]; |
| 392 | } |
| 393 | DesignatorFolder designatorFolder{context}; |
| 394 | const Symbol *symbol{nullptr}; |
| 395 | ConstantSubscript baseOffset{0}; |
| 396 | std::size_t elementSize{0}; |
| 397 | for (std::size_t j{0}; j < elements; ++j) { |
| 398 | auto folded{designatorFolder.FoldDesignator(expr)}; |
| 399 | CHECK(folded); |
| 400 | if (j == 0) { |
| 401 | symbol = &folded->symbol(); |
| 402 | baseOffset = folded->offset(); |
| 403 | elementSize = folded->size(); |
| 404 | } else { |
| 405 | CHECK(symbol == &folded->symbol()); |
| 406 | CHECK(elementSize == folded->size()); |
| 407 | } |
| 408 | int twoDim{-1}; |
| 409 | for (int k{0}; k < rank; ++k) { |
| 410 | if (at[k] == 2 && twoDim == -1) { |
| 411 | twoDim = k; |
| 412 | } else if (at[k] != 1) { |
| 413 | twoDim = -2; |
| 414 | } |
| 415 | } |
| 416 | if (twoDim >= 0) { |
| 417 | // Exactly one subscript is a 2 and the rest are 1. |
| 418 | dimensions[twoDim].byteStride = folded->offset() - baseOffset; |
| 419 | } |
| 420 | ConstantSubscript checkOffset{baseOffset}; |
| 421 | for (int k{0}; k < rank; ++k) { |
| 422 | checkOffset += (at[k] - 1) * dimensions[twoDim].byteStride; |
| 423 | } |
| 424 | CHECK(checkOffset == folded->offset()); |
| 425 | CHECK(IncrementSubscripts(at, *extents) == (j + 1 < elements)); |
| 426 | } |
| 427 | CHECK(!designatorFolder.FoldDesignator(expr)); |
| 428 | return ConstantObjectPointer{ |
| 429 | DEREF(symbol), elementSize, std::move(dimensions)}; |
| 430 | } |
| 431 | } // namespace Fortran::evaluate |
| 432 | |