1 //===-- lib/Evaluate/shape.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 #include "flang/Evaluate/shape.h" 10 #include "flang/Common/idioms.h" 11 #include "flang/Common/template.h" 12 #include "flang/Evaluate/characteristics.h" 13 #include "flang/Evaluate/fold.h" 14 #include "flang/Evaluate/intrinsics.h" 15 #include "flang/Evaluate/tools.h" 16 #include "flang/Evaluate/type.h" 17 #include "flang/Parser/message.h" 18 #include "flang/Semantics/symbol.h" 19 #include <functional> 20 21 using namespace std::placeholders; // _1, _2, &c. for std::bind() 22 23 namespace Fortran::evaluate { 24 25 bool IsImpliedShape(const Symbol &original) { 26 const Symbol &symbol{ResolveAssociations(original)}; 27 const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}; 28 return details && symbol.attrs().test(semantics::Attr::PARAMETER) && 29 details->shape().IsImpliedShape(); 30 } 31 32 bool IsExplicitShape(const Symbol &original) { 33 const Symbol &symbol{ResolveAssociations(original)}; 34 if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 35 const auto &shape{details->shape()}; 36 return shape.Rank() == 0 || 37 shape.IsExplicitShape(); // true when scalar, too 38 } else { 39 return symbol 40 .has<semantics::AssocEntityDetails>(); // exprs have explicit shape 41 } 42 } 43 44 Shape GetShapeHelper::ConstantShape(const Constant<ExtentType> &arrayConstant) { 45 CHECK(arrayConstant.Rank() == 1); 46 Shape result; 47 std::size_t dimensions{arrayConstant.size()}; 48 for (std::size_t j{0}; j < dimensions; ++j) { 49 Scalar<ExtentType> extent{arrayConstant.values().at(j)}; 50 result.emplace_back(MaybeExtentExpr{ExtentExpr{std::move(extent)}}); 51 } 52 return result; 53 } 54 55 auto GetShapeHelper::AsShape(ExtentExpr &&arrayExpr) const -> Result { 56 if (context_) { 57 arrayExpr = Fold(*context_, std::move(arrayExpr)); 58 } 59 if (const auto *constArray{UnwrapConstantValue<ExtentType>(arrayExpr)}) { 60 return ConstantShape(*constArray); 61 } 62 if (auto *constructor{UnwrapExpr<ArrayConstructor<ExtentType>>(arrayExpr)}) { 63 Shape result; 64 for (auto &value : *constructor) { 65 if (auto *expr{std::get_if<ExtentExpr>(&value.u)}) { 66 if (expr->Rank() == 0) { 67 result.emplace_back(std::move(*expr)); 68 continue; 69 } 70 } 71 return std::nullopt; 72 } 73 return result; 74 } 75 return std::nullopt; 76 } 77 78 Shape GetShapeHelper::CreateShape(int rank, NamedEntity &base) { 79 Shape shape; 80 for (int dimension{0}; dimension < rank; ++dimension) { 81 shape.emplace_back(GetExtent(base, dimension)); 82 } 83 return shape; 84 } 85 86 std::optional<ExtentExpr> AsExtentArrayExpr(const Shape &shape) { 87 ArrayConstructorValues<ExtentType> values; 88 for (const auto &dim : shape) { 89 if (dim) { 90 values.Push(common::Clone(*dim)); 91 } else { 92 return std::nullopt; 93 } 94 } 95 return ExtentExpr{ArrayConstructor<ExtentType>{std::move(values)}}; 96 } 97 98 std::optional<Constant<ExtentType>> AsConstantShape( 99 FoldingContext &context, const Shape &shape) { 100 if (auto shapeArray{AsExtentArrayExpr(shape)}) { 101 auto folded{Fold(context, std::move(*shapeArray))}; 102 if (auto *p{UnwrapConstantValue<ExtentType>(folded)}) { 103 return std::move(*p); 104 } 105 } 106 return std::nullopt; 107 } 108 109 Constant<SubscriptInteger> AsConstantShape(const ConstantSubscripts &shape) { 110 using IntType = Scalar<SubscriptInteger>; 111 std::vector<IntType> result; 112 for (auto dim : shape) { 113 result.emplace_back(dim); 114 } 115 return {std::move(result), ConstantSubscripts{GetRank(shape)}}; 116 } 117 118 ConstantSubscripts AsConstantExtents(const Constant<ExtentType> &shape) { 119 ConstantSubscripts result; 120 for (const auto &extent : shape.values()) { 121 result.push_back(extent.ToInt64()); 122 } 123 return result; 124 } 125 126 std::optional<ConstantSubscripts> AsConstantExtents( 127 FoldingContext &context, const Shape &shape) { 128 if (auto shapeConstant{AsConstantShape(context, shape)}) { 129 return AsConstantExtents(*shapeConstant); 130 } else { 131 return std::nullopt; 132 } 133 } 134 135 Shape AsShape(const ConstantSubscripts &shape) { 136 Shape result; 137 for (const auto &extent : shape) { 138 result.emplace_back(ExtentExpr{extent}); 139 } 140 return result; 141 } 142 143 std::optional<Shape> AsShape(const std::optional<ConstantSubscripts> &shape) { 144 if (shape) { 145 return AsShape(*shape); 146 } else { 147 return std::nullopt; 148 } 149 } 150 151 Shape Fold(FoldingContext &context, Shape &&shape) { 152 for (auto &dim : shape) { 153 dim = Fold(context, std::move(dim)); 154 } 155 return std::move(shape); 156 } 157 158 std::optional<Shape> Fold( 159 FoldingContext &context, std::optional<Shape> &&shape) { 160 if (shape) { 161 return Fold(context, std::move(*shape)); 162 } else { 163 return std::nullopt; 164 } 165 } 166 167 static ExtentExpr ComputeTripCount( 168 ExtentExpr &&lower, ExtentExpr &&upper, ExtentExpr &&stride) { 169 ExtentExpr strideCopy{common::Clone(stride)}; 170 ExtentExpr span{ 171 (std::move(upper) - std::move(lower) + std::move(strideCopy)) / 172 std::move(stride)}; 173 return ExtentExpr{ 174 Extremum<ExtentType>{Ordering::Greater, std::move(span), ExtentExpr{0}}}; 175 } 176 177 ExtentExpr CountTrips( 178 ExtentExpr &&lower, ExtentExpr &&upper, ExtentExpr &&stride) { 179 return ComputeTripCount( 180 std::move(lower), std::move(upper), std::move(stride)); 181 } 182 183 ExtentExpr CountTrips(const ExtentExpr &lower, const ExtentExpr &upper, 184 const ExtentExpr &stride) { 185 return ComputeTripCount( 186 common::Clone(lower), common::Clone(upper), common::Clone(stride)); 187 } 188 189 MaybeExtentExpr CountTrips(MaybeExtentExpr &&lower, MaybeExtentExpr &&upper, 190 MaybeExtentExpr &&stride) { 191 std::function<ExtentExpr(ExtentExpr &&, ExtentExpr &&, ExtentExpr &&)> bound{ 192 std::bind(ComputeTripCount, _1, _2, _3)}; 193 return common::MapOptional( 194 std::move(bound), std::move(lower), std::move(upper), std::move(stride)); 195 } 196 197 MaybeExtentExpr GetSize(Shape &&shape) { 198 ExtentExpr extent{1}; 199 for (auto &&dim : std::move(shape)) { 200 if (dim) { 201 extent = std::move(extent) * std::move(*dim); 202 } else { 203 return std::nullopt; 204 } 205 } 206 return extent; 207 } 208 209 ConstantSubscript GetSize(const ConstantSubscripts &shape) { 210 ConstantSubscript size{1}; 211 for (auto dim : std::move(shape)) { 212 size *= dim; 213 } 214 return size; 215 } 216 217 bool ContainsAnyImpliedDoIndex(const ExtentExpr &expr) { 218 struct MyVisitor : public AnyTraverse<MyVisitor> { 219 using Base = AnyTraverse<MyVisitor>; 220 MyVisitor() : Base{*this} {} 221 using Base::operator(); 222 bool operator()(const ImpliedDoIndex &) { return true; } 223 }; 224 return MyVisitor{}(expr); 225 } 226 227 // Determines lower bound on a dimension. This can be other than 1 only 228 // for a reference to a whole array object or component. (See LBOUND, 16.9.109). 229 // ASSOCIATE construct entities may require traversal of their referents. 230 class GetLowerBoundHelper : public Traverse<GetLowerBoundHelper, ExtentExpr> { 231 public: 232 using Result = ExtentExpr; 233 using Base = Traverse<GetLowerBoundHelper, ExtentExpr>; 234 using Base::operator(); 235 explicit GetLowerBoundHelper(int d) : Base{*this}, dimension_{d} {} 236 static ExtentExpr Default() { return ExtentExpr{1}; } 237 static ExtentExpr Combine(Result &&, Result &&) { return Default(); } 238 ExtentExpr operator()(const Symbol &); 239 ExtentExpr operator()(const Component &); 240 241 private: 242 int dimension_; 243 }; 244 245 auto GetLowerBoundHelper::operator()(const Symbol &symbol0) -> Result { 246 const Symbol &symbol{symbol0.GetUltimate()}; 247 if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 248 int j{0}; 249 for (const auto &shapeSpec : details->shape()) { 250 if (j++ == dimension_) { 251 if (const auto &bound{shapeSpec.lbound().GetExplicit()}) { 252 return *bound; 253 } else if (IsDescriptor(symbol)) { 254 return ExtentExpr{DescriptorInquiry{NamedEntity{symbol0}, 255 DescriptorInquiry::Field::LowerBound, dimension_}}; 256 } else { 257 break; 258 } 259 } 260 } 261 } else if (const auto *assoc{ 262 symbol.detailsIf<semantics::AssocEntityDetails>()}) { 263 return (*this)(assoc->expr()); 264 } 265 return Default(); 266 } 267 268 auto GetLowerBoundHelper::operator()(const Component &component) -> Result { 269 if (component.base().Rank() == 0) { 270 const Symbol &symbol{component.GetLastSymbol().GetUltimate()}; 271 if (const auto *details{ 272 symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 273 int j{0}; 274 for (const auto &shapeSpec : details->shape()) { 275 if (j++ == dimension_) { 276 if (const auto &bound{shapeSpec.lbound().GetExplicit()}) { 277 return *bound; 278 } else if (IsDescriptor(symbol)) { 279 return ExtentExpr{ 280 DescriptorInquiry{NamedEntity{common::Clone(component)}, 281 DescriptorInquiry::Field::LowerBound, dimension_}}; 282 } else { 283 break; 284 } 285 } 286 } 287 } 288 } 289 return Default(); 290 } 291 292 ExtentExpr GetLowerBound(const NamedEntity &base, int dimension) { 293 return GetLowerBoundHelper{dimension}(base); 294 } 295 296 ExtentExpr GetLowerBound( 297 FoldingContext &context, const NamedEntity &base, int dimension) { 298 return Fold(context, GetLowerBound(base, dimension)); 299 } 300 301 Shape GetLowerBounds(const NamedEntity &base) { 302 Shape result; 303 int rank{base.Rank()}; 304 for (int dim{0}; dim < rank; ++dim) { 305 result.emplace_back(GetLowerBound(base, dim)); 306 } 307 return result; 308 } 309 310 Shape GetLowerBounds(FoldingContext &context, const NamedEntity &base) { 311 Shape result; 312 int rank{base.Rank()}; 313 for (int dim{0}; dim < rank; ++dim) { 314 result.emplace_back(GetLowerBound(context, base, dim)); 315 } 316 return result; 317 } 318 319 // If the upper and lower bounds are constant, return a constant expression for 320 // the extent. In particular, if the upper bound is less than the lower bound, 321 // return zero. 322 static MaybeExtentExpr GetNonNegativeExtent( 323 const semantics::ShapeSpec &shapeSpec) { 324 const auto &ubound{shapeSpec.ubound().GetExplicit()}; 325 const auto &lbound{shapeSpec.lbound().GetExplicit()}; 326 std::optional<ConstantSubscript> uval{ToInt64(ubound)}; 327 std::optional<ConstantSubscript> lval{ToInt64(lbound)}; 328 if (uval && lval) { 329 if (*uval < *lval) { 330 return ExtentExpr{0}; 331 } else { 332 return ExtentExpr{*uval - *lval + 1}; 333 } 334 } 335 return common::Clone(ubound.value()) - common::Clone(lbound.value()) + 336 ExtentExpr{1}; 337 } 338 339 MaybeExtentExpr GetExtent(const NamedEntity &base, int dimension) { 340 CHECK(dimension >= 0); 341 const Symbol &symbol{ResolveAssociations(base.GetLastSymbol())}; 342 if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 343 if (IsImpliedShape(symbol) && details->init()) { 344 if (auto shape{GetShape(symbol)}) { 345 if (dimension < static_cast<int>(shape->size())) { 346 return std::move(shape->at(dimension)); 347 } 348 } 349 } else { 350 int j{0}; 351 for (const auto &shapeSpec : details->shape()) { 352 if (j++ == dimension) { 353 if (const auto &ubound{shapeSpec.ubound().GetExplicit()}) { 354 if (shapeSpec.ubound().GetExplicit()) { 355 // 8.5.8.2, paragraph 3. If the upper bound is less than the 356 // lower bound, the extent is zero. 357 if (shapeSpec.lbound().GetExplicit()) { 358 return GetNonNegativeExtent(shapeSpec); 359 } else { 360 return ubound.value(); 361 } 362 } 363 } else if (details->IsAssumedSize() && j == symbol.Rank()) { 364 return std::nullopt; 365 } else if (semantics::IsDescriptor(symbol)) { 366 return ExtentExpr{DescriptorInquiry{NamedEntity{base}, 367 DescriptorInquiry::Field::Extent, dimension}}; 368 } 369 } 370 } 371 } 372 } else if (const auto *assoc{ 373 symbol.detailsIf<semantics::AssocEntityDetails>()}) { 374 if (auto shape{GetShape(assoc->expr())}) { 375 if (dimension < static_cast<int>(shape->size())) { 376 return std::move(shape->at(dimension)); 377 } 378 } 379 } 380 return std::nullopt; 381 } 382 383 MaybeExtentExpr GetExtent( 384 FoldingContext &context, const NamedEntity &base, int dimension) { 385 return Fold(context, GetExtent(base, dimension)); 386 } 387 388 MaybeExtentExpr GetExtent( 389 const Subscript &subscript, const NamedEntity &base, int dimension) { 390 return std::visit( 391 common::visitors{ 392 [&](const Triplet &triplet) -> MaybeExtentExpr { 393 MaybeExtentExpr upper{triplet.upper()}; 394 if (!upper) { 395 upper = GetUpperBound(base, dimension); 396 } 397 MaybeExtentExpr lower{triplet.lower()}; 398 if (!lower) { 399 lower = GetLowerBound(base, dimension); 400 } 401 return CountTrips(std::move(lower), std::move(upper), 402 MaybeExtentExpr{triplet.stride()}); 403 }, 404 [&](const IndirectSubscriptIntegerExpr &subs) -> MaybeExtentExpr { 405 if (auto shape{GetShape(subs.value())}) { 406 if (GetRank(*shape) > 0) { 407 CHECK(GetRank(*shape) == 1); // vector-valued subscript 408 return std::move(shape->at(0)); 409 } 410 } 411 return std::nullopt; 412 }, 413 }, 414 subscript.u); 415 } 416 417 MaybeExtentExpr GetExtent(FoldingContext &context, const Subscript &subscript, 418 const NamedEntity &base, int dimension) { 419 return Fold(context, GetExtent(subscript, base, dimension)); 420 } 421 422 MaybeExtentExpr ComputeUpperBound( 423 ExtentExpr &&lower, MaybeExtentExpr &&extent) { 424 if (extent) { 425 return std::move(*extent) + std::move(lower) - ExtentExpr{1}; 426 } else { 427 return std::nullopt; 428 } 429 } 430 431 MaybeExtentExpr ComputeUpperBound( 432 FoldingContext &context, ExtentExpr &&lower, MaybeExtentExpr &&extent) { 433 return Fold(context, ComputeUpperBound(std::move(lower), std::move(extent))); 434 } 435 436 MaybeExtentExpr GetUpperBound(const NamedEntity &base, int dimension) { 437 const Symbol &symbol{ResolveAssociations(base.GetLastSymbol())}; 438 if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 439 int j{0}; 440 for (const auto &shapeSpec : details->shape()) { 441 if (j++ == dimension) { 442 if (const auto &bound{shapeSpec.ubound().GetExplicit()}) { 443 return *bound; 444 } else if (details->IsAssumedSize() && dimension + 1 == symbol.Rank()) { 445 break; 446 } else { 447 return ComputeUpperBound( 448 GetLowerBound(base, dimension), GetExtent(base, dimension)); 449 } 450 } 451 } 452 } else if (const auto *assoc{ 453 symbol.detailsIf<semantics::AssocEntityDetails>()}) { 454 if (auto shape{GetShape(assoc->expr())}) { 455 if (dimension < static_cast<int>(shape->size())) { 456 return ComputeUpperBound( 457 GetLowerBound(base, dimension), std::move(shape->at(dimension))); 458 } 459 } 460 } 461 return std::nullopt; 462 } 463 464 MaybeExtentExpr GetUpperBound( 465 FoldingContext &context, const NamedEntity &base, int dimension) { 466 return Fold(context, GetUpperBound(base, dimension)); 467 } 468 469 Shape GetUpperBounds(const NamedEntity &base) { 470 const Symbol &symbol{ResolveAssociations(base.GetLastSymbol())}; 471 if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 472 Shape result; 473 int dim{0}; 474 for (const auto &shapeSpec : details->shape()) { 475 if (const auto &bound{shapeSpec.ubound().GetExplicit()}) { 476 result.push_back(*bound); 477 } else if (details->IsAssumedSize()) { 478 CHECK(dim + 1 == base.Rank()); 479 result.emplace_back(std::nullopt); // UBOUND folding replaces with -1 480 } else { 481 result.emplace_back( 482 ComputeUpperBound(GetLowerBound(base, dim), GetExtent(base, dim))); 483 } 484 ++dim; 485 } 486 CHECK(GetRank(result) == symbol.Rank()); 487 return result; 488 } else { 489 return std::move(GetShape(symbol).value()); 490 } 491 } 492 493 Shape GetUpperBounds(FoldingContext &context, const NamedEntity &base) { 494 return Fold(context, GetUpperBounds(base)); 495 } 496 497 auto GetShapeHelper::operator()(const Symbol &symbol) const -> Result { 498 return std::visit( 499 common::visitors{ 500 [&](const semantics::ObjectEntityDetails &object) { 501 if (IsImpliedShape(symbol) && object.init()) { 502 return (*this)(object.init()); 503 } else if (IsAssumedRank(symbol)) { 504 return Result{}; 505 } else { 506 int n{object.shape().Rank()}; 507 NamedEntity base{symbol}; 508 return Result{CreateShape(n, base)}; 509 } 510 }, 511 [](const semantics::EntityDetails &) { 512 return ScalarShape(); // no dimensions seen 513 }, 514 [&](const semantics::ProcEntityDetails &proc) { 515 if (const Symbol * interface{proc.interface().symbol()}) { 516 return (*this)(*interface); 517 } else { 518 return ScalarShape(); 519 } 520 }, 521 [&](const semantics::AssocEntityDetails &assoc) { 522 if (assoc.rank()) { // SELECT RANK case 523 int n{assoc.rank().value()}; 524 NamedEntity base{symbol}; 525 return Result{CreateShape(n, base)}; 526 } else { 527 return (*this)(assoc.expr()); 528 } 529 }, 530 [&](const semantics::SubprogramDetails &subp) { 531 if (subp.isFunction()) { 532 return (*this)(subp.result()); 533 } else { 534 return Result{}; 535 } 536 }, 537 [&](const semantics::ProcBindingDetails &binding) { 538 return (*this)(binding.symbol()); 539 }, 540 [](const semantics::TypeParamDetails &) { return ScalarShape(); }, 541 [](const auto &) { return Result{}; }, 542 }, 543 symbol.GetUltimate().details()); 544 } 545 546 auto GetShapeHelper::operator()(const Component &component) const -> Result { 547 const Symbol &symbol{component.GetLastSymbol()}; 548 int rank{symbol.Rank()}; 549 if (rank == 0) { 550 return (*this)(component.base()); 551 } else if (symbol.has<semantics::ObjectEntityDetails>()) { 552 NamedEntity base{Component{component}}; 553 return CreateShape(rank, base); 554 } else if (symbol.has<semantics::AssocEntityDetails>()) { 555 NamedEntity base{Component{component}}; 556 return Result{CreateShape(rank, base)}; 557 } else { 558 return (*this)(symbol); 559 } 560 } 561 562 auto GetShapeHelper::operator()(const ArrayRef &arrayRef) const -> Result { 563 Shape shape; 564 int dimension{0}; 565 const NamedEntity &base{arrayRef.base()}; 566 for (const Subscript &ss : arrayRef.subscript()) { 567 if (ss.Rank() > 0) { 568 shape.emplace_back(GetExtent(ss, base, dimension)); 569 } 570 ++dimension; 571 } 572 if (shape.empty()) { 573 if (const Component * component{base.UnwrapComponent()}) { 574 return (*this)(component->base()); 575 } 576 } 577 return shape; 578 } 579 580 auto GetShapeHelper::operator()(const CoarrayRef &coarrayRef) const -> Result { 581 NamedEntity base{coarrayRef.GetBase()}; 582 if (coarrayRef.subscript().empty()) { 583 return (*this)(base); 584 } else { 585 Shape shape; 586 int dimension{0}; 587 for (const Subscript &ss : coarrayRef.subscript()) { 588 if (ss.Rank() > 0) { 589 shape.emplace_back(GetExtent(ss, base, dimension)); 590 } 591 ++dimension; 592 } 593 return shape; 594 } 595 } 596 597 auto GetShapeHelper::operator()(const Substring &substring) const -> Result { 598 return (*this)(substring.parent()); 599 } 600 601 auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result { 602 if (call.Rank() == 0) { 603 return ScalarShape(); 604 } else if (call.IsElemental()) { 605 for (const auto &arg : call.arguments()) { 606 if (arg && arg->Rank() > 0) { 607 return (*this)(*arg); 608 } 609 } 610 return ScalarShape(); 611 } else if (const Symbol * symbol{call.proc().GetSymbol()}) { 612 return (*this)(*symbol); 613 } else if (const auto *intrinsic{call.proc().GetSpecificIntrinsic()}) { 614 if (intrinsic->name == "shape" || intrinsic->name == "lbound" || 615 intrinsic->name == "ubound") { 616 // These are the array-valued cases for LBOUND and UBOUND (no DIM=). 617 const auto *expr{call.arguments().front().value().UnwrapExpr()}; 618 CHECK(expr); 619 return Shape{MaybeExtentExpr{ExtentExpr{expr->Rank()}}}; 620 } else if (intrinsic->name == "all" || intrinsic->name == "any" || 621 intrinsic->name == "count" || intrinsic->name == "iall" || 622 intrinsic->name == "iany" || intrinsic->name == "iparity" || 623 intrinsic->name == "maxval" || intrinsic->name == "minval" || 624 intrinsic->name == "norm2" || intrinsic->name == "parity" || 625 intrinsic->name == "product" || intrinsic->name == "sum") { 626 // Reduction with DIM= 627 if (call.arguments().size() >= 2) { 628 auto arrayShape{ 629 (*this)(UnwrapExpr<Expr<SomeType>>(call.arguments().at(0)))}; 630 const auto *dimArg{UnwrapExpr<Expr<SomeType>>(call.arguments().at(1))}; 631 if (arrayShape && dimArg) { 632 if (auto dim{ToInt64(*dimArg)}) { 633 if (*dim >= 1 && 634 static_cast<std::size_t>(*dim) <= arrayShape->size()) { 635 arrayShape->erase(arrayShape->begin() + (*dim - 1)); 636 return std::move(*arrayShape); 637 } 638 } 639 } 640 } 641 } else if (intrinsic->name == "maxloc" || intrinsic->name == "minloc") { 642 // TODO: FINDLOC 643 if (call.arguments().size() >= 2) { 644 if (auto arrayShape{ 645 (*this)(UnwrapExpr<Expr<SomeType>>(call.arguments().at(0)))}) { 646 auto rank{static_cast<int>(arrayShape->size())}; 647 if (const auto *dimArg{ 648 UnwrapExpr<Expr<SomeType>>(call.arguments()[1])}) { 649 auto dim{ToInt64(*dimArg)}; 650 if (dim && *dim >= 1 && *dim <= rank) { 651 arrayShape->erase(arrayShape->begin() + (*dim - 1)); 652 return std::move(*arrayShape); 653 } 654 } else { 655 // xxxLOC(no DIM=) result is vector(1:RANK(ARRAY=)) 656 return Shape{ExtentExpr{rank}}; 657 } 658 } 659 } 660 } else if (intrinsic->name == "cshift" || intrinsic->name == "eoshift") { 661 if (!call.arguments().empty()) { 662 return (*this)(call.arguments()[0]); 663 } 664 } else if (intrinsic->name == "matmul") { 665 if (call.arguments().size() == 2) { 666 if (auto ashape{(*this)(call.arguments()[0])}) { 667 if (auto bshape{(*this)(call.arguments()[1])}) { 668 if (ashape->size() == 1 && bshape->size() == 2) { 669 bshape->erase(bshape->begin()); 670 return std::move(*bshape); // matmul(vector, matrix) 671 } else if (ashape->size() == 2 && bshape->size() == 1) { 672 ashape->pop_back(); 673 return std::move(*ashape); // matmul(matrix, vector) 674 } else if (ashape->size() == 2 && bshape->size() == 2) { 675 (*ashape)[1] = std::move((*bshape)[1]); 676 return std::move(*ashape); // matmul(matrix, matrix) 677 } 678 } 679 } 680 } 681 } else if (intrinsic->name == "reshape") { 682 if (call.arguments().size() >= 2 && call.arguments().at(1)) { 683 // SHAPE(RESHAPE(array,shape)) -> shape 684 if (const auto *shapeExpr{ 685 call.arguments().at(1).value().UnwrapExpr()}) { 686 auto shape{std::get<Expr<SomeInteger>>(shapeExpr->u)}; 687 return AsShape(ConvertToType<ExtentType>(std::move(shape))); 688 } 689 } 690 } else if (intrinsic->name == "pack") { 691 if (call.arguments().size() >= 3 && call.arguments().at(2)) { 692 // SHAPE(PACK(,,VECTOR=v)) -> SHAPE(v) 693 return (*this)(call.arguments().at(2)); 694 } else if (call.arguments().size() >= 2 && context_) { 695 if (auto maskShape{(*this)(call.arguments().at(1))}) { 696 if (maskShape->size() == 0) { 697 // Scalar MASK= -> [MERGE(SIZE(ARRAY=), 0, mask)] 698 if (auto arrayShape{(*this)(call.arguments().at(0))}) { 699 auto arraySize{GetSize(std::move(*arrayShape))}; 700 CHECK(arraySize); 701 ActualArguments toMerge{ 702 ActualArgument{AsGenericExpr(std::move(*arraySize))}, 703 ActualArgument{AsGenericExpr(ExtentExpr{0})}, 704 common::Clone(call.arguments().at(1))}; 705 auto specific{context_->intrinsics().Probe( 706 CallCharacteristics{"merge"}, toMerge, *context_)}; 707 CHECK(specific); 708 return Shape{ExtentExpr{FunctionRef<ExtentType>{ 709 ProcedureDesignator{std::move(specific->specificIntrinsic)}, 710 std::move(specific->arguments)}}}; 711 } 712 } else { 713 // Non-scalar MASK= -> [COUNT(mask)] 714 ActualArguments toCount{ActualArgument{common::Clone( 715 DEREF(call.arguments().at(1).value().UnwrapExpr()))}}; 716 auto specific{context_->intrinsics().Probe( 717 CallCharacteristics{"count"}, toCount, *context_)}; 718 CHECK(specific); 719 return Shape{ExtentExpr{FunctionRef<ExtentType>{ 720 ProcedureDesignator{std::move(specific->specificIntrinsic)}, 721 std::move(specific->arguments)}}}; 722 } 723 } 724 } 725 } else if (intrinsic->name == "spread") { 726 // SHAPE(SPREAD(ARRAY,DIM,NCOPIES)) = SHAPE(ARRAY) with NCOPIES inserted 727 // at position DIM. 728 if (call.arguments().size() == 3) { 729 auto arrayShape{ 730 (*this)(UnwrapExpr<Expr<SomeType>>(call.arguments().at(0)))}; 731 const auto *dimArg{UnwrapExpr<Expr<SomeType>>(call.arguments().at(1))}; 732 const auto *nCopies{ 733 UnwrapExpr<Expr<SomeInteger>>(call.arguments().at(2))}; 734 if (arrayShape && dimArg && nCopies) { 735 if (auto dim{ToInt64(*dimArg)}) { 736 if (*dim >= 1 && 737 static_cast<std::size_t>(*dim) <= arrayShape->size() + 1) { 738 arrayShape->emplace(arrayShape->begin() + *dim - 1, 739 ConvertToType<ExtentType>(common::Clone(*nCopies))); 740 return std::move(*arrayShape); 741 } 742 } 743 } 744 } 745 } else if (intrinsic->name == "transfer") { 746 if (call.arguments().size() == 3 && call.arguments().at(2)) { 747 // SIZE= is present; shape is vector [SIZE=] 748 if (const auto *size{ 749 UnwrapExpr<Expr<SomeInteger>>(call.arguments().at(2))}) { 750 return Shape{ 751 MaybeExtentExpr{ConvertToType<ExtentType>(common::Clone(*size))}}; 752 } 753 } else if (context_) { 754 if (auto moldTypeAndShape{characteristics::TypeAndShape::Characterize( 755 call.arguments().at(1), *context_)}) { 756 if (GetRank(moldTypeAndShape->shape()) == 0) { 757 // SIZE= is absent and MOLD= is scalar: result is scalar 758 return ScalarShape(); 759 } else { 760 // SIZE= is absent and MOLD= is array: result is vector whose 761 // length is determined by sizes of types. See 16.9.193p4 case(ii). 762 if (auto sourceTypeAndShape{ 763 characteristics::TypeAndShape::Characterize( 764 call.arguments().at(0), *context_)}) { 765 auto sourceBytes{ 766 sourceTypeAndShape->MeasureSizeInBytes(*context_)}; 767 auto moldElementBytes{ 768 moldTypeAndShape->MeasureElementSizeInBytes(*context_, true)}; 769 if (sourceBytes && moldElementBytes) { 770 ExtentExpr extent{Fold(*context_, 771 (std::move(*sourceBytes) + 772 common::Clone(*moldElementBytes) - ExtentExpr{1}) / 773 common::Clone(*moldElementBytes))}; 774 return Shape{MaybeExtentExpr{std::move(extent)}}; 775 } 776 } 777 } 778 } 779 } 780 } else if (intrinsic->name == "transpose") { 781 if (call.arguments().size() >= 1) { 782 if (auto shape{(*this)(call.arguments().at(0))}) { 783 if (shape->size() == 2) { 784 std::swap((*shape)[0], (*shape)[1]); 785 return shape; 786 } 787 } 788 } 789 } else if (intrinsic->name == "unpack") { 790 if (call.arguments().size() >= 2) { 791 return (*this)(call.arguments()[1]); // MASK= 792 } 793 } else if (intrinsic->characteristics.value().attrs.test(characteristics:: 794 Procedure::Attr::NullPointer)) { // NULL(MOLD=) 795 return (*this)(call.arguments()); 796 } else { 797 // TODO: shapes of other non-elemental intrinsic results 798 } 799 } 800 return std::nullopt; 801 } 802 803 // Check conformance of the passed shapes. 804 std::optional<bool> CheckConformance(parser::ContextualMessages &messages, 805 const Shape &left, const Shape &right, CheckConformanceFlags::Flags flags, 806 const char *leftIs, const char *rightIs) { 807 int n{GetRank(left)}; 808 if (n == 0 && (flags & CheckConformanceFlags::LeftScalarExpandable)) { 809 return true; 810 } 811 int rn{GetRank(right)}; 812 if (rn == 0 && (flags & CheckConformanceFlags::RightScalarExpandable)) { 813 return true; 814 } 815 if (n != rn) { 816 messages.Say("Rank of %1$s is %2$d, but %3$s has rank %4$d"_err_en_US, 817 leftIs, n, rightIs, rn); 818 return false; 819 } 820 for (int j{0}; j < n; ++j) { 821 if (auto leftDim{ToInt64(left[j])}) { 822 if (auto rightDim{ToInt64(right[j])}) { 823 if (*leftDim != *rightDim) { 824 messages.Say("Dimension %1$d of %2$s has extent %3$jd, " 825 "but %4$s has extent %5$jd"_err_en_US, 826 j + 1, leftIs, *leftDim, rightIs, *rightDim); 827 return false; 828 } 829 } else if (!(flags & CheckConformanceFlags::RightIsDeferredShape)) { 830 return std::nullopt; 831 } 832 } else if (!(flags & CheckConformanceFlags::LeftIsDeferredShape)) { 833 return std::nullopt; 834 } 835 } 836 return true; 837 } 838 839 bool IncrementSubscripts( 840 ConstantSubscripts &indices, const ConstantSubscripts &extents) { 841 std::size_t rank(indices.size()); 842 CHECK(rank <= extents.size()); 843 for (std::size_t j{0}; j < rank; ++j) { 844 if (extents[j] < 1) { 845 return false; 846 } 847 } 848 for (std::size_t j{0}; j < rank; ++j) { 849 if (indices[j]++ < extents[j]) { 850 return true; 851 } 852 indices[j] = 1; 853 } 854 return false; 855 } 856 857 } // namespace Fortran::evaluate 858