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