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 "flang/Semantics/tools.h" 20 #include "llvm/Support/raw_ostream.h" 21 #include <initializer_list> 22 23 using namespace Fortran::parser::literals; 24 25 namespace Fortran::evaluate::characteristics { 26 27 // Copy attributes from a symbol to dst based on the mapping in pairs. 28 template <typename A, typename B> 29 static void CopyAttrs(const semantics::Symbol &src, A &dst, 30 const std::initializer_list<std::pair<semantics::Attr, B>> &pairs) { 31 for (const auto &pair : pairs) { 32 if (src.attrs().test(pair.first)) { 33 dst.attrs.set(pair.second); 34 } 35 } 36 } 37 38 // Shapes of function results and dummy arguments have to have 39 // the same rank, the same deferred dimensions, and the same 40 // values for explicit dimensions when constant. 41 bool ShapesAreCompatible(const Shape &x, const Shape &y) { 42 if (x.size() != y.size()) { 43 return false; 44 } 45 auto yIter{y.begin()}; 46 for (const auto &xDim : x) { 47 const auto &yDim{*yIter++}; 48 if (xDim) { 49 if (!yDim || ToInt64(*xDim) != ToInt64(*yDim)) { 50 return false; 51 } 52 } else if (yDim) { 53 return false; 54 } 55 } 56 return true; 57 } 58 59 bool TypeAndShape::operator==(const TypeAndShape &that) const { 60 return type_ == that.type_ && ShapesAreCompatible(shape_, that.shape_) && 61 attrs_ == that.attrs_ && corank_ == that.corank_; 62 } 63 64 TypeAndShape &TypeAndShape::Rewrite(FoldingContext &context) { 65 LEN_ = Fold(context, std::move(LEN_)); 66 if (LEN_) { 67 if (auto n{ToInt64(*LEN_)}) { 68 type_ = DynamicType{type_.kind(), *n}; 69 } 70 } 71 shape_ = Fold(context, std::move(shape_)); 72 return *this; 73 } 74 75 std::optional<TypeAndShape> TypeAndShape::Characterize( 76 const semantics::Symbol &symbol, FoldingContext &context) { 77 const auto &ultimate{symbol.GetUltimate()}; 78 return common::visit( 79 common::visitors{ 80 [&](const semantics::ProcEntityDetails &proc) { 81 if (proc.procInterface()) { 82 return Characterize(*proc.procInterface(), context); 83 } else if (proc.type()) { 84 return Characterize(*proc.type(), context); 85 } else { 86 return std::optional<TypeAndShape>{}; 87 } 88 }, 89 [&](const semantics::AssocEntityDetails &assoc) { 90 return Characterize(assoc, context); 91 }, 92 [&](const semantics::ProcBindingDetails &binding) { 93 return Characterize(binding.symbol(), context); 94 }, 95 [&](const auto &x) -> std::optional<TypeAndShape> { 96 using Ty = std::decay_t<decltype(x)>; 97 if constexpr (std::is_same_v<Ty, semantics::EntityDetails> || 98 std::is_same_v<Ty, semantics::ObjectEntityDetails> || 99 std::is_same_v<Ty, semantics::TypeParamDetails>) { 100 if (const semantics::DeclTypeSpec * type{ultimate.GetType()}) { 101 if (auto dyType{DynamicType::From(*type)}) { 102 TypeAndShape result{ 103 std::move(*dyType), GetShape(context, ultimate)}; 104 result.AcquireAttrs(ultimate); 105 result.AcquireLEN(ultimate); 106 return std::move(result.Rewrite(context)); 107 } 108 } 109 } 110 return std::nullopt; 111 }, 112 }, 113 // GetUltimate() used here, not ResolveAssociations(), because 114 // we need the type/rank of an associate entity from TYPE IS, 115 // CLASS IS, or RANK statement. 116 ultimate.details()); 117 } 118 119 std::optional<TypeAndShape> TypeAndShape::Characterize( 120 const semantics::AssocEntityDetails &assoc, FoldingContext &context) { 121 std::optional<TypeAndShape> result; 122 if (auto type{DynamicType::From(assoc.type())}) { 123 if (auto rank{assoc.rank()}) { 124 if (*rank >= 0 && *rank <= common::maxRank) { 125 result = TypeAndShape{std::move(*type), Shape(*rank)}; 126 } 127 } else if (auto shape{GetShape(context, assoc.expr())}) { 128 result = TypeAndShape{std::move(*type), std::move(*shape)}; 129 } 130 if (result && type->category() == TypeCategory::Character) { 131 if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc.expr())}) { 132 if (auto len{chExpr->LEN()}) { 133 result->set_LEN(std::move(*len)); 134 } 135 } 136 } 137 } 138 return Fold(context, std::move(result)); 139 } 140 141 std::optional<TypeAndShape> TypeAndShape::Characterize( 142 const semantics::DeclTypeSpec &spec, FoldingContext &context) { 143 if (auto type{DynamicType::From(spec)}) { 144 return Fold(context, TypeAndShape{std::move(*type)}); 145 } else { 146 return std::nullopt; 147 } 148 } 149 150 std::optional<TypeAndShape> TypeAndShape::Characterize( 151 const ActualArgument &arg, FoldingContext &context) { 152 if (const auto *expr{arg.UnwrapExpr()}) { 153 return Characterize(*expr, context); 154 } else if (const Symbol * assumed{arg.GetAssumedTypeDummy()}) { 155 return Characterize(*assumed, context); 156 } else { 157 return std::nullopt; 158 } 159 } 160 161 bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages, 162 const TypeAndShape &that, const char *thisIs, const char *thatIs, 163 bool omitShapeConformanceCheck, 164 enum CheckConformanceFlags::Flags flags) const { 165 if (!type_.IsTkCompatibleWith(that.type_)) { 166 messages.Say( 167 "%1$s type '%2$s' is not compatible with %3$s type '%4$s'"_err_en_US, 168 thatIs, that.AsFortran(), thisIs, AsFortran()); 169 return false; 170 } 171 return omitShapeConformanceCheck || 172 CheckConformance(messages, shape_, that.shape_, flags, thisIs, thatIs) 173 .value_or(true /*fail only when nonconformance is known now*/); 174 } 175 176 std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureElementSizeInBytes( 177 FoldingContext &foldingContext, bool align) const { 178 if (LEN_) { 179 CHECK(type_.category() == TypeCategory::Character); 180 return Fold(foldingContext, 181 Expr<SubscriptInteger>{ 182 foldingContext.targetCharacteristics().GetByteSize( 183 type_.category(), type_.kind())} * 184 Expr<SubscriptInteger>{*LEN_}); 185 } 186 if (auto elementBytes{type_.MeasureSizeInBytes(foldingContext, align)}) { 187 return Fold(foldingContext, std::move(*elementBytes)); 188 } 189 return std::nullopt; 190 } 191 192 std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureSizeInBytes( 193 FoldingContext &foldingContext) const { 194 if (auto elements{GetSize(Shape{shape_})}) { 195 // Sizes of arrays (even with single elements) are multiples of 196 // their alignments. 197 if (auto elementBytes{ 198 MeasureElementSizeInBytes(foldingContext, GetRank(shape_) > 0)}) { 199 return Fold( 200 foldingContext, std::move(*elements) * std::move(*elementBytes)); 201 } 202 } 203 return std::nullopt; 204 } 205 206 void TypeAndShape::AcquireAttrs(const semantics::Symbol &symbol) { 207 if (IsAssumedShape(symbol)) { 208 attrs_.set(Attr::AssumedShape); 209 } 210 if (IsDeferredShape(symbol)) { 211 attrs_.set(Attr::DeferredShape); 212 } 213 if (const auto *object{ 214 symbol.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()}) { 215 corank_ = object->coshape().Rank(); 216 if (object->IsAssumedRank()) { 217 attrs_.set(Attr::AssumedRank); 218 } 219 if (object->IsAssumedSize()) { 220 attrs_.set(Attr::AssumedSize); 221 } 222 if (object->IsCoarray()) { 223 attrs_.set(Attr::Coarray); 224 } 225 } 226 } 227 228 void TypeAndShape::AcquireLEN() { 229 if (auto len{type_.GetCharLength()}) { 230 LEN_ = std::move(len); 231 } 232 } 233 234 void TypeAndShape::AcquireLEN(const semantics::Symbol &symbol) { 235 if (type_.category() == TypeCategory::Character) { 236 if (auto len{DataRef{symbol}.LEN()}) { 237 LEN_ = std::move(*len); 238 } 239 } 240 } 241 242 std::string TypeAndShape::AsFortran() const { 243 return type_.AsFortran(LEN_ ? LEN_->AsFortran() : ""); 244 } 245 246 llvm::raw_ostream &TypeAndShape::Dump(llvm::raw_ostream &o) const { 247 o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : ""); 248 attrs_.Dump(o, EnumToString); 249 if (!shape_.empty()) { 250 o << " dimension"; 251 char sep{'('}; 252 for (const auto &expr : shape_) { 253 o << sep; 254 sep = ','; 255 if (expr) { 256 expr->AsFortran(o); 257 } else { 258 o << ':'; 259 } 260 } 261 o << ')'; 262 } 263 return o; 264 } 265 266 bool DummyDataObject::operator==(const DummyDataObject &that) const { 267 return type == that.type && attrs == that.attrs && intent == that.intent && 268 coshape == that.coshape; 269 } 270 271 static bool AreCompatibleDummyDataObjectShapes(const Shape &x, const Shape &y) { 272 int n{GetRank(x)}; 273 if (n != GetRank(y)) { 274 return false; 275 } 276 auto xIter{x.begin()}; 277 auto yIter{y.begin()}; 278 for (; n-- > 0; ++xIter, ++yIter) { 279 if (auto xVal{ToInt64(*xIter)}) { 280 if (auto yVal{ToInt64(*yIter)}) { 281 if (*xVal != *yVal) { 282 return false; 283 } 284 } 285 } 286 } 287 return true; 288 } 289 290 bool DummyDataObject::IsCompatibleWith( 291 const DummyDataObject &actual, std::string *whyNot) const { 292 if (!AreCompatibleDummyDataObjectShapes(type.shape(), actual.type.shape())) { 293 if (whyNot) { 294 *whyNot = "incompatible dummy data object shapes"; 295 } 296 return false; 297 } 298 if (!type.type().IsTkLenCompatibleWith(actual.type.type())) { 299 if (whyNot) { 300 *whyNot = "incompatible dummy data object types: "s + 301 type.type().AsFortran() + " vs " + actual.type.type().AsFortran(); 302 } 303 return false; 304 } 305 if (attrs != actual.attrs) { 306 if (whyNot) { 307 *whyNot = "incompatible dummy data object attributes"; 308 } 309 return false; 310 } 311 if (intent != actual.intent) { 312 if (whyNot) { 313 *whyNot = "incompatible dummy data object intents"; 314 } 315 return false; 316 } 317 if (coshape != actual.coshape) { 318 if (whyNot) { 319 *whyNot = "incompatible dummy data object coshapes"; 320 } 321 return false; 322 } 323 if (ignoreTKR != actual.ignoreTKR) { 324 if (whyNot) { 325 *whyNot = "incompatible !DIR$ IGNORE_TKR directives"; 326 } 327 } 328 return true; 329 } 330 331 static common::Intent GetIntent(const semantics::Attrs &attrs) { 332 if (attrs.test(semantics::Attr::INTENT_IN)) { 333 return common::Intent::In; 334 } else if (attrs.test(semantics::Attr::INTENT_OUT)) { 335 return common::Intent::Out; 336 } else if (attrs.test(semantics::Attr::INTENT_INOUT)) { 337 return common::Intent::InOut; 338 } else { 339 return common::Intent::Default; 340 } 341 } 342 343 std::optional<DummyDataObject> DummyDataObject::Characterize( 344 const semantics::Symbol &symbol, FoldingContext &context) { 345 if (const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()}; 346 object || symbol.has<semantics::EntityDetails>()) { 347 if (auto type{TypeAndShape::Characterize(symbol, context)}) { 348 std::optional<DummyDataObject> result{std::move(*type)}; 349 using semantics::Attr; 350 CopyAttrs<DummyDataObject, DummyDataObject::Attr>(symbol, *result, 351 { 352 {Attr::OPTIONAL, DummyDataObject::Attr::Optional}, 353 {Attr::ALLOCATABLE, DummyDataObject::Attr::Allocatable}, 354 {Attr::ASYNCHRONOUS, DummyDataObject::Attr::Asynchronous}, 355 {Attr::CONTIGUOUS, DummyDataObject::Attr::Contiguous}, 356 {Attr::VALUE, DummyDataObject::Attr::Value}, 357 {Attr::VOLATILE, DummyDataObject::Attr::Volatile}, 358 {Attr::POINTER, DummyDataObject::Attr::Pointer}, 359 {Attr::TARGET, DummyDataObject::Attr::Target}, 360 }); 361 result->intent = GetIntent(symbol.attrs()); 362 result->ignoreTKR = GetIgnoreTKR(symbol); 363 return result; 364 } 365 } 366 return std::nullopt; 367 } 368 369 bool DummyDataObject::CanBePassedViaImplicitInterface() const { 370 if ((attrs & 371 Attrs{Attr::Allocatable, Attr::Asynchronous, Attr::Optional, 372 Attr::Pointer, Attr::Target, Attr::Value, Attr::Volatile}) 373 .any()) { 374 return false; // 15.4.2.2(3)(a) 375 } else if ((type.attrs() & 376 TypeAndShape::Attrs{TypeAndShape::Attr::AssumedShape, 377 TypeAndShape::Attr::AssumedRank, 378 TypeAndShape::Attr::Coarray}) 379 .any()) { 380 return false; // 15.4.2.2(3)(b-d) 381 } else if (type.type().IsPolymorphic()) { 382 return false; // 15.4.2.2(3)(f) 383 } else if (const auto *derived{GetDerivedTypeSpec(type.type())}) { 384 return derived->parameters().empty(); // 15.4.2.2(3)(e) 385 } else { 386 return true; 387 } 388 } 389 390 llvm::raw_ostream &DummyDataObject::Dump(llvm::raw_ostream &o) const { 391 attrs.Dump(o, EnumToString); 392 if (intent != common::Intent::Default) { 393 o << "INTENT(" << common::EnumToString(intent) << ')'; 394 } 395 type.Dump(o); 396 if (!coshape.empty()) { 397 char sep{'['}; 398 for (const auto &expr : coshape) { 399 expr.AsFortran(o << sep); 400 sep = ','; 401 } 402 } 403 return o; 404 } 405 406 DummyProcedure::DummyProcedure(Procedure &&p) 407 : procedure{new Procedure{std::move(p)}} {} 408 409 bool DummyProcedure::operator==(const DummyProcedure &that) const { 410 return attrs == that.attrs && intent == that.intent && 411 procedure.value() == that.procedure.value(); 412 } 413 414 bool DummyProcedure::IsCompatibleWith( 415 const DummyProcedure &actual, std::string *whyNot) const { 416 if (attrs != actual.attrs) { 417 if (whyNot) { 418 *whyNot = "incompatible dummy procedure attributes"; 419 } 420 return false; 421 } 422 if (intent != actual.intent) { 423 if (whyNot) { 424 *whyNot = "incompatible dummy procedure intents"; 425 } 426 return false; 427 } 428 if (!procedure.value().IsCompatibleWith(actual.procedure.value(), whyNot)) { 429 if (whyNot) { 430 *whyNot = "incompatible dummy procedure interfaces: "s + *whyNot; 431 } 432 return false; 433 } 434 return true; 435 } 436 437 bool DummyProcedure::CanBePassedViaImplicitInterface() const { 438 if ((attrs & Attrs{Attr::Optional, Attr::Pointer}).any()) { 439 return false; // 15.4.2.2(3)(a) 440 } 441 return true; 442 } 443 444 static std::string GetSeenProcs( 445 const semantics::UnorderedSymbolSet &seenProcs) { 446 // Sort the symbols so that they appear in the same order on all platforms 447 auto ordered{semantics::OrderBySourcePosition(seenProcs)}; 448 std::string result; 449 llvm::interleave( 450 ordered, 451 [&](const SymbolRef p) { result += '\'' + p->name().ToString() + '\''; }, 452 [&]() { result += ", "; }); 453 return result; 454 } 455 456 // These functions with arguments of type UnorderedSymbolSet are used with 457 // mutually recursive calls when characterizing a Procedure, a DummyArgument, 458 // or a DummyProcedure to detect circularly defined procedures as required by 459 // 15.4.3.6, paragraph 2. 460 static std::optional<DummyArgument> CharacterizeDummyArgument( 461 const semantics::Symbol &symbol, FoldingContext &context, 462 semantics::UnorderedSymbolSet seenProcs); 463 static std::optional<FunctionResult> CharacterizeFunctionResult( 464 const semantics::Symbol &symbol, FoldingContext &context, 465 semantics::UnorderedSymbolSet seenProcs); 466 467 static std::optional<Procedure> CharacterizeProcedure( 468 const semantics::Symbol &original, FoldingContext &context, 469 semantics::UnorderedSymbolSet seenProcs) { 470 Procedure result; 471 const auto &symbol{ResolveAssociations(original)}; 472 if (seenProcs.find(symbol) != seenProcs.end()) { 473 std::string procsList{GetSeenProcs(seenProcs)}; 474 context.messages().Say(symbol.name(), 475 "Procedure '%s' is recursively defined. Procedures in the cycle:" 476 " %s"_err_en_US, 477 symbol.name(), procsList); 478 return std::nullopt; 479 } 480 seenProcs.insert(symbol); 481 if (IsElementalProcedure(symbol)) { 482 result.attrs.set(Procedure::Attr::Elemental); 483 } 484 CopyAttrs<Procedure, Procedure::Attr>(symbol, result, 485 { 486 {semantics::Attr::BIND_C, Procedure::Attr::BindC}, 487 }); 488 if (IsPureProcedure(symbol) || // works for ENTRY too 489 (!symbol.attrs().test(semantics::Attr::IMPURE) && 490 result.attrs.test(Procedure::Attr::Elemental))) { 491 result.attrs.set(Procedure::Attr::Pure); 492 } 493 return common::visit( 494 common::visitors{ 495 [&](const semantics::SubprogramDetails &subp) 496 -> std::optional<Procedure> { 497 if (subp.isFunction()) { 498 if (auto fr{CharacterizeFunctionResult( 499 subp.result(), context, seenProcs)}) { 500 result.functionResult = std::move(fr); 501 } else { 502 return std::nullopt; 503 } 504 } else { 505 result.attrs.set(Procedure::Attr::Subroutine); 506 } 507 for (const semantics::Symbol *arg : subp.dummyArgs()) { 508 if (!arg) { 509 if (subp.isFunction()) { 510 return std::nullopt; 511 } else { 512 result.dummyArguments.emplace_back(AlternateReturn{}); 513 } 514 } else if (auto argCharacteristics{CharacterizeDummyArgument( 515 *arg, context, seenProcs)}) { 516 result.dummyArguments.emplace_back( 517 std::move(argCharacteristics.value())); 518 } else { 519 return std::nullopt; 520 } 521 } 522 return result; 523 }, 524 [&](const semantics::ProcEntityDetails &proc) 525 -> std::optional<Procedure> { 526 if (symbol.attrs().test(semantics::Attr::INTRINSIC)) { 527 // Fails when the intrinsic is not a specific intrinsic function 528 // from F'2018 table 16.2. In order to handle forward references, 529 // attempts to use impermissible intrinsic procedures as the 530 // interfaces of procedure pointers are caught and flagged in 531 // declaration checking in Semantics. 532 auto intrinsic{context.intrinsics().IsSpecificIntrinsicFunction( 533 symbol.name().ToString())}; 534 if (intrinsic && intrinsic->isRestrictedSpecific) { 535 intrinsic.reset(); // Exclude intrinsics from table 16.3. 536 } 537 return intrinsic; 538 } 539 if (const semantics::Symbol * 540 interfaceSymbol{proc.procInterface()}) { 541 auto interface { 542 CharacterizeProcedure(*interfaceSymbol, context, seenProcs) 543 }; 544 if (interface && IsPointer(symbol)) { 545 interface->attrs.reset(Procedure::Attr::Elemental); 546 } 547 return interface; 548 } else { 549 result.attrs.set(Procedure::Attr::ImplicitInterface); 550 const semantics::DeclTypeSpec *type{proc.type()}; 551 if (symbol.test(semantics::Symbol::Flag::Subroutine)) { 552 // ignore any implicit typing 553 result.attrs.set(Procedure::Attr::Subroutine); 554 } else if (type) { 555 if (auto resultType{DynamicType::From(*type)}) { 556 result.functionResult = FunctionResult{*resultType}; 557 } else { 558 return std::nullopt; 559 } 560 } else if (symbol.test(semantics::Symbol::Flag::Function)) { 561 return std::nullopt; 562 } 563 // The PASS name, if any, is not a characteristic. 564 return result; 565 } 566 }, 567 [&](const semantics::ProcBindingDetails &binding) { 568 if (auto result{CharacterizeProcedure( 569 binding.symbol(), context, seenProcs)}) { 570 if (binding.symbol().attrs().test(semantics::Attr::INTRINSIC)) { 571 result->attrs.reset(Procedure::Attr::Elemental); 572 } 573 if (!symbol.attrs().test(semantics::Attr::NOPASS)) { 574 auto passName{binding.passName()}; 575 for (auto &dummy : result->dummyArguments) { 576 if (!passName || dummy.name.c_str() == *passName) { 577 dummy.pass = true; 578 break; 579 } 580 } 581 } 582 return result; 583 } else { 584 return std::optional<Procedure>{}; 585 } 586 }, 587 [&](const semantics::UseDetails &use) { 588 return CharacterizeProcedure(use.symbol(), context, seenProcs); 589 }, 590 [](const semantics::UseErrorDetails &) { 591 // Ambiguous use-association will be handled later during symbol 592 // checks, ignore UseErrorDetails here without actual symbol usage. 593 return std::optional<Procedure>{}; 594 }, 595 [&](const semantics::HostAssocDetails &assoc) { 596 return CharacterizeProcedure(assoc.symbol(), context, seenProcs); 597 }, 598 [&](const semantics::GenericDetails &generic) { 599 if (const semantics::Symbol * specific{generic.specific()}) { 600 return CharacterizeProcedure(*specific, context, seenProcs); 601 } else { 602 return std::optional<Procedure>{}; 603 } 604 }, 605 [&](const semantics::EntityDetails &) { 606 context.messages().Say( 607 "Procedure '%s' is referenced before being sufficiently defined in a context where it must be so"_err_en_US, 608 symbol.name()); 609 return std::optional<Procedure>{}; 610 }, 611 [&](const semantics::SubprogramNameDetails &) { 612 context.messages().Say( 613 "Procedure '%s' is referenced before being sufficiently defined in a context where it must be so"_err_en_US, 614 symbol.name()); 615 return std::optional<Procedure>{}; 616 }, 617 [&](const auto &) { 618 context.messages().Say( 619 "'%s' is not a procedure"_err_en_US, symbol.name()); 620 return std::optional<Procedure>{}; 621 }, 622 }, 623 symbol.details()); 624 } 625 626 static std::optional<DummyProcedure> CharacterizeDummyProcedure( 627 const semantics::Symbol &symbol, FoldingContext &context, 628 semantics::UnorderedSymbolSet seenProcs) { 629 if (auto procedure{CharacterizeProcedure(symbol, context, seenProcs)}) { 630 // Dummy procedures may not be elemental. Elemental dummy procedure 631 // interfaces are errors when the interface is not intrinsic, and that 632 // error is caught elsewhere. Elemental intrinsic interfaces are 633 // made non-elemental. 634 procedure->attrs.reset(Procedure::Attr::Elemental); 635 DummyProcedure result{std::move(procedure.value())}; 636 CopyAttrs<DummyProcedure, DummyProcedure::Attr>(symbol, result, 637 { 638 {semantics::Attr::OPTIONAL, DummyProcedure::Attr::Optional}, 639 {semantics::Attr::POINTER, DummyProcedure::Attr::Pointer}, 640 }); 641 result.intent = GetIntent(symbol.attrs()); 642 return result; 643 } else { 644 return std::nullopt; 645 } 646 } 647 648 llvm::raw_ostream &DummyProcedure::Dump(llvm::raw_ostream &o) const { 649 attrs.Dump(o, EnumToString); 650 if (intent != common::Intent::Default) { 651 o << "INTENT(" << common::EnumToString(intent) << ')'; 652 } 653 procedure.value().Dump(o); 654 return o; 655 } 656 657 llvm::raw_ostream &AlternateReturn::Dump(llvm::raw_ostream &o) const { 658 return o << '*'; 659 } 660 661 DummyArgument::~DummyArgument() {} 662 663 bool DummyArgument::operator==(const DummyArgument &that) const { 664 return u == that.u; // name and passed-object usage are not characteristics 665 } 666 667 bool DummyArgument::IsCompatibleWith( 668 const DummyArgument &actual, std::string *whyNot) const { 669 if (const auto *ifaceData{std::get_if<DummyDataObject>(&u)}) { 670 if (const auto *actualData{std::get_if<DummyDataObject>(&actual.u)}) { 671 return ifaceData->IsCompatibleWith(*actualData, whyNot); 672 } 673 if (whyNot) { 674 *whyNot = "one dummy argument is an object, the other is not"; 675 } 676 } else if (const auto *ifaceProc{std::get_if<DummyProcedure>(&u)}) { 677 if (const auto *actualProc{std::get_if<DummyProcedure>(&actual.u)}) { 678 return ifaceProc->IsCompatibleWith(*actualProc, whyNot); 679 } 680 if (whyNot) { 681 *whyNot = "one dummy argument is a procedure, the other is not"; 682 } 683 } else { 684 CHECK(std::holds_alternative<AlternateReturn>(u)); 685 if (std::holds_alternative<AlternateReturn>(actual.u)) { 686 return true; 687 } 688 if (whyNot) { 689 *whyNot = "one dummy argument is an alternate return, the other is not"; 690 } 691 } 692 return false; 693 } 694 695 static std::optional<DummyArgument> CharacterizeDummyArgument( 696 const semantics::Symbol &symbol, FoldingContext &context, 697 semantics::UnorderedSymbolSet seenProcs) { 698 auto name{symbol.name().ToString()}; 699 if (symbol.has<semantics::ObjectEntityDetails>() || 700 symbol.has<semantics::EntityDetails>()) { 701 if (auto obj{DummyDataObject::Characterize(symbol, context)}) { 702 return DummyArgument{std::move(name), std::move(obj.value())}; 703 } 704 } else if (auto proc{ 705 CharacterizeDummyProcedure(symbol, context, seenProcs)}) { 706 return DummyArgument{std::move(name), std::move(proc.value())}; 707 } 708 return std::nullopt; 709 } 710 711 std::optional<DummyArgument> DummyArgument::FromActual( 712 std::string &&name, const Expr<SomeType> &expr, FoldingContext &context) { 713 return common::visit( 714 common::visitors{ 715 [&](const BOZLiteralConstant &) { 716 return std::make_optional<DummyArgument>(std::move(name), 717 DummyDataObject{ 718 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}}); 719 }, 720 [&](const NullPointer &) { 721 return std::make_optional<DummyArgument>(std::move(name), 722 DummyDataObject{ 723 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}}); 724 }, 725 [&](const ProcedureDesignator &designator) { 726 if (auto proc{Procedure::Characterize(designator, context)}) { 727 return std::make_optional<DummyArgument>( 728 std::move(name), DummyProcedure{std::move(*proc)}); 729 } else { 730 return std::optional<DummyArgument>{}; 731 } 732 }, 733 [&](const ProcedureRef &call) { 734 if (auto proc{Procedure::Characterize(call, context)}) { 735 return std::make_optional<DummyArgument>( 736 std::move(name), DummyProcedure{std::move(*proc)}); 737 } else { 738 return std::optional<DummyArgument>{}; 739 } 740 }, 741 [&](const auto &) { 742 if (auto type{TypeAndShape::Characterize(expr, context)}) { 743 return std::make_optional<DummyArgument>( 744 std::move(name), DummyDataObject{std::move(*type)}); 745 } else { 746 return std::optional<DummyArgument>{}; 747 } 748 }, 749 }, 750 expr.u); 751 } 752 753 std::optional<DummyArgument> DummyArgument::FromActual( 754 std::string &&name, const ActualArgument &arg, FoldingContext &context) { 755 if (const auto *expr{arg.UnwrapExpr()}) { 756 return FromActual(std::move(name), *expr, context); 757 } else if (arg.GetAssumedTypeDummy()) { 758 return std::nullopt; 759 } else { 760 return DummyArgument{AlternateReturn{}}; 761 } 762 } 763 764 bool DummyArgument::IsOptional() const { 765 return common::visit( 766 common::visitors{ 767 [](const DummyDataObject &data) { 768 return data.attrs.test(DummyDataObject::Attr::Optional); 769 }, 770 [](const DummyProcedure &proc) { 771 return proc.attrs.test(DummyProcedure::Attr::Optional); 772 }, 773 [](const AlternateReturn &) { return false; }, 774 }, 775 u); 776 } 777 778 void DummyArgument::SetOptional(bool value) { 779 common::visit(common::visitors{ 780 [value](DummyDataObject &data) { 781 data.attrs.set(DummyDataObject::Attr::Optional, value); 782 }, 783 [value](DummyProcedure &proc) { 784 proc.attrs.set(DummyProcedure::Attr::Optional, value); 785 }, 786 [](AlternateReturn &) { DIE("cannot set optional"); }, 787 }, 788 u); 789 } 790 791 void DummyArgument::SetIntent(common::Intent intent) { 792 common::visit(common::visitors{ 793 [intent](DummyDataObject &data) { data.intent = intent; }, 794 [intent](DummyProcedure &proc) { proc.intent = intent; }, 795 [](AlternateReturn &) { DIE("cannot set intent"); }, 796 }, 797 u); 798 } 799 800 common::Intent DummyArgument::GetIntent() const { 801 return common::visit( 802 common::visitors{ 803 [](const DummyDataObject &data) { return data.intent; }, 804 [](const DummyProcedure &proc) { return proc.intent; }, 805 [](const AlternateReturn &) -> common::Intent { 806 DIE("Alternate returns have no intent"); 807 }, 808 }, 809 u); 810 } 811 812 bool DummyArgument::CanBePassedViaImplicitInterface() const { 813 if (const auto *object{std::get_if<DummyDataObject>(&u)}) { 814 return object->CanBePassedViaImplicitInterface(); 815 } else if (const auto *proc{std::get_if<DummyProcedure>(&u)}) { 816 return proc->CanBePassedViaImplicitInterface(); 817 } else { 818 return true; 819 } 820 } 821 822 bool DummyArgument::IsTypelessIntrinsicDummy() const { 823 const auto *argObj{std::get_if<characteristics::DummyDataObject>(&u)}; 824 return argObj && argObj->type.type().IsTypelessIntrinsicArgument(); 825 } 826 827 llvm::raw_ostream &DummyArgument::Dump(llvm::raw_ostream &o) const { 828 if (!name.empty()) { 829 o << name << '='; 830 } 831 if (pass) { 832 o << " PASS"; 833 } 834 common::visit([&](const auto &x) { x.Dump(o); }, u); 835 return o; 836 } 837 838 FunctionResult::FunctionResult(DynamicType t) : u{TypeAndShape{t}} {} 839 FunctionResult::FunctionResult(TypeAndShape &&t) : u{std::move(t)} {} 840 FunctionResult::FunctionResult(Procedure &&p) : u{std::move(p)} {} 841 FunctionResult::~FunctionResult() {} 842 843 bool FunctionResult::operator==(const FunctionResult &that) const { 844 return attrs == that.attrs && u == that.u; 845 } 846 847 static std::optional<FunctionResult> CharacterizeFunctionResult( 848 const semantics::Symbol &symbol, FoldingContext &context, 849 semantics::UnorderedSymbolSet seenProcs) { 850 if (symbol.has<semantics::ObjectEntityDetails>()) { 851 if (auto type{TypeAndShape::Characterize(symbol, context)}) { 852 FunctionResult result{std::move(*type)}; 853 CopyAttrs<FunctionResult, FunctionResult::Attr>(symbol, result, 854 { 855 {semantics::Attr::ALLOCATABLE, FunctionResult::Attr::Allocatable}, 856 {semantics::Attr::CONTIGUOUS, FunctionResult::Attr::Contiguous}, 857 {semantics::Attr::POINTER, FunctionResult::Attr::Pointer}, 858 }); 859 return result; 860 } 861 } else if (auto maybeProc{ 862 CharacterizeProcedure(symbol, context, seenProcs)}) { 863 FunctionResult result{std::move(*maybeProc)}; 864 result.attrs.set(FunctionResult::Attr::Pointer); 865 return result; 866 } 867 return std::nullopt; 868 } 869 870 std::optional<FunctionResult> FunctionResult::Characterize( 871 const Symbol &symbol, FoldingContext &context) { 872 semantics::UnorderedSymbolSet seenProcs; 873 return CharacterizeFunctionResult(symbol, context, seenProcs); 874 } 875 876 bool FunctionResult::IsAssumedLengthCharacter() const { 877 if (const auto *ts{std::get_if<TypeAndShape>(&u)}) { 878 return ts->type().IsAssumedLengthCharacter(); 879 } else { 880 return false; 881 } 882 } 883 884 bool FunctionResult::CanBeReturnedViaImplicitInterface() const { 885 if (attrs.test(Attr::Pointer) || attrs.test(Attr::Allocatable)) { 886 return false; // 15.4.2.2(4)(b) 887 } else if (const auto *typeAndShape{GetTypeAndShape()}) { 888 if (typeAndShape->Rank() > 0) { 889 return false; // 15.4.2.2(4)(a) 890 } else { 891 const DynamicType &type{typeAndShape->type()}; 892 switch (type.category()) { 893 case TypeCategory::Character: 894 if (type.knownLength()) { 895 return true; 896 } else if (const auto *param{type.charLengthParamValue()}) { 897 if (const auto &expr{param->GetExplicit()}) { 898 return IsConstantExpr(*expr); // 15.4.2.2(4)(c) 899 } else if (param->isAssumed()) { 900 return true; 901 } 902 } 903 return false; 904 case TypeCategory::Derived: 905 if (!type.IsPolymorphic()) { 906 const auto &spec{type.GetDerivedTypeSpec()}; 907 for (const auto &pair : spec.parameters()) { 908 if (const auto &expr{pair.second.GetExplicit()}) { 909 if (!IsConstantExpr(*expr)) { 910 return false; // 15.4.2.2(4)(c) 911 } 912 } 913 } 914 return true; 915 } 916 return false; 917 default: 918 return true; 919 } 920 } 921 } else { 922 return false; // 15.4.2.2(4)(b) - procedure pointer 923 } 924 } 925 926 static bool AreCompatibleFunctionResultShapes(const Shape &x, const Shape &y) { 927 int rank{GetRank(x)}; 928 if (GetRank(y) != rank) { 929 return false; 930 } 931 for (int j{0}; j < rank; ++j) { 932 if (auto xDim{ToInt64(x[j])}) { 933 if (auto yDim{ToInt64(y[j])}) { 934 if (*xDim != *yDim) { 935 return false; 936 } 937 } 938 } 939 } 940 return true; 941 } 942 943 bool FunctionResult::IsCompatibleWith( 944 const FunctionResult &actual, std::string *whyNot) const { 945 Attrs actualAttrs{actual.attrs}; 946 if (!attrs.test(Attr::Contiguous)) { 947 actualAttrs.reset(Attr::Contiguous); 948 } 949 if (attrs != actualAttrs) { 950 if (whyNot) { 951 *whyNot = "function results have incompatible attributes"; 952 } 953 } else if (const auto *ifaceTypeShape{std::get_if<TypeAndShape>(&u)}) { 954 if (const auto *actualTypeShape{std::get_if<TypeAndShape>(&actual.u)}) { 955 if (ifaceTypeShape->Rank() != actualTypeShape->Rank()) { 956 if (whyNot) { 957 *whyNot = "function results have distinct ranks"; 958 } 959 } else if (!attrs.test(Attr::Allocatable) && !attrs.test(Attr::Pointer) && 960 !AreCompatibleFunctionResultShapes( 961 ifaceTypeShape->shape(), actualTypeShape->shape())) { 962 if (whyNot) { 963 *whyNot = "function results have distinct constant extents"; 964 } 965 } else if (ifaceTypeShape->type() != actualTypeShape->type()) { 966 if (ifaceTypeShape->type().category() == 967 actualTypeShape->type().category()) { 968 if (ifaceTypeShape->type().category() == TypeCategory::Character) { 969 if (ifaceTypeShape->type().kind() == 970 actualTypeShape->type().kind()) { 971 auto ifaceLen{ifaceTypeShape->type().knownLength()}; 972 auto actualLen{actualTypeShape->type().knownLength()}; 973 if (!ifaceLen || !actualLen || *ifaceLen == *actualLen) { 974 return true; 975 } 976 } 977 } else if (ifaceTypeShape->type().category() == 978 TypeCategory::Derived) { 979 if (ifaceTypeShape->type().IsPolymorphic() == 980 actualTypeShape->type().IsPolymorphic() && 981 !ifaceTypeShape->type().IsUnlimitedPolymorphic() && 982 !actualTypeShape->type().IsUnlimitedPolymorphic() && 983 AreSameDerivedType(ifaceTypeShape->type().GetDerivedTypeSpec(), 984 actualTypeShape->type().GetDerivedTypeSpec())) { 985 return true; 986 } 987 } 988 } 989 if (whyNot) { 990 *whyNot = "function results have distinct types: "s + 991 ifaceTypeShape->type().AsFortran() + " vs "s + 992 actualTypeShape->type().AsFortran(); 993 } 994 } else { 995 return true; 996 } 997 } else { 998 if (whyNot) { 999 *whyNot = "function result type and shape are not known"; 1000 } 1001 } 1002 } else { 1003 const auto *ifaceProc{std::get_if<CopyableIndirection<Procedure>>(&u)}; 1004 CHECK(ifaceProc != nullptr); 1005 if (const auto *actualProc{ 1006 std::get_if<CopyableIndirection<Procedure>>(&actual.u)}) { 1007 if (ifaceProc->value().IsCompatibleWith(actualProc->value(), whyNot)) { 1008 return true; 1009 } 1010 if (whyNot) { 1011 *whyNot = 1012 "function results are incompatible procedure pointers: "s + *whyNot; 1013 } 1014 } else { 1015 if (whyNot) { 1016 *whyNot = 1017 "one function result is a procedure pointer, the other is not"; 1018 } 1019 } 1020 } 1021 return false; 1022 } 1023 1024 llvm::raw_ostream &FunctionResult::Dump(llvm::raw_ostream &o) const { 1025 attrs.Dump(o, EnumToString); 1026 common::visit(common::visitors{ 1027 [&](const TypeAndShape &ts) { ts.Dump(o); }, 1028 [&](const CopyableIndirection<Procedure> &p) { 1029 p.value().Dump(o << " procedure(") << ')'; 1030 }, 1031 }, 1032 u); 1033 return o; 1034 } 1035 1036 Procedure::Procedure(FunctionResult &&fr, DummyArguments &&args, Attrs a) 1037 : functionResult{std::move(fr)}, dummyArguments{std::move(args)}, attrs{a} { 1038 } 1039 Procedure::Procedure(DummyArguments &&args, Attrs a) 1040 : dummyArguments{std::move(args)}, attrs{a} {} 1041 Procedure::~Procedure() {} 1042 1043 bool Procedure::operator==(const Procedure &that) const { 1044 return attrs == that.attrs && functionResult == that.functionResult && 1045 dummyArguments == that.dummyArguments; 1046 } 1047 1048 bool Procedure::IsCompatibleWith(const Procedure &actual, std::string *whyNot, 1049 const SpecificIntrinsic *specificIntrinsic) const { 1050 // 15.5.2.9(1): if dummy is not pure, actual need not be. 1051 // Ditto with elemental. 1052 Attrs actualAttrs{actual.attrs}; 1053 if (!attrs.test(Attr::Pure)) { 1054 actualAttrs.reset(Attr::Pure); 1055 } 1056 if (!attrs.test(Attr::Elemental) && specificIntrinsic) { 1057 actualAttrs.reset(Attr::Elemental); 1058 } 1059 Attrs differences{attrs ^ actualAttrs}; 1060 differences.reset(Attr::Subroutine); // dealt with specifically later 1061 if (!differences.empty()) { 1062 if (whyNot) { 1063 auto sep{": "s}; 1064 *whyNot = "incompatible procedure attributes"; 1065 differences.IterateOverMembers([&](Attr x) { 1066 *whyNot += sep + std::string{EnumToString(x)}; 1067 sep = ", "; 1068 }); 1069 } 1070 } else if ((IsFunction() && actual.IsSubroutine()) || 1071 (IsSubroutine() && actual.IsFunction())) { 1072 if (whyNot) { 1073 *whyNot = 1074 "incompatible procedures: one is a function, the other a subroutine"; 1075 } 1076 } else if (functionResult && actual.functionResult && 1077 !functionResult->IsCompatibleWith(*actual.functionResult, whyNot)) { 1078 } else if (dummyArguments.size() != actual.dummyArguments.size()) { 1079 if (whyNot) { 1080 *whyNot = "distinct numbers of dummy arguments"; 1081 } 1082 } else { 1083 for (std::size_t j{0}; j < dummyArguments.size(); ++j) { 1084 // Subtlety: the dummy/actual distinction must be reversed for this 1085 // compatibility test in order to correctly check extended vs. 1086 // base types. Example: 1087 // subroutine s1(base); subroutine s2(extended) 1088 // procedure(s1), pointer :: p 1089 // p => s2 ! an error, s2 is more restricted, can't handle "base" 1090 if (!actual.dummyArguments[j].IsCompatibleWith( 1091 dummyArguments[j], whyNot)) { 1092 if (whyNot) { 1093 *whyNot = "incompatible dummy argument #"s + std::to_string(j + 1) + 1094 ": "s + *whyNot; 1095 } 1096 return false; 1097 } 1098 } 1099 return true; 1100 } 1101 return false; 1102 } 1103 1104 int Procedure::FindPassIndex(std::optional<parser::CharBlock> name) const { 1105 int argCount{static_cast<int>(dummyArguments.size())}; 1106 int index{0}; 1107 if (name) { 1108 while (index < argCount && *name != dummyArguments[index].name.c_str()) { 1109 ++index; 1110 } 1111 } 1112 CHECK(index < argCount); 1113 return index; 1114 } 1115 1116 bool Procedure::CanOverride( 1117 const Procedure &that, std::optional<int> passIndex) const { 1118 // A pure procedure may override an impure one (7.5.7.3(2)) 1119 if ((that.attrs.test(Attr::Pure) && !attrs.test(Attr::Pure)) || 1120 that.attrs.test(Attr::Elemental) != attrs.test(Attr::Elemental) || 1121 functionResult != that.functionResult) { 1122 return false; 1123 } 1124 int argCount{static_cast<int>(dummyArguments.size())}; 1125 if (argCount != static_cast<int>(that.dummyArguments.size())) { 1126 return false; 1127 } 1128 for (int j{0}; j < argCount; ++j) { 1129 if (passIndex && j == *passIndex) { 1130 if (!that.dummyArguments[j].IsCompatibleWith(dummyArguments[j])) { 1131 return false; 1132 } 1133 } else if (dummyArguments[j] != that.dummyArguments[j]) { 1134 return false; 1135 } 1136 } 1137 return true; 1138 } 1139 1140 std::optional<Procedure> Procedure::Characterize( 1141 const semantics::Symbol &original, FoldingContext &context) { 1142 semantics::UnorderedSymbolSet seenProcs; 1143 return CharacterizeProcedure(original, context, seenProcs); 1144 } 1145 1146 std::optional<Procedure> Procedure::Characterize( 1147 const ProcedureDesignator &proc, FoldingContext &context) { 1148 if (const auto *symbol{proc.GetSymbol()}) { 1149 if (auto result{ 1150 characteristics::Procedure::Characterize(*symbol, context)}) { 1151 return result; 1152 } 1153 } else if (const auto *intrinsic{proc.GetSpecificIntrinsic()}) { 1154 return intrinsic->characteristics.value(); 1155 } 1156 return std::nullopt; 1157 } 1158 1159 std::optional<Procedure> Procedure::Characterize( 1160 const ProcedureRef &ref, FoldingContext &context) { 1161 if (auto callee{Characterize(ref.proc(), context)}) { 1162 if (callee->functionResult) { 1163 if (const Procedure * 1164 proc{callee->functionResult->IsProcedurePointer()}) { 1165 return {*proc}; 1166 } 1167 } 1168 } 1169 return std::nullopt; 1170 } 1171 1172 std::optional<Procedure> Procedure::FromActuals(const ProcedureDesignator &proc, 1173 const ActualArguments &args, FoldingContext &context) { 1174 auto callee{Characterize(proc, context)}; 1175 if (callee) { 1176 if (callee->dummyArguments.empty() && 1177 callee->attrs.test(Procedure::Attr::ImplicitInterface)) { 1178 int j{0}; 1179 for (const auto &arg : args) { 1180 ++j; 1181 if (arg) { 1182 if (auto dummy{DummyArgument::FromActual( 1183 "x"s + std::to_string(j), *arg, context)}) { 1184 callee->dummyArguments.emplace_back(std::move(*dummy)); 1185 continue; 1186 } 1187 } 1188 callee.reset(); 1189 break; 1190 } 1191 } 1192 } 1193 return callee; 1194 } 1195 1196 bool Procedure::CanBeCalledViaImplicitInterface() const { 1197 // TODO: Pass back information on why we return false 1198 if (attrs.test(Attr::Elemental) || attrs.test(Attr::BindC)) { 1199 return false; // 15.4.2.2(5,6) 1200 } else if (IsFunction() && 1201 !functionResult->CanBeReturnedViaImplicitInterface()) { 1202 return false; 1203 } else { 1204 for (const DummyArgument &arg : dummyArguments) { 1205 if (!arg.CanBePassedViaImplicitInterface()) { 1206 return false; 1207 } 1208 } 1209 return true; 1210 } 1211 } 1212 1213 llvm::raw_ostream &Procedure::Dump(llvm::raw_ostream &o) const { 1214 attrs.Dump(o, EnumToString); 1215 if (functionResult) { 1216 functionResult->Dump(o << "TYPE(") << ") FUNCTION"; 1217 } else if (attrs.test(Attr::Subroutine)) { 1218 o << "SUBROUTINE"; 1219 } else { 1220 o << "EXTERNAL"; 1221 } 1222 char sep{'('}; 1223 for (const auto &dummy : dummyArguments) { 1224 dummy.Dump(o << sep); 1225 sep = ','; 1226 } 1227 return o << (sep == '(' ? "()" : ")"); 1228 } 1229 1230 // Utility class to determine if Procedures, etc. are distinguishable 1231 class DistinguishUtils { 1232 public: 1233 explicit DistinguishUtils(const common::LanguageFeatureControl &features) 1234 : features_{features} {} 1235 1236 // Are these procedures distinguishable for a generic name? 1237 bool Distinguishable(const Procedure &, const Procedure &) const; 1238 // Are these procedures distinguishable for a generic operator or assignment? 1239 bool DistinguishableOpOrAssign(const Procedure &, const Procedure &) const; 1240 1241 private: 1242 struct CountDummyProcedures { 1243 CountDummyProcedures(const DummyArguments &args) { 1244 for (const DummyArgument &arg : args) { 1245 if (std::holds_alternative<DummyProcedure>(arg.u)) { 1246 total += 1; 1247 notOptional += !arg.IsOptional(); 1248 } 1249 } 1250 } 1251 int total{0}; 1252 int notOptional{0}; 1253 }; 1254 1255 bool Rule3Distinguishable(const Procedure &, const Procedure &) const; 1256 const DummyArgument *Rule1DistinguishingArg( 1257 const DummyArguments &, const DummyArguments &) const; 1258 int FindFirstToDistinguishByPosition( 1259 const DummyArguments &, const DummyArguments &) const; 1260 int FindLastToDistinguishByName( 1261 const DummyArguments &, const DummyArguments &) const; 1262 int CountCompatibleWith(const DummyArgument &, const DummyArguments &) const; 1263 int CountNotDistinguishableFrom( 1264 const DummyArgument &, const DummyArguments &) const; 1265 bool Distinguishable(const DummyArgument &, const DummyArgument &) const; 1266 bool Distinguishable(const DummyDataObject &, const DummyDataObject &) const; 1267 bool Distinguishable(const DummyProcedure &, const DummyProcedure &) const; 1268 bool Distinguishable(const FunctionResult &, const FunctionResult &) const; 1269 bool Distinguishable( 1270 const TypeAndShape &, const TypeAndShape &, common::IgnoreTKRSet) const; 1271 bool IsTkrCompatible(const DummyArgument &, const DummyArgument &) const; 1272 bool IsTkCompatible(const DummyDataObject &, const DummyDataObject &) const; 1273 const DummyArgument *GetAtEffectivePosition( 1274 const DummyArguments &, int) const; 1275 const DummyArgument *GetPassArg(const Procedure &) const; 1276 1277 const common::LanguageFeatureControl &features_; 1278 }; 1279 1280 // Simpler distinguishability rules for operators and assignment 1281 bool DistinguishUtils::DistinguishableOpOrAssign( 1282 const Procedure &proc1, const Procedure &proc2) const { 1283 if ((proc1.IsFunction() && proc2.IsSubroutine()) || 1284 (proc1.IsSubroutine() && proc2.IsFunction())) { 1285 return true; 1286 } 1287 auto &args1{proc1.dummyArguments}; 1288 auto &args2{proc2.dummyArguments}; 1289 if (args1.size() != args2.size()) { 1290 return true; // C1511: distinguishable based on number of arguments 1291 } 1292 for (std::size_t i{0}; i < args1.size(); ++i) { 1293 if (Distinguishable(args1[i], args2[i])) { 1294 return true; // C1511, C1512: distinguishable based on this arg 1295 } 1296 } 1297 return false; 1298 } 1299 1300 bool DistinguishUtils::Distinguishable( 1301 const Procedure &proc1, const Procedure &proc2) const { 1302 if ((proc1.IsFunction() && proc2.IsSubroutine()) || 1303 (proc1.IsSubroutine() && proc2.IsFunction())) { 1304 return true; 1305 } 1306 auto &args1{proc1.dummyArguments}; 1307 auto &args2{proc2.dummyArguments}; 1308 auto count1{CountDummyProcedures(args1)}; 1309 auto count2{CountDummyProcedures(args2)}; 1310 if (count1.notOptional > count2.total || count2.notOptional > count1.total) { 1311 return true; // distinguishable based on C1514 rule 2 1312 } 1313 if (Rule3Distinguishable(proc1, proc2)) { 1314 return true; // distinguishable based on C1514 rule 3 1315 } 1316 if (Rule1DistinguishingArg(args1, args2)) { 1317 return true; // distinguishable based on C1514 rule 1 1318 } 1319 int pos1{FindFirstToDistinguishByPosition(args1, args2)}; 1320 int name1{FindLastToDistinguishByName(args1, args2)}; 1321 if (pos1 >= 0 && pos1 <= name1) { 1322 return true; // distinguishable based on C1514 rule 4 1323 } 1324 int pos2{FindFirstToDistinguishByPosition(args2, args1)}; 1325 int name2{FindLastToDistinguishByName(args2, args1)}; 1326 if (pos2 >= 0 && pos2 <= name2) { 1327 return true; // distinguishable based on C1514 rule 4 1328 } 1329 return false; 1330 } 1331 1332 // C1514 rule 3: Procedures are distinguishable if both have a passed-object 1333 // dummy argument and those are distinguishable. 1334 bool DistinguishUtils::Rule3Distinguishable( 1335 const Procedure &proc1, const Procedure &proc2) const { 1336 const DummyArgument *pass1{GetPassArg(proc1)}; 1337 const DummyArgument *pass2{GetPassArg(proc2)}; 1338 return pass1 && pass2 && Distinguishable(*pass1, *pass2); 1339 } 1340 1341 // Find a non-passed-object dummy data object in one of the argument lists 1342 // that satisfies C1514 rule 1. I.e. x such that: 1343 // - m is the number of dummy data objects in one that are nonoptional, 1344 // are not passed-object, that x is TKR compatible with 1345 // - n is the number of non-passed-object dummy data objects, in the other 1346 // that are not distinguishable from x 1347 // - m is greater than n 1348 const DummyArgument *DistinguishUtils::Rule1DistinguishingArg( 1349 const DummyArguments &args1, const DummyArguments &args2) const { 1350 auto size1{args1.size()}; 1351 auto size2{args2.size()}; 1352 for (std::size_t i{0}; i < size1 + size2; ++i) { 1353 const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]}; 1354 if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) { 1355 if (CountCompatibleWith(x, args1) > 1356 CountNotDistinguishableFrom(x, args2) || 1357 CountCompatibleWith(x, args2) > 1358 CountNotDistinguishableFrom(x, args1)) { 1359 return &x; 1360 } 1361 } 1362 } 1363 return nullptr; 1364 } 1365 1366 // Find the index of the first nonoptional non-passed-object dummy argument 1367 // in args1 at an effective position such that either: 1368 // - args2 has no dummy argument at that effective position 1369 // - the dummy argument at that position is distinguishable from it 1370 int DistinguishUtils::FindFirstToDistinguishByPosition( 1371 const DummyArguments &args1, const DummyArguments &args2) const { 1372 int effective{0}; // position of arg1 in list, ignoring passed arg 1373 for (std::size_t i{0}; i < args1.size(); ++i) { 1374 const DummyArgument &arg1{args1.at(i)}; 1375 if (!arg1.pass && !arg1.IsOptional()) { 1376 const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)}; 1377 if (!arg2 || Distinguishable(arg1, *arg2)) { 1378 return i; 1379 } 1380 } 1381 effective += !arg1.pass; 1382 } 1383 return -1; 1384 } 1385 1386 // Find the index of the last nonoptional non-passed-object dummy argument 1387 // in args1 whose name is such that either: 1388 // - args2 has no dummy argument with that name 1389 // - the dummy argument with that name is distinguishable from it 1390 int DistinguishUtils::FindLastToDistinguishByName( 1391 const DummyArguments &args1, const DummyArguments &args2) const { 1392 std::map<std::string, const DummyArgument *> nameToArg; 1393 for (const auto &arg2 : args2) { 1394 nameToArg.emplace(arg2.name, &arg2); 1395 } 1396 for (int i = args1.size() - 1; i >= 0; --i) { 1397 const DummyArgument &arg1{args1.at(i)}; 1398 if (!arg1.pass && !arg1.IsOptional()) { 1399 auto it{nameToArg.find(arg1.name)}; 1400 if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) { 1401 return i; 1402 } 1403 } 1404 } 1405 return -1; 1406 } 1407 1408 // Count the dummy data objects in args that are nonoptional, are not 1409 // passed-object, and that x is TKR compatible with 1410 int DistinguishUtils::CountCompatibleWith( 1411 const DummyArgument &x, const DummyArguments &args) const { 1412 return llvm::count_if(args, [&](const DummyArgument &y) { 1413 return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y); 1414 }); 1415 } 1416 1417 // Return the number of dummy data objects in args that are not 1418 // distinguishable from x and not passed-object. 1419 int DistinguishUtils::CountNotDistinguishableFrom( 1420 const DummyArgument &x, const DummyArguments &args) const { 1421 return llvm::count_if(args, [&](const DummyArgument &y) { 1422 return !y.pass && std::holds_alternative<DummyDataObject>(y.u) && 1423 !Distinguishable(y, x); 1424 }); 1425 } 1426 1427 bool DistinguishUtils::Distinguishable( 1428 const DummyArgument &x, const DummyArgument &y) const { 1429 if (x.u.index() != y.u.index()) { 1430 return true; // different kind: data/proc/alt-return 1431 } 1432 return common::visit( 1433 common::visitors{ 1434 [&](const DummyDataObject &z) { 1435 return Distinguishable(z, std::get<DummyDataObject>(y.u)); 1436 }, 1437 [&](const DummyProcedure &z) { 1438 return Distinguishable(z, std::get<DummyProcedure>(y.u)); 1439 }, 1440 [&](const AlternateReturn &) { return false; }, 1441 }, 1442 x.u); 1443 } 1444 1445 bool DistinguishUtils::Distinguishable( 1446 const DummyDataObject &x, const DummyDataObject &y) const { 1447 using Attr = DummyDataObject::Attr; 1448 if (Distinguishable(x.type, y.type, x.ignoreTKR | y.ignoreTKR)) { 1449 return true; 1450 } else if (x.attrs.test(Attr::Allocatable) && y.attrs.test(Attr::Pointer) && 1451 y.intent != common::Intent::In) { 1452 return true; 1453 } else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) && 1454 x.intent != common::Intent::In) { 1455 return true; 1456 } else if (features_.IsEnabled( 1457 common::LanguageFeature::DistinguishableSpecifics) && 1458 (x.attrs.test(Attr::Allocatable) || x.attrs.test(Attr::Pointer)) && 1459 (y.attrs.test(Attr::Allocatable) || y.attrs.test(Attr::Pointer)) && 1460 (x.type.type().IsUnlimitedPolymorphic() != 1461 y.type.type().IsUnlimitedPolymorphic() || 1462 x.type.type().IsPolymorphic() != y.type.type().IsPolymorphic())) { 1463 // Extension: Per 15.5.2.5(2), an allocatable/pointer dummy and its 1464 // corresponding actual argument must both or neither be polymorphic, 1465 // and must both or neither be unlimited polymorphic. So when exactly 1466 // one of two dummy arguments is polymorphic or unlimited polymorphic, 1467 // any actual argument that is admissible to one of them cannot also match 1468 // the other one. 1469 return true; 1470 } else { 1471 return false; 1472 } 1473 } 1474 1475 bool DistinguishUtils::Distinguishable( 1476 const DummyProcedure &x, const DummyProcedure &y) const { 1477 const Procedure &xProc{x.procedure.value()}; 1478 const Procedure &yProc{y.procedure.value()}; 1479 if (Distinguishable(xProc, yProc)) { 1480 return true; 1481 } else { 1482 const std::optional<FunctionResult> &xResult{xProc.functionResult}; 1483 const std::optional<FunctionResult> &yResult{yProc.functionResult}; 1484 return xResult ? !yResult || Distinguishable(*xResult, *yResult) 1485 : yResult.has_value(); 1486 } 1487 } 1488 1489 bool DistinguishUtils::Distinguishable( 1490 const FunctionResult &x, const FunctionResult &y) const { 1491 if (x.u.index() != y.u.index()) { 1492 return true; // one is data object, one is procedure 1493 } 1494 return common::visit( 1495 common::visitors{ 1496 [&](const TypeAndShape &z) { 1497 return Distinguishable( 1498 z, std::get<TypeAndShape>(y.u), common::IgnoreTKRSet{}); 1499 }, 1500 [&](const CopyableIndirection<Procedure> &z) { 1501 return Distinguishable(z.value(), 1502 std::get<CopyableIndirection<Procedure>>(y.u).value()); 1503 }, 1504 }, 1505 x.u); 1506 } 1507 1508 bool DistinguishUtils::Distinguishable(const TypeAndShape &x, 1509 const TypeAndShape &y, common::IgnoreTKRSet ignoreTKR) const { 1510 if (!x.type().IsTkCompatibleWith(y.type(), ignoreTKR) && 1511 !y.type().IsTkCompatibleWith(x.type(), ignoreTKR)) { 1512 return true; 1513 } 1514 if (ignoreTKR.test(common::IgnoreTKR::Rank)) { 1515 } else if (x.attrs().test(TypeAndShape::Attr::AssumedRank) || 1516 y.attrs().test(TypeAndShape::Attr::AssumedRank)) { 1517 } else if (x.Rank() != y.Rank()) { 1518 return true; 1519 } 1520 return false; 1521 } 1522 1523 // Compatibility based on type, kind, and rank 1524 1525 bool DistinguishUtils::IsTkrCompatible( 1526 const DummyArgument &x, const DummyArgument &y) const { 1527 const auto *obj1{std::get_if<DummyDataObject>(&x.u)}; 1528 const auto *obj2{std::get_if<DummyDataObject>(&y.u)}; 1529 return obj1 && obj2 && IsTkCompatible(*obj1, *obj2) && 1530 (obj1->type.Rank() == obj2->type.Rank() || 1531 obj1->type.attrs().test(TypeAndShape::Attr::AssumedRank) || 1532 obj2->type.attrs().test(TypeAndShape::Attr::AssumedRank) || 1533 obj1->ignoreTKR.test(common::IgnoreTKR::Rank) || 1534 obj2->ignoreTKR.test(common::IgnoreTKR::Rank)); 1535 } 1536 1537 bool DistinguishUtils::IsTkCompatible( 1538 const DummyDataObject &x, const DummyDataObject &y) const { 1539 return x.type.type().IsTkCompatibleWith( 1540 y.type.type(), x.ignoreTKR | y.ignoreTKR); 1541 } 1542 1543 // Return the argument at the given index, ignoring the passed arg 1544 const DummyArgument *DistinguishUtils::GetAtEffectivePosition( 1545 const DummyArguments &args, int index) const { 1546 for (const DummyArgument &arg : args) { 1547 if (!arg.pass) { 1548 if (index == 0) { 1549 return &arg; 1550 } 1551 --index; 1552 } 1553 } 1554 return nullptr; 1555 } 1556 1557 // Return the passed-object dummy argument of this procedure, if any 1558 const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) const { 1559 for (const auto &arg : proc.dummyArguments) { 1560 if (arg.pass) { 1561 return &arg; 1562 } 1563 } 1564 return nullptr; 1565 } 1566 1567 bool Distinguishable(const common::LanguageFeatureControl &features, 1568 const Procedure &x, const Procedure &y) { 1569 return DistinguishUtils{features}.Distinguishable(x, y); 1570 } 1571 1572 bool DistinguishableOpOrAssign(const common::LanguageFeatureControl &features, 1573 const Procedure &x, const Procedure &y) { 1574 return DistinguishUtils{features}.DistinguishableOpOrAssign(x, y); 1575 } 1576 1577 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument) 1578 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure) 1579 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult) 1580 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure) 1581 } // namespace Fortran::evaluate::characteristics 1582 1583 template class Fortran::common::Indirection< 1584 Fortran::evaluate::characteristics::Procedure, true>; 1585