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