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 { 504 int n{object.shape().Rank()}; 505 NamedEntity base{symbol}; 506 return Result{CreateShape(n, base)}; 507 } 508 }, 509 [](const semantics::EntityDetails &) { 510 return ScalarShape(); // no dimensions seen 511 }, 512 [&](const semantics::ProcEntityDetails &proc) { 513 if (const Symbol * interface{proc.interface().symbol()}) { 514 return (*this)(*interface); 515 } else { 516 return ScalarShape(); 517 } 518 }, 519 [&](const semantics::AssocEntityDetails &assoc) { 520 if (!assoc.rank()) { 521 return (*this)(assoc.expr()); 522 } else { 523 int n{assoc.rank().value()}; 524 NamedEntity base{symbol}; 525 return Result{CreateShape(n, base)}; 526 } 527 }, 528 [&](const semantics::SubprogramDetails &subp) { 529 if (subp.isFunction()) { 530 return (*this)(subp.result()); 531 } else { 532 return Result{}; 533 } 534 }, 535 [&](const semantics::ProcBindingDetails &binding) { 536 return (*this)(binding.symbol()); 537 }, 538 [](const semantics::TypeParamDetails &) { return ScalarShape(); }, 539 [](const auto &) { return Result{}; }, 540 }, 541 symbol.GetUltimate().details()); 542 } 543 544 auto GetShapeHelper::operator()(const Component &component) const -> Result { 545 const Symbol &symbol{component.GetLastSymbol()}; 546 int rank{symbol.Rank()}; 547 if (rank == 0) { 548 return (*this)(component.base()); 549 } else if (symbol.has<semantics::ObjectEntityDetails>()) { 550 NamedEntity base{Component{component}}; 551 return CreateShape(rank, base); 552 } else if (symbol.has<semantics::AssocEntityDetails>()) { 553 NamedEntity base{Component{component}}; 554 return Result{CreateShape(rank, base)}; 555 } else { 556 return (*this)(symbol); 557 } 558 } 559 560 auto GetShapeHelper::operator()(const ArrayRef &arrayRef) const -> Result { 561 Shape shape; 562 int dimension{0}; 563 const NamedEntity &base{arrayRef.base()}; 564 for (const Subscript &ss : arrayRef.subscript()) { 565 if (ss.Rank() > 0) { 566 shape.emplace_back(GetExtent(ss, base, dimension)); 567 } 568 ++dimension; 569 } 570 if (shape.empty()) { 571 if (const Component * component{base.UnwrapComponent()}) { 572 return (*this)(component->base()); 573 } 574 } 575 return shape; 576 } 577 578 auto GetShapeHelper::operator()(const CoarrayRef &coarrayRef) const -> Result { 579 NamedEntity base{coarrayRef.GetBase()}; 580 if (coarrayRef.subscript().empty()) { 581 return (*this)(base); 582 } else { 583 Shape shape; 584 int dimension{0}; 585 for (const Subscript &ss : coarrayRef.subscript()) { 586 if (ss.Rank() > 0) { 587 shape.emplace_back(GetExtent(ss, base, dimension)); 588 } 589 ++dimension; 590 } 591 return shape; 592 } 593 } 594 595 auto GetShapeHelper::operator()(const Substring &substring) const -> Result { 596 return (*this)(substring.parent()); 597 } 598 599 auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result { 600 if (call.Rank() == 0) { 601 return ScalarShape(); 602 } else if (call.IsElemental()) { 603 for (const auto &arg : call.arguments()) { 604 if (arg && arg->Rank() > 0) { 605 return (*this)(*arg); 606 } 607 } 608 return ScalarShape(); 609 } else if (const Symbol * symbol{call.proc().GetSymbol()}) { 610 return (*this)(*symbol); 611 } else if (const auto *intrinsic{call.proc().GetSpecificIntrinsic()}) { 612 if (intrinsic->name == "shape" || intrinsic->name == "lbound" || 613 intrinsic->name == "ubound") { 614 // These are the array-valued cases for LBOUND and UBOUND (no DIM=). 615 const auto *expr{call.arguments().front().value().UnwrapExpr()}; 616 CHECK(expr); 617 return Shape{MaybeExtentExpr{ExtentExpr{expr->Rank()}}}; 618 } else if (intrinsic->name == "all" || intrinsic->name == "any" || 619 intrinsic->name == "count" || intrinsic->name == "iall" || 620 intrinsic->name == "iany" || intrinsic->name == "iparity" || 621 intrinsic->name == "maxval" || intrinsic->name == "minval" || 622 intrinsic->name == "norm2" || intrinsic->name == "parity" || 623 intrinsic->name == "product" || intrinsic->name == "sum") { 624 // Reduction with DIM= 625 if (call.arguments().size() >= 2) { 626 auto arrayShape{ 627 (*this)(UnwrapExpr<Expr<SomeType>>(call.arguments().at(0)))}; 628 const auto *dimArg{UnwrapExpr<Expr<SomeType>>(call.arguments().at(1))}; 629 if (arrayShape && dimArg) { 630 if (auto dim{ToInt64(*dimArg)}) { 631 if (*dim >= 1 && 632 static_cast<std::size_t>(*dim) <= arrayShape->size()) { 633 arrayShape->erase(arrayShape->begin() + (*dim - 1)); 634 return std::move(*arrayShape); 635 } 636 } 637 } 638 } 639 } else if (intrinsic->name == "maxloc" || intrinsic->name == "minloc") { 640 // TODO: FINDLOC 641 if (call.arguments().size() >= 2) { 642 if (auto arrayShape{ 643 (*this)(UnwrapExpr<Expr<SomeType>>(call.arguments().at(0)))}) { 644 auto rank{static_cast<int>(arrayShape->size())}; 645 if (const auto *dimArg{ 646 UnwrapExpr<Expr<SomeType>>(call.arguments()[1])}) { 647 auto dim{ToInt64(*dimArg)}; 648 if (dim && *dim >= 1 && *dim <= rank) { 649 arrayShape->erase(arrayShape->begin() + (*dim - 1)); 650 return std::move(*arrayShape); 651 } 652 } else { 653 // xxxLOC(no DIM=) result is vector(1:RANK(ARRAY=)) 654 return Shape{ExtentExpr{rank}}; 655 } 656 } 657 } 658 } else if (intrinsic->name == "cshift" || intrinsic->name == "eoshift") { 659 if (!call.arguments().empty()) { 660 return (*this)(call.arguments()[0]); 661 } 662 } else if (intrinsic->name == "matmul") { 663 if (call.arguments().size() == 2) { 664 if (auto ashape{(*this)(call.arguments()[0])}) { 665 if (auto bshape{(*this)(call.arguments()[1])}) { 666 if (ashape->size() == 1 && bshape->size() == 2) { 667 bshape->erase(bshape->begin()); 668 return std::move(*bshape); // matmul(vector, matrix) 669 } else if (ashape->size() == 2 && bshape->size() == 1) { 670 ashape->pop_back(); 671 return std::move(*ashape); // matmul(matrix, vector) 672 } else if (ashape->size() == 2 && bshape->size() == 2) { 673 (*ashape)[1] = std::move((*bshape)[1]); 674 return std::move(*ashape); // matmul(matrix, matrix) 675 } 676 } 677 } 678 } 679 } else if (intrinsic->name == "reshape") { 680 if (call.arguments().size() >= 2 && call.arguments().at(1)) { 681 // SHAPE(RESHAPE(array,shape)) -> shape 682 if (const auto *shapeExpr{ 683 call.arguments().at(1).value().UnwrapExpr()}) { 684 auto shape{std::get<Expr<SomeInteger>>(shapeExpr->u)}; 685 return AsShape(ConvertToType<ExtentType>(std::move(shape))); 686 } 687 } 688 } else if (intrinsic->name == "pack") { 689 if (call.arguments().size() >= 3 && call.arguments().at(2)) { 690 // SHAPE(PACK(,,VECTOR=v)) -> SHAPE(v) 691 return (*this)(call.arguments().at(2)); 692 } else if (call.arguments().size() >= 2 && context_) { 693 if (auto maskShape{(*this)(call.arguments().at(1))}) { 694 if (maskShape->size() == 0) { 695 // Scalar MASK= -> [MERGE(SIZE(ARRAY=), 0, mask)] 696 if (auto arrayShape{(*this)(call.arguments().at(0))}) { 697 auto arraySize{GetSize(std::move(*arrayShape))}; 698 CHECK(arraySize); 699 ActualArguments toMerge{ 700 ActualArgument{AsGenericExpr(std::move(*arraySize))}, 701 ActualArgument{AsGenericExpr(ExtentExpr{0})}, 702 common::Clone(call.arguments().at(1))}; 703 auto specific{context_->intrinsics().Probe( 704 CallCharacteristics{"merge"}, toMerge, *context_)}; 705 CHECK(specific); 706 return Shape{ExtentExpr{FunctionRef<ExtentType>{ 707 ProcedureDesignator{std::move(specific->specificIntrinsic)}, 708 std::move(specific->arguments)}}}; 709 } 710 } else { 711 // Non-scalar MASK= -> [COUNT(mask)] 712 ActualArguments toCount{ActualArgument{common::Clone( 713 DEREF(call.arguments().at(1).value().UnwrapExpr()))}}; 714 auto specific{context_->intrinsics().Probe( 715 CallCharacteristics{"count"}, toCount, *context_)}; 716 CHECK(specific); 717 return Shape{ExtentExpr{FunctionRef<ExtentType>{ 718 ProcedureDesignator{std::move(specific->specificIntrinsic)}, 719 std::move(specific->arguments)}}}; 720 } 721 } 722 } 723 } else if (intrinsic->name == "spread") { 724 // SHAPE(SPREAD(ARRAY,DIM,NCOPIES)) = SHAPE(ARRAY) with NCOPIES inserted 725 // at position DIM. 726 if (call.arguments().size() == 3) { 727 auto arrayShape{ 728 (*this)(UnwrapExpr<Expr<SomeType>>(call.arguments().at(0)))}; 729 const auto *dimArg{UnwrapExpr<Expr<SomeType>>(call.arguments().at(1))}; 730 const auto *nCopies{ 731 UnwrapExpr<Expr<SomeInteger>>(call.arguments().at(2))}; 732 if (arrayShape && dimArg && nCopies) { 733 if (auto dim{ToInt64(*dimArg)}) { 734 if (*dim >= 1 && 735 static_cast<std::size_t>(*dim) <= arrayShape->size() + 1) { 736 arrayShape->emplace(arrayShape->begin() + *dim - 1, 737 ConvertToType<ExtentType>(common::Clone(*nCopies))); 738 return std::move(*arrayShape); 739 } 740 } 741 } 742 } 743 } else if (intrinsic->name == "transfer") { 744 if (call.arguments().size() == 3 && call.arguments().at(2)) { 745 // SIZE= is present; shape is vector [SIZE=] 746 if (const auto *size{ 747 UnwrapExpr<Expr<SomeInteger>>(call.arguments().at(2))}) { 748 return Shape{ 749 MaybeExtentExpr{ConvertToType<ExtentType>(common::Clone(*size))}}; 750 } 751 } else if (context_) { 752 if (auto moldTypeAndShape{characteristics::TypeAndShape::Characterize( 753 call.arguments().at(1), *context_)}) { 754 if (GetRank(moldTypeAndShape->shape()) == 0) { 755 // SIZE= is absent and MOLD= is scalar: result is scalar 756 return ScalarShape(); 757 } else { 758 // SIZE= is absent and MOLD= is array: result is vector whose 759 // length is determined by sizes of types. See 16.9.193p4 case(ii). 760 if (auto sourceTypeAndShape{ 761 characteristics::TypeAndShape::Characterize( 762 call.arguments().at(0), *context_)}) { 763 auto sourceBytes{ 764 sourceTypeAndShape->MeasureSizeInBytes(*context_)}; 765 auto moldElementBytes{ 766 moldTypeAndShape->MeasureElementSizeInBytes(*context_, true)}; 767 if (sourceBytes && moldElementBytes) { 768 ExtentExpr extent{Fold(*context_, 769 (std::move(*sourceBytes) + 770 common::Clone(*moldElementBytes) - ExtentExpr{1}) / 771 common::Clone(*moldElementBytes))}; 772 return Shape{MaybeExtentExpr{std::move(extent)}}; 773 } 774 } 775 } 776 } 777 } 778 } else if (intrinsic->name == "transpose") { 779 if (call.arguments().size() >= 1) { 780 if (auto shape{(*this)(call.arguments().at(0))}) { 781 if (shape->size() == 2) { 782 std::swap((*shape)[0], (*shape)[1]); 783 return shape; 784 } 785 } 786 } 787 } else if (intrinsic->name == "unpack") { 788 if (call.arguments().size() >= 2) { 789 return (*this)(call.arguments()[1]); // MASK= 790 } 791 } else if (intrinsic->characteristics.value().attrs.test(characteristics:: 792 Procedure::Attr::NullPointer)) { // NULL(MOLD=) 793 return (*this)(call.arguments()); 794 } else { 795 // TODO: shapes of other non-elemental intrinsic results 796 } 797 } 798 return std::nullopt; 799 } 800 801 // Check conformance of the passed shapes. 802 std::optional<bool> CheckConformance(parser::ContextualMessages &messages, 803 const Shape &left, const Shape &right, CheckConformanceFlags::Flags flags, 804 const char *leftIs, const char *rightIs) { 805 int n{GetRank(left)}; 806 if (n == 0 && (flags & CheckConformanceFlags::LeftScalarExpandable)) { 807 return true; 808 } 809 int rn{GetRank(right)}; 810 if (rn == 0 && (flags & CheckConformanceFlags::RightScalarExpandable)) { 811 return true; 812 } 813 if (n != rn) { 814 messages.Say("Rank of %1$s is %2$d, but %3$s has rank %4$d"_err_en_US, 815 leftIs, n, rightIs, rn); 816 return false; 817 } 818 for (int j{0}; j < n; ++j) { 819 if (auto leftDim{ToInt64(left[j])}) { 820 if (auto rightDim{ToInt64(right[j])}) { 821 if (*leftDim != *rightDim) { 822 messages.Say("Dimension %1$d of %2$s has extent %3$jd, " 823 "but %4$s has extent %5$jd"_err_en_US, 824 j + 1, leftIs, *leftDim, rightIs, *rightDim); 825 return false; 826 } 827 } else if (!(flags & CheckConformanceFlags::RightIsDeferredShape)) { 828 return std::nullopt; 829 } 830 } else if (!(flags & CheckConformanceFlags::LeftIsDeferredShape)) { 831 return std::nullopt; 832 } 833 } 834 return true; 835 } 836 837 bool IncrementSubscripts( 838 ConstantSubscripts &indices, const ConstantSubscripts &extents) { 839 std::size_t rank(indices.size()); 840 CHECK(rank <= extents.size()); 841 for (std::size_t j{0}; j < rank; ++j) { 842 if (extents[j] < 1) { 843 return false; 844 } 845 } 846 for (std::size_t j{0}; j < rank; ++j) { 847 if (indices[j]++ < extents[j]) { 848 return true; 849 } 850 indices[j] = 1; 851 } 852 return false; 853 } 854 855 } // namespace Fortran::evaluate 856