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 <initializer_list> 20 #include <ostream> 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 std::ostream &TypeAndShape::Dump(std::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 std::ostream &DummyDataObject::Dump(std::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 std::ostream &DummyProcedure::Dump(std::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 std::ostream &AlternateReturn::Dump(std::ostream &o) const { return o << '*'; } 319 320 DummyArgument::~DummyArgument() {} 321 322 bool DummyArgument::operator==(const DummyArgument &that) const { 323 return u == that.u; // name and passed-object usage are not characteristics 324 } 325 326 std::optional<DummyArgument> DummyArgument::Characterize( 327 const semantics::Symbol &symbol, const IntrinsicProcTable &intrinsics) { 328 auto name{symbol.name().ToString()}; 329 if (symbol.has<semantics::ObjectEntityDetails>()) { 330 if (auto obj{DummyDataObject::Characterize(symbol)}) { 331 return DummyArgument{std::move(name), std::move(obj.value())}; 332 } 333 } else if (auto proc{DummyProcedure::Characterize(symbol, intrinsics)}) { 334 return DummyArgument{std::move(name), std::move(proc.value())}; 335 } 336 return std::nullopt; 337 } 338 339 std::optional<DummyArgument> DummyArgument::FromActual( 340 std::string &&name, const Expr<SomeType> &expr, FoldingContext &context) { 341 return std::visit( 342 common::visitors{ 343 [&](const BOZLiteralConstant &) { 344 return std::make_optional<DummyArgument>(std::move(name), 345 DummyDataObject{ 346 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}}); 347 }, 348 [](const NullPointer &) { return std::optional<DummyArgument>{}; }, 349 [&](const ProcedureDesignator &designator) { 350 if (auto proc{Procedure::Characterize( 351 designator, context.intrinsics())}) { 352 return std::make_optional<DummyArgument>( 353 std::move(name), DummyProcedure{std::move(*proc)}); 354 } else { 355 return std::optional<DummyArgument>{}; 356 } 357 }, 358 [&](const ProcedureRef &call) { 359 if (auto proc{ 360 Procedure::Characterize(call, context.intrinsics())}) { 361 return std::make_optional<DummyArgument>( 362 std::move(name), DummyProcedure{std::move(*proc)}); 363 } else { 364 return std::optional<DummyArgument>{}; 365 } 366 }, 367 [&](const auto &) { 368 if (auto type{expr.GetType()}) { 369 if (auto shape{GetShape(context, expr)}) { 370 return std::make_optional<DummyArgument>(std::move(name), 371 DummyDataObject{TypeAndShape{*type, std::move(*shape)}}); 372 } else { 373 return std::make_optional<DummyArgument>( 374 std::move(name), DummyDataObject{TypeAndShape{*type}}); 375 } 376 } else { 377 return std::optional<DummyArgument>{}; 378 } 379 }, 380 }, 381 expr.u); 382 } 383 384 bool DummyArgument::IsOptional() const { 385 return std::visit( 386 common::visitors{ 387 [](const DummyDataObject &data) { 388 return data.attrs.test(DummyDataObject::Attr::Optional); 389 }, 390 [](const DummyProcedure &proc) { 391 return proc.attrs.test(DummyProcedure::Attr::Optional); 392 }, 393 [](const AlternateReturn &) { return false; }, 394 }, 395 u); 396 } 397 398 void DummyArgument::SetOptional(bool value) { 399 std::visit( 400 common::visitors{ 401 [value](DummyDataObject &data) { 402 data.attrs.set(DummyDataObject::Attr::Optional, value); 403 }, 404 [value](DummyProcedure &proc) { 405 proc.attrs.set(DummyProcedure::Attr::Optional, value); 406 }, 407 [](AlternateReturn &) { DIE("cannot set optional"); }, 408 }, 409 u); 410 } 411 412 bool DummyArgument::CanBePassedViaImplicitInterface() const { 413 if (const auto *object{std::get_if<DummyDataObject>(&u)}) { 414 return object->CanBePassedViaImplicitInterface(); 415 } else { 416 return true; 417 } 418 } 419 420 std::ostream &DummyArgument::Dump(std::ostream &o) const { 421 if (!name.empty()) { 422 o << name << '='; 423 } 424 if (pass) { 425 o << " PASS"; 426 } 427 std::visit([&](const auto &x) { x.Dump(o); }, u); 428 return o; 429 } 430 431 FunctionResult::FunctionResult(DynamicType t) : u{TypeAndShape{t}} {} 432 FunctionResult::FunctionResult(TypeAndShape &&t) : u{std::move(t)} {} 433 FunctionResult::FunctionResult(Procedure &&p) : u{std::move(p)} {} 434 FunctionResult::~FunctionResult() {} 435 436 bool FunctionResult::operator==(const FunctionResult &that) const { 437 return attrs == that.attrs && u == that.u; 438 } 439 440 std::optional<FunctionResult> FunctionResult::Characterize( 441 const Symbol &symbol, const IntrinsicProcTable &intrinsics) { 442 if (const auto *obj{symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 443 if (auto type{TypeAndShape::Characterize(*obj)}) { 444 FunctionResult result{std::move(*type)}; 445 CopyAttrs<FunctionResult, FunctionResult::Attr>(symbol, result, 446 { 447 {semantics::Attr::ALLOCATABLE, FunctionResult::Attr::Allocatable}, 448 {semantics::Attr::CONTIGUOUS, FunctionResult::Attr::Contiguous}, 449 {semantics::Attr::POINTER, FunctionResult::Attr::Pointer}, 450 }); 451 return result; 452 } 453 } else if (auto maybeProc{Procedure::Characterize(symbol, intrinsics)}) { 454 FunctionResult result{std::move(*maybeProc)}; 455 result.attrs.set(FunctionResult::Attr::Pointer); 456 return result; 457 } 458 return std::nullopt; 459 } 460 461 bool FunctionResult::IsAssumedLengthCharacter() const { 462 if (const auto *ts{std::get_if<TypeAndShape>(&u)}) { 463 return ts->type().IsAssumedLengthCharacter(); 464 } else { 465 return false; 466 } 467 } 468 469 bool FunctionResult::CanBeReturnedViaImplicitInterface() const { 470 if (attrs.test(Attr::Pointer) || attrs.test(Attr::Allocatable)) { 471 return false; // 15.4.2.2(4)(b) 472 } else if (const auto *typeAndShape{GetTypeAndShape()}) { 473 if (typeAndShape->Rank() > 0) { 474 return false; // 15.4.2.2(4)(a) 475 } else { 476 const DynamicType &type{typeAndShape->type()}; 477 switch (type.category()) { 478 case TypeCategory::Character: 479 if (const auto *param{type.charLength()}) { 480 if (const auto &expr{param->GetExplicit()}) { 481 return IsConstantExpr(*expr); // 15.4.2.2(4)(c) 482 } 483 } 484 return false; 485 case TypeCategory::Derived: 486 if (!type.IsPolymorphic()) { 487 const auto &spec{type.GetDerivedTypeSpec()}; 488 for (const auto &pair : spec.parameters()) { 489 if (const auto &expr{pair.second.GetExplicit()}) { 490 if (!IsConstantExpr(*expr)) { 491 return false; // 15.4.2.2(4)(c) 492 } 493 } 494 } 495 return true; 496 } 497 return false; 498 default: return true; 499 } 500 } 501 } else { 502 return false; // 15.4.2.2(4)(b) - procedure pointer 503 } 504 } 505 506 std::ostream &FunctionResult::Dump(std::ostream &o) const { 507 attrs.Dump(o, EnumToString); 508 std::visit( 509 common::visitors{ 510 [&](const TypeAndShape &ts) { ts.Dump(o); }, 511 [&](const CopyableIndirection<Procedure> &p) { 512 p.value().Dump(o << " procedure(") << ')'; 513 }, 514 }, 515 u); 516 return o; 517 } 518 519 Procedure::Procedure(FunctionResult &&fr, DummyArguments &&args, Attrs a) 520 : functionResult{std::move(fr)}, dummyArguments{std::move(args)}, attrs{a} {} 521 Procedure::Procedure(DummyArguments &&args, Attrs a) 522 : dummyArguments{std::move(args)}, attrs{a} {} 523 Procedure::~Procedure() {} 524 525 bool Procedure::operator==(const Procedure &that) const { 526 return attrs == that.attrs && functionResult == that.functionResult && 527 dummyArguments == that.dummyArguments; 528 } 529 530 int Procedure::FindPassIndex(std::optional<parser::CharBlock> name) const { 531 int argCount{static_cast<int>(dummyArguments.size())}; 532 int index{0}; 533 if (name) { 534 while (index < argCount && *name != dummyArguments[index].name.c_str()) { 535 ++index; 536 } 537 } 538 CHECK(index < argCount); 539 return index; 540 } 541 542 bool Procedure::CanOverride( 543 const Procedure &that, std::optional<int> passIndex) const { 544 // A pure procedure may override an impure one (7.5.7.3(2)) 545 if ((that.attrs.test(Attr::Pure) && !attrs.test(Attr::Pure)) || 546 that.attrs.test(Attr::Elemental) != attrs.test(Attr::Elemental) || 547 functionResult != that.functionResult) { 548 return false; 549 } 550 int argCount{static_cast<int>(dummyArguments.size())}; 551 if (argCount != static_cast<int>(that.dummyArguments.size())) { 552 return false; 553 } 554 for (int j{0}; j < argCount; ++j) { 555 if ((!passIndex || j != *passIndex) && 556 dummyArguments[j] != that.dummyArguments[j]) { 557 return false; 558 } 559 } 560 return true; 561 } 562 563 std::optional<Procedure> Procedure::Characterize( 564 const semantics::Symbol &symbol, const IntrinsicProcTable &intrinsics) { 565 Procedure result; 566 CopyAttrs<Procedure, Procedure::Attr>(symbol, result, 567 { 568 {semantics::Attr::PURE, Procedure::Attr::Pure}, 569 {semantics::Attr::ELEMENTAL, Procedure::Attr::Elemental}, 570 {semantics::Attr::BIND_C, Procedure::Attr::BindC}, 571 }); 572 if (result.attrs.test(Attr::Elemental) && 573 !symbol.attrs().test(semantics::Attr::IMPURE)) { 574 result.attrs.set(Attr::Pure); // explicitly flag pure procedures 575 } 576 return std::visit( 577 common::visitors{ 578 [&](const semantics::SubprogramDetails &subp) 579 -> std::optional<Procedure> { 580 if (subp.isFunction()) { 581 if (auto fr{FunctionResult::Characterize( 582 subp.result(), intrinsics)}) { 583 result.functionResult = std::move(fr); 584 } else { 585 return std::nullopt; 586 } 587 } else { 588 result.attrs.set(Attr::Subroutine); 589 } 590 for (const semantics::Symbol *arg : subp.dummyArgs()) { 591 if (!arg) { 592 result.dummyArguments.emplace_back(AlternateReturn{}); 593 } else if (auto argCharacteristics{ 594 DummyArgument::Characterize(*arg, intrinsics)}) { 595 result.dummyArguments.emplace_back( 596 std::move(argCharacteristics.value())); 597 } else { 598 return std::nullopt; 599 } 600 } 601 return result; 602 }, 603 [&](const semantics::ProcEntityDetails &proc) 604 -> std::optional<Procedure> { 605 if (symbol.attrs().test(semantics::Attr::INTRINSIC)) { 606 return intrinsics.IsSpecificIntrinsicFunction( 607 symbol.name().ToString()); 608 } 609 const semantics::ProcInterface &interface{proc.interface()}; 610 if (const semantics::Symbol * interfaceSymbol{interface.symbol()}) { 611 return Characterize(*interfaceSymbol, intrinsics); 612 } else { 613 result.attrs.set(Attr::ImplicitInterface); 614 const semantics::DeclTypeSpec *type{interface.type()}; 615 if (symbol.test(semantics::Symbol::Flag::Subroutine)) { 616 // ignore any implicit typing 617 result.attrs.set(Attr::Subroutine); 618 } else if (type) { 619 if (auto resultType{DynamicType::From(*type)}) { 620 result.functionResult = FunctionResult{*resultType}; 621 } else { 622 return std::nullopt; 623 } 624 } else if (symbol.test(semantics::Symbol::Flag::Function)) { 625 return std::nullopt; 626 } 627 // The PASS name, if any, is not a characteristic. 628 return result; 629 } 630 }, 631 [&](const semantics::ProcBindingDetails &binding) { 632 if (auto result{Characterize(binding.symbol(), intrinsics)}) { 633 if (!symbol.attrs().test(semantics::Attr::NOPASS)) { 634 auto passName{binding.passName()}; 635 for (auto &dummy : result->dummyArguments) { 636 if (!passName || dummy.name.c_str() == *passName) { 637 dummy.pass = true; 638 return result; 639 } 640 } 641 DIE("PASS argument missing"); 642 } 643 return result; 644 } else { 645 return std::optional<Procedure>{}; 646 } 647 }, 648 [&](const semantics::UseDetails &use) { 649 return Characterize(use.symbol(), intrinsics); 650 }, 651 [&](const semantics::HostAssocDetails &assoc) { 652 return Characterize(assoc.symbol(), intrinsics); 653 }, 654 [](const auto &) { return std::optional<Procedure>{}; }, 655 }, 656 symbol.details()); 657 } 658 659 std::optional<Procedure> Procedure::Characterize( 660 const ProcedureDesignator &proc, const IntrinsicProcTable &intrinsics) { 661 if (const auto *symbol{proc.GetSymbol()}) { 662 if (auto result{characteristics::Procedure::Characterize( 663 ResolveAssociations(*symbol), intrinsics)}) { 664 return result; 665 } 666 } else if (const auto *intrinsic{proc.GetSpecificIntrinsic()}) { 667 return intrinsic->characteristics.value(); 668 } 669 return std::nullopt; 670 } 671 672 std::optional<Procedure> Procedure::Characterize( 673 const ProcedureRef &ref, const IntrinsicProcTable &intrinsics) { 674 if (auto callee{Characterize(ref.proc(), intrinsics)}) { 675 if (callee->functionResult) { 676 if (const Procedure * 677 proc{callee->functionResult->IsProcedurePointer()}) { 678 return {*proc}; 679 } 680 } 681 } 682 return std::nullopt; 683 } 684 685 bool Procedure::CanBeCalledViaImplicitInterface() const { 686 if (attrs.test(Attr::Elemental) || attrs.test(Attr::BindC)) { 687 return false; // 15.4.2.2(5,6) 688 } else if (IsFunction() && 689 !functionResult->CanBeReturnedViaImplicitInterface()) { 690 return false; 691 } else { 692 for (const DummyArgument &arg : dummyArguments) { 693 if (!arg.CanBePassedViaImplicitInterface()) { 694 return false; 695 } 696 } 697 return true; 698 } 699 } 700 701 std::ostream &Procedure::Dump(std::ostream &o) const { 702 attrs.Dump(o, EnumToString); 703 if (functionResult) { 704 functionResult->Dump(o << "TYPE(") << ") FUNCTION"; 705 } else { 706 o << "SUBROUTINE"; 707 } 708 char sep{'('}; 709 for (const auto &dummy : dummyArguments) { 710 dummy.Dump(o << sep); 711 sep = ','; 712 } 713 return o << (sep == '(' ? "()" : ")"); 714 } 715 716 // Utility class to determine if Procedures, etc. are distinguishable 717 class DistinguishUtils { 718 public: 719 // Are these procedures distinguishable for a generic name? 720 static bool Distinguishable(const Procedure &, const Procedure &); 721 // Are these procedures distinguishable for a generic operator or assignment? 722 static bool DistinguishableOpOrAssign(const Procedure &, const Procedure &); 723 724 private: 725 struct CountDummyProcedures { 726 CountDummyProcedures(const DummyArguments &args) { 727 for (const DummyArgument &arg : args) { 728 if (std::holds_alternative<DummyProcedure>(arg.u)) { 729 total += 1; 730 notOptional += !arg.IsOptional(); 731 } 732 } 733 } 734 int total{0}; 735 int notOptional{0}; 736 }; 737 738 static bool Rule3Distinguishable(const Procedure &, const Procedure &); 739 static const DummyArgument *Rule1DistinguishingArg( 740 const DummyArguments &, const DummyArguments &); 741 static int FindFirstToDistinguishByPosition( 742 const DummyArguments &, const DummyArguments &); 743 static int FindLastToDistinguishByName( 744 const DummyArguments &, const DummyArguments &); 745 static int CountCompatibleWith(const DummyArgument &, const DummyArguments &); 746 static int CountNotDistinguishableFrom( 747 const DummyArgument &, const DummyArguments &); 748 static bool Distinguishable(const DummyArgument &, const DummyArgument &); 749 static bool Distinguishable(const DummyDataObject &, const DummyDataObject &); 750 static bool Distinguishable(const DummyProcedure &, const DummyProcedure &); 751 static bool Distinguishable(const FunctionResult &, const FunctionResult &); 752 static bool Distinguishable(const TypeAndShape &, const TypeAndShape &); 753 static bool IsTkrCompatible(const DummyArgument &, const DummyArgument &); 754 static bool IsTkrCompatible(const TypeAndShape &, const TypeAndShape &); 755 static const DummyArgument *GetAtEffectivePosition( 756 const DummyArguments &, int); 757 static const DummyArgument *GetPassArg(const Procedure &); 758 }; 759 760 // Simpler distinguishability rules for operators and assignment 761 bool DistinguishUtils::DistinguishableOpOrAssign( 762 const Procedure &proc1, const Procedure &proc2) { 763 auto &args1{proc1.dummyArguments}; 764 auto &args2{proc2.dummyArguments}; 765 if (args1.size() != args2.size()) { 766 return true; // C1511: distinguishable based on number of arguments 767 } 768 for (std::size_t i{0}; i < args1.size(); ++i) { 769 if (Distinguishable(args1[i], args2[i])) { 770 return true; // C1511, C1512: distinguishable based on this arg 771 } 772 } 773 return false; 774 } 775 776 bool DistinguishUtils::Distinguishable( 777 const Procedure &proc1, const Procedure &proc2) { 778 auto &args1{proc1.dummyArguments}; 779 auto &args2{proc2.dummyArguments}; 780 auto count1{CountDummyProcedures(args1)}; 781 auto count2{CountDummyProcedures(args2)}; 782 if (count1.notOptional > count2.total || count2.notOptional > count1.total) { 783 return true; // distinguishable based on C1514 rule 2 784 } 785 if (Rule3Distinguishable(proc1, proc2)) { 786 return true; // distinguishable based on C1514 rule 3 787 } 788 if (Rule1DistinguishingArg(args1, args2)) { 789 return true; // distinguishable based on C1514 rule 1 790 } 791 int pos1{FindFirstToDistinguishByPosition(args1, args2)}; 792 int name1{FindLastToDistinguishByName(args1, args2)}; 793 if (pos1 >= 0 && pos1 <= name1) { 794 return true; // distinguishable based on C1514 rule 4 795 } 796 int pos2{FindFirstToDistinguishByPosition(args2, args1)}; 797 int name2{FindLastToDistinguishByName(args2, args1)}; 798 if (pos2 >= 0 && pos2 <= name2) { 799 return true; // distinguishable based on C1514 rule 4 800 } 801 return false; 802 } 803 804 // C1514 rule 3: Procedures are distinguishable if both have a passed-object 805 // dummy argument and those are distinguishable. 806 bool DistinguishUtils::Rule3Distinguishable( 807 const Procedure &proc1, const Procedure &proc2) { 808 const DummyArgument *pass1{GetPassArg(proc1)}; 809 const DummyArgument *pass2{GetPassArg(proc2)}; 810 return pass1 && pass2 && Distinguishable(*pass1, *pass2); 811 } 812 813 // Find a non-passed-object dummy data object in one of the argument lists 814 // that satisfies C1514 rule 1. I.e. x such that: 815 // - m is the number of dummy data objects in one that are nonoptional, 816 // are not passed-object, that x is TKR compatible with 817 // - n is the number of non-passed-object dummy data objects, in the other 818 // that are not distinguishable from x 819 // - m is greater than n 820 const DummyArgument *DistinguishUtils::Rule1DistinguishingArg( 821 const DummyArguments &args1, const DummyArguments &args2) { 822 auto size1{args1.size()}; 823 auto size2{args2.size()}; 824 for (std::size_t i{0}; i < size1 + size2; ++i) { 825 const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]}; 826 if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) { 827 if (CountCompatibleWith(x, args1) > 828 CountNotDistinguishableFrom(x, args2) || 829 CountCompatibleWith(x, args2) > 830 CountNotDistinguishableFrom(x, args1)) { 831 return &x; 832 } 833 } 834 } 835 return nullptr; 836 } 837 838 // Find the index of the first nonoptional non-passed-object dummy argument 839 // in args1 at an effective position such that either: 840 // - args2 has no dummy argument at that effective position 841 // - the dummy argument at that position is distinguishable from it 842 int DistinguishUtils::FindFirstToDistinguishByPosition( 843 const DummyArguments &args1, const DummyArguments &args2) { 844 int effective{0}; // position of arg1 in list, ignoring passed arg 845 for (std::size_t i{0}; i < args1.size(); ++i) { 846 const DummyArgument &arg1{args1.at(i)}; 847 if (!arg1.pass && !arg1.IsOptional()) { 848 const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)}; 849 if (!arg2 || Distinguishable(arg1, *arg2)) { 850 return i; 851 } 852 } 853 effective += !arg1.pass; 854 } 855 return -1; 856 } 857 858 // Find the index of the last nonoptional non-passed-object dummy argument 859 // in args1 whose name is such that either: 860 // - args2 has no dummy argument with that name 861 // - the dummy argument with that name is distinguishable from it 862 int DistinguishUtils::FindLastToDistinguishByName( 863 const DummyArguments &args1, const DummyArguments &args2) { 864 std::map<std::string, const DummyArgument *> nameToArg; 865 for (const auto &arg2 : args2) { 866 nameToArg.emplace(arg2.name, &arg2); 867 } 868 for (int i = args1.size() - 1; i >= 0; --i) { 869 const DummyArgument &arg1{args1.at(i)}; 870 if (!arg1.pass && !arg1.IsOptional()) { 871 auto it{nameToArg.find(arg1.name)}; 872 if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) { 873 return i; 874 } 875 } 876 } 877 return -1; 878 } 879 880 // Count the dummy data objects in args that are nonoptional, are not 881 // passed-object, and that x is TKR compatible with 882 int DistinguishUtils::CountCompatibleWith( 883 const DummyArgument &x, const DummyArguments &args) { 884 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) { 885 return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y); 886 }); 887 } 888 889 // Return the number of dummy data objects in args that are not 890 // distinguishable from x and not passed-object. 891 int DistinguishUtils::CountNotDistinguishableFrom( 892 const DummyArgument &x, const DummyArguments &args) { 893 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) { 894 return !y.pass && std::holds_alternative<DummyDataObject>(y.u) && 895 !Distinguishable(y, x); 896 }); 897 } 898 899 bool DistinguishUtils::Distinguishable( 900 const DummyArgument &x, const DummyArgument &y) { 901 if (x.u.index() != y.u.index()) { 902 return true; // different kind: data/proc/alt-return 903 } 904 return std::visit( 905 common::visitors{ 906 [&](const DummyDataObject &z) { 907 return Distinguishable(z, std::get<DummyDataObject>(y.u)); 908 }, 909 [&](const DummyProcedure &z) { 910 return Distinguishable(z, std::get<DummyProcedure>(y.u)); 911 }, 912 [&](const AlternateReturn &) { return false; }, 913 }, 914 x.u); 915 } 916 917 bool DistinguishUtils::Distinguishable( 918 const DummyDataObject &x, const DummyDataObject &y) { 919 using Attr = DummyDataObject::Attr; 920 if (Distinguishable(x.type, y.type)) { 921 return true; 922 } else if (x.attrs.test(Attr::Allocatable) && y.attrs.test(Attr::Pointer) && 923 y.intent != common::Intent::In) { 924 return true; 925 } else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) && 926 x.intent != common::Intent::In) { 927 return true; 928 } else { 929 return false; 930 } 931 } 932 933 bool DistinguishUtils::Distinguishable( 934 const DummyProcedure &x, const DummyProcedure &y) { 935 const Procedure &xProc{x.procedure.value()}; 936 const Procedure &yProc{y.procedure.value()}; 937 if (Distinguishable(xProc, yProc)) { 938 return true; 939 } else { 940 const std::optional<FunctionResult> &xResult{xProc.functionResult}; 941 const std::optional<FunctionResult> &yResult{yProc.functionResult}; 942 return xResult ? !yResult || Distinguishable(*xResult, *yResult) 943 : yResult.has_value(); 944 } 945 } 946 947 bool DistinguishUtils::Distinguishable( 948 const FunctionResult &x, const FunctionResult &y) { 949 if (x.u.index() != y.u.index()) { 950 return true; // one is data object, one is procedure 951 } 952 return std::visit( 953 common::visitors{ 954 [&](const TypeAndShape &z) { 955 return Distinguishable(z, std::get<TypeAndShape>(y.u)); 956 }, 957 [&](const CopyableIndirection<Procedure> &z) { 958 return Distinguishable(z.value(), 959 std::get<CopyableIndirection<Procedure>>(y.u).value()); 960 }, 961 }, 962 x.u); 963 } 964 965 bool DistinguishUtils::Distinguishable( 966 const TypeAndShape &x, const TypeAndShape &y) { 967 return !IsTkrCompatible(x, y) && !IsTkrCompatible(y, x); 968 } 969 970 // Compatibility based on type, kind, and rank 971 bool DistinguishUtils::IsTkrCompatible( 972 const DummyArgument &x, const DummyArgument &y) { 973 const auto *obj1{std::get_if<DummyDataObject>(&x.u)}; 974 const auto *obj2{std::get_if<DummyDataObject>(&y.u)}; 975 return obj1 && obj2 && IsTkrCompatible(obj1->type, obj2->type); 976 } 977 bool DistinguishUtils::IsTkrCompatible( 978 const TypeAndShape &x, const TypeAndShape &y) { 979 return x.type().IsTkCompatibleWith(y.type()) && 980 (x.attrs().test(TypeAndShape::Attr::AssumedRank) || 981 y.attrs().test(TypeAndShape::Attr::AssumedRank) || 982 x.Rank() == y.Rank()); 983 } 984 985 // Return the argument at the given index, ignoring the passed arg 986 const DummyArgument *DistinguishUtils::GetAtEffectivePosition( 987 const DummyArguments &args, int index) { 988 for (const DummyArgument &arg : args) { 989 if (!arg.pass) { 990 if (index == 0) { 991 return &arg; 992 } 993 --index; 994 } 995 } 996 return nullptr; 997 } 998 999 // Return the passed-object dummy argument of this procedure, if any 1000 const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) { 1001 for (const auto &arg : proc.dummyArguments) { 1002 if (arg.pass) { 1003 return &arg; 1004 } 1005 } 1006 return nullptr; 1007 } 1008 1009 bool Distinguishable(const Procedure &x, const Procedure &y) { 1010 return DistinguishUtils::Distinguishable(x, y); 1011 } 1012 1013 bool DistinguishableOpOrAssign(const Procedure &x, const Procedure &y) { 1014 return DistinguishUtils::DistinguishableOpOrAssign(x, y); 1015 } 1016 1017 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument) 1018 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure) 1019 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult) 1020 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure) 1021 } 1022 1023 template class Fortran::common::Indirection< 1024 Fortran::evaluate::characteristics::Procedure, true>; 1025