1 //===-- lib/Evaluate/characteristics.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/characteristics.h" 10 #include "flang/Common/indirection.h" 11 #include "flang/Evaluate/check-expression.h" 12 #include "flang/Evaluate/fold.h" 13 #include "flang/Evaluate/intrinsics.h" 14 #include "flang/Evaluate/tools.h" 15 #include "flang/Evaluate/type.h" 16 #include "flang/Parser/message.h" 17 #include "flang/Semantics/scope.h" 18 #include "flang/Semantics/symbol.h" 19 #include "llvm/Support/raw_ostream.h" 20 #include <initializer_list> 21 22 using namespace Fortran::parser::literals; 23 24 namespace Fortran::evaluate::characteristics { 25 26 // Copy attributes from a symbol to dst based on the mapping in pairs. 27 template <typename A, typename B> 28 static void CopyAttrs(const semantics::Symbol &src, A &dst, 29 const std::initializer_list<std::pair<semantics::Attr, B>> &pairs) { 30 for (const auto &pair : pairs) { 31 if (src.attrs().test(pair.first)) { 32 dst.attrs.set(pair.second); 33 } 34 } 35 } 36 37 // Shapes of function results and dummy arguments have to have 38 // the same rank, the same deferred dimensions, and the same 39 // values for explicit dimensions when constant. 40 bool ShapesAreCompatible(const Shape &x, const Shape &y) { 41 if (x.size() != y.size()) { 42 return false; 43 } 44 auto yIter{y.begin()}; 45 for (const auto &xDim : x) { 46 const auto &yDim{*yIter++}; 47 if (xDim) { 48 if (!yDim || ToInt64(*xDim) != ToInt64(*yDim)) { 49 return false; 50 } 51 } else if (yDim) { 52 return false; 53 } 54 } 55 return true; 56 } 57 58 bool TypeAndShape::operator==(const TypeAndShape &that) const { 59 return type_ == that.type_ && ShapesAreCompatible(shape_, that.shape_) && 60 attrs_ == that.attrs_ && corank_ == that.corank_; 61 } 62 63 std::optional<TypeAndShape> TypeAndShape::Characterize( 64 const semantics::Symbol &symbol, FoldingContext &context) { 65 return std::visit( 66 common::visitors{ 67 [&](const semantics::ObjectEntityDetails &object) { 68 return Characterize(object); 69 }, 70 [&](const semantics::ProcEntityDetails &proc) { 71 const semantics::ProcInterface &interface{proc.interface()}; 72 if (interface.type()) { 73 return Characterize(*interface.type()); 74 } else if (interface.symbol()) { 75 return Characterize(*interface.symbol(), context); 76 } else { 77 return std::optional<TypeAndShape>{}; 78 } 79 }, 80 [&](const semantics::UseDetails &use) { 81 return Characterize(use.symbol(), context); 82 }, 83 [&](const semantics::HostAssocDetails &assoc) { 84 return Characterize(assoc.symbol(), context); 85 }, 86 [&](const semantics::AssocEntityDetails &assoc) { 87 return Characterize(assoc, context); 88 }, 89 [](const auto &) { return std::optional<TypeAndShape>{}; }, 90 }, 91 symbol.details()); 92 } 93 94 std::optional<TypeAndShape> TypeAndShape::Characterize( 95 const semantics::ObjectEntityDetails &object) { 96 if (auto type{DynamicType::From(object.type())}) { 97 TypeAndShape result{std::move(*type)}; 98 result.AcquireShape(object); 99 return result; 100 } else { 101 return std::nullopt; 102 } 103 } 104 105 std::optional<TypeAndShape> TypeAndShape::Characterize( 106 const semantics::AssocEntityDetails &assoc, FoldingContext &context) { 107 if (auto type{DynamicType::From(assoc.type())}) { 108 if (auto shape{GetShape(context, assoc.expr())}) { 109 return TypeAndShape{std::move(*type), std::move(*shape)}; 110 } 111 } 112 return std::nullopt; 113 } 114 115 std::optional<TypeAndShape> TypeAndShape::Characterize( 116 const semantics::DeclTypeSpec &spec) { 117 if (auto type{DynamicType::From(spec)}) { 118 return TypeAndShape{std::move(*type)}; 119 } else { 120 return std::nullopt; 121 } 122 } 123 124 bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages, 125 const TypeAndShape &that, const char *thisIs, const char *thatIs, 126 bool isElemental) const { 127 const auto &len{that.LEN()}; 128 if (!type_.IsTypeCompatibleWith(that.type_)) { 129 messages.Say( 130 "%1$s type '%2$s' is not compatible with %3$s type '%4$s'"_err_en_US, 131 thatIs, that.type_.AsFortran(len ? len->AsFortran() : ""), thisIs, 132 type_.AsFortran()); 133 return false; 134 } 135 return isElemental || 136 CheckConformance(messages, shape_, that.shape_, thisIs, thatIs); 137 } 138 139 void TypeAndShape::AcquireShape(const semantics::ObjectEntityDetails &object) { 140 CHECK(shape_.empty() && !attrs_.test(Attr::AssumedRank)); 141 corank_ = object.coshape().Rank(); 142 if (object.IsAssumedRank()) { 143 attrs_.set(Attr::AssumedRank); 144 return; 145 } 146 if (object.IsAssumedShape()) { 147 attrs_.set(Attr::AssumedShape); 148 } 149 if (object.IsAssumedSize()) { 150 attrs_.set(Attr::AssumedSize); 151 } 152 if (object.IsDeferredShape()) { 153 attrs_.set(Attr::DeferredShape); 154 } 155 if (object.IsCoarray()) { 156 attrs_.set(Attr::Coarray); 157 } 158 for (const semantics::ShapeSpec &dim : object.shape()) { 159 if (dim.ubound().GetExplicit()) { 160 Expr<SubscriptInteger> extent{*dim.ubound().GetExplicit()}; 161 if (auto lbound{dim.lbound().GetExplicit()}) { 162 extent = 163 std::move(extent) + Expr<SubscriptInteger>{1} - std::move(*lbound); 164 } 165 shape_.emplace_back(std::move(extent)); 166 } else { 167 shape_.push_back(std::nullopt); 168 } 169 } 170 } 171 172 void TypeAndShape::AcquireLEN() { 173 if (type_.category() == TypeCategory::Character) { 174 if (const auto *param{type_.charLength()}) { 175 if (const auto &intExpr{param->GetExplicit()}) { 176 LEN_ = *intExpr; 177 } 178 } 179 } 180 } 181 182 llvm::raw_ostream &TypeAndShape::Dump(llvm::raw_ostream &o) const { 183 o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : ""); 184 attrs_.Dump(o, EnumToString); 185 if (!shape_.empty()) { 186 o << " dimension("; 187 char sep{'('}; 188 for (const auto &expr : shape_) { 189 o << sep; 190 sep = ','; 191 if (expr) { 192 expr->AsFortran(o); 193 } else { 194 o << ':'; 195 } 196 } 197 o << ')'; 198 } 199 return o; 200 } 201 202 bool DummyDataObject::operator==(const DummyDataObject &that) const { 203 return type == that.type && attrs == that.attrs && intent == that.intent && 204 coshape == that.coshape; 205 } 206 207 static common::Intent GetIntent(const semantics::Attrs &attrs) { 208 if (attrs.test(semantics::Attr::INTENT_IN)) { 209 return common::Intent::In; 210 } else if (attrs.test(semantics::Attr::INTENT_OUT)) { 211 return common::Intent::Out; 212 } else if (attrs.test(semantics::Attr::INTENT_INOUT)) { 213 return common::Intent::InOut; 214 } else { 215 return common::Intent::Default; 216 } 217 } 218 219 std::optional<DummyDataObject> DummyDataObject::Characterize( 220 const semantics::Symbol &symbol) { 221 if (const auto *obj{symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 222 if (auto type{TypeAndShape::Characterize(*obj)}) { 223 std::optional<DummyDataObject> result{std::move(*type)}; 224 using semantics::Attr; 225 CopyAttrs<DummyDataObject, DummyDataObject::Attr>(symbol, *result, 226 { 227 {Attr::OPTIONAL, DummyDataObject::Attr::Optional}, 228 {Attr::ALLOCATABLE, DummyDataObject::Attr::Allocatable}, 229 {Attr::ASYNCHRONOUS, DummyDataObject::Attr::Asynchronous}, 230 {Attr::CONTIGUOUS, DummyDataObject::Attr::Contiguous}, 231 {Attr::VALUE, DummyDataObject::Attr::Value}, 232 {Attr::VOLATILE, DummyDataObject::Attr::Volatile}, 233 {Attr::POINTER, DummyDataObject::Attr::Pointer}, 234 {Attr::TARGET, DummyDataObject::Attr::Target}, 235 }); 236 result->intent = GetIntent(symbol.attrs()); 237 return result; 238 } 239 } 240 return std::nullopt; 241 } 242 243 bool DummyDataObject::CanBePassedViaImplicitInterface() const { 244 if ((attrs & 245 Attrs{Attr::Allocatable, Attr::Asynchronous, Attr::Optional, 246 Attr::Pointer, Attr::Target, Attr::Value, Attr::Volatile}) 247 .any()) { 248 return false; // 15.4.2.2(3)(a) 249 } else if ((type.attrs() & 250 TypeAndShape::Attrs{TypeAndShape::Attr::AssumedShape, 251 TypeAndShape::Attr::AssumedRank, 252 TypeAndShape::Attr::Coarray}) 253 .any()) { 254 return false; // 15.4.2.2(3)(b-d) 255 } else if (type.type().IsPolymorphic()) { 256 return false; // 15.4.2.2(3)(f) 257 } else if (const auto *derived{GetDerivedTypeSpec(type.type())}) { 258 return derived->parameters().empty(); // 15.4.2.2(3)(e) 259 } else { 260 return true; 261 } 262 } 263 264 llvm::raw_ostream &DummyDataObject::Dump(llvm::raw_ostream &o) const { 265 attrs.Dump(o, EnumToString); 266 if (intent != common::Intent::Default) { 267 o << "INTENT(" << common::EnumToString(intent) << ')'; 268 } 269 type.Dump(o); 270 if (!coshape.empty()) { 271 char sep{'['}; 272 for (const auto &expr : coshape) { 273 expr.AsFortran(o << sep); 274 sep = ','; 275 } 276 } 277 return o; 278 } 279 280 DummyProcedure::DummyProcedure(Procedure &&p) 281 : procedure{new Procedure{std::move(p)}} {} 282 283 bool DummyProcedure::operator==(const DummyProcedure &that) const { 284 return attrs == that.attrs && intent == that.intent && 285 procedure.value() == that.procedure.value(); 286 } 287 288 std::optional<DummyProcedure> DummyProcedure::Characterize( 289 const semantics::Symbol &symbol, const IntrinsicProcTable &intrinsics) { 290 if (auto procedure{Procedure::Characterize(symbol, intrinsics)}) { 291 // Dummy procedures may not be elemental. Elemental dummy procedure 292 // interfaces are errors when the interface is not intrinsic, and that 293 // error is caught elsewhere. Elemental intrinsic interfaces are 294 // made non-elemental. 295 procedure->attrs.reset(Procedure::Attr::Elemental); 296 DummyProcedure result{std::move(procedure.value())}; 297 CopyAttrs<DummyProcedure, DummyProcedure::Attr>(symbol, result, 298 { 299 {semantics::Attr::OPTIONAL, DummyProcedure::Attr::Optional}, 300 {semantics::Attr::POINTER, DummyProcedure::Attr::Pointer}, 301 }); 302 result.intent = GetIntent(symbol.attrs()); 303 return result; 304 } else { 305 return std::nullopt; 306 } 307 } 308 309 llvm::raw_ostream &DummyProcedure::Dump(llvm::raw_ostream &o) const { 310 attrs.Dump(o, EnumToString); 311 if (intent != common::Intent::Default) { 312 o << "INTENT(" << common::EnumToString(intent) << ')'; 313 } 314 procedure.value().Dump(o); 315 return o; 316 } 317 318 llvm::raw_ostream &AlternateReturn::Dump(llvm::raw_ostream &o) const { 319 return o << '*'; 320 } 321 322 DummyArgument::~DummyArgument() {} 323 324 bool DummyArgument::operator==(const DummyArgument &that) const { 325 return u == that.u; // name and passed-object usage are not characteristics 326 } 327 328 std::optional<DummyArgument> DummyArgument::Characterize( 329 const semantics::Symbol &symbol, const IntrinsicProcTable &intrinsics) { 330 auto name{symbol.name().ToString()}; 331 if (symbol.has<semantics::ObjectEntityDetails>()) { 332 if (auto obj{DummyDataObject::Characterize(symbol)}) { 333 return DummyArgument{std::move(name), std::move(obj.value())}; 334 } 335 } else if (auto proc{DummyProcedure::Characterize(symbol, intrinsics)}) { 336 return DummyArgument{std::move(name), std::move(proc.value())}; 337 } 338 return std::nullopt; 339 } 340 341 std::optional<DummyArgument> DummyArgument::FromActual( 342 std::string &&name, const Expr<SomeType> &expr, FoldingContext &context) { 343 return std::visit( 344 common::visitors{ 345 [&](const BOZLiteralConstant &) { 346 return std::make_optional<DummyArgument>(std::move(name), 347 DummyDataObject{ 348 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}}); 349 }, 350 [](const NullPointer &) { return std::optional<DummyArgument>{}; }, 351 [&](const ProcedureDesignator &designator) { 352 if (auto proc{Procedure::Characterize( 353 designator, context.intrinsics())}) { 354 return std::make_optional<DummyArgument>( 355 std::move(name), DummyProcedure{std::move(*proc)}); 356 } else { 357 return std::optional<DummyArgument>{}; 358 } 359 }, 360 [&](const ProcedureRef &call) { 361 if (auto proc{ 362 Procedure::Characterize(call, context.intrinsics())}) { 363 return std::make_optional<DummyArgument>( 364 std::move(name), DummyProcedure{std::move(*proc)}); 365 } else { 366 return std::optional<DummyArgument>{}; 367 } 368 }, 369 [&](const auto &) { 370 if (auto type{expr.GetType()}) { 371 if (auto shape{GetShape(context, expr)}) { 372 return std::make_optional<DummyArgument>(std::move(name), 373 DummyDataObject{TypeAndShape{*type, std::move(*shape)}}); 374 } else { 375 return std::make_optional<DummyArgument>( 376 std::move(name), DummyDataObject{TypeAndShape{*type}}); 377 } 378 } else { 379 return std::optional<DummyArgument>{}; 380 } 381 }, 382 }, 383 expr.u); 384 } 385 386 bool DummyArgument::IsOptional() const { 387 return std::visit( 388 common::visitors{ 389 [](const DummyDataObject &data) { 390 return data.attrs.test(DummyDataObject::Attr::Optional); 391 }, 392 [](const DummyProcedure &proc) { 393 return proc.attrs.test(DummyProcedure::Attr::Optional); 394 }, 395 [](const AlternateReturn &) { return false; }, 396 }, 397 u); 398 } 399 400 void DummyArgument::SetOptional(bool value) { 401 std::visit(common::visitors{ 402 [value](DummyDataObject &data) { 403 data.attrs.set(DummyDataObject::Attr::Optional, value); 404 }, 405 [value](DummyProcedure &proc) { 406 proc.attrs.set(DummyProcedure::Attr::Optional, value); 407 }, 408 [](AlternateReturn &) { DIE("cannot set optional"); }, 409 }, 410 u); 411 } 412 413 bool DummyArgument::CanBePassedViaImplicitInterface() const { 414 if (const auto *object{std::get_if<DummyDataObject>(&u)}) { 415 return object->CanBePassedViaImplicitInterface(); 416 } else { 417 return true; 418 } 419 } 420 421 llvm::raw_ostream &DummyArgument::Dump(llvm::raw_ostream &o) const { 422 if (!name.empty()) { 423 o << name << '='; 424 } 425 if (pass) { 426 o << " PASS"; 427 } 428 std::visit([&](const auto &x) { x.Dump(o); }, u); 429 return o; 430 } 431 432 FunctionResult::FunctionResult(DynamicType t) : u{TypeAndShape{t}} {} 433 FunctionResult::FunctionResult(TypeAndShape &&t) : u{std::move(t)} {} 434 FunctionResult::FunctionResult(Procedure &&p) : u{std::move(p)} {} 435 FunctionResult::~FunctionResult() {} 436 437 bool FunctionResult::operator==(const FunctionResult &that) const { 438 return attrs == that.attrs && u == that.u; 439 } 440 441 std::optional<FunctionResult> FunctionResult::Characterize( 442 const Symbol &symbol, const IntrinsicProcTable &intrinsics) { 443 if (const auto *obj{symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 444 if (auto type{TypeAndShape::Characterize(*obj)}) { 445 FunctionResult result{std::move(*type)}; 446 CopyAttrs<FunctionResult, FunctionResult::Attr>(symbol, result, 447 { 448 {semantics::Attr::ALLOCATABLE, FunctionResult::Attr::Allocatable}, 449 {semantics::Attr::CONTIGUOUS, FunctionResult::Attr::Contiguous}, 450 {semantics::Attr::POINTER, FunctionResult::Attr::Pointer}, 451 }); 452 return result; 453 } 454 } else if (auto maybeProc{Procedure::Characterize(symbol, intrinsics)}) { 455 FunctionResult result{std::move(*maybeProc)}; 456 result.attrs.set(FunctionResult::Attr::Pointer); 457 return result; 458 } 459 return std::nullopt; 460 } 461 462 bool FunctionResult::IsAssumedLengthCharacter() const { 463 if (const auto *ts{std::get_if<TypeAndShape>(&u)}) { 464 return ts->type().IsAssumedLengthCharacter(); 465 } else { 466 return false; 467 } 468 } 469 470 bool FunctionResult::CanBeReturnedViaImplicitInterface() const { 471 if (attrs.test(Attr::Pointer) || attrs.test(Attr::Allocatable)) { 472 return false; // 15.4.2.2(4)(b) 473 } else if (const auto *typeAndShape{GetTypeAndShape()}) { 474 if (typeAndShape->Rank() > 0) { 475 return false; // 15.4.2.2(4)(a) 476 } else { 477 const DynamicType &type{typeAndShape->type()}; 478 switch (type.category()) { 479 case TypeCategory::Character: 480 if (const auto *param{type.charLength()}) { 481 if (const auto &expr{param->GetExplicit()}) { 482 return IsConstantExpr(*expr); // 15.4.2.2(4)(c) 483 } 484 } 485 return false; 486 case TypeCategory::Derived: 487 if (!type.IsPolymorphic()) { 488 const auto &spec{type.GetDerivedTypeSpec()}; 489 for (const auto &pair : spec.parameters()) { 490 if (const auto &expr{pair.second.GetExplicit()}) { 491 if (!IsConstantExpr(*expr)) { 492 return false; // 15.4.2.2(4)(c) 493 } 494 } 495 } 496 return true; 497 } 498 return false; 499 default: 500 return true; 501 } 502 } 503 } else { 504 return false; // 15.4.2.2(4)(b) - procedure pointer 505 } 506 } 507 508 llvm::raw_ostream &FunctionResult::Dump(llvm::raw_ostream &o) const { 509 attrs.Dump(o, EnumToString); 510 std::visit(common::visitors{ 511 [&](const TypeAndShape &ts) { ts.Dump(o); }, 512 [&](const CopyableIndirection<Procedure> &p) { 513 p.value().Dump(o << " procedure(") << ')'; 514 }, 515 }, 516 u); 517 return o; 518 } 519 520 Procedure::Procedure(FunctionResult &&fr, DummyArguments &&args, Attrs a) 521 : functionResult{std::move(fr)}, dummyArguments{std::move(args)}, attrs{a} { 522 } 523 Procedure::Procedure(DummyArguments &&args, Attrs a) 524 : dummyArguments{std::move(args)}, attrs{a} {} 525 Procedure::~Procedure() {} 526 527 bool Procedure::operator==(const Procedure &that) const { 528 return attrs == that.attrs && functionResult == that.functionResult && 529 dummyArguments == that.dummyArguments; 530 } 531 532 int Procedure::FindPassIndex(std::optional<parser::CharBlock> name) const { 533 int argCount{static_cast<int>(dummyArguments.size())}; 534 int index{0}; 535 if (name) { 536 while (index < argCount && *name != dummyArguments[index].name.c_str()) { 537 ++index; 538 } 539 } 540 CHECK(index < argCount); 541 return index; 542 } 543 544 bool Procedure::CanOverride( 545 const Procedure &that, std::optional<int> passIndex) const { 546 // A pure procedure may override an impure one (7.5.7.3(2)) 547 if ((that.attrs.test(Attr::Pure) && !attrs.test(Attr::Pure)) || 548 that.attrs.test(Attr::Elemental) != attrs.test(Attr::Elemental) || 549 functionResult != that.functionResult) { 550 return false; 551 } 552 int argCount{static_cast<int>(dummyArguments.size())}; 553 if (argCount != static_cast<int>(that.dummyArguments.size())) { 554 return false; 555 } 556 for (int j{0}; j < argCount; ++j) { 557 if ((!passIndex || j != *passIndex) && 558 dummyArguments[j] != that.dummyArguments[j]) { 559 return false; 560 } 561 } 562 return true; 563 } 564 565 std::optional<Procedure> Procedure::Characterize( 566 const semantics::Symbol &symbol, const IntrinsicProcTable &intrinsics) { 567 Procedure result; 568 CopyAttrs<Procedure, Procedure::Attr>(symbol, result, 569 { 570 {semantics::Attr::PURE, Procedure::Attr::Pure}, 571 {semantics::Attr::ELEMENTAL, Procedure::Attr::Elemental}, 572 {semantics::Attr::BIND_C, Procedure::Attr::BindC}, 573 }); 574 if (result.attrs.test(Attr::Elemental) && 575 !symbol.attrs().test(semantics::Attr::IMPURE)) { 576 result.attrs.set(Attr::Pure); // explicitly flag pure procedures 577 } 578 return std::visit( 579 common::visitors{ 580 [&](const semantics::SubprogramDetails &subp) 581 -> std::optional<Procedure> { 582 if (subp.isFunction()) { 583 if (auto fr{FunctionResult::Characterize( 584 subp.result(), intrinsics)}) { 585 result.functionResult = std::move(fr); 586 } else { 587 return std::nullopt; 588 } 589 } else { 590 result.attrs.set(Attr::Subroutine); 591 } 592 for (const semantics::Symbol *arg : subp.dummyArgs()) { 593 if (!arg) { 594 result.dummyArguments.emplace_back(AlternateReturn{}); 595 } else if (auto argCharacteristics{ 596 DummyArgument::Characterize(*arg, intrinsics)}) { 597 result.dummyArguments.emplace_back( 598 std::move(argCharacteristics.value())); 599 } else { 600 return std::nullopt; 601 } 602 } 603 return result; 604 }, 605 [&](const semantics::ProcEntityDetails &proc) 606 -> std::optional<Procedure> { 607 if (symbol.attrs().test(semantics::Attr::INTRINSIC)) { 608 return intrinsics.IsSpecificIntrinsicFunction( 609 symbol.name().ToString()); 610 } 611 const semantics::ProcInterface &interface{proc.interface()}; 612 if (const semantics::Symbol * interfaceSymbol{interface.symbol()}) { 613 return Characterize(*interfaceSymbol, intrinsics); 614 } else { 615 result.attrs.set(Attr::ImplicitInterface); 616 const semantics::DeclTypeSpec *type{interface.type()}; 617 if (symbol.test(semantics::Symbol::Flag::Subroutine)) { 618 // ignore any implicit typing 619 result.attrs.set(Attr::Subroutine); 620 } else if (type) { 621 if (auto resultType{DynamicType::From(*type)}) { 622 result.functionResult = FunctionResult{*resultType}; 623 } else { 624 return std::nullopt; 625 } 626 } else if (symbol.test(semantics::Symbol::Flag::Function)) { 627 return std::nullopt; 628 } 629 // The PASS name, if any, is not a characteristic. 630 return result; 631 } 632 }, 633 [&](const semantics::ProcBindingDetails &binding) { 634 if (auto result{Characterize(binding.symbol(), intrinsics)}) { 635 if (!symbol.attrs().test(semantics::Attr::NOPASS)) { 636 auto passName{binding.passName()}; 637 for (auto &dummy : result->dummyArguments) { 638 if (!passName || dummy.name.c_str() == *passName) { 639 dummy.pass = true; 640 return result; 641 } 642 } 643 DIE("PASS argument missing"); 644 } 645 return result; 646 } else { 647 return std::optional<Procedure>{}; 648 } 649 }, 650 [&](const semantics::UseDetails &use) { 651 return Characterize(use.symbol(), intrinsics); 652 }, 653 [&](const semantics::HostAssocDetails &assoc) { 654 return Characterize(assoc.symbol(), intrinsics); 655 }, 656 [](const auto &) { return std::optional<Procedure>{}; }, 657 }, 658 symbol.details()); 659 } 660 661 std::optional<Procedure> Procedure::Characterize( 662 const ProcedureDesignator &proc, const IntrinsicProcTable &intrinsics) { 663 if (const auto *symbol{proc.GetSymbol()}) { 664 if (auto result{characteristics::Procedure::Characterize( 665 ResolveAssociations(*symbol), intrinsics)}) { 666 return result; 667 } 668 } else if (const auto *intrinsic{proc.GetSpecificIntrinsic()}) { 669 return intrinsic->characteristics.value(); 670 } 671 return std::nullopt; 672 } 673 674 std::optional<Procedure> Procedure::Characterize( 675 const ProcedureRef &ref, const IntrinsicProcTable &intrinsics) { 676 if (auto callee{Characterize(ref.proc(), intrinsics)}) { 677 if (callee->functionResult) { 678 if (const Procedure * 679 proc{callee->functionResult->IsProcedurePointer()}) { 680 return {*proc}; 681 } 682 } 683 } 684 return std::nullopt; 685 } 686 687 bool Procedure::CanBeCalledViaImplicitInterface() const { 688 if (attrs.test(Attr::Elemental) || attrs.test(Attr::BindC)) { 689 return false; // 15.4.2.2(5,6) 690 } else if (IsFunction() && 691 !functionResult->CanBeReturnedViaImplicitInterface()) { 692 return false; 693 } else { 694 for (const DummyArgument &arg : dummyArguments) { 695 if (!arg.CanBePassedViaImplicitInterface()) { 696 return false; 697 } 698 } 699 return true; 700 } 701 } 702 703 llvm::raw_ostream &Procedure::Dump(llvm::raw_ostream &o) const { 704 attrs.Dump(o, EnumToString); 705 if (functionResult) { 706 functionResult->Dump(o << "TYPE(") << ") FUNCTION"; 707 } else { 708 o << "SUBROUTINE"; 709 } 710 char sep{'('}; 711 for (const auto &dummy : dummyArguments) { 712 dummy.Dump(o << sep); 713 sep = ','; 714 } 715 return o << (sep == '(' ? "()" : ")"); 716 } 717 718 // Utility class to determine if Procedures, etc. are distinguishable 719 class DistinguishUtils { 720 public: 721 // Are these procedures distinguishable for a generic name? 722 static bool Distinguishable(const Procedure &, const Procedure &); 723 // Are these procedures distinguishable for a generic operator or assignment? 724 static bool DistinguishableOpOrAssign(const Procedure &, const Procedure &); 725 726 private: 727 struct CountDummyProcedures { 728 CountDummyProcedures(const DummyArguments &args) { 729 for (const DummyArgument &arg : args) { 730 if (std::holds_alternative<DummyProcedure>(arg.u)) { 731 total += 1; 732 notOptional += !arg.IsOptional(); 733 } 734 } 735 } 736 int total{0}; 737 int notOptional{0}; 738 }; 739 740 static bool Rule3Distinguishable(const Procedure &, const Procedure &); 741 static const DummyArgument *Rule1DistinguishingArg( 742 const DummyArguments &, const DummyArguments &); 743 static int FindFirstToDistinguishByPosition( 744 const DummyArguments &, const DummyArguments &); 745 static int FindLastToDistinguishByName( 746 const DummyArguments &, const DummyArguments &); 747 static int CountCompatibleWith(const DummyArgument &, const DummyArguments &); 748 static int CountNotDistinguishableFrom( 749 const DummyArgument &, const DummyArguments &); 750 static bool Distinguishable(const DummyArgument &, const DummyArgument &); 751 static bool Distinguishable(const DummyDataObject &, const DummyDataObject &); 752 static bool Distinguishable(const DummyProcedure &, const DummyProcedure &); 753 static bool Distinguishable(const FunctionResult &, const FunctionResult &); 754 static bool Distinguishable(const TypeAndShape &, const TypeAndShape &); 755 static bool IsTkrCompatible(const DummyArgument &, const DummyArgument &); 756 static bool IsTkrCompatible(const TypeAndShape &, const TypeAndShape &); 757 static const DummyArgument *GetAtEffectivePosition( 758 const DummyArguments &, int); 759 static const DummyArgument *GetPassArg(const Procedure &); 760 }; 761 762 // Simpler distinguishability rules for operators and assignment 763 bool DistinguishUtils::DistinguishableOpOrAssign( 764 const Procedure &proc1, const Procedure &proc2) { 765 auto &args1{proc1.dummyArguments}; 766 auto &args2{proc2.dummyArguments}; 767 if (args1.size() != args2.size()) { 768 return true; // C1511: distinguishable based on number of arguments 769 } 770 for (std::size_t i{0}; i < args1.size(); ++i) { 771 if (Distinguishable(args1[i], args2[i])) { 772 return true; // C1511, C1512: distinguishable based on this arg 773 } 774 } 775 return false; 776 } 777 778 bool DistinguishUtils::Distinguishable( 779 const Procedure &proc1, const Procedure &proc2) { 780 auto &args1{proc1.dummyArguments}; 781 auto &args2{proc2.dummyArguments}; 782 auto count1{CountDummyProcedures(args1)}; 783 auto count2{CountDummyProcedures(args2)}; 784 if (count1.notOptional > count2.total || count2.notOptional > count1.total) { 785 return true; // distinguishable based on C1514 rule 2 786 } 787 if (Rule3Distinguishable(proc1, proc2)) { 788 return true; // distinguishable based on C1514 rule 3 789 } 790 if (Rule1DistinguishingArg(args1, args2)) { 791 return true; // distinguishable based on C1514 rule 1 792 } 793 int pos1{FindFirstToDistinguishByPosition(args1, args2)}; 794 int name1{FindLastToDistinguishByName(args1, args2)}; 795 if (pos1 >= 0 && pos1 <= name1) { 796 return true; // distinguishable based on C1514 rule 4 797 } 798 int pos2{FindFirstToDistinguishByPosition(args2, args1)}; 799 int name2{FindLastToDistinguishByName(args2, args1)}; 800 if (pos2 >= 0 && pos2 <= name2) { 801 return true; // distinguishable based on C1514 rule 4 802 } 803 return false; 804 } 805 806 // C1514 rule 3: Procedures are distinguishable if both have a passed-object 807 // dummy argument and those are distinguishable. 808 bool DistinguishUtils::Rule3Distinguishable( 809 const Procedure &proc1, const Procedure &proc2) { 810 const DummyArgument *pass1{GetPassArg(proc1)}; 811 const DummyArgument *pass2{GetPassArg(proc2)}; 812 return pass1 && pass2 && Distinguishable(*pass1, *pass2); 813 } 814 815 // Find a non-passed-object dummy data object in one of the argument lists 816 // that satisfies C1514 rule 1. I.e. x such that: 817 // - m is the number of dummy data objects in one that are nonoptional, 818 // are not passed-object, that x is TKR compatible with 819 // - n is the number of non-passed-object dummy data objects, in the other 820 // that are not distinguishable from x 821 // - m is greater than n 822 const DummyArgument *DistinguishUtils::Rule1DistinguishingArg( 823 const DummyArguments &args1, const DummyArguments &args2) { 824 auto size1{args1.size()}; 825 auto size2{args2.size()}; 826 for (std::size_t i{0}; i < size1 + size2; ++i) { 827 const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]}; 828 if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) { 829 if (CountCompatibleWith(x, args1) > 830 CountNotDistinguishableFrom(x, args2) || 831 CountCompatibleWith(x, args2) > 832 CountNotDistinguishableFrom(x, args1)) { 833 return &x; 834 } 835 } 836 } 837 return nullptr; 838 } 839 840 // Find the index of the first nonoptional non-passed-object dummy argument 841 // in args1 at an effective position such that either: 842 // - args2 has no dummy argument at that effective position 843 // - the dummy argument at that position is distinguishable from it 844 int DistinguishUtils::FindFirstToDistinguishByPosition( 845 const DummyArguments &args1, const DummyArguments &args2) { 846 int effective{0}; // position of arg1 in list, ignoring passed arg 847 for (std::size_t i{0}; i < args1.size(); ++i) { 848 const DummyArgument &arg1{args1.at(i)}; 849 if (!arg1.pass && !arg1.IsOptional()) { 850 const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)}; 851 if (!arg2 || Distinguishable(arg1, *arg2)) { 852 return i; 853 } 854 } 855 effective += !arg1.pass; 856 } 857 return -1; 858 } 859 860 // Find the index of the last nonoptional non-passed-object dummy argument 861 // in args1 whose name is such that either: 862 // - args2 has no dummy argument with that name 863 // - the dummy argument with that name is distinguishable from it 864 int DistinguishUtils::FindLastToDistinguishByName( 865 const DummyArguments &args1, const DummyArguments &args2) { 866 std::map<std::string, const DummyArgument *> nameToArg; 867 for (const auto &arg2 : args2) { 868 nameToArg.emplace(arg2.name, &arg2); 869 } 870 for (int i = args1.size() - 1; i >= 0; --i) { 871 const DummyArgument &arg1{args1.at(i)}; 872 if (!arg1.pass && !arg1.IsOptional()) { 873 auto it{nameToArg.find(arg1.name)}; 874 if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) { 875 return i; 876 } 877 } 878 } 879 return -1; 880 } 881 882 // Count the dummy data objects in args that are nonoptional, are not 883 // passed-object, and that x is TKR compatible with 884 int DistinguishUtils::CountCompatibleWith( 885 const DummyArgument &x, const DummyArguments &args) { 886 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) { 887 return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y); 888 }); 889 } 890 891 // Return the number of dummy data objects in args that are not 892 // distinguishable from x and not passed-object. 893 int DistinguishUtils::CountNotDistinguishableFrom( 894 const DummyArgument &x, const DummyArguments &args) { 895 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) { 896 return !y.pass && std::holds_alternative<DummyDataObject>(y.u) && 897 !Distinguishable(y, x); 898 }); 899 } 900 901 bool DistinguishUtils::Distinguishable( 902 const DummyArgument &x, const DummyArgument &y) { 903 if (x.u.index() != y.u.index()) { 904 return true; // different kind: data/proc/alt-return 905 } 906 return std::visit( 907 common::visitors{ 908 [&](const DummyDataObject &z) { 909 return Distinguishable(z, std::get<DummyDataObject>(y.u)); 910 }, 911 [&](const DummyProcedure &z) { 912 return Distinguishable(z, std::get<DummyProcedure>(y.u)); 913 }, 914 [&](const AlternateReturn &) { return false; }, 915 }, 916 x.u); 917 } 918 919 bool DistinguishUtils::Distinguishable( 920 const DummyDataObject &x, const DummyDataObject &y) { 921 using Attr = DummyDataObject::Attr; 922 if (Distinguishable(x.type, y.type)) { 923 return true; 924 } else if (x.attrs.test(Attr::Allocatable) && y.attrs.test(Attr::Pointer) && 925 y.intent != common::Intent::In) { 926 return true; 927 } else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) && 928 x.intent != common::Intent::In) { 929 return true; 930 } else { 931 return false; 932 } 933 } 934 935 bool DistinguishUtils::Distinguishable( 936 const DummyProcedure &x, const DummyProcedure &y) { 937 const Procedure &xProc{x.procedure.value()}; 938 const Procedure &yProc{y.procedure.value()}; 939 if (Distinguishable(xProc, yProc)) { 940 return true; 941 } else { 942 const std::optional<FunctionResult> &xResult{xProc.functionResult}; 943 const std::optional<FunctionResult> &yResult{yProc.functionResult}; 944 return xResult ? !yResult || Distinguishable(*xResult, *yResult) 945 : yResult.has_value(); 946 } 947 } 948 949 bool DistinguishUtils::Distinguishable( 950 const FunctionResult &x, const FunctionResult &y) { 951 if (x.u.index() != y.u.index()) { 952 return true; // one is data object, one is procedure 953 } 954 return std::visit( 955 common::visitors{ 956 [&](const TypeAndShape &z) { 957 return Distinguishable(z, std::get<TypeAndShape>(y.u)); 958 }, 959 [&](const CopyableIndirection<Procedure> &z) { 960 return Distinguishable(z.value(), 961 std::get<CopyableIndirection<Procedure>>(y.u).value()); 962 }, 963 }, 964 x.u); 965 } 966 967 bool DistinguishUtils::Distinguishable( 968 const TypeAndShape &x, const TypeAndShape &y) { 969 return !IsTkrCompatible(x, y) && !IsTkrCompatible(y, x); 970 } 971 972 // Compatibility based on type, kind, and rank 973 bool DistinguishUtils::IsTkrCompatible( 974 const DummyArgument &x, const DummyArgument &y) { 975 const auto *obj1{std::get_if<DummyDataObject>(&x.u)}; 976 const auto *obj2{std::get_if<DummyDataObject>(&y.u)}; 977 return obj1 && obj2 && IsTkrCompatible(obj1->type, obj2->type); 978 } 979 bool DistinguishUtils::IsTkrCompatible( 980 const TypeAndShape &x, const TypeAndShape &y) { 981 return x.type().IsTkCompatibleWith(y.type()) && 982 (x.attrs().test(TypeAndShape::Attr::AssumedRank) || 983 y.attrs().test(TypeAndShape::Attr::AssumedRank) || 984 x.Rank() == y.Rank()); 985 } 986 987 // Return the argument at the given index, ignoring the passed arg 988 const DummyArgument *DistinguishUtils::GetAtEffectivePosition( 989 const DummyArguments &args, int index) { 990 for (const DummyArgument &arg : args) { 991 if (!arg.pass) { 992 if (index == 0) { 993 return &arg; 994 } 995 --index; 996 } 997 } 998 return nullptr; 999 } 1000 1001 // Return the passed-object dummy argument of this procedure, if any 1002 const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) { 1003 for (const auto &arg : proc.dummyArguments) { 1004 if (arg.pass) { 1005 return &arg; 1006 } 1007 } 1008 return nullptr; 1009 } 1010 1011 bool Distinguishable(const Procedure &x, const Procedure &y) { 1012 return DistinguishUtils::Distinguishable(x, y); 1013 } 1014 1015 bool DistinguishableOpOrAssign(const Procedure &x, const Procedure &y) { 1016 return DistinguishUtils::DistinguishableOpOrAssign(x, y); 1017 } 1018 1019 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument) 1020 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure) 1021 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult) 1022 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure) 1023 } // namespace Fortran::evaluate::characteristics 1024 1025 template class Fortran::common::Indirection< 1026 Fortran::evaluate::characteristics::Procedure, true>; 1027