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 } 489 } 490 return false; 491 case TypeCategory::Derived: 492 if (!type.IsPolymorphic()) { 493 const auto &spec{type.GetDerivedTypeSpec()}; 494 for (const auto &pair : spec.parameters()) { 495 if (const auto &expr{pair.second.GetExplicit()}) { 496 if (!IsConstantExpr(*expr)) { 497 return false; // 15.4.2.2(4)(c) 498 } 499 } 500 } 501 return true; 502 } 503 return false; 504 default: 505 return true; 506 } 507 } 508 } else { 509 return false; // 15.4.2.2(4)(b) - procedure pointer 510 } 511 } 512 513 llvm::raw_ostream &FunctionResult::Dump(llvm::raw_ostream &o) const { 514 attrs.Dump(o, EnumToString); 515 std::visit(common::visitors{ 516 [&](const TypeAndShape &ts) { ts.Dump(o); }, 517 [&](const CopyableIndirection<Procedure> &p) { 518 p.value().Dump(o << " procedure(") << ')'; 519 }, 520 }, 521 u); 522 return o; 523 } 524 525 Procedure::Procedure(FunctionResult &&fr, DummyArguments &&args, Attrs a) 526 : functionResult{std::move(fr)}, dummyArguments{std::move(args)}, attrs{a} { 527 } 528 Procedure::Procedure(DummyArguments &&args, Attrs a) 529 : dummyArguments{std::move(args)}, attrs{a} {} 530 Procedure::~Procedure() {} 531 532 bool Procedure::operator==(const Procedure &that) const { 533 return attrs == that.attrs && functionResult == that.functionResult && 534 dummyArguments == that.dummyArguments; 535 } 536 537 int Procedure::FindPassIndex(std::optional<parser::CharBlock> name) const { 538 int argCount{static_cast<int>(dummyArguments.size())}; 539 int index{0}; 540 if (name) { 541 while (index < argCount && *name != dummyArguments[index].name.c_str()) { 542 ++index; 543 } 544 } 545 CHECK(index < argCount); 546 return index; 547 } 548 549 bool Procedure::CanOverride( 550 const Procedure &that, std::optional<int> passIndex) const { 551 // A pure procedure may override an impure one (7.5.7.3(2)) 552 if ((that.attrs.test(Attr::Pure) && !attrs.test(Attr::Pure)) || 553 that.attrs.test(Attr::Elemental) != attrs.test(Attr::Elemental) || 554 functionResult != that.functionResult) { 555 return false; 556 } 557 int argCount{static_cast<int>(dummyArguments.size())}; 558 if (argCount != static_cast<int>(that.dummyArguments.size())) { 559 return false; 560 } 561 for (int j{0}; j < argCount; ++j) { 562 if ((!passIndex || j != *passIndex) && 563 dummyArguments[j] != that.dummyArguments[j]) { 564 return false; 565 } 566 } 567 return true; 568 } 569 570 std::optional<Procedure> Procedure::Characterize( 571 const semantics::Symbol &original, const IntrinsicProcTable &intrinsics) { 572 Procedure result; 573 const auto &symbol{ResolveAssociations(original)}; 574 CopyAttrs<Procedure, Procedure::Attr>(symbol, result, 575 { 576 {semantics::Attr::PURE, Procedure::Attr::Pure}, 577 {semantics::Attr::ELEMENTAL, Procedure::Attr::Elemental}, 578 {semantics::Attr::BIND_C, Procedure::Attr::BindC}, 579 }); 580 if (result.attrs.test(Attr::Elemental) && 581 !symbol.attrs().test(semantics::Attr::IMPURE)) { 582 result.attrs.set(Attr::Pure); // explicitly flag pure procedures 583 } 584 return std::visit( 585 common::visitors{ 586 [&](const semantics::SubprogramDetails &subp) 587 -> std::optional<Procedure> { 588 if (subp.isFunction()) { 589 if (auto fr{FunctionResult::Characterize( 590 subp.result(), intrinsics)}) { 591 result.functionResult = std::move(fr); 592 } else { 593 return std::nullopt; 594 } 595 } else { 596 result.attrs.set(Attr::Subroutine); 597 } 598 for (const semantics::Symbol *arg : subp.dummyArgs()) { 599 if (!arg) { 600 result.dummyArguments.emplace_back(AlternateReturn{}); 601 } else if (auto argCharacteristics{ 602 DummyArgument::Characterize(*arg, intrinsics)}) { 603 result.dummyArguments.emplace_back( 604 std::move(argCharacteristics.value())); 605 } else { 606 return std::nullopt; 607 } 608 } 609 return result; 610 }, 611 [&](const semantics::ProcEntityDetails &proc) 612 -> std::optional<Procedure> { 613 if (symbol.attrs().test(semantics::Attr::INTRINSIC)) { 614 return intrinsics.IsSpecificIntrinsicFunction( 615 symbol.name().ToString()); 616 } 617 const semantics::ProcInterface &interface{proc.interface()}; 618 if (const semantics::Symbol * interfaceSymbol{interface.symbol()}) { 619 return Characterize(*interfaceSymbol, intrinsics); 620 } else { 621 result.attrs.set(Attr::ImplicitInterface); 622 const semantics::DeclTypeSpec *type{interface.type()}; 623 if (symbol.test(semantics::Symbol::Flag::Subroutine)) { 624 // ignore any implicit typing 625 result.attrs.set(Attr::Subroutine); 626 } else if (type) { 627 if (auto resultType{DynamicType::From(*type)}) { 628 result.functionResult = FunctionResult{*resultType}; 629 } else { 630 return std::nullopt; 631 } 632 } else if (symbol.test(semantics::Symbol::Flag::Function)) { 633 return std::nullopt; 634 } 635 // The PASS name, if any, is not a characteristic. 636 return result; 637 } 638 }, 639 [&](const semantics::ProcBindingDetails &binding) { 640 if (auto result{Characterize(binding.symbol(), intrinsics)}) { 641 if (!symbol.attrs().test(semantics::Attr::NOPASS)) { 642 auto passName{binding.passName()}; 643 for (auto &dummy : result->dummyArguments) { 644 if (!passName || dummy.name.c_str() == *passName) { 645 dummy.pass = true; 646 return result; 647 } 648 } 649 DIE("PASS argument missing"); 650 } 651 return result; 652 } else { 653 return std::optional<Procedure>{}; 654 } 655 }, 656 [&](const semantics::UseDetails &use) { 657 return Characterize(use.symbol(), intrinsics); 658 }, 659 [&](const semantics::HostAssocDetails &assoc) { 660 return Characterize(assoc.symbol(), intrinsics); 661 }, 662 [](const auto &) { return std::optional<Procedure>{}; }, 663 }, 664 symbol.details()); 665 } 666 667 std::optional<Procedure> Procedure::Characterize( 668 const ProcedureDesignator &proc, const IntrinsicProcTable &intrinsics) { 669 if (const auto *symbol{proc.GetSymbol()}) { 670 if (auto result{characteristics::Procedure::Characterize( 671 ResolveAssociations(*symbol), intrinsics)}) { 672 return result; 673 } 674 } else if (const auto *intrinsic{proc.GetSpecificIntrinsic()}) { 675 return intrinsic->characteristics.value(); 676 } 677 return std::nullopt; 678 } 679 680 std::optional<Procedure> Procedure::Characterize( 681 const ProcedureRef &ref, const IntrinsicProcTable &intrinsics) { 682 if (auto callee{Characterize(ref.proc(), intrinsics)}) { 683 if (callee->functionResult) { 684 if (const Procedure * 685 proc{callee->functionResult->IsProcedurePointer()}) { 686 return {*proc}; 687 } 688 } 689 } 690 return std::nullopt; 691 } 692 693 bool Procedure::CanBeCalledViaImplicitInterface() const { 694 if (attrs.test(Attr::Elemental) || attrs.test(Attr::BindC)) { 695 return false; // 15.4.2.2(5,6) 696 } else if (IsFunction() && 697 !functionResult->CanBeReturnedViaImplicitInterface()) { 698 return false; 699 } else { 700 for (const DummyArgument &arg : dummyArguments) { 701 if (!arg.CanBePassedViaImplicitInterface()) { 702 return false; 703 } 704 } 705 return true; 706 } 707 } 708 709 llvm::raw_ostream &Procedure::Dump(llvm::raw_ostream &o) const { 710 attrs.Dump(o, EnumToString); 711 if (functionResult) { 712 functionResult->Dump(o << "TYPE(") << ") FUNCTION"; 713 } else { 714 o << "SUBROUTINE"; 715 } 716 char sep{'('}; 717 for (const auto &dummy : dummyArguments) { 718 dummy.Dump(o << sep); 719 sep = ','; 720 } 721 return o << (sep == '(' ? "()" : ")"); 722 } 723 724 // Utility class to determine if Procedures, etc. are distinguishable 725 class DistinguishUtils { 726 public: 727 // Are these procedures distinguishable for a generic name? 728 static bool Distinguishable(const Procedure &, const Procedure &); 729 // Are these procedures distinguishable for a generic operator or assignment? 730 static bool DistinguishableOpOrAssign(const Procedure &, const Procedure &); 731 732 private: 733 struct CountDummyProcedures { 734 CountDummyProcedures(const DummyArguments &args) { 735 for (const DummyArgument &arg : args) { 736 if (std::holds_alternative<DummyProcedure>(arg.u)) { 737 total += 1; 738 notOptional += !arg.IsOptional(); 739 } 740 } 741 } 742 int total{0}; 743 int notOptional{0}; 744 }; 745 746 static bool Rule3Distinguishable(const Procedure &, const Procedure &); 747 static const DummyArgument *Rule1DistinguishingArg( 748 const DummyArguments &, const DummyArguments &); 749 static int FindFirstToDistinguishByPosition( 750 const DummyArguments &, const DummyArguments &); 751 static int FindLastToDistinguishByName( 752 const DummyArguments &, const DummyArguments &); 753 static int CountCompatibleWith(const DummyArgument &, const DummyArguments &); 754 static int CountNotDistinguishableFrom( 755 const DummyArgument &, const DummyArguments &); 756 static bool Distinguishable(const DummyArgument &, const DummyArgument &); 757 static bool Distinguishable(const DummyDataObject &, const DummyDataObject &); 758 static bool Distinguishable(const DummyProcedure &, const DummyProcedure &); 759 static bool Distinguishable(const FunctionResult &, const FunctionResult &); 760 static bool Distinguishable(const TypeAndShape &, const TypeAndShape &); 761 static bool IsTkrCompatible(const DummyArgument &, const DummyArgument &); 762 static bool IsTkrCompatible(const TypeAndShape &, const TypeAndShape &); 763 static const DummyArgument *GetAtEffectivePosition( 764 const DummyArguments &, int); 765 static const DummyArgument *GetPassArg(const Procedure &); 766 }; 767 768 // Simpler distinguishability rules for operators and assignment 769 bool DistinguishUtils::DistinguishableOpOrAssign( 770 const Procedure &proc1, const Procedure &proc2) { 771 auto &args1{proc1.dummyArguments}; 772 auto &args2{proc2.dummyArguments}; 773 if (args1.size() != args2.size()) { 774 return true; // C1511: distinguishable based on number of arguments 775 } 776 for (std::size_t i{0}; i < args1.size(); ++i) { 777 if (Distinguishable(args1[i], args2[i])) { 778 return true; // C1511, C1512: distinguishable based on this arg 779 } 780 } 781 return false; 782 } 783 784 bool DistinguishUtils::Distinguishable( 785 const Procedure &proc1, const Procedure &proc2) { 786 auto &args1{proc1.dummyArguments}; 787 auto &args2{proc2.dummyArguments}; 788 auto count1{CountDummyProcedures(args1)}; 789 auto count2{CountDummyProcedures(args2)}; 790 if (count1.notOptional > count2.total || count2.notOptional > count1.total) { 791 return true; // distinguishable based on C1514 rule 2 792 } 793 if (Rule3Distinguishable(proc1, proc2)) { 794 return true; // distinguishable based on C1514 rule 3 795 } 796 if (Rule1DistinguishingArg(args1, args2)) { 797 return true; // distinguishable based on C1514 rule 1 798 } 799 int pos1{FindFirstToDistinguishByPosition(args1, args2)}; 800 int name1{FindLastToDistinguishByName(args1, args2)}; 801 if (pos1 >= 0 && pos1 <= name1) { 802 return true; // distinguishable based on C1514 rule 4 803 } 804 int pos2{FindFirstToDistinguishByPosition(args2, args1)}; 805 int name2{FindLastToDistinguishByName(args2, args1)}; 806 if (pos2 >= 0 && pos2 <= name2) { 807 return true; // distinguishable based on C1514 rule 4 808 } 809 return false; 810 } 811 812 // C1514 rule 3: Procedures are distinguishable if both have a passed-object 813 // dummy argument and those are distinguishable. 814 bool DistinguishUtils::Rule3Distinguishable( 815 const Procedure &proc1, const Procedure &proc2) { 816 const DummyArgument *pass1{GetPassArg(proc1)}; 817 const DummyArgument *pass2{GetPassArg(proc2)}; 818 return pass1 && pass2 && Distinguishable(*pass1, *pass2); 819 } 820 821 // Find a non-passed-object dummy data object in one of the argument lists 822 // that satisfies C1514 rule 1. I.e. x such that: 823 // - m is the number of dummy data objects in one that are nonoptional, 824 // are not passed-object, that x is TKR compatible with 825 // - n is the number of non-passed-object dummy data objects, in the other 826 // that are not distinguishable from x 827 // - m is greater than n 828 const DummyArgument *DistinguishUtils::Rule1DistinguishingArg( 829 const DummyArguments &args1, const DummyArguments &args2) { 830 auto size1{args1.size()}; 831 auto size2{args2.size()}; 832 for (std::size_t i{0}; i < size1 + size2; ++i) { 833 const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]}; 834 if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) { 835 if (CountCompatibleWith(x, args1) > 836 CountNotDistinguishableFrom(x, args2) || 837 CountCompatibleWith(x, args2) > 838 CountNotDistinguishableFrom(x, args1)) { 839 return &x; 840 } 841 } 842 } 843 return nullptr; 844 } 845 846 // Find the index of the first nonoptional non-passed-object dummy argument 847 // in args1 at an effective position such that either: 848 // - args2 has no dummy argument at that effective position 849 // - the dummy argument at that position is distinguishable from it 850 int DistinguishUtils::FindFirstToDistinguishByPosition( 851 const DummyArguments &args1, const DummyArguments &args2) { 852 int effective{0}; // position of arg1 in list, ignoring passed arg 853 for (std::size_t i{0}; i < args1.size(); ++i) { 854 const DummyArgument &arg1{args1.at(i)}; 855 if (!arg1.pass && !arg1.IsOptional()) { 856 const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)}; 857 if (!arg2 || Distinguishable(arg1, *arg2)) { 858 return i; 859 } 860 } 861 effective += !arg1.pass; 862 } 863 return -1; 864 } 865 866 // Find the index of the last nonoptional non-passed-object dummy argument 867 // in args1 whose name is such that either: 868 // - args2 has no dummy argument with that name 869 // - the dummy argument with that name is distinguishable from it 870 int DistinguishUtils::FindLastToDistinguishByName( 871 const DummyArguments &args1, const DummyArguments &args2) { 872 std::map<std::string, const DummyArgument *> nameToArg; 873 for (const auto &arg2 : args2) { 874 nameToArg.emplace(arg2.name, &arg2); 875 } 876 for (int i = args1.size() - 1; i >= 0; --i) { 877 const DummyArgument &arg1{args1.at(i)}; 878 if (!arg1.pass && !arg1.IsOptional()) { 879 auto it{nameToArg.find(arg1.name)}; 880 if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) { 881 return i; 882 } 883 } 884 } 885 return -1; 886 } 887 888 // Count the dummy data objects in args that are nonoptional, are not 889 // passed-object, and that x is TKR compatible with 890 int DistinguishUtils::CountCompatibleWith( 891 const DummyArgument &x, const DummyArguments &args) { 892 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) { 893 return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y); 894 }); 895 } 896 897 // Return the number of dummy data objects in args that are not 898 // distinguishable from x and not passed-object. 899 int DistinguishUtils::CountNotDistinguishableFrom( 900 const DummyArgument &x, const DummyArguments &args) { 901 return std::count_if(args.begin(), args.end(), [&](const DummyArgument &y) { 902 return !y.pass && std::holds_alternative<DummyDataObject>(y.u) && 903 !Distinguishable(y, x); 904 }); 905 } 906 907 bool DistinguishUtils::Distinguishable( 908 const DummyArgument &x, const DummyArgument &y) { 909 if (x.u.index() != y.u.index()) { 910 return true; // different kind: data/proc/alt-return 911 } 912 return std::visit( 913 common::visitors{ 914 [&](const DummyDataObject &z) { 915 return Distinguishable(z, std::get<DummyDataObject>(y.u)); 916 }, 917 [&](const DummyProcedure &z) { 918 return Distinguishable(z, std::get<DummyProcedure>(y.u)); 919 }, 920 [&](const AlternateReturn &) { return false; }, 921 }, 922 x.u); 923 } 924 925 bool DistinguishUtils::Distinguishable( 926 const DummyDataObject &x, const DummyDataObject &y) { 927 using Attr = DummyDataObject::Attr; 928 if (Distinguishable(x.type, y.type)) { 929 return true; 930 } else if (x.attrs.test(Attr::Allocatable) && y.attrs.test(Attr::Pointer) && 931 y.intent != common::Intent::In) { 932 return true; 933 } else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) && 934 x.intent != common::Intent::In) { 935 return true; 936 } else { 937 return false; 938 } 939 } 940 941 bool DistinguishUtils::Distinguishable( 942 const DummyProcedure &x, const DummyProcedure &y) { 943 const Procedure &xProc{x.procedure.value()}; 944 const Procedure &yProc{y.procedure.value()}; 945 if (Distinguishable(xProc, yProc)) { 946 return true; 947 } else { 948 const std::optional<FunctionResult> &xResult{xProc.functionResult}; 949 const std::optional<FunctionResult> &yResult{yProc.functionResult}; 950 return xResult ? !yResult || Distinguishable(*xResult, *yResult) 951 : yResult.has_value(); 952 } 953 } 954 955 bool DistinguishUtils::Distinguishable( 956 const FunctionResult &x, const FunctionResult &y) { 957 if (x.u.index() != y.u.index()) { 958 return true; // one is data object, one is procedure 959 } 960 return std::visit( 961 common::visitors{ 962 [&](const TypeAndShape &z) { 963 return Distinguishable(z, std::get<TypeAndShape>(y.u)); 964 }, 965 [&](const CopyableIndirection<Procedure> &z) { 966 return Distinguishable(z.value(), 967 std::get<CopyableIndirection<Procedure>>(y.u).value()); 968 }, 969 }, 970 x.u); 971 } 972 973 bool DistinguishUtils::Distinguishable( 974 const TypeAndShape &x, const TypeAndShape &y) { 975 return !IsTkrCompatible(x, y) && !IsTkrCompatible(y, x); 976 } 977 978 // Compatibility based on type, kind, and rank 979 bool DistinguishUtils::IsTkrCompatible( 980 const DummyArgument &x, const DummyArgument &y) { 981 const auto *obj1{std::get_if<DummyDataObject>(&x.u)}; 982 const auto *obj2{std::get_if<DummyDataObject>(&y.u)}; 983 return obj1 && obj2 && IsTkrCompatible(obj1->type, obj2->type); 984 } 985 bool DistinguishUtils::IsTkrCompatible( 986 const TypeAndShape &x, const TypeAndShape &y) { 987 return x.type().IsTkCompatibleWith(y.type()) && 988 (x.attrs().test(TypeAndShape::Attr::AssumedRank) || 989 y.attrs().test(TypeAndShape::Attr::AssumedRank) || 990 x.Rank() == y.Rank()); 991 } 992 993 // Return the argument at the given index, ignoring the passed arg 994 const DummyArgument *DistinguishUtils::GetAtEffectivePosition( 995 const DummyArguments &args, int index) { 996 for (const DummyArgument &arg : args) { 997 if (!arg.pass) { 998 if (index == 0) { 999 return &arg; 1000 } 1001 --index; 1002 } 1003 } 1004 return nullptr; 1005 } 1006 1007 // Return the passed-object dummy argument of this procedure, if any 1008 const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) { 1009 for (const auto &arg : proc.dummyArguments) { 1010 if (arg.pass) { 1011 return &arg; 1012 } 1013 } 1014 return nullptr; 1015 } 1016 1017 bool Distinguishable(const Procedure &x, const Procedure &y) { 1018 return DistinguishUtils::Distinguishable(x, y); 1019 } 1020 1021 bool DistinguishableOpOrAssign(const Procedure &x, const Procedure &y) { 1022 return DistinguishUtils::DistinguishableOpOrAssign(x, y); 1023 } 1024 1025 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument) 1026 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure) 1027 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult) 1028 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure) 1029 } // namespace Fortran::evaluate::characteristics 1030 1031 template class Fortran::common::Indirection< 1032 Fortran::evaluate::characteristics::Procedure, true>; 1033