xref: /llvm-project/flang/lib/Evaluate/characteristics.cpp (revision d732c86c928271cf3a829d95a1fcc560894ab8e4)
164ab3302SCarolineConcatto //===-- lib/Evaluate/characteristics.cpp ----------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #include "flang/Evaluate/characteristics.h"
1064ab3302SCarolineConcatto #include "flang/Common/indirection.h"
1164ab3302SCarolineConcatto #include "flang/Evaluate/check-expression.h"
1264ab3302SCarolineConcatto #include "flang/Evaluate/fold.h"
1364ab3302SCarolineConcatto #include "flang/Evaluate/intrinsics.h"
1464ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
1564ab3302SCarolineConcatto #include "flang/Evaluate/type.h"
1664ab3302SCarolineConcatto #include "flang/Parser/message.h"
1764ab3302SCarolineConcatto #include "flang/Semantics/scope.h"
1864ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
1995f4ca7fSPeter Klausler #include "flang/Semantics/tools.h"
208670e499SCaroline Concatto #include "llvm/Support/raw_ostream.h"
2164ab3302SCarolineConcatto #include <initializer_list>
2264ab3302SCarolineConcatto 
2364ab3302SCarolineConcatto using namespace Fortran::parser::literals;
2464ab3302SCarolineConcatto 
2564ab3302SCarolineConcatto namespace Fortran::evaluate::characteristics {
2664ab3302SCarolineConcatto 
2764ab3302SCarolineConcatto // Copy attributes from a symbol to dst based on the mapping in pairs.
28f31ac3cbSPeter Klausler // An ASYNCHRONOUS attribute counts even if it is implied.
2964ab3302SCarolineConcatto template <typename A, typename B>
3064ab3302SCarolineConcatto static void CopyAttrs(const semantics::Symbol &src, A &dst,
3164ab3302SCarolineConcatto     const std::initializer_list<std::pair<semantics::Attr, B>> &pairs) {
3264ab3302SCarolineConcatto   for (const auto &pair : pairs) {
3364ab3302SCarolineConcatto     if (src.attrs().test(pair.first)) {
3464ab3302SCarolineConcatto       dst.attrs.set(pair.second);
3564ab3302SCarolineConcatto     }
3664ab3302SCarolineConcatto   }
3764ab3302SCarolineConcatto }
3864ab3302SCarolineConcatto 
3964ab3302SCarolineConcatto // Shapes of function results and dummy arguments have to have
4064ab3302SCarolineConcatto // the same rank, the same deferred dimensions, and the same
4164ab3302SCarolineConcatto // values for explicit dimensions when constant.
4273cf0142SjeanPerier bool ShapesAreCompatible(const std::optional<Shape> &x,
4373cf0142SjeanPerier     const std::optional<Shape> &y, bool *possibleWarning) {
4473cf0142SjeanPerier   if (!x || !y) {
4573cf0142SjeanPerier     return !x && !y;
4673cf0142SjeanPerier   }
4773cf0142SjeanPerier   if (x->size() != y->size()) {
4864ab3302SCarolineConcatto     return false;
4964ab3302SCarolineConcatto   }
5073cf0142SjeanPerier   auto yIter{y->begin()};
5173cf0142SjeanPerier   for (const auto &xDim : *x) {
5264ab3302SCarolineConcatto     const auto &yDim{*yIter++};
53e86591b3SPeter Klausler     if (xDim && yDim) {
54e86591b3SPeter Klausler       if (auto equiv{AreEquivalentInInterface(*xDim, *yDim)}) {
55e86591b3SPeter Klausler         if (!*equiv) {
5664ab3302SCarolineConcatto           return false;
5764ab3302SCarolineConcatto         }
58e86591b3SPeter Klausler       } else if (possibleWarning) {
59e86591b3SPeter Klausler         *possibleWarning = true;
60e86591b3SPeter Klausler       }
61e86591b3SPeter Klausler     } else if (xDim || yDim) {
6264ab3302SCarolineConcatto       return false;
6364ab3302SCarolineConcatto     }
6464ab3302SCarolineConcatto   }
6564ab3302SCarolineConcatto   return true;
6664ab3302SCarolineConcatto }
6764ab3302SCarolineConcatto 
6864ab3302SCarolineConcatto bool TypeAndShape::operator==(const TypeAndShape &that) const {
69b0bdc7fcSPeter Klausler   return type_.IsEquivalentTo(that.type_) &&
70b0bdc7fcSPeter Klausler       ShapesAreCompatible(shape_, that.shape_) && attrs_ == that.attrs_ &&
71b0bdc7fcSPeter Klausler       corank_ == that.corank_;
7264ab3302SCarolineConcatto }
7364ab3302SCarolineConcatto 
74a50bb84eSpeter klausler TypeAndShape &TypeAndShape::Rewrite(FoldingContext &context) {
75a50bb84eSpeter klausler   LEN_ = Fold(context, std::move(LEN_));
7650960e93SPeter Klausler   if (LEN_) {
7750960e93SPeter Klausler     if (auto n{ToInt64(*LEN_)}) {
7850960e93SPeter Klausler       type_ = DynamicType{type_.kind(), *n};
7950960e93SPeter Klausler     }
8050960e93SPeter Klausler   }
81a50bb84eSpeter klausler   shape_ = Fold(context, std::move(shape_));
82a50bb84eSpeter klausler   return *this;
83a50bb84eSpeter klausler }
84a50bb84eSpeter klausler 
8564ab3302SCarolineConcatto std::optional<TypeAndShape> TypeAndShape::Characterize(
860c0b2ea9SPeter Klausler     const semantics::Symbol &symbol, FoldingContext &context,
870c0b2ea9SPeter Klausler     bool invariantOnly) {
886f3d322fSpeter klausler   const auto &ultimate{symbol.GetUltimate()};
89cd03e96fSPeter Klausler   return common::visit(
9064ab3302SCarolineConcatto       common::visitors{
9164ab3302SCarolineConcatto           [&](const semantics::ProcEntityDetails &proc) {
92635656f4SPeter Klausler             if (proc.procInterface()) {
930c0b2ea9SPeter Klausler               return Characterize(
940c0b2ea9SPeter Klausler                   *proc.procInterface(), context, invariantOnly);
95635656f4SPeter Klausler             } else if (proc.type()) {
960c0b2ea9SPeter Klausler               return Characterize(*proc.type(), context, invariantOnly);
9764ab3302SCarolineConcatto             } else {
9864ab3302SCarolineConcatto               return std::optional<TypeAndShape>{};
9964ab3302SCarolineConcatto             }
10064ab3302SCarolineConcatto           },
10164ab3302SCarolineConcatto           [&](const semantics::AssocEntityDetails &assoc) {
1020c0b2ea9SPeter Klausler             return Characterize(assoc, context, invariantOnly);
10364ab3302SCarolineConcatto           },
1040996b590Speter klausler           [&](const semantics::ProcBindingDetails &binding) {
1050c0b2ea9SPeter Klausler             return Characterize(binding.symbol(), context, invariantOnly);
1060996b590Speter klausler           },
10773c3530fSpeter klausler           [&](const auto &x) -> std::optional<TypeAndShape> {
10873c3530fSpeter klausler             using Ty = std::decay_t<decltype(x)>;
10973c3530fSpeter klausler             if constexpr (std::is_same_v<Ty, semantics::EntityDetails> ||
11073c3530fSpeter klausler                 std::is_same_v<Ty, semantics::ObjectEntityDetails> ||
11173c3530fSpeter klausler                 std::is_same_v<Ty, semantics::TypeParamDetails>) {
11273c3530fSpeter klausler               if (const semantics::DeclTypeSpec * type{ultimate.GetType()}) {
11373c3530fSpeter klausler                 if (auto dyType{DynamicType::From(*type)}) {
1140c0b2ea9SPeter Klausler                   TypeAndShape result{std::move(*dyType),
1150c0b2ea9SPeter Klausler                       GetShape(context, ultimate, invariantOnly)};
11673c3530fSpeter klausler                   result.AcquireAttrs(ultimate);
11773c3530fSpeter klausler                   result.AcquireLEN(ultimate);
11873c3530fSpeter klausler                   return std::move(result.Rewrite(context));
11973c3530fSpeter klausler                 }
12073c3530fSpeter klausler               }
12173c3530fSpeter klausler             }
12273c3530fSpeter klausler             return std::nullopt;
12373c3530fSpeter klausler           },
12464ab3302SCarolineConcatto       },
125a50bb84eSpeter klausler       // GetUltimate() used here, not ResolveAssociations(), because
126a50bb84eSpeter klausler       // we need the type/rank of an associate entity from TYPE IS,
127a50bb84eSpeter klausler       // CLASS IS, or RANK statement.
1286f3d322fSpeter klausler       ultimate.details());
12964ab3302SCarolineConcatto }
13064ab3302SCarolineConcatto 
13164ab3302SCarolineConcatto std::optional<TypeAndShape> TypeAndShape::Characterize(
1320c0b2ea9SPeter Klausler     const semantics::AssocEntityDetails &assoc, FoldingContext &context,
1330c0b2ea9SPeter Klausler     bool invariantOnly) {
134a50bb84eSpeter klausler   std::optional<TypeAndShape> result;
13564ab3302SCarolineConcatto   if (auto type{DynamicType::From(assoc.type())}) {
136a50bb84eSpeter klausler     if (auto rank{assoc.rank()}) {
137a50bb84eSpeter klausler       if (*rank >= 0 && *rank <= common::maxRank) {
138a50bb84eSpeter klausler         result = TypeAndShape{std::move(*type), Shape(*rank)};
139a50bb84eSpeter klausler       }
1400c0b2ea9SPeter Klausler     } else if (auto shape{GetShape(context, assoc.expr(), invariantOnly)}) {
141a50bb84eSpeter klausler       result = TypeAndShape{std::move(*type), std::move(*shape)};
142a50bb84eSpeter klausler     }
143a50bb84eSpeter klausler     if (result && type->category() == TypeCategory::Character) {
14453bf28b8Speter klausler       if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc.expr())}) {
14553bf28b8Speter klausler         if (auto len{chExpr->LEN()}) {
146a50bb84eSpeter klausler           result->set_LEN(std::move(*len));
14753bf28b8Speter klausler         }
14853bf28b8Speter klausler       }
14953bf28b8Speter klausler     }
15064ab3302SCarolineConcatto   }
151a50bb84eSpeter klausler   return Fold(context, std::move(result));
15264ab3302SCarolineConcatto }
15364ab3302SCarolineConcatto 
15464ab3302SCarolineConcatto std::optional<TypeAndShape> TypeAndShape::Characterize(
1550c0b2ea9SPeter Klausler     const semantics::DeclTypeSpec &spec, FoldingContext &context,
1560c0b2ea9SPeter Klausler     bool /*invariantOnly=*/) {
15764ab3302SCarolineConcatto   if (auto type{DynamicType::From(spec)}) {
158a50bb84eSpeter klausler     return Fold(context, TypeAndShape{std::move(*type)});
15964ab3302SCarolineConcatto   } else {
16064ab3302SCarolineConcatto     return std::nullopt;
16164ab3302SCarolineConcatto   }
16264ab3302SCarolineConcatto }
16364ab3302SCarolineConcatto 
164fad31d60Speter klausler std::optional<TypeAndShape> TypeAndShape::Characterize(
1650c0b2ea9SPeter Klausler     const ActualArgument &arg, FoldingContext &context, bool invariantOnly) {
166a88cee1fSPeter Klausler   if (const auto *expr{arg.UnwrapExpr()}) {
1670c0b2ea9SPeter Klausler     return Characterize(*expr, context, invariantOnly);
168a88cee1fSPeter Klausler   } else if (const Symbol * assumed{arg.GetAssumedTypeDummy()}) {
1690c0b2ea9SPeter Klausler     return Characterize(*assumed, context, invariantOnly);
170a88cee1fSPeter Klausler   } else {
171a88cee1fSPeter Klausler     return std::nullopt;
172a88cee1fSPeter Klausler   }
173fad31d60Speter klausler }
174fad31d60Speter klausler 
17564ab3302SCarolineConcatto bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages,
17664ab3302SCarolineConcatto     const TypeAndShape &that, const char *thisIs, const char *thatIs,
1777763c014SPeter Klausler     bool omitShapeConformanceCheck,
1787763c014SPeter Klausler     enum CheckConformanceFlags::Flags flags) const {
17937b2e2b0Speter klausler   if (!type_.IsTkCompatibleWith(that.type_)) {
18064ab3302SCarolineConcatto     messages.Say(
18164ab3302SCarolineConcatto         "%1$s type '%2$s' is not compatible with %3$s type '%4$s'"_err_en_US,
182b82a8c3fSpeter klausler         thatIs, that.AsFortran(), thisIs, AsFortran());
18364ab3302SCarolineConcatto     return false;
18464ab3302SCarolineConcatto   }
18573cf0142SjeanPerier   return omitShapeConformanceCheck || (!shape_ && !that.shape_) ||
18673cf0142SjeanPerier       (shape_ && that.shape_ &&
18773cf0142SjeanPerier           CheckConformance(
18873cf0142SjeanPerier               messages, *shape_, *that.shape_, flags, thisIs, thatIs)
18973cf0142SjeanPerier               .value_or(true /*fail only when nonconformance is known now*/));
19064ab3302SCarolineConcatto }
19164ab3302SCarolineConcatto 
192efc5926cSpeter klausler std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureElementSizeInBytes(
193efc5926cSpeter klausler     FoldingContext &foldingContext, bool align) const {
194efc5926cSpeter klausler   if (LEN_) {
195efc5926cSpeter klausler     CHECK(type_.category() == TypeCategory::Character);
196efc5926cSpeter klausler     return Fold(foldingContext,
19723c2bedfSPeter Klausler         Expr<SubscriptInteger>{
19823c2bedfSPeter Klausler             foldingContext.targetCharacteristics().GetByteSize(
19923c2bedfSPeter Klausler                 type_.category(), type_.kind())} *
20023c2bedfSPeter Klausler             Expr<SubscriptInteger>{*LEN_});
201efc5926cSpeter klausler   }
202efc5926cSpeter klausler   if (auto elementBytes{type_.MeasureSizeInBytes(foldingContext, align)}) {
203efc5926cSpeter klausler     return Fold(foldingContext, std::move(*elementBytes));
204efc5926cSpeter klausler   }
205efc5926cSpeter klausler   return std::nullopt;
206efc5926cSpeter klausler }
207efc5926cSpeter klausler 
20853bf28b8Speter klausler std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureSizeInBytes(
2096aa3591eSpeter klausler     FoldingContext &foldingContext) const {
21073cf0142SjeanPerier   if (auto elements{GetSize(shape_)}) {
2116aa3591eSpeter klausler     // Sizes of arrays (even with single elements) are multiples of
2126aa3591eSpeter klausler     // their alignments.
2136aa3591eSpeter klausler     if (auto elementBytes{
21473cf0142SjeanPerier             MeasureElementSizeInBytes(foldingContext, Rank() > 0)}) {
2156aa3591eSpeter klausler       return Fold(
2166aa3591eSpeter klausler           foldingContext, std::move(*elements) * std::move(*elementBytes));
21753bf28b8Speter klausler     }
21853bf28b8Speter klausler   }
2196aa3591eSpeter klausler   return std::nullopt;
22053bf28b8Speter klausler }
22153bf28b8Speter klausler 
2226f3d322fSpeter klausler void TypeAndShape::AcquireAttrs(const semantics::Symbol &symbol) {
22344bc97c8SPeter Klausler   if (IsAssumedShape(symbol)) {
22444bc97c8SPeter Klausler     attrs_.set(Attr::AssumedShape);
2257b801233SPeter Klausler   } else if (IsDeferredShape(symbol)) {
22644bc97c8SPeter Klausler     attrs_.set(Attr::DeferredShape);
2277b801233SPeter Klausler   } else if (semantics::IsAssumedSizeArray(symbol)) {
2287b801233SPeter Klausler     attrs_.set(Attr::AssumedSize);
22944bc97c8SPeter Klausler   }
230*d732c86cSPeter Klausler   if (int corank{GetCorank(symbol)}; corank > 0) {
231*d732c86cSPeter Klausler     corank_ = corank;
23264ab3302SCarolineConcatto   }
2333a8a52f4SPeter Klausler   if (const auto *object{
2343a8a52f4SPeter Klausler           symbol.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()};
2353a8a52f4SPeter Klausler       object && object->IsAssumedRank()) {
2363a8a52f4SPeter Klausler     attrs_.set(Attr::AssumedRank);
23764ab3302SCarolineConcatto   }
23864ab3302SCarolineConcatto }
23964ab3302SCarolineConcatto 
24064ab3302SCarolineConcatto void TypeAndShape::AcquireLEN() {
241ac964175Speter klausler   if (auto len{type_.GetCharLength()}) {
242ac964175Speter klausler     LEN_ = std::move(len);
24364ab3302SCarolineConcatto   }
24464ab3302SCarolineConcatto }
24564ab3302SCarolineConcatto 
2466f3d322fSpeter klausler void TypeAndShape::AcquireLEN(const semantics::Symbol &symbol) {
2476f3d322fSpeter klausler   if (type_.category() == TypeCategory::Character) {
2486f3d322fSpeter klausler     if (auto len{DataRef{symbol}.LEN()}) {
2496f3d322fSpeter klausler       LEN_ = std::move(*len);
2506f3d322fSpeter klausler     }
2516f3d322fSpeter klausler   }
2526f3d322fSpeter klausler }
2536f3d322fSpeter klausler 
254b82a8c3fSpeter klausler std::string TypeAndShape::AsFortran() const {
255b82a8c3fSpeter klausler   return type_.AsFortran(LEN_ ? LEN_->AsFortran() : "");
256b82a8c3fSpeter klausler }
257b82a8c3fSpeter klausler 
2588670e499SCaroline Concatto llvm::raw_ostream &TypeAndShape::Dump(llvm::raw_ostream &o) const {
25964ab3302SCarolineConcatto   o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : "");
26064ab3302SCarolineConcatto   attrs_.Dump(o, EnumToString);
26173cf0142SjeanPerier   if (!shape_) {
26273cf0142SjeanPerier     o << " dimension(..)";
26373cf0142SjeanPerier   } else if (!shape_->empty()) {
264fad31d60Speter klausler     o << " dimension";
26564ab3302SCarolineConcatto     char sep{'('};
26673cf0142SjeanPerier     for (const auto &expr : *shape_) {
26764ab3302SCarolineConcatto       o << sep;
26864ab3302SCarolineConcatto       sep = ',';
26964ab3302SCarolineConcatto       if (expr) {
27064ab3302SCarolineConcatto         expr->AsFortran(o);
27164ab3302SCarolineConcatto       } else {
27264ab3302SCarolineConcatto         o << ':';
27364ab3302SCarolineConcatto       }
27464ab3302SCarolineConcatto     }
27564ab3302SCarolineConcatto     o << ')';
27664ab3302SCarolineConcatto   }
27764ab3302SCarolineConcatto   return o;
27864ab3302SCarolineConcatto }
27964ab3302SCarolineConcatto 
28064ab3302SCarolineConcatto bool DummyDataObject::operator==(const DummyDataObject &that) const {
28164ab3302SCarolineConcatto   return type == that.type && attrs == that.attrs && intent == that.intent &&
282f513bd80SPeter Klausler       coshape == that.coshape && cudaDataAttr == that.cudaDataAttr;
28364ab3302SCarolineConcatto }
28464ab3302SCarolineConcatto 
285e86591b3SPeter Klausler bool DummyDataObject::IsCompatibleWith(const DummyDataObject &actual,
286e86591b3SPeter Klausler     std::string *whyNot, std::optional<std::string> *warning) const {
287e86591b3SPeter Klausler   bool possibleWarning{false};
288e86591b3SPeter Klausler   if (!ShapesAreCompatible(
289e86591b3SPeter Klausler           type.shape(), actual.type.shape(), &possibleWarning)) {
29062d874f2SPeter Klausler     if (whyNot) {
29162d874f2SPeter Klausler       *whyNot = "incompatible dummy data object shapes";
29262d874f2SPeter Klausler     }
29362d874f2SPeter Klausler     return false;
294e86591b3SPeter Klausler   } else if (warning && possibleWarning) {
295e86591b3SPeter Klausler     *warning = "distinct dummy data object shapes";
29662d874f2SPeter Klausler   }
297c7040509SPeter Klausler   // Treat deduced dummy character type as if it were assumed-length character
298c7040509SPeter Klausler   // to avoid useless "implicit interfaces have distinct type" warnings from
299c7040509SPeter Klausler   // CALL FOO('abc'); CALL FOO('abcd').
300c7040509SPeter Klausler   bool deducedAssumedLength{type.type().category() == TypeCategory::Character &&
301c7040509SPeter Klausler       attrs.test(Attr::DeducedFromActual)};
302c7040509SPeter Klausler   bool compatibleTypes{deducedAssumedLength
303c7040509SPeter Klausler           ? type.type().IsTkCompatibleWith(actual.type.type())
304c7040509SPeter Klausler           : type.type().IsTkLenCompatibleWith(actual.type.type())};
305c7040509SPeter Klausler   if (!compatibleTypes) {
30662d874f2SPeter Klausler     if (whyNot) {
30762d874f2SPeter Klausler       *whyNot = "incompatible dummy data object types: "s +
30862d874f2SPeter Klausler           type.type().AsFortran() + " vs " + actual.type.type().AsFortran();
30962d874f2SPeter Klausler     }
31062d874f2SPeter Klausler     return false;
31162d874f2SPeter Klausler   }
312f7e43041SPeter Klausler   if (type.type().IsPolymorphic() != actual.type.type().IsPolymorphic()) {
313f7e43041SPeter Klausler     if (whyNot) {
314f7e43041SPeter Klausler       *whyNot = "incompatible dummy data object polymorphism: "s +
315f7e43041SPeter Klausler           type.type().AsFortran() + " vs " + actual.type.type().AsFortran();
316f7e43041SPeter Klausler     }
317f7e43041SPeter Klausler     return false;
318f7e43041SPeter Klausler   }
319c7040509SPeter Klausler   if (type.type().category() == TypeCategory::Character &&
320c7040509SPeter Klausler       !deducedAssumedLength) {
3216ceba01aSPeter Klausler     if (actual.type.type().IsAssumedLengthCharacter() !=
3226ceba01aSPeter Klausler         type.type().IsAssumedLengthCharacter()) {
3236ceba01aSPeter Klausler       if (whyNot) {
3246ceba01aSPeter Klausler         *whyNot = "assumed-length character vs explicit-length character";
3256ceba01aSPeter Klausler       }
3266ceba01aSPeter Klausler       return false;
3276ceba01aSPeter Klausler     }
3286ceba01aSPeter Klausler     if (!type.type().IsAssumedLengthCharacter() && type.LEN() &&
3296ceba01aSPeter Klausler         actual.type.LEN()) {
3306ceba01aSPeter Klausler       auto len{ToInt64(*type.LEN())};
3316ceba01aSPeter Klausler       auto actualLen{ToInt64(*actual.type.LEN())};
3326ceba01aSPeter Klausler       if (len.has_value() != actualLen.has_value()) {
3336ceba01aSPeter Klausler         if (whyNot) {
3346ceba01aSPeter Klausler           *whyNot = "constant-length vs non-constant-length character dummy "
3356ceba01aSPeter Klausler                     "arguments";
3366ceba01aSPeter Klausler         }
3376ceba01aSPeter Klausler         return false;
3386ceba01aSPeter Klausler       } else if (len && *len != *actualLen) {
3396ceba01aSPeter Klausler         if (whyNot) {
3406ceba01aSPeter Klausler           *whyNot = "character dummy arguments with distinct lengths";
3416ceba01aSPeter Klausler         }
3426ceba01aSPeter Klausler         return false;
3436ceba01aSPeter Klausler       }
3446ceba01aSPeter Klausler     }
3456ceba01aSPeter Klausler   }
3465718a425SPeter Klausler   if (!IdenticalSignificantAttrs(attrs, actual.attrs) ||
3475718a425SPeter Klausler       type.attrs() != actual.type.attrs()) {
34862d874f2SPeter Klausler     if (whyNot) {
34962d874f2SPeter Klausler       *whyNot = "incompatible dummy data object attributes";
35062d874f2SPeter Klausler     }
35162d874f2SPeter Klausler     return false;
35262d874f2SPeter Klausler   }
35362d874f2SPeter Klausler   if (intent != actual.intent) {
35462d874f2SPeter Klausler     if (whyNot) {
35562d874f2SPeter Klausler       *whyNot = "incompatible dummy data object intents";
35662d874f2SPeter Klausler     }
35762d874f2SPeter Klausler     return false;
35862d874f2SPeter Klausler   }
35962d874f2SPeter Klausler   if (coshape != actual.coshape) {
36062d874f2SPeter Klausler     if (whyNot) {
36162d874f2SPeter Klausler       *whyNot = "incompatible dummy data object coshapes";
36262d874f2SPeter Klausler     }
36362d874f2SPeter Klausler     return false;
36462d874f2SPeter Klausler   }
365864cb2aaSPeter Klausler   if (ignoreTKR != actual.ignoreTKR) {
366864cb2aaSPeter Klausler     if (whyNot) {
367864cb2aaSPeter Klausler       *whyNot = "incompatible !DIR$ IGNORE_TKR directives";
368864cb2aaSPeter Klausler     }
369864cb2aaSPeter Klausler   }
370f513bd80SPeter Klausler   if (!attrs.test(Attr::Value) &&
3713e930864SValentin Clement       !common::AreCompatibleCUDADataAttrs(cudaDataAttr, actual.cudaDataAttr,
37230d80009SValentin Clement (バレンタイン クレメン)           ignoreTKR, warning,
3733e930864SValentin Clement           /*allowUnifiedMatchingRule=*/false)) {
374f513bd80SPeter Klausler     if (whyNot) {
375f513bd80SPeter Klausler       *whyNot = "incompatible CUDA data attributes";
376f513bd80SPeter Klausler     }
377f513bd80SPeter Klausler   }
37862d874f2SPeter Klausler   return true;
3793bfe9074SPeter Klausler }
3803bfe9074SPeter Klausler 
38164ab3302SCarolineConcatto static common::Intent GetIntent(const semantics::Attrs &attrs) {
38264ab3302SCarolineConcatto   if (attrs.test(semantics::Attr::INTENT_IN)) {
38364ab3302SCarolineConcatto     return common::Intent::In;
38464ab3302SCarolineConcatto   } else if (attrs.test(semantics::Attr::INTENT_OUT)) {
38564ab3302SCarolineConcatto     return common::Intent::Out;
38664ab3302SCarolineConcatto   } else if (attrs.test(semantics::Attr::INTENT_INOUT)) {
38764ab3302SCarolineConcatto     return common::Intent::InOut;
38864ab3302SCarolineConcatto   } else {
38964ab3302SCarolineConcatto     return common::Intent::Default;
39064ab3302SCarolineConcatto   }
39164ab3302SCarolineConcatto }
39264ab3302SCarolineConcatto 
39364ab3302SCarolineConcatto std::optional<DummyDataObject> DummyDataObject::Characterize(
394641ede93Speter klausler     const semantics::Symbol &symbol, FoldingContext &context) {
395864cb2aaSPeter Klausler   if (const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()};
396864cb2aaSPeter Klausler       object || symbol.has<semantics::EntityDetails>()) {
3970c0b2ea9SPeter Klausler     if (auto type{TypeAndShape::Characterize(
3980c0b2ea9SPeter Klausler             symbol, context, /*invariantOnly=*/false)}) {
39964ab3302SCarolineConcatto       std::optional<DummyDataObject> result{std::move(*type)};
40064ab3302SCarolineConcatto       using semantics::Attr;
40164ab3302SCarolineConcatto       CopyAttrs<DummyDataObject, DummyDataObject::Attr>(symbol, *result,
40264ab3302SCarolineConcatto           {
40364ab3302SCarolineConcatto               {Attr::OPTIONAL, DummyDataObject::Attr::Optional},
40464ab3302SCarolineConcatto               {Attr::ALLOCATABLE, DummyDataObject::Attr::Allocatable},
40564ab3302SCarolineConcatto               {Attr::ASYNCHRONOUS, DummyDataObject::Attr::Asynchronous},
40664ab3302SCarolineConcatto               {Attr::CONTIGUOUS, DummyDataObject::Attr::Contiguous},
40764ab3302SCarolineConcatto               {Attr::VALUE, DummyDataObject::Attr::Value},
40864ab3302SCarolineConcatto               {Attr::VOLATILE, DummyDataObject::Attr::Volatile},
40964ab3302SCarolineConcatto               {Attr::POINTER, DummyDataObject::Attr::Pointer},
41064ab3302SCarolineConcatto               {Attr::TARGET, DummyDataObject::Attr::Target},
41164ab3302SCarolineConcatto           });
41264ab3302SCarolineConcatto       result->intent = GetIntent(symbol.attrs());
413864cb2aaSPeter Klausler       result->ignoreTKR = GetIgnoreTKR(symbol);
414f513bd80SPeter Klausler       if (object) {
415f513bd80SPeter Klausler         result->cudaDataAttr = object->cudaDataAttr();
416f513bd80SPeter Klausler         if (!result->cudaDataAttr &&
417f513bd80SPeter Klausler             !result->attrs.test(DummyDataObject::Attr::Value) &&
418f513bd80SPeter Klausler             semantics::IsCUDADeviceContext(&symbol.owner())) {
419f513bd80SPeter Klausler           result->cudaDataAttr = common::CUDADataAttr::Device;
420f513bd80SPeter Klausler         }
421f513bd80SPeter Klausler       }
42264ab3302SCarolineConcatto       return result;
42364ab3302SCarolineConcatto     }
42464ab3302SCarolineConcatto   }
42564ab3302SCarolineConcatto   return std::nullopt;
42664ab3302SCarolineConcatto }
42764ab3302SCarolineConcatto 
4286e0a2031SPeter Klausler bool DummyDataObject::CanBePassedViaImplicitInterface(
4296e0a2031SPeter Klausler     std::string *whyNot) const {
43064ab3302SCarolineConcatto   if ((attrs &
43164ab3302SCarolineConcatto           Attrs{Attr::Allocatable, Attr::Asynchronous, Attr::Optional,
43264ab3302SCarolineConcatto               Attr::Pointer, Attr::Target, Attr::Value, Attr::Volatile})
43364ab3302SCarolineConcatto           .any()) {
4346e0a2031SPeter Klausler     if (whyNot) {
4356e0a2031SPeter Klausler       *whyNot = "a dummy argument has the allocatable, asynchronous, optional, "
4366e0a2031SPeter Klausler                 "pointer, target, value, or volatile attribute";
4376e0a2031SPeter Klausler     }
43864ab3302SCarolineConcatto     return false; // 15.4.2.2(3)(a)
43964ab3302SCarolineConcatto   } else if ((type.attrs() &
44064ab3302SCarolineConcatto                  TypeAndShape::Attrs{TypeAndShape::Attr::AssumedShape,
441*d732c86cSPeter Klausler                      TypeAndShape::Attr::AssumedRank})
442*d732c86cSPeter Klausler                  .any() ||
443*d732c86cSPeter Klausler       type.corank() > 0) {
4446e0a2031SPeter Klausler     if (whyNot) {
4456e0a2031SPeter Klausler       *whyNot = "a dummy argument is assumed-shape, assumed-rank, or a coarray";
4466e0a2031SPeter Klausler     }
44764ab3302SCarolineConcatto     return false; // 15.4.2.2(3)(b-d)
44864ab3302SCarolineConcatto   } else if (type.type().IsPolymorphic()) {
4496e0a2031SPeter Klausler     if (whyNot) {
4506e0a2031SPeter Klausler       *whyNot = "a dummy argument is polymorphic";
4516e0a2031SPeter Klausler     }
45264ab3302SCarolineConcatto     return false; // 15.4.2.2(3)(f)
453f513bd80SPeter Klausler   } else if (cudaDataAttr) {
4546e0a2031SPeter Klausler     if (whyNot) {
4556e0a2031SPeter Klausler       *whyNot = "a dummy argument has a CUDA data attribute";
4566e0a2031SPeter Klausler     }
457f513bd80SPeter Klausler     return false;
45864ab3302SCarolineConcatto   } else if (const auto *derived{GetDerivedTypeSpec(type.type())}) {
4596e0a2031SPeter Klausler     if (derived->parameters().empty()) { // 15.4.2.2(3)(e)
4606e0a2031SPeter Klausler       return true;
4616e0a2031SPeter Klausler     } else {
4626e0a2031SPeter Klausler       if (whyNot) {
4636e0a2031SPeter Klausler         *whyNot = "a dummy argument has derived type parameters";
4646e0a2031SPeter Klausler       }
4656e0a2031SPeter Klausler       return false;
4666e0a2031SPeter Klausler     }
46764ab3302SCarolineConcatto   } else {
46864ab3302SCarolineConcatto     return true;
46964ab3302SCarolineConcatto   }
47064ab3302SCarolineConcatto }
47164ab3302SCarolineConcatto 
472b477d39bSjeanPerier bool DummyDataObject::IsPassedByDescriptor(bool isBindC) const {
473*d732c86cSPeter Klausler   constexpr TypeAndShape::Attrs shapeRequiringBox{
474b477d39bSjeanPerier       TypeAndShape::Attr::AssumedShape, TypeAndShape::Attr::DeferredShape,
475*d732c86cSPeter Klausler       TypeAndShape::Attr::AssumedRank};
476b477d39bSjeanPerier   if ((attrs & Attrs{Attr::Allocatable, Attr::Pointer}).any()) {
477b477d39bSjeanPerier     return true;
478b477d39bSjeanPerier   } else if ((type.attrs() & shapeRequiringBox).any()) {
479*d732c86cSPeter Klausler     return true; // pass shape in descriptor
480*d732c86cSPeter Klausler   } else if (type.corank() > 0) {
481*d732c86cSPeter Klausler     return true; // pass coshape in descriptor
482b477d39bSjeanPerier   } else if (type.type().IsPolymorphic() && !type.type().IsAssumedType()) {
483b477d39bSjeanPerier     // Need to pass dynamic type info in a descriptor.
484b477d39bSjeanPerier     return true;
485b477d39bSjeanPerier   } else if (const auto *derived{GetDerivedTypeSpec(type.type())}) {
48638b54c72SJean Perier     if (!derived->parameters().empty()) {
48738b54c72SJean Perier       for (const auto &param : derived->parameters()) {
48838b54c72SJean Perier         if (param.second.isLen()) {
48938b54c72SJean Perier           // Need to pass length type parameters in a descriptor.
49038b54c72SJean Perier           return true;
49138b54c72SJean Perier         }
49238b54c72SJean Perier       }
493b477d39bSjeanPerier     }
494b477d39bSjeanPerier   } else if (isBindC && type.type().IsAssumedLengthCharacter()) {
495b477d39bSjeanPerier     // Fortran 2018 18.3.6 point 2 (5)
496b477d39bSjeanPerier     return true;
497b477d39bSjeanPerier   }
498b477d39bSjeanPerier   return false;
499b477d39bSjeanPerier }
500b477d39bSjeanPerier 
5018670e499SCaroline Concatto llvm::raw_ostream &DummyDataObject::Dump(llvm::raw_ostream &o) const {
50264ab3302SCarolineConcatto   attrs.Dump(o, EnumToString);
50364ab3302SCarolineConcatto   if (intent != common::Intent::Default) {
50464ab3302SCarolineConcatto     o << "INTENT(" << common::EnumToString(intent) << ')';
50564ab3302SCarolineConcatto   }
50664ab3302SCarolineConcatto   type.Dump(o);
50764ab3302SCarolineConcatto   if (!coshape.empty()) {
50864ab3302SCarolineConcatto     char sep{'['};
50964ab3302SCarolineConcatto     for (const auto &expr : coshape) {
51064ab3302SCarolineConcatto       expr.AsFortran(o << sep);
51164ab3302SCarolineConcatto       sep = ',';
51264ab3302SCarolineConcatto     }
51364ab3302SCarolineConcatto   }
514f513bd80SPeter Klausler   if (cudaDataAttr) {
515f513bd80SPeter Klausler     o << " cudaDataAttr: " << common::EnumToString(*cudaDataAttr);
516f513bd80SPeter Klausler   }
517e5ccfbbfSPeter Klausler   if (!ignoreTKR.empty()) {
518e5ccfbbfSPeter Klausler     ignoreTKR.Dump(o << ' ', common::EnumToString);
519e5ccfbbfSPeter Klausler   }
52064ab3302SCarolineConcatto   return o;
52164ab3302SCarolineConcatto }
52264ab3302SCarolineConcatto 
52364ab3302SCarolineConcatto DummyProcedure::DummyProcedure(Procedure &&p)
52464ab3302SCarolineConcatto     : procedure{new Procedure{std::move(p)}} {}
52564ab3302SCarolineConcatto 
52664ab3302SCarolineConcatto bool DummyProcedure::operator==(const DummyProcedure &that) const {
52764ab3302SCarolineConcatto   return attrs == that.attrs && intent == that.intent &&
52864ab3302SCarolineConcatto       procedure.value() == that.procedure.value();
52964ab3302SCarolineConcatto }
53064ab3302SCarolineConcatto 
53162d874f2SPeter Klausler bool DummyProcedure::IsCompatibleWith(
53262d874f2SPeter Klausler     const DummyProcedure &actual, std::string *whyNot) const {
53362d874f2SPeter Klausler   if (attrs != actual.attrs) {
53462d874f2SPeter Klausler     if (whyNot) {
53562d874f2SPeter Klausler       *whyNot = "incompatible dummy procedure attributes";
53662d874f2SPeter Klausler     }
53762d874f2SPeter Klausler     return false;
53862d874f2SPeter Klausler   }
53962d874f2SPeter Klausler   if (intent != actual.intent) {
54062d874f2SPeter Klausler     if (whyNot) {
54162d874f2SPeter Klausler       *whyNot = "incompatible dummy procedure intents";
54262d874f2SPeter Klausler     }
54362d874f2SPeter Klausler     return false;
54462d874f2SPeter Klausler   }
5451c530b3dSPeter Klausler   if (!procedure.value().IsCompatibleWith(actual.procedure.value(),
5461c530b3dSPeter Klausler           /*ignoreImplicitVsExplicit=*/false, whyNot)) {
54762d874f2SPeter Klausler     if (whyNot) {
54862d874f2SPeter Klausler       *whyNot = "incompatible dummy procedure interfaces: "s + *whyNot;
54962d874f2SPeter Klausler     }
55062d874f2SPeter Klausler     return false;
55162d874f2SPeter Klausler   }
55262d874f2SPeter Klausler   return true;
5533bfe9074SPeter Klausler }
5543bfe9074SPeter Klausler 
5556e0a2031SPeter Klausler bool DummyProcedure::CanBePassedViaImplicitInterface(
5566e0a2031SPeter Klausler     std::string *whyNot) const {
55766fdfff7SPeter Klausler   if ((attrs & Attrs{Attr::Optional, Attr::Pointer}).any()) {
5586e0a2031SPeter Klausler     if (whyNot) {
5596e0a2031SPeter Klausler       *whyNot = "a dummy procedure is optional or a pointer";
5606e0a2031SPeter Klausler     }
56166fdfff7SPeter Klausler     return false; // 15.4.2.2(3)(a)
56266fdfff7SPeter Klausler   }
56366fdfff7SPeter Klausler   return true;
56466fdfff7SPeter Klausler }
56566fdfff7SPeter Klausler 
5660d8331c0Speter klausler static std::string GetSeenProcs(
5670d8331c0Speter klausler     const semantics::UnorderedSymbolSet &seenProcs) {
56895540f9dSPeter Steinfeld   // Sort the symbols so that they appear in the same order on all platforms
5690d8331c0Speter klausler   auto ordered{semantics::OrderBySourcePosition(seenProcs)};
57077dc203cSPeter Steinfeld   std::string result;
57177dc203cSPeter Steinfeld   llvm::interleave(
5720d8331c0Speter klausler       ordered,
57377dc203cSPeter Steinfeld       [&](const SymbolRef p) { result += '\'' + p->name().ToString() + '\''; },
57477dc203cSPeter Steinfeld       [&]() { result += ", "; });
57577dc203cSPeter Steinfeld   return result;
57677dc203cSPeter Steinfeld }
57777dc203cSPeter Steinfeld 
5780d8331c0Speter klausler // These functions with arguments of type UnorderedSymbolSet are used with
5790d8331c0Speter klausler // mutually recursive calls when characterizing a Procedure, a DummyArgument,
5800d8331c0Speter klausler // or a DummyProcedure to detect circularly defined procedures as required by
58177dc203cSPeter Steinfeld // 15.4.3.6, paragraph 2.
58277dc203cSPeter Steinfeld static std::optional<DummyArgument> CharacterizeDummyArgument(
58377dc203cSPeter Steinfeld     const semantics::Symbol &symbol, FoldingContext &context,
584174cabedSPeter Klausler     semantics::UnorderedSymbolSet seenProcs);
585488b9fd1SDaniil Dudkin static std::optional<FunctionResult> CharacterizeFunctionResult(
586488b9fd1SDaniil Dudkin     const semantics::Symbol &symbol, FoldingContext &context,
587cb263919SPeter Klausler     semantics::UnorderedSymbolSet seenProcs, bool emitError);
58877dc203cSPeter Steinfeld 
58977dc203cSPeter Steinfeld static std::optional<Procedure> CharacterizeProcedure(
59077dc203cSPeter Steinfeld     const semantics::Symbol &original, FoldingContext &context,
591cb263919SPeter Klausler     semantics::UnorderedSymbolSet seenProcs, bool emitError) {
592f8f70028Speter klausler   const auto &symbol{ResolveAssociations(original)};
59377dc203cSPeter Steinfeld   if (seenProcs.find(symbol) != seenProcs.end()) {
59477dc203cSPeter Steinfeld     std::string procsList{GetSeenProcs(seenProcs)};
59577dc203cSPeter Steinfeld     context.messages().Say(symbol.name(),
59677dc203cSPeter Steinfeld         "Procedure '%s' is recursively defined.  Procedures in the cycle:"
59795540f9dSPeter Steinfeld         " %s"_err_en_US,
59877dc203cSPeter Steinfeld         symbol.name(), procsList);
59977dc203cSPeter Steinfeld     return std::nullopt;
60077dc203cSPeter Steinfeld   }
60177dc203cSPeter Steinfeld   seenProcs.insert(symbol);
602cb263919SPeter Klausler   auto CheckForNested{[&](const Symbol &symbol) {
603cb263919SPeter Klausler     if (emitError) {
604cb263919SPeter Klausler       context.messages().Say(
605cb263919SPeter Klausler           "Procedure '%s' is referenced before being sufficiently defined in a context where it must be so"_err_en_US,
606cb263919SPeter Klausler           symbol.name());
607cb263919SPeter Klausler     }
608cb263919SPeter Klausler   }};
6090c0b2ea9SPeter Klausler   auto result{common::visit(
61077dc203cSPeter Steinfeld       common::visitors{
61177dc203cSPeter Steinfeld           [&](const semantics::SubprogramDetails &subp)
61277dc203cSPeter Steinfeld               -> std::optional<Procedure> {
6130c0b2ea9SPeter Klausler             Procedure result;
61477dc203cSPeter Steinfeld             if (subp.isFunction()) {
615488b9fd1SDaniil Dudkin               if (auto fr{CharacterizeFunctionResult(
616cb263919SPeter Klausler                       subp.result(), context, seenProcs, emitError)}) {
61777dc203cSPeter Steinfeld                 result.functionResult = std::move(fr);
61877dc203cSPeter Steinfeld               } else {
61977dc203cSPeter Steinfeld                 return std::nullopt;
62077dc203cSPeter Steinfeld               }
62177dc203cSPeter Steinfeld             } else {
62277dc203cSPeter Steinfeld               result.attrs.set(Procedure::Attr::Subroutine);
62377dc203cSPeter Steinfeld             }
62477dc203cSPeter Steinfeld             for (const semantics::Symbol *arg : subp.dummyArgs()) {
62577dc203cSPeter Steinfeld               if (!arg) {
62673c3530fSpeter klausler                 if (subp.isFunction()) {
62773c3530fSpeter klausler                   return std::nullopt;
62873c3530fSpeter klausler                 } else {
62977dc203cSPeter Steinfeld                   result.dummyArguments.emplace_back(AlternateReturn{});
63073c3530fSpeter klausler                 }
63177dc203cSPeter Steinfeld               } else if (auto argCharacteristics{CharacterizeDummyArgument(
63277dc203cSPeter Steinfeld                              *arg, context, seenProcs)}) {
63377dc203cSPeter Steinfeld                 result.dummyArguments.emplace_back(
63477dc203cSPeter Steinfeld                     std::move(argCharacteristics.value()));
63577dc203cSPeter Steinfeld               } else {
63677dc203cSPeter Steinfeld                 return std::nullopt;
63777dc203cSPeter Steinfeld               }
63877dc203cSPeter Steinfeld             }
639f513bd80SPeter Klausler             result.cudaSubprogramAttrs = subp.cudaSubprogramAttrs();
6400c0b2ea9SPeter Klausler             return std::move(result);
64177dc203cSPeter Steinfeld           },
64277dc203cSPeter Steinfeld           [&](const semantics::ProcEntityDetails &proc)
64377dc203cSPeter Steinfeld               -> std::optional<Procedure> {
64477dc203cSPeter Steinfeld             if (symbol.attrs().test(semantics::Attr::INTRINSIC)) {
645f8f70028Speter klausler               // Fails when the intrinsic is not a specific intrinsic function
646f8f70028Speter klausler               // from F'2018 table 16.2.  In order to handle forward references,
647f8f70028Speter klausler               // attempts to use impermissible intrinsic procedures as the
648f8f70028Speter klausler               // interfaces of procedure pointers are caught and flagged in
649f8f70028Speter klausler               // declaration checking in Semantics.
650848cca6cSEmil Kieri               auto intrinsic{context.intrinsics().IsSpecificIntrinsicFunction(
651848cca6cSEmil Kieri                   symbol.name().ToString())};
652848cca6cSEmil Kieri               if (intrinsic && intrinsic->isRestrictedSpecific) {
653848cca6cSEmil Kieri                 intrinsic.reset(); // Exclude intrinsics from table 16.3.
654848cca6cSEmil Kieri               }
655848cca6cSEmil Kieri               return intrinsic;
65677dc203cSPeter Steinfeld             }
657635656f4SPeter Klausler             if (const semantics::Symbol *
658635656f4SPeter Klausler                 interfaceSymbol{proc.procInterface()}) {
659cb263919SPeter Klausler               auto result{CharacterizeProcedure(
660cb263919SPeter Klausler                   *interfaceSymbol, context, seenProcs, /*emitError=*/false)};
6610c0b2ea9SPeter Klausler               if (result && (IsDummy(symbol) || IsPointer(symbol))) {
6620c0b2ea9SPeter Klausler                 // Dummy procedures and procedure pointers may not be
6630c0b2ea9SPeter Klausler                 // ELEMENTAL, but we do accept the use of elemental intrinsic
6640c0b2ea9SPeter Klausler                 // functions as their interfaces.
6650c0b2ea9SPeter Klausler                 result->attrs.reset(Procedure::Attr::Elemental);
66695f4ca7fSPeter Klausler               }
6670c0b2ea9SPeter Klausler               return result;
66877dc203cSPeter Steinfeld             } else {
6690c0b2ea9SPeter Klausler               Procedure result;
67077dc203cSPeter Steinfeld               result.attrs.set(Procedure::Attr::ImplicitInterface);
671635656f4SPeter Klausler               const semantics::DeclTypeSpec *type{proc.type()};
67277dc203cSPeter Steinfeld               if (symbol.test(semantics::Symbol::Flag::Subroutine)) {
67377dc203cSPeter Steinfeld                 // ignore any implicit typing
67477dc203cSPeter Steinfeld                 result.attrs.set(Procedure::Attr::Subroutine);
675f513bd80SPeter Klausler                 if (proc.isCUDAKernel()) {
676f513bd80SPeter Klausler                   result.cudaSubprogramAttrs =
677f513bd80SPeter Klausler                       common::CUDASubprogramAttrs::Global;
678f513bd80SPeter Klausler                 }
67977dc203cSPeter Steinfeld               } else if (type) {
68077dc203cSPeter Steinfeld                 if (auto resultType{DynamicType::From(*type)}) {
68177dc203cSPeter Steinfeld                   result.functionResult = FunctionResult{*resultType};
68277dc203cSPeter Steinfeld                 } else {
68377dc203cSPeter Steinfeld                   return std::nullopt;
68477dc203cSPeter Steinfeld                 }
68577dc203cSPeter Steinfeld               } else if (symbol.test(semantics::Symbol::Flag::Function)) {
68677dc203cSPeter Steinfeld                 return std::nullopt;
68777dc203cSPeter Steinfeld               }
68877dc203cSPeter Steinfeld               // The PASS name, if any, is not a characteristic.
6890c0b2ea9SPeter Klausler               return std::move(result);
69077dc203cSPeter Steinfeld             }
69177dc203cSPeter Steinfeld           },
69277dc203cSPeter Steinfeld           [&](const semantics::ProcBindingDetails &binding) {
693cb263919SPeter Klausler             if (auto result{CharacterizeProcedure(binding.symbol(), context,
694cb263919SPeter Klausler                     seenProcs, /*emitError=*/false)}) {
6955e8094baSPeter Klausler               if (binding.symbol().attrs().test(semantics::Attr::INTRINSIC)) {
6965e8094baSPeter Klausler                 result->attrs.reset(Procedure::Attr::Elemental);
6975e8094baSPeter Klausler               }
69877dc203cSPeter Steinfeld               if (!symbol.attrs().test(semantics::Attr::NOPASS)) {
69977dc203cSPeter Steinfeld                 auto passName{binding.passName()};
70077dc203cSPeter Steinfeld                 for (auto &dummy : result->dummyArguments) {
70177dc203cSPeter Steinfeld                   if (!passName || dummy.name.c_str() == *passName) {
70277dc203cSPeter Steinfeld                     dummy.pass = true;
7035e8094baSPeter Klausler                     break;
70477dc203cSPeter Steinfeld                   }
70577dc203cSPeter Steinfeld                 }
70677dc203cSPeter Steinfeld               }
70777dc203cSPeter Steinfeld               return result;
70877dc203cSPeter Steinfeld             } else {
70977dc203cSPeter Steinfeld               return std::optional<Procedure>{};
71077dc203cSPeter Steinfeld             }
71177dc203cSPeter Steinfeld           },
71277dc203cSPeter Steinfeld           [&](const semantics::UseDetails &use) {
713cb263919SPeter Klausler             return CharacterizeProcedure(
714cb263919SPeter Klausler                 use.symbol(), context, seenProcs, /*emitError=*/false);
71577dc203cSPeter Steinfeld           },
716a61835b1SDaniil Dudkin           [](const semantics::UseErrorDetails &) {
717a61835b1SDaniil Dudkin             // Ambiguous use-association will be handled later during symbol
718a61835b1SDaniil Dudkin             // checks, ignore UseErrorDetails here without actual symbol usage.
719a61835b1SDaniil Dudkin             return std::optional<Procedure>{};
720a61835b1SDaniil Dudkin           },
72177dc203cSPeter Steinfeld           [&](const semantics::HostAssocDetails &assoc) {
722cb263919SPeter Klausler             return CharacterizeProcedure(
723cb263919SPeter Klausler                 assoc.symbol(), context, seenProcs, /*emitError=*/false);
72477dc203cSPeter Steinfeld           },
72546c49e66SPeter Klausler           [&](const semantics::GenericDetails &generic) {
72646c49e66SPeter Klausler             if (const semantics::Symbol * specific{generic.specific()}) {
727cb263919SPeter Klausler               return CharacterizeProcedure(
728cb263919SPeter Klausler                   *specific, context, seenProcs, emitError);
72946c49e66SPeter Klausler             } else {
73046c49e66SPeter Klausler               return std::optional<Procedure>{};
73146c49e66SPeter Klausler             }
73246c49e66SPeter Klausler           },
733a54e8b2cSPeter Klausler           [&](const semantics::EntityDetails &x) {
734cb263919SPeter Klausler             CheckForNested(symbol);
735562bfe12Speter klausler             return std::optional<Procedure>{};
736562bfe12Speter klausler           },
737562bfe12Speter klausler           [&](const semantics::SubprogramNameDetails &) {
738a54e8b2cSPeter Klausler             if (const semantics::Symbol *
739a54e8b2cSPeter Klausler                 ancestor{FindAncestorModuleProcedure(&symbol)}) {
740a54e8b2cSPeter Klausler               return CharacterizeProcedure(
741a54e8b2cSPeter Klausler                   *ancestor, context, seenProcs, emitError);
742a54e8b2cSPeter Klausler             }
743cb263919SPeter Klausler             CheckForNested(symbol);
744562bfe12Speter klausler             return std::optional<Procedure>{};
745562bfe12Speter klausler           },
746562bfe12Speter klausler           [&](const auto &) {
747562bfe12Speter klausler             context.messages().Say(
748562bfe12Speter klausler                 "'%s' is not a procedure"_err_en_US, symbol.name());
749562bfe12Speter klausler             return std::optional<Procedure>{};
750562bfe12Speter klausler           },
75177dc203cSPeter Steinfeld       },
7520c0b2ea9SPeter Klausler       symbol.details())};
7530c0b2ea9SPeter Klausler   if (result && !symbol.has<semantics::ProcBindingDetails>()) {
75409c544e7SjeanPerier     CopyAttrs<Procedure, Procedure::Attr>(symbol, *result,
7550c0b2ea9SPeter Klausler         {
7560c0b2ea9SPeter Klausler             {semantics::Attr::BIND_C, Procedure::Attr::BindC},
75709c544e7SjeanPerier         });
75809c544e7SjeanPerier     CopyAttrs<Procedure, Procedure::Attr>(DEREF(GetMainEntry(&symbol)), *result,
75909c544e7SjeanPerier         {
7600c0b2ea9SPeter Klausler             {semantics::Attr::ELEMENTAL, Procedure::Attr::Elemental},
7610c0b2ea9SPeter Klausler         });
7620c0b2ea9SPeter Klausler     if (IsPureProcedure(symbol) || // works for ENTRY too
7630c0b2ea9SPeter Klausler         (!IsExplicitlyImpureProcedure(symbol) &&
7640c0b2ea9SPeter Klausler             result->attrs.test(Procedure::Attr::Elemental))) {
7650c0b2ea9SPeter Klausler       result->attrs.set(Procedure::Attr::Pure);
7660c0b2ea9SPeter Klausler     }
7670c0b2ea9SPeter Klausler   }
7680c0b2ea9SPeter Klausler   return result;
76977dc203cSPeter Steinfeld }
77077dc203cSPeter Steinfeld 
77177dc203cSPeter Steinfeld static std::optional<DummyProcedure> CharacterizeDummyProcedure(
77277dc203cSPeter Steinfeld     const semantics::Symbol &symbol, FoldingContext &context,
773174cabedSPeter Klausler     semantics::UnorderedSymbolSet seenProcs) {
774cb263919SPeter Klausler   if (auto procedure{CharacterizeProcedure(
775cb263919SPeter Klausler           symbol, context, seenProcs, /*emitError=*/true)}) {
77664ab3302SCarolineConcatto     // Dummy procedures may not be elemental.  Elemental dummy procedure
77764ab3302SCarolineConcatto     // interfaces are errors when the interface is not intrinsic, and that
77864ab3302SCarolineConcatto     // error is caught elsewhere.  Elemental intrinsic interfaces are
77964ab3302SCarolineConcatto     // made non-elemental.
78064ab3302SCarolineConcatto     procedure->attrs.reset(Procedure::Attr::Elemental);
78164ab3302SCarolineConcatto     DummyProcedure result{std::move(procedure.value())};
78264ab3302SCarolineConcatto     CopyAttrs<DummyProcedure, DummyProcedure::Attr>(symbol, result,
78364ab3302SCarolineConcatto         {
78464ab3302SCarolineConcatto             {semantics::Attr::OPTIONAL, DummyProcedure::Attr::Optional},
78564ab3302SCarolineConcatto             {semantics::Attr::POINTER, DummyProcedure::Attr::Pointer},
78664ab3302SCarolineConcatto         });
78764ab3302SCarolineConcatto     result.intent = GetIntent(symbol.attrs());
78864ab3302SCarolineConcatto     return result;
78964ab3302SCarolineConcatto   } else {
79064ab3302SCarolineConcatto     return std::nullopt;
79164ab3302SCarolineConcatto   }
79264ab3302SCarolineConcatto }
79364ab3302SCarolineConcatto 
7948670e499SCaroline Concatto llvm::raw_ostream &DummyProcedure::Dump(llvm::raw_ostream &o) const {
79564ab3302SCarolineConcatto   attrs.Dump(o, EnumToString);
79664ab3302SCarolineConcatto   if (intent != common::Intent::Default) {
79764ab3302SCarolineConcatto     o << "INTENT(" << common::EnumToString(intent) << ')';
79864ab3302SCarolineConcatto   }
79964ab3302SCarolineConcatto   procedure.value().Dump(o);
80064ab3302SCarolineConcatto   return o;
80164ab3302SCarolineConcatto }
80264ab3302SCarolineConcatto 
8038670e499SCaroline Concatto llvm::raw_ostream &AlternateReturn::Dump(llvm::raw_ostream &o) const {
8048670e499SCaroline Concatto   return o << '*';
8058670e499SCaroline Concatto }
80664ab3302SCarolineConcatto 
80764ab3302SCarolineConcatto DummyArgument::~DummyArgument() {}
80864ab3302SCarolineConcatto 
80964ab3302SCarolineConcatto bool DummyArgument::operator==(const DummyArgument &that) const {
81064ab3302SCarolineConcatto   return u == that.u; // name and passed-object usage are not characteristics
81164ab3302SCarolineConcatto }
81264ab3302SCarolineConcatto 
813e86591b3SPeter Klausler bool DummyArgument::IsCompatibleWith(const DummyArgument &actual,
814e86591b3SPeter Klausler     std::string *whyNot, std::optional<std::string> *warning) const {
8153bfe9074SPeter Klausler   if (const auto *ifaceData{std::get_if<DummyDataObject>(&u)}) {
81662d874f2SPeter Klausler     if (const auto *actualData{std::get_if<DummyDataObject>(&actual.u)}) {
817e86591b3SPeter Klausler       return ifaceData->IsCompatibleWith(*actualData, whyNot, warning);
8183bfe9074SPeter Klausler     }
81962d874f2SPeter Klausler     if (whyNot) {
82062d874f2SPeter Klausler       *whyNot = "one dummy argument is an object, the other is not";
82162d874f2SPeter Klausler     }
82262d874f2SPeter Klausler   } else if (const auto *ifaceProc{std::get_if<DummyProcedure>(&u)}) {
82362d874f2SPeter Klausler     if (const auto *actualProc{std::get_if<DummyProcedure>(&actual.u)}) {
82462d874f2SPeter Klausler       return ifaceProc->IsCompatibleWith(*actualProc, whyNot);
82562d874f2SPeter Klausler     }
82662d874f2SPeter Klausler     if (whyNot) {
82762d874f2SPeter Klausler       *whyNot = "one dummy argument is a procedure, the other is not";
82862d874f2SPeter Klausler     }
82962d874f2SPeter Klausler   } else {
83062d874f2SPeter Klausler     CHECK(std::holds_alternative<AlternateReturn>(u));
83162d874f2SPeter Klausler     if (std::holds_alternative<AlternateReturn>(actual.u)) {
83262d874f2SPeter Klausler       return true;
83362d874f2SPeter Klausler     }
83462d874f2SPeter Klausler     if (whyNot) {
83562d874f2SPeter Klausler       *whyNot = "one dummy argument is an alternate return, the other is not";
83662d874f2SPeter Klausler     }
83762d874f2SPeter Klausler   }
83862d874f2SPeter Klausler   return false;
8393bfe9074SPeter Klausler }
8403bfe9074SPeter Klausler 
84177dc203cSPeter Steinfeld static std::optional<DummyArgument> CharacterizeDummyArgument(
84277dc203cSPeter Steinfeld     const semantics::Symbol &symbol, FoldingContext &context,
843174cabedSPeter Klausler     semantics::UnorderedSymbolSet seenProcs) {
84464ab3302SCarolineConcatto   auto name{symbol.name().ToString()};
84573c3530fSpeter klausler   if (symbol.has<semantics::ObjectEntityDetails>() ||
84673c3530fSpeter klausler       symbol.has<semantics::EntityDetails>()) {
847641ede93Speter klausler     if (auto obj{DummyDataObject::Characterize(symbol, context)}) {
84864ab3302SCarolineConcatto       return DummyArgument{std::move(name), std::move(obj.value())};
84964ab3302SCarolineConcatto     }
85077dc203cSPeter Steinfeld   } else if (auto proc{
85177dc203cSPeter Steinfeld                  CharacterizeDummyProcedure(symbol, context, seenProcs)}) {
85264ab3302SCarolineConcatto     return DummyArgument{std::move(name), std::move(proc.value())};
85364ab3302SCarolineConcatto   }
85464ab3302SCarolineConcatto   return std::nullopt;
85564ab3302SCarolineConcatto }
85664ab3302SCarolineConcatto 
85729fd3e2aSPeter Klausler std::optional<DummyArgument> DummyArgument::FromActual(std::string &&name,
85829fd3e2aSPeter Klausler     const Expr<SomeType> &expr, FoldingContext &context,
85929fd3e2aSPeter Klausler     bool forImplicitInterface) {
860cd03e96fSPeter Klausler   return common::visit(
86164ab3302SCarolineConcatto       common::visitors{
86264ab3302SCarolineConcatto           [&](const BOZLiteralConstant &) {
8635718a425SPeter Klausler             DummyDataObject obj{
8645718a425SPeter Klausler                 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}};
8655718a425SPeter Klausler             obj.attrs.set(DummyDataObject::Attr::DeducedFromActual);
8665718a425SPeter Klausler             return std::make_optional<DummyArgument>(
8675718a425SPeter Klausler                 std::move(name), std::move(obj));
86864ab3302SCarolineConcatto           },
869c7574188SPeter Steinfeld           [&](const NullPointer &) {
8705718a425SPeter Klausler             DummyDataObject obj{
8715718a425SPeter Klausler                 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}};
8725718a425SPeter Klausler             obj.attrs.set(DummyDataObject::Attr::DeducedFromActual);
8735718a425SPeter Klausler             return std::make_optional<DummyArgument>(
8745718a425SPeter Klausler                 std::move(name), std::move(obj));
875c7574188SPeter Steinfeld           },
87664ab3302SCarolineConcatto           [&](const ProcedureDesignator &designator) {
877cb263919SPeter Klausler             if (auto proc{Procedure::Characterize(
878cb263919SPeter Klausler                     designator, context, /*emitError=*/true)}) {
87964ab3302SCarolineConcatto               return std::make_optional<DummyArgument>(
88064ab3302SCarolineConcatto                   std::move(name), DummyProcedure{std::move(*proc)});
88164ab3302SCarolineConcatto             } else {
88264ab3302SCarolineConcatto               return std::optional<DummyArgument>{};
88364ab3302SCarolineConcatto             }
88464ab3302SCarolineConcatto           },
88564ab3302SCarolineConcatto           [&](const ProcedureRef &call) {
886641ede93Speter klausler             if (auto proc{Procedure::Characterize(call, context)}) {
88764ab3302SCarolineConcatto               return std::make_optional<DummyArgument>(
88864ab3302SCarolineConcatto                   std::move(name), DummyProcedure{std::move(*proc)});
88964ab3302SCarolineConcatto             } else {
89064ab3302SCarolineConcatto               return std::optional<DummyArgument>{};
89164ab3302SCarolineConcatto             }
89264ab3302SCarolineConcatto           },
89364ab3302SCarolineConcatto           [&](const auto &) {
894934b27a9Speter klausler             if (auto type{TypeAndShape::Characterize(expr, context)}) {
89529fd3e2aSPeter Klausler               if (forImplicitInterface &&
89629fd3e2aSPeter Klausler                   !type->type().IsUnlimitedPolymorphic() &&
89729fd3e2aSPeter Klausler                   type->type().IsPolymorphic()) {
89829fd3e2aSPeter Klausler                 // Pass the monomorphic declared type to an implicit interface
89929fd3e2aSPeter Klausler                 type->set_type(DynamicType{
90029fd3e2aSPeter Klausler                     type->type().GetDerivedTypeSpec(), /*poly=*/false});
90129fd3e2aSPeter Klausler               }
9025718a425SPeter Klausler               DummyDataObject obj{std::move(*type)};
9035718a425SPeter Klausler               obj.attrs.set(DummyDataObject::Attr::DeducedFromActual);
90464ab3302SCarolineConcatto               return std::make_optional<DummyArgument>(
9055718a425SPeter Klausler                   std::move(name), std::move(obj));
90664ab3302SCarolineConcatto             } else {
90764ab3302SCarolineConcatto               return std::optional<DummyArgument>{};
90864ab3302SCarolineConcatto             }
90964ab3302SCarolineConcatto           },
91064ab3302SCarolineConcatto       },
91164ab3302SCarolineConcatto       expr.u);
91264ab3302SCarolineConcatto }
91364ab3302SCarolineConcatto 
91429fd3e2aSPeter Klausler std::optional<DummyArgument> DummyArgument::FromActual(std::string &&name,
91529fd3e2aSPeter Klausler     const ActualArgument &arg, FoldingContext &context,
91629fd3e2aSPeter Klausler     bool forImplicitInterface) {
917bdbebef8SPeter Klausler   if (const auto *expr{arg.UnwrapExpr()}) {
91829fd3e2aSPeter Klausler     return FromActual(std::move(name), *expr, context, forImplicitInterface);
919bdbebef8SPeter Klausler   } else if (arg.GetAssumedTypeDummy()) {
920bdbebef8SPeter Klausler     return std::nullopt;
921bdbebef8SPeter Klausler   } else {
922bdbebef8SPeter Klausler     return DummyArgument{AlternateReturn{}};
923bdbebef8SPeter Klausler   }
924bdbebef8SPeter Klausler }
925bdbebef8SPeter Klausler 
92664ab3302SCarolineConcatto bool DummyArgument::IsOptional() const {
927cd03e96fSPeter Klausler   return common::visit(
92864ab3302SCarolineConcatto       common::visitors{
92964ab3302SCarolineConcatto           [](const DummyDataObject &data) {
93064ab3302SCarolineConcatto             return data.attrs.test(DummyDataObject::Attr::Optional);
93164ab3302SCarolineConcatto           },
93264ab3302SCarolineConcatto           [](const DummyProcedure &proc) {
93364ab3302SCarolineConcatto             return proc.attrs.test(DummyProcedure::Attr::Optional);
93464ab3302SCarolineConcatto           },
93564ab3302SCarolineConcatto           [](const AlternateReturn &) { return false; },
93664ab3302SCarolineConcatto       },
93764ab3302SCarolineConcatto       u);
93864ab3302SCarolineConcatto }
93964ab3302SCarolineConcatto 
94064ab3302SCarolineConcatto void DummyArgument::SetOptional(bool value) {
941cd03e96fSPeter Klausler   common::visit(common::visitors{
94264ab3302SCarolineConcatto                     [value](DummyDataObject &data) {
94364ab3302SCarolineConcatto                       data.attrs.set(DummyDataObject::Attr::Optional, value);
94464ab3302SCarolineConcatto                     },
94564ab3302SCarolineConcatto                     [value](DummyProcedure &proc) {
94664ab3302SCarolineConcatto                       proc.attrs.set(DummyProcedure::Attr::Optional, value);
94764ab3302SCarolineConcatto                     },
94864ab3302SCarolineConcatto                     [](AlternateReturn &) { DIE("cannot set optional"); },
94964ab3302SCarolineConcatto                 },
95064ab3302SCarolineConcatto       u);
95164ab3302SCarolineConcatto }
95264ab3302SCarolineConcatto 
95329d1a494SJean Perier void DummyArgument::SetIntent(common::Intent intent) {
954cd03e96fSPeter Klausler   common::visit(common::visitors{
95529d1a494SJean Perier                     [intent](DummyDataObject &data) { data.intent = intent; },
95629d1a494SJean Perier                     [intent](DummyProcedure &proc) { proc.intent = intent; },
95729d1a494SJean Perier                     [](AlternateReturn &) { DIE("cannot set intent"); },
95829d1a494SJean Perier                 },
95929d1a494SJean Perier       u);
96029d1a494SJean Perier }
96129d1a494SJean Perier 
96229d1a494SJean Perier common::Intent DummyArgument::GetIntent() const {
963cd03e96fSPeter Klausler   return common::visit(
964cd03e96fSPeter Klausler       common::visitors{
96529d1a494SJean Perier           [](const DummyDataObject &data) { return data.intent; },
96629d1a494SJean Perier           [](const DummyProcedure &proc) { return proc.intent; },
96729d1a494SJean Perier           [](const AlternateReturn &) -> common::Intent {
968bc56620bSPeter Steinfeld             DIE("Alternate returns have no intent");
96929d1a494SJean Perier           },
97029d1a494SJean Perier       },
97129d1a494SJean Perier       u);
97229d1a494SJean Perier }
97329d1a494SJean Perier 
9746e0a2031SPeter Klausler bool DummyArgument::CanBePassedViaImplicitInterface(std::string *whyNot) const {
97564ab3302SCarolineConcatto   if (const auto *object{std::get_if<DummyDataObject>(&u)}) {
9766e0a2031SPeter Klausler     return object->CanBePassedViaImplicitInterface(whyNot);
97766fdfff7SPeter Klausler   } else if (const auto *proc{std::get_if<DummyProcedure>(&u)}) {
9786e0a2031SPeter Klausler     return proc->CanBePassedViaImplicitInterface(whyNot);
97964ab3302SCarolineConcatto   } else {
98064ab3302SCarolineConcatto     return true;
98164ab3302SCarolineConcatto   }
98264ab3302SCarolineConcatto }
98364ab3302SCarolineConcatto 
984c7574188SPeter Steinfeld bool DummyArgument::IsTypelessIntrinsicDummy() const {
985c7574188SPeter Steinfeld   const auto *argObj{std::get_if<characteristics::DummyDataObject>(&u)};
986c7574188SPeter Steinfeld   return argObj && argObj->type.type().IsTypelessIntrinsicArgument();
987c7574188SPeter Steinfeld }
988c7574188SPeter Steinfeld 
9898670e499SCaroline Concatto llvm::raw_ostream &DummyArgument::Dump(llvm::raw_ostream &o) const {
99064ab3302SCarolineConcatto   if (!name.empty()) {
99164ab3302SCarolineConcatto     o << name << '=';
99264ab3302SCarolineConcatto   }
99364ab3302SCarolineConcatto   if (pass) {
99464ab3302SCarolineConcatto     o << " PASS";
99564ab3302SCarolineConcatto   }
996cd03e96fSPeter Klausler   common::visit([&](const auto &x) { x.Dump(o); }, u);
99764ab3302SCarolineConcatto   return o;
99864ab3302SCarolineConcatto }
99964ab3302SCarolineConcatto 
100064ab3302SCarolineConcatto FunctionResult::FunctionResult(DynamicType t) : u{TypeAndShape{t}} {}
100164ab3302SCarolineConcatto FunctionResult::FunctionResult(TypeAndShape &&t) : u{std::move(t)} {}
100264ab3302SCarolineConcatto FunctionResult::FunctionResult(Procedure &&p) : u{std::move(p)} {}
100364ab3302SCarolineConcatto FunctionResult::~FunctionResult() {}
100464ab3302SCarolineConcatto 
100564ab3302SCarolineConcatto bool FunctionResult::operator==(const FunctionResult &that) const {
1006f513bd80SPeter Klausler   return attrs == that.attrs && cudaDataAttr == that.cudaDataAttr &&
1007f513bd80SPeter Klausler       u == that.u;
100864ab3302SCarolineConcatto }
100964ab3302SCarolineConcatto 
1010488b9fd1SDaniil Dudkin static std::optional<FunctionResult> CharacterizeFunctionResult(
1011488b9fd1SDaniil Dudkin     const semantics::Symbol &symbol, FoldingContext &context,
1012cb263919SPeter Klausler     semantics::UnorderedSymbolSet seenProcs, bool emitError) {
1013f513bd80SPeter Klausler   if (const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
10140c0b2ea9SPeter Klausler     if (auto type{TypeAndShape::Characterize(
10150c0b2ea9SPeter Klausler             symbol, context, /*invariantOnly=*/false)}) {
101664ab3302SCarolineConcatto       FunctionResult result{std::move(*type)};
101764ab3302SCarolineConcatto       CopyAttrs<FunctionResult, FunctionResult::Attr>(symbol, result,
101864ab3302SCarolineConcatto           {
101964ab3302SCarolineConcatto               {semantics::Attr::ALLOCATABLE, FunctionResult::Attr::Allocatable},
102064ab3302SCarolineConcatto               {semantics::Attr::CONTIGUOUS, FunctionResult::Attr::Contiguous},
102164ab3302SCarolineConcatto               {semantics::Attr::POINTER, FunctionResult::Attr::Pointer},
102264ab3302SCarolineConcatto           });
1023f513bd80SPeter Klausler       result.cudaDataAttr = object->cudaDataAttr();
102464ab3302SCarolineConcatto       return result;
102564ab3302SCarolineConcatto     }
1026cb263919SPeter Klausler   } else if (auto maybeProc{CharacterizeProcedure(
1027cb263919SPeter Klausler                  symbol, context, seenProcs, emitError)}) {
102864ab3302SCarolineConcatto     FunctionResult result{std::move(*maybeProc)};
102964ab3302SCarolineConcatto     result.attrs.set(FunctionResult::Attr::Pointer);
103064ab3302SCarolineConcatto     return result;
103164ab3302SCarolineConcatto   }
103264ab3302SCarolineConcatto   return std::nullopt;
103364ab3302SCarolineConcatto }
103464ab3302SCarolineConcatto 
1035488b9fd1SDaniil Dudkin std::optional<FunctionResult> FunctionResult::Characterize(
1036488b9fd1SDaniil Dudkin     const Symbol &symbol, FoldingContext &context) {
1037488b9fd1SDaniil Dudkin   semantics::UnorderedSymbolSet seenProcs;
1038cb263919SPeter Klausler   return CharacterizeFunctionResult(
1039cb263919SPeter Klausler       symbol, context, seenProcs, /*emitError=*/false);
1040488b9fd1SDaniil Dudkin }
1041488b9fd1SDaniil Dudkin 
104264ab3302SCarolineConcatto bool FunctionResult::IsAssumedLengthCharacter() const {
104364ab3302SCarolineConcatto   if (const auto *ts{std::get_if<TypeAndShape>(&u)}) {
104464ab3302SCarolineConcatto     return ts->type().IsAssumedLengthCharacter();
104564ab3302SCarolineConcatto   } else {
104664ab3302SCarolineConcatto     return false;
104764ab3302SCarolineConcatto   }
104864ab3302SCarolineConcatto }
104964ab3302SCarolineConcatto 
10506e0a2031SPeter Klausler bool FunctionResult::CanBeReturnedViaImplicitInterface(
10516e0a2031SPeter Klausler     std::string *whyNot) const {
105264ab3302SCarolineConcatto   if (attrs.test(Attr::Pointer) || attrs.test(Attr::Allocatable)) {
10536e0a2031SPeter Klausler     if (whyNot) {
10546e0a2031SPeter Klausler       *whyNot = "the function result is a pointer or allocatable";
10556e0a2031SPeter Klausler     }
105664ab3302SCarolineConcatto     return false; // 15.4.2.2(4)(b)
1057f513bd80SPeter Klausler   } else if (cudaDataAttr) {
10586e0a2031SPeter Klausler     if (whyNot) {
10596e0a2031SPeter Klausler       *whyNot = "the function result has CUDA attributes";
10606e0a2031SPeter Klausler     }
1061f513bd80SPeter Klausler     return false;
106264ab3302SCarolineConcatto   } else if (const auto *typeAndShape{GetTypeAndShape()}) {
106364ab3302SCarolineConcatto     if (typeAndShape->Rank() > 0) {
10646e0a2031SPeter Klausler       if (whyNot) {
10656e0a2031SPeter Klausler         *whyNot = "the function result is an array";
10666e0a2031SPeter Klausler       }
106764ab3302SCarolineConcatto       return false; // 15.4.2.2(4)(a)
106864ab3302SCarolineConcatto     } else {
106964ab3302SCarolineConcatto       const DynamicType &type{typeAndShape->type()};
107064ab3302SCarolineConcatto       switch (type.category()) {
107164ab3302SCarolineConcatto       case TypeCategory::Character:
1072ac964175Speter klausler         if (type.knownLength()) {
1073ac964175Speter klausler           return true;
1074ac964175Speter klausler         } else if (const auto *param{type.charLengthParamValue()}) {
107564ab3302SCarolineConcatto           if (const auto &expr{param->GetExplicit()}) {
10766e0a2031SPeter Klausler             if (IsConstantExpr(*expr)) { // 15.4.2.2(4)(c)
10776e0a2031SPeter Klausler               return true;
10786e0a2031SPeter Klausler             } else {
10796e0a2031SPeter Klausler               if (whyNot) {
10806e0a2031SPeter Klausler                 *whyNot = "the function result's length is not constant";
10816e0a2031SPeter Klausler               }
10826e0a2031SPeter Klausler               return false;
10836e0a2031SPeter Klausler             }
1084bd72ed93SJean Perier           } else if (param->isAssumed()) {
1085bd72ed93SJean Perier             return true;
108664ab3302SCarolineConcatto           }
108764ab3302SCarolineConcatto         }
10886e0a2031SPeter Klausler         if (whyNot) {
10896e0a2031SPeter Klausler           *whyNot = "the function result's length is not known to the caller";
10906e0a2031SPeter Klausler         }
109164ab3302SCarolineConcatto         return false;
109264ab3302SCarolineConcatto       case TypeCategory::Derived:
10936e0a2031SPeter Klausler         if (type.IsPolymorphic()) {
10946e0a2031SPeter Klausler           if (whyNot) {
10956e0a2031SPeter Klausler             *whyNot = "the function result is polymorphic";
10966e0a2031SPeter Klausler           }
10976e0a2031SPeter Klausler           return false;
10986e0a2031SPeter Klausler         } else {
109964ab3302SCarolineConcatto           const auto &spec{type.GetDerivedTypeSpec()};
110064ab3302SCarolineConcatto           for (const auto &pair : spec.parameters()) {
110164ab3302SCarolineConcatto             if (const auto &expr{pair.second.GetExplicit()}) {
110264ab3302SCarolineConcatto               if (!IsConstantExpr(*expr)) {
11036e0a2031SPeter Klausler                 if (whyNot) {
11046e0a2031SPeter Klausler                   *whyNot = "the function result's derived type has a "
11056e0a2031SPeter Klausler                             "non-constant parameter";
11066e0a2031SPeter Klausler                 }
110764ab3302SCarolineConcatto                 return false; // 15.4.2.2(4)(c)
110864ab3302SCarolineConcatto               }
110964ab3302SCarolineConcatto             }
111064ab3302SCarolineConcatto           }
111164ab3302SCarolineConcatto           return true;
111264ab3302SCarolineConcatto         }
11131f879005STim Keith       default:
11141f879005STim Keith         return true;
111564ab3302SCarolineConcatto       }
111664ab3302SCarolineConcatto     }
111764ab3302SCarolineConcatto   } else {
11186e0a2031SPeter Klausler     if (whyNot) {
11196e0a2031SPeter Klausler       *whyNot = "the function result has unknown type or shape";
11206e0a2031SPeter Klausler     }
11216e0a2031SPeter Klausler     return false; // 15.4.2.2(4)(b) - procedure pointer?
112264ab3302SCarolineConcatto   }
112364ab3302SCarolineConcatto }
112464ab3302SCarolineConcatto 
11250c0b2ea9SPeter Klausler static std::optional<std::string> AreIncompatibleFunctionResultShapes(
11260c0b2ea9SPeter Klausler     const Shape &x, const Shape &y) {
112773cf0142SjeanPerier   // Function results cannot be assumed-rank, hence the non optional arguments.
1128c1a77839SPeter Klausler   int rank{GetRank(x)};
11290c0b2ea9SPeter Klausler   if (int yrank{GetRank(y)}; yrank != rank) {
11300c0b2ea9SPeter Klausler     return "rank "s + std::to_string(rank) + " vs " + std::to_string(yrank);
1131c1a77839SPeter Klausler   }
1132c1a77839SPeter Klausler   for (int j{0}; j < rank; ++j) {
11330c0b2ea9SPeter Klausler     if (x[j] && y[j] && !(*x[j] == *y[j])) {
11340c0b2ea9SPeter Klausler       return x[j]->AsFortran() + " vs " + y[j]->AsFortran();
1135c1a77839SPeter Klausler     }
1136c1a77839SPeter Klausler   }
11370c0b2ea9SPeter Klausler   return std::nullopt;
1138c1a77839SPeter Klausler }
1139c1a77839SPeter Klausler 
114062d874f2SPeter Klausler bool FunctionResult::IsCompatibleWith(
114162d874f2SPeter Klausler     const FunctionResult &actual, std::string *whyNot) const {
11423bfe9074SPeter Klausler   Attrs actualAttrs{actual.attrs};
114362d874f2SPeter Klausler   if (!attrs.test(Attr::Contiguous)) {
11443bfe9074SPeter Klausler     actualAttrs.reset(Attr::Contiguous);
114562d874f2SPeter Klausler   }
11463bfe9074SPeter Klausler   if (attrs != actualAttrs) {
114762d874f2SPeter Klausler     if (whyNot) {
114862d874f2SPeter Klausler       *whyNot = "function results have incompatible attributes";
114962d874f2SPeter Klausler     }
1150f513bd80SPeter Klausler   } else if (cudaDataAttr != actual.cudaDataAttr) {
1151f513bd80SPeter Klausler     if (whyNot) {
1152f513bd80SPeter Klausler       *whyNot = "function results have incompatible CUDA data attributes";
1153f513bd80SPeter Klausler     }
11543bfe9074SPeter Klausler   } else if (const auto *ifaceTypeShape{std::get_if<TypeAndShape>(&u)}) {
11553bfe9074SPeter Klausler     if (const auto *actualTypeShape{std::get_if<TypeAndShape>(&actual.u)}) {
11560c0b2ea9SPeter Klausler       std::optional<std::string> details;
1157948d0b34SPeter Klausler       if (ifaceTypeShape->Rank() != actualTypeShape->Rank()) {
115862d874f2SPeter Klausler         if (whyNot) {
115962d874f2SPeter Klausler           *whyNot = "function results have distinct ranks";
116062d874f2SPeter Klausler         }
1161948d0b34SPeter Klausler       } else if (!attrs.test(Attr::Allocatable) && !attrs.test(Attr::Pointer) &&
11620c0b2ea9SPeter Klausler           (details = AreIncompatibleFunctionResultShapes(
116373cf0142SjeanPerier                ifaceTypeShape->shape().value(),
116473cf0142SjeanPerier                actualTypeShape->shape().value()))) {
116562d874f2SPeter Klausler         if (whyNot) {
11660c0b2ea9SPeter Klausler           *whyNot = "function results have distinct extents (" + *details + ')';
116762d874f2SPeter Klausler         }
1168c6b9df0fSPeter Klausler       } else if (ifaceTypeShape->type() != actualTypeShape->type()) {
11690c0b2ea9SPeter Klausler         if (ifaceTypeShape->type().category() !=
1170c6b9df0fSPeter Klausler             actualTypeShape->type().category()) {
11710c0b2ea9SPeter Klausler         } else if (ifaceTypeShape->type().category() ==
11720c0b2ea9SPeter Klausler             TypeCategory::Character) {
11730c0b2ea9SPeter Klausler           if (ifaceTypeShape->type().kind() == actualTypeShape->type().kind()) {
11740c0b2ea9SPeter Klausler             if (IsAssumedLengthCharacter() ||
11750c0b2ea9SPeter Klausler                 actual.IsAssumedLengthCharacter()) {
11760c0b2ea9SPeter Klausler               return true;
11770c0b2ea9SPeter Klausler             } else {
117811529d5bSPeter Klausler               auto len{ToInt64(ifaceTypeShape->LEN())};
117911529d5bSPeter Klausler               auto actualLen{ToInt64(actualTypeShape->LEN())};
118011529d5bSPeter Klausler               if (len.has_value() != actualLen.has_value()) {
118111529d5bSPeter Klausler                 if (whyNot) {
118211529d5bSPeter Klausler                   *whyNot = "constant-length vs non-constant-length character "
118311529d5bSPeter Klausler                             "results";
118411529d5bSPeter Klausler                 }
118511529d5bSPeter Klausler               } else if (len && *len != *actualLen) {
118611529d5bSPeter Klausler                 if (whyNot) {
118711529d5bSPeter Klausler                   *whyNot = "character results with distinct lengths";
118811529d5bSPeter Klausler                 }
118911529d5bSPeter Klausler               } else {
11900c0b2ea9SPeter Klausler                 const auto *ifaceLenParam{
11910c0b2ea9SPeter Klausler                     ifaceTypeShape->type().charLengthParamValue()};
11920c0b2ea9SPeter Klausler                 const auto *actualLenParam{
11930c0b2ea9SPeter Klausler                     actualTypeShape->type().charLengthParamValue()};
11940c0b2ea9SPeter Klausler                 if (ifaceLenParam && actualLenParam &&
119511529d5bSPeter Klausler                     ifaceLenParam->isExplicit() !=
119611529d5bSPeter Klausler                         actualLenParam->isExplicit()) {
119711529d5bSPeter Klausler                   if (whyNot) {
119811529d5bSPeter Klausler                     *whyNot =
119911529d5bSPeter Klausler                         "explicit-length vs deferred-length character results";
120011529d5bSPeter Klausler                   }
120111529d5bSPeter Klausler                 } else {
1202c6b9df0fSPeter Klausler                   return true;
1203c6b9df0fSPeter Klausler                 }
1204c6b9df0fSPeter Klausler               }
12050c0b2ea9SPeter Klausler             }
120611529d5bSPeter Klausler           }
12070c0b2ea9SPeter Klausler         } else if (ifaceTypeShape->type().category() == TypeCategory::Derived) {
1208c6b9df0fSPeter Klausler           if (ifaceTypeShape->type().IsPolymorphic() ==
1209c6b9df0fSPeter Klausler                   actualTypeShape->type().IsPolymorphic() &&
1210c6b9df0fSPeter Klausler               !ifaceTypeShape->type().IsUnlimitedPolymorphic() &&
1211c6b9df0fSPeter Klausler               !actualTypeShape->type().IsUnlimitedPolymorphic() &&
1212c6b9df0fSPeter Klausler               AreSameDerivedType(ifaceTypeShape->type().GetDerivedTypeSpec(),
1213c6b9df0fSPeter Klausler                   actualTypeShape->type().GetDerivedTypeSpec())) {
1214c6b9df0fSPeter Klausler             return true;
1215c6b9df0fSPeter Klausler           }
1216c6b9df0fSPeter Klausler         }
121762d874f2SPeter Klausler         if (whyNot) {
1218c6b9df0fSPeter Klausler           *whyNot = "function results have distinct types: "s +
121962d874f2SPeter Klausler               ifaceTypeShape->type().AsFortran() + " vs "s +
122062d874f2SPeter Klausler               actualTypeShape->type().AsFortran();
12213bfe9074SPeter Klausler         }
12223bfe9074SPeter Klausler       } else {
122362d874f2SPeter Klausler         return true;
122462d874f2SPeter Klausler       }
122562d874f2SPeter Klausler     } else {
122662d874f2SPeter Klausler       if (whyNot) {
122762d874f2SPeter Klausler         *whyNot = "function result type and shape are not known";
122862d874f2SPeter Klausler       }
12293bfe9074SPeter Klausler     }
12303bfe9074SPeter Klausler   } else {
12313bfe9074SPeter Klausler     const auto *ifaceProc{std::get_if<CopyableIndirection<Procedure>>(&u)};
123262d874f2SPeter Klausler     CHECK(ifaceProc != nullptr);
12333bfe9074SPeter Klausler     if (const auto *actualProc{
12343bfe9074SPeter Klausler             std::get_if<CopyableIndirection<Procedure>>(&actual.u)}) {
12351c530b3dSPeter Klausler       if (ifaceProc->value().IsCompatibleWith(actualProc->value(),
12361c530b3dSPeter Klausler               /*ignoreImplicitVsExplicit=*/false, whyNot)) {
123762d874f2SPeter Klausler         return true;
123862d874f2SPeter Klausler       }
123962d874f2SPeter Klausler       if (whyNot) {
124062d874f2SPeter Klausler         *whyNot =
124162d874f2SPeter Klausler             "function results are incompatible procedure pointers: "s + *whyNot;
124262d874f2SPeter Klausler       }
12433bfe9074SPeter Klausler     } else {
124462d874f2SPeter Klausler       if (whyNot) {
124562d874f2SPeter Klausler         *whyNot =
124662d874f2SPeter Klausler             "one function result is a procedure pointer, the other is not";
124762d874f2SPeter Klausler       }
124862d874f2SPeter Klausler     }
124962d874f2SPeter Klausler   }
12503bfe9074SPeter Klausler   return false;
12513bfe9074SPeter Klausler }
12523bfe9074SPeter Klausler 
12538670e499SCaroline Concatto llvm::raw_ostream &FunctionResult::Dump(llvm::raw_ostream &o) const {
125464ab3302SCarolineConcatto   attrs.Dump(o, EnumToString);
1255cd03e96fSPeter Klausler   common::visit(common::visitors{
125664ab3302SCarolineConcatto                     [&](const TypeAndShape &ts) { ts.Dump(o); },
125764ab3302SCarolineConcatto                     [&](const CopyableIndirection<Procedure> &p) {
125864ab3302SCarolineConcatto                       p.value().Dump(o << " procedure(") << ')';
125964ab3302SCarolineConcatto                     },
126064ab3302SCarolineConcatto                 },
126164ab3302SCarolineConcatto       u);
1262f513bd80SPeter Klausler   if (cudaDataAttr) {
1263f513bd80SPeter Klausler     o << " cudaDataAttr: " << common::EnumToString(*cudaDataAttr);
1264f513bd80SPeter Klausler   }
126564ab3302SCarolineConcatto   return o;
126664ab3302SCarolineConcatto }
126764ab3302SCarolineConcatto 
126864ab3302SCarolineConcatto Procedure::Procedure(FunctionResult &&fr, DummyArguments &&args, Attrs a)
12691f879005STim Keith     : functionResult{std::move(fr)}, dummyArguments{std::move(args)}, attrs{a} {
12701f879005STim Keith }
127164ab3302SCarolineConcatto Procedure::Procedure(DummyArguments &&args, Attrs a)
127264ab3302SCarolineConcatto     : dummyArguments{std::move(args)}, attrs{a} {}
127364ab3302SCarolineConcatto Procedure::~Procedure() {}
127464ab3302SCarolineConcatto 
127564ab3302SCarolineConcatto bool Procedure::operator==(const Procedure &that) const {
127664ab3302SCarolineConcatto   return attrs == that.attrs && functionResult == that.functionResult &&
1277f513bd80SPeter Klausler       dummyArguments == that.dummyArguments &&
1278f513bd80SPeter Klausler       cudaSubprogramAttrs == that.cudaSubprogramAttrs;
127964ab3302SCarolineConcatto }
128064ab3302SCarolineConcatto 
12811c530b3dSPeter Klausler bool Procedure::IsCompatibleWith(const Procedure &actual,
12821c530b3dSPeter Klausler     bool ignoreImplicitVsExplicit, std::string *whyNot,
1283e86591b3SPeter Klausler     const SpecificIntrinsic *specificIntrinsic,
1284e86591b3SPeter Klausler     std::optional<std::string> *warning) const {
12853bfe9074SPeter Klausler   // 15.5.2.9(1): if dummy is not pure, actual need not be.
128662d874f2SPeter Klausler   // Ditto with elemental.
12873bfe9074SPeter Klausler   Attrs actualAttrs{actual.attrs};
12883bfe9074SPeter Klausler   if (!attrs.test(Attr::Pure)) {
12893bfe9074SPeter Klausler     actualAttrs.reset(Attr::Pure);
12903bfe9074SPeter Klausler   }
129195f4ca7fSPeter Klausler   if (!attrs.test(Attr::Elemental) && specificIntrinsic) {
129262d874f2SPeter Klausler     actualAttrs.reset(Attr::Elemental);
129362d874f2SPeter Klausler   }
1294b09c8905SPeter Klausler   Attrs differences{attrs ^ actualAttrs};
1295b09c8905SPeter Klausler   differences.reset(Attr::Subroutine); // dealt with specifically later
12961c530b3dSPeter Klausler   if (ignoreImplicitVsExplicit) {
12971c530b3dSPeter Klausler     differences.reset(Attr::ImplicitInterface);
12981c530b3dSPeter Klausler   }
1299b09c8905SPeter Klausler   if (!differences.empty()) {
130062d874f2SPeter Klausler     if (whyNot) {
1301b09c8905SPeter Klausler       auto sep{": "s};
130262d874f2SPeter Klausler       *whyNot = "incompatible procedure attributes";
1303b09c8905SPeter Klausler       differences.IterateOverMembers([&](Attr x) {
1304bcba39a5SPeter Klausler         *whyNot += sep + std::string{EnumToString(x)};
1305b09c8905SPeter Klausler         sep = ", ";
1306b09c8905SPeter Klausler       });
130762d874f2SPeter Klausler     }
130862d874f2SPeter Klausler   } else if ((IsFunction() && actual.IsSubroutine()) ||
130962d874f2SPeter Klausler       (IsSubroutine() && actual.IsFunction())) {
131062d874f2SPeter Klausler     if (whyNot) {
131162d874f2SPeter Klausler       *whyNot =
131262d874f2SPeter Klausler           "incompatible procedures: one is a function, the other a subroutine";
131362d874f2SPeter Klausler     }
131462d874f2SPeter Klausler   } else if (functionResult && actual.functionResult &&
131562d874f2SPeter Klausler       !functionResult->IsCompatibleWith(*actual.functionResult, whyNot)) {
1316f513bd80SPeter Klausler   } else if (cudaSubprogramAttrs != actual.cudaSubprogramAttrs) {
1317f513bd80SPeter Klausler     if (whyNot) {
1318f513bd80SPeter Klausler       *whyNot = "incompatible CUDA subprogram attributes";
1319f513bd80SPeter Klausler     }
13203bfe9074SPeter Klausler   } else if (dummyArguments.size() != actual.dummyArguments.size()) {
132162d874f2SPeter Klausler     if (whyNot) {
132262d874f2SPeter Klausler       *whyNot = "distinct numbers of dummy arguments";
132362d874f2SPeter Klausler     }
13243bfe9074SPeter Klausler   } else {
13253bfe9074SPeter Klausler     for (std::size_t j{0}; j < dummyArguments.size(); ++j) {
1326036701a1SPeter Klausler       // Subtlety: the dummy/actual distinction must be reversed for this
1327036701a1SPeter Klausler       // compatibility test in order to correctly check extended vs.
1328036701a1SPeter Klausler       // base types.  Example:
1329036701a1SPeter Klausler       //   subroutine s1(base); subroutine s2(extended)
1330036701a1SPeter Klausler       //   procedure(s1), pointer :: p
1331036701a1SPeter Klausler       //   p => s2 ! an error, s2 is more restricted, can't handle "base"
1332e86591b3SPeter Klausler       std::optional<std::string> gotWarning;
1333036701a1SPeter Klausler       if (!actual.dummyArguments[j].IsCompatibleWith(
1334e86591b3SPeter Klausler               dummyArguments[j], whyNot, warning ? &gotWarning : nullptr)) {
133562d874f2SPeter Klausler         if (whyNot) {
133662d874f2SPeter Klausler           *whyNot = "incompatible dummy argument #"s + std::to_string(j + 1) +
133762d874f2SPeter Klausler               ": "s + *whyNot;
133862d874f2SPeter Klausler         }
13393bfe9074SPeter Klausler         return false;
1340e86591b3SPeter Klausler       } else if (warning && !*warning && gotWarning) {
1341e86591b3SPeter Klausler         *warning = "possibly incompatible dummy argument #"s +
1342e86591b3SPeter Klausler             std::to_string(j + 1) + ": "s + std::move(*gotWarning);
13433bfe9074SPeter Klausler       }
13443bfe9074SPeter Klausler     }
13453bfe9074SPeter Klausler     return true;
13463bfe9074SPeter Klausler   }
134762d874f2SPeter Klausler   return false;
13483bfe9074SPeter Klausler }
13493bfe9074SPeter Klausler 
13506d2b23c4SPeter Klausler std::optional<int> Procedure::FindPassIndex(
13516d2b23c4SPeter Klausler     std::optional<parser::CharBlock> name) const {
135264ab3302SCarolineConcatto   int argCount{static_cast<int>(dummyArguments.size())};
135364ab3302SCarolineConcatto   if (name) {
13546d2b23c4SPeter Klausler     for (int index{0}; index < argCount; ++index) {
13556d2b23c4SPeter Klausler       if (*name == dummyArguments[index].name.c_str()) {
135664ab3302SCarolineConcatto         return index;
135764ab3302SCarolineConcatto       }
13586d2b23c4SPeter Klausler     }
13596d2b23c4SPeter Klausler     return std::nullopt;
13606d2b23c4SPeter Klausler   } else if (argCount > 0) {
13616d2b23c4SPeter Klausler     return 0;
13626d2b23c4SPeter Klausler   } else {
13636d2b23c4SPeter Klausler     return std::nullopt;
13646d2b23c4SPeter Klausler   }
13656d2b23c4SPeter Klausler }
136664ab3302SCarolineConcatto 
136764ab3302SCarolineConcatto bool Procedure::CanOverride(
136864ab3302SCarolineConcatto     const Procedure &that, std::optional<int> passIndex) const {
136964ab3302SCarolineConcatto   // A pure procedure may override an impure one (7.5.7.3(2))
137064ab3302SCarolineConcatto   if ((that.attrs.test(Attr::Pure) && !attrs.test(Attr::Pure)) ||
137164ab3302SCarolineConcatto       that.attrs.test(Attr::Elemental) != attrs.test(Attr::Elemental) ||
137264ab3302SCarolineConcatto       functionResult != that.functionResult) {
137364ab3302SCarolineConcatto     return false;
137464ab3302SCarolineConcatto   }
137564ab3302SCarolineConcatto   int argCount{static_cast<int>(dummyArguments.size())};
137664ab3302SCarolineConcatto   if (argCount != static_cast<int>(that.dummyArguments.size())) {
137764ab3302SCarolineConcatto     return false;
137864ab3302SCarolineConcatto   }
137964ab3302SCarolineConcatto   for (int j{0}; j < argCount; ++j) {
1380a3c6a7d5SPeter Klausler     if (passIndex && j == *passIndex) {
1381a3c6a7d5SPeter Klausler       if (!that.dummyArguments[j].IsCompatibleWith(dummyArguments[j])) {
1382a3c6a7d5SPeter Klausler         return false;
1383a3c6a7d5SPeter Klausler       }
1384a3c6a7d5SPeter Klausler     } else if (dummyArguments[j] != that.dummyArguments[j]) {
138564ab3302SCarolineConcatto       return false;
138664ab3302SCarolineConcatto     }
138764ab3302SCarolineConcatto   }
138864ab3302SCarolineConcatto   return true;
138964ab3302SCarolineConcatto }
139064ab3302SCarolineConcatto 
139164ab3302SCarolineConcatto std::optional<Procedure> Procedure::Characterize(
1392cb263919SPeter Klausler     const semantics::Symbol &symbol, FoldingContext &context) {
13930d8331c0Speter klausler   semantics::UnorderedSymbolSet seenProcs;
1394cb263919SPeter Klausler   return CharacterizeProcedure(symbol, context, seenProcs, /*emitError=*/true);
139564ab3302SCarolineConcatto }
139664ab3302SCarolineConcatto 
139764ab3302SCarolineConcatto std::optional<Procedure> Procedure::Characterize(
1398cb263919SPeter Klausler     const ProcedureDesignator &proc, FoldingContext &context, bool emitError) {
139964ab3302SCarolineConcatto   if (const auto *symbol{proc.GetSymbol()}) {
1400cb263919SPeter Klausler     semantics::UnorderedSymbolSet seenProcs;
1401cb263919SPeter Klausler     return CharacterizeProcedure(*symbol, context, seenProcs, emitError);
140264ab3302SCarolineConcatto   } else if (const auto *intrinsic{proc.GetSpecificIntrinsic()}) {
140364ab3302SCarolineConcatto     return intrinsic->characteristics.value();
1404cb263919SPeter Klausler   } else {
140564ab3302SCarolineConcatto     return std::nullopt;
140664ab3302SCarolineConcatto   }
1407cb263919SPeter Klausler }
140864ab3302SCarolineConcatto 
140964ab3302SCarolineConcatto std::optional<Procedure> Procedure::Characterize(
1410641ede93Speter klausler     const ProcedureRef &ref, FoldingContext &context) {
1411cb263919SPeter Klausler   if (auto callee{Characterize(ref.proc(), context, /*emitError=*/true)}) {
141264ab3302SCarolineConcatto     if (callee->functionResult) {
141364ab3302SCarolineConcatto       if (const Procedure *
141464ab3302SCarolineConcatto           proc{callee->functionResult->IsProcedurePointer()}) {
141564ab3302SCarolineConcatto         return {*proc};
141664ab3302SCarolineConcatto       }
141764ab3302SCarolineConcatto     }
141864ab3302SCarolineConcatto   }
141964ab3302SCarolineConcatto   return std::nullopt;
142064ab3302SCarolineConcatto }
142164ab3302SCarolineConcatto 
1422f025e411SPeter Klausler std::optional<Procedure> Procedure::Characterize(
1423f025e411SPeter Klausler     const Expr<SomeType> &expr, FoldingContext &context) {
1424f025e411SPeter Klausler   if (const auto *procRef{UnwrapProcedureRef(expr)}) {
1425f025e411SPeter Klausler     return Characterize(*procRef, context);
1426f025e411SPeter Klausler   } else if (const auto *procDesignator{
1427f025e411SPeter Klausler                  std::get_if<ProcedureDesignator>(&expr.u)}) {
1428cb263919SPeter Klausler     return Characterize(*procDesignator, context, /*emitError=*/true);
1429f025e411SPeter Klausler   } else if (const Symbol * symbol{UnwrapWholeSymbolOrComponentDataRef(expr)}) {
1430f025e411SPeter Klausler     return Characterize(*symbol, context);
1431f025e411SPeter Klausler   } else {
1432f025e411SPeter Klausler     context.messages().Say(
1433f025e411SPeter Klausler         "Expression '%s' is not a procedure"_err_en_US, expr.AsFortran());
1434f025e411SPeter Klausler     return std::nullopt;
1435f025e411SPeter Klausler   }
1436f025e411SPeter Klausler }
1437f025e411SPeter Klausler 
1438bdbebef8SPeter Klausler std::optional<Procedure> Procedure::FromActuals(const ProcedureDesignator &proc,
1439bdbebef8SPeter Klausler     const ActualArguments &args, FoldingContext &context) {
1440cb263919SPeter Klausler   auto callee{Characterize(proc, context, /*emitError=*/true)};
1441bdbebef8SPeter Klausler   if (callee) {
1442bdbebef8SPeter Klausler     if (callee->dummyArguments.empty() &&
1443bdbebef8SPeter Klausler         callee->attrs.test(Procedure::Attr::ImplicitInterface)) {
1444bdbebef8SPeter Klausler       int j{0};
1445bdbebef8SPeter Klausler       for (const auto &arg : args) {
1446bdbebef8SPeter Klausler         ++j;
1447bdbebef8SPeter Klausler         if (arg) {
144829fd3e2aSPeter Klausler           if (auto dummy{DummyArgument::FromActual("x"s + std::to_string(j),
144929fd3e2aSPeter Klausler                   *arg, context,
145029fd3e2aSPeter Klausler                   /*forImplicitInterface=*/true)}) {
1451bdbebef8SPeter Klausler             callee->dummyArguments.emplace_back(std::move(*dummy));
1452bdbebef8SPeter Klausler             continue;
1453bdbebef8SPeter Klausler           }
1454bdbebef8SPeter Klausler         }
1455bdbebef8SPeter Klausler         callee.reset();
1456bdbebef8SPeter Klausler         break;
1457bdbebef8SPeter Klausler       }
1458bdbebef8SPeter Klausler     }
1459bdbebef8SPeter Klausler   }
1460bdbebef8SPeter Klausler   return callee;
1461bdbebef8SPeter Klausler }
1462bdbebef8SPeter Klausler 
14636e0a2031SPeter Klausler bool Procedure::CanBeCalledViaImplicitInterface(std::string *whyNot) const {
14646e0a2031SPeter Klausler   if (attrs.test(Attr::Elemental)) {
14656e0a2031SPeter Klausler     if (whyNot) {
14666e0a2031SPeter Klausler       *whyNot = "the procedure is elemental";
14676e0a2031SPeter Klausler     }
14686e0a2031SPeter Klausler     return false; // 15.4.2.2(5,6)
14696e0a2031SPeter Klausler   } else if (attrs.test(Attr::BindC)) {
14706e0a2031SPeter Klausler     if (whyNot) {
14716e0a2031SPeter Klausler       *whyNot = "the procedure is BIND(C)";
14726e0a2031SPeter Klausler     }
147364ab3302SCarolineConcatto     return false; // 15.4.2.2(5,6)
1474f513bd80SPeter Klausler   } else if (cudaSubprogramAttrs &&
1475f513bd80SPeter Klausler       *cudaSubprogramAttrs != common::CUDASubprogramAttrs::Host &&
1476f513bd80SPeter Klausler       *cudaSubprogramAttrs != common::CUDASubprogramAttrs::Global) {
14776e0a2031SPeter Klausler     if (whyNot) {
14786e0a2031SPeter Klausler       *whyNot = "the procedure is CUDA but neither HOST nor GLOBAL";
14796e0a2031SPeter Klausler     }
1480f513bd80SPeter Klausler     return false;
148164ab3302SCarolineConcatto   } else if (IsFunction() &&
14826e0a2031SPeter Klausler       !functionResult->CanBeReturnedViaImplicitInterface(whyNot)) {
148364ab3302SCarolineConcatto     return false;
148464ab3302SCarolineConcatto   } else {
148564ab3302SCarolineConcatto     for (const DummyArgument &arg : dummyArguments) {
14866e0a2031SPeter Klausler       if (!arg.CanBePassedViaImplicitInterface(whyNot)) {
148764ab3302SCarolineConcatto         return false;
148864ab3302SCarolineConcatto       }
148964ab3302SCarolineConcatto     }
149064ab3302SCarolineConcatto     return true;
149164ab3302SCarolineConcatto   }
149264ab3302SCarolineConcatto }
149364ab3302SCarolineConcatto 
14948670e499SCaroline Concatto llvm::raw_ostream &Procedure::Dump(llvm::raw_ostream &o) const {
149564ab3302SCarolineConcatto   attrs.Dump(o, EnumToString);
149664ab3302SCarolineConcatto   if (functionResult) {
149764ab3302SCarolineConcatto     functionResult->Dump(o << "TYPE(") << ") FUNCTION";
149862d874f2SPeter Klausler   } else if (attrs.test(Attr::Subroutine)) {
149964ab3302SCarolineConcatto     o << "SUBROUTINE";
150062d874f2SPeter Klausler   } else {
150162d874f2SPeter Klausler     o << "EXTERNAL";
150264ab3302SCarolineConcatto   }
150364ab3302SCarolineConcatto   char sep{'('};
150464ab3302SCarolineConcatto   for (const auto &dummy : dummyArguments) {
150564ab3302SCarolineConcatto     dummy.Dump(o << sep);
150664ab3302SCarolineConcatto     sep = ',';
150764ab3302SCarolineConcatto   }
1508f513bd80SPeter Klausler   o << (sep == '(' ? "()" : ")");
1509f513bd80SPeter Klausler   if (cudaSubprogramAttrs) {
1510f513bd80SPeter Klausler     o << " cudaSubprogramAttrs: " << common::EnumToString(*cudaSubprogramAttrs);
1511f513bd80SPeter Klausler   }
1512f513bd80SPeter Klausler   return o;
151364ab3302SCarolineConcatto }
151464ab3302SCarolineConcatto 
151564ab3302SCarolineConcatto // Utility class to determine if Procedures, etc. are distinguishable
151664ab3302SCarolineConcatto class DistinguishUtils {
151764ab3302SCarolineConcatto public:
1518c4ba1108Speter klausler   explicit DistinguishUtils(const common::LanguageFeatureControl &features)
1519c4ba1108Speter klausler       : features_{features} {}
1520c4ba1108Speter klausler 
152164ab3302SCarolineConcatto   // Are these procedures distinguishable for a generic name?
15223d115700SPeter Klausler   std::optional<bool> Distinguishable(
15233d115700SPeter Klausler       const Procedure &, const Procedure &) const;
152464ab3302SCarolineConcatto   // Are these procedures distinguishable for a generic operator or assignment?
15253d115700SPeter Klausler   std::optional<bool> DistinguishableOpOrAssign(
15263d115700SPeter Klausler       const Procedure &, const Procedure &) const;
152764ab3302SCarolineConcatto 
152864ab3302SCarolineConcatto private:
152964ab3302SCarolineConcatto   struct CountDummyProcedures {
153064ab3302SCarolineConcatto     CountDummyProcedures(const DummyArguments &args) {
153164ab3302SCarolineConcatto       for (const DummyArgument &arg : args) {
153264ab3302SCarolineConcatto         if (std::holds_alternative<DummyProcedure>(arg.u)) {
153364ab3302SCarolineConcatto           total += 1;
153464ab3302SCarolineConcatto           notOptional += !arg.IsOptional();
153564ab3302SCarolineConcatto         }
153664ab3302SCarolineConcatto       }
153764ab3302SCarolineConcatto     }
153864ab3302SCarolineConcatto     int total{0};
153964ab3302SCarolineConcatto     int notOptional{0};
154064ab3302SCarolineConcatto   };
154164ab3302SCarolineConcatto 
15423d115700SPeter Klausler   bool AnyOptionalData(const DummyArguments &) const;
15433d115700SPeter Klausler   bool AnyUnlimitedPolymorphicData(const DummyArguments &) const;
1544c4ba1108Speter klausler   bool Rule3Distinguishable(const Procedure &, const Procedure &) const;
1545c4ba1108Speter klausler   const DummyArgument *Rule1DistinguishingArg(
1546c4ba1108Speter klausler       const DummyArguments &, const DummyArguments &) const;
1547c4ba1108Speter klausler   int FindFirstToDistinguishByPosition(
1548c4ba1108Speter klausler       const DummyArguments &, const DummyArguments &) const;
1549c4ba1108Speter klausler   int FindLastToDistinguishByName(
1550c4ba1108Speter klausler       const DummyArguments &, const DummyArguments &) const;
1551c4ba1108Speter klausler   int CountCompatibleWith(const DummyArgument &, const DummyArguments &) const;
1552c4ba1108Speter klausler   int CountNotDistinguishableFrom(
1553c4ba1108Speter klausler       const DummyArgument &, const DummyArguments &) const;
1554c4ba1108Speter klausler   bool Distinguishable(const DummyArgument &, const DummyArgument &) const;
1555c4ba1108Speter klausler   bool Distinguishable(const DummyDataObject &, const DummyDataObject &) const;
1556c4ba1108Speter klausler   bool Distinguishable(const DummyProcedure &, const DummyProcedure &) const;
1557c4ba1108Speter klausler   bool Distinguishable(const FunctionResult &, const FunctionResult &) const;
1558864cb2aaSPeter Klausler   bool Distinguishable(
1559864cb2aaSPeter Klausler       const TypeAndShape &, const TypeAndShape &, common::IgnoreTKRSet) const;
1560c4ba1108Speter klausler   bool IsTkrCompatible(const DummyArgument &, const DummyArgument &) const;
1561864cb2aaSPeter Klausler   bool IsTkCompatible(const DummyDataObject &, const DummyDataObject &) const;
1562c4ba1108Speter klausler   const DummyArgument *GetAtEffectivePosition(
1563c4ba1108Speter klausler       const DummyArguments &, int) const;
1564c4ba1108Speter klausler   const DummyArgument *GetPassArg(const Procedure &) const;
1565c4ba1108Speter klausler 
1566c4ba1108Speter klausler   const common::LanguageFeatureControl &features_;
156764ab3302SCarolineConcatto };
156864ab3302SCarolineConcatto 
156964ab3302SCarolineConcatto // Simpler distinguishability rules for operators and assignment
15703d115700SPeter Klausler std::optional<bool> DistinguishUtils::DistinguishableOpOrAssign(
1571c4ba1108Speter klausler     const Procedure &proc1, const Procedure &proc2) const {
1572042c964dSPeter Klausler   if ((proc1.IsFunction() && proc2.IsSubroutine()) ||
1573042c964dSPeter Klausler       (proc1.IsSubroutine() && proc2.IsFunction())) {
1574042c964dSPeter Klausler     return true;
1575042c964dSPeter Klausler   }
157664ab3302SCarolineConcatto   auto &args1{proc1.dummyArguments};
157764ab3302SCarolineConcatto   auto &args2{proc2.dummyArguments};
157864ab3302SCarolineConcatto   if (args1.size() != args2.size()) {
157964ab3302SCarolineConcatto     return true; // C1511: distinguishable based on number of arguments
158064ab3302SCarolineConcatto   }
158164ab3302SCarolineConcatto   for (std::size_t i{0}; i < args1.size(); ++i) {
158264ab3302SCarolineConcatto     if (Distinguishable(args1[i], args2[i])) {
158364ab3302SCarolineConcatto       return true; // C1511, C1512: distinguishable based on this arg
158464ab3302SCarolineConcatto     }
158564ab3302SCarolineConcatto   }
158664ab3302SCarolineConcatto   return false;
158764ab3302SCarolineConcatto }
158864ab3302SCarolineConcatto 
15893d115700SPeter Klausler std::optional<bool> DistinguishUtils::Distinguishable(
1590c4ba1108Speter klausler     const Procedure &proc1, const Procedure &proc2) const {
1591042c964dSPeter Klausler   if ((proc1.IsFunction() && proc2.IsSubroutine()) ||
1592042c964dSPeter Klausler       (proc1.IsSubroutine() && proc2.IsFunction())) {
1593042c964dSPeter Klausler     return true;
1594042c964dSPeter Klausler   }
159564ab3302SCarolineConcatto   auto &args1{proc1.dummyArguments};
159664ab3302SCarolineConcatto   auto &args2{proc2.dummyArguments};
159764ab3302SCarolineConcatto   auto count1{CountDummyProcedures(args1)};
159864ab3302SCarolineConcatto   auto count2{CountDummyProcedures(args2)};
159964ab3302SCarolineConcatto   if (count1.notOptional > count2.total || count2.notOptional > count1.total) {
160064ab3302SCarolineConcatto     return true; // distinguishable based on C1514 rule 2
160164ab3302SCarolineConcatto   }
160264ab3302SCarolineConcatto   if (Rule3Distinguishable(proc1, proc2)) {
160364ab3302SCarolineConcatto     return true; // distinguishable based on C1514 rule 3
160464ab3302SCarolineConcatto   }
160564ab3302SCarolineConcatto   if (Rule1DistinguishingArg(args1, args2)) {
160664ab3302SCarolineConcatto     return true; // distinguishable based on C1514 rule 1
160764ab3302SCarolineConcatto   }
160864ab3302SCarolineConcatto   int pos1{FindFirstToDistinguishByPosition(args1, args2)};
160964ab3302SCarolineConcatto   int name1{FindLastToDistinguishByName(args1, args2)};
161064ab3302SCarolineConcatto   if (pos1 >= 0 && pos1 <= name1) {
161164ab3302SCarolineConcatto     return true; // distinguishable based on C1514 rule 4
161264ab3302SCarolineConcatto   }
161364ab3302SCarolineConcatto   int pos2{FindFirstToDistinguishByPosition(args2, args1)};
161464ab3302SCarolineConcatto   int name2{FindLastToDistinguishByName(args2, args1)};
161564ab3302SCarolineConcatto   if (pos2 >= 0 && pos2 <= name2) {
161664ab3302SCarolineConcatto     return true; // distinguishable based on C1514 rule 4
161764ab3302SCarolineConcatto   }
1618f513bd80SPeter Klausler   if (proc1.cudaSubprogramAttrs != proc2.cudaSubprogramAttrs) {
1619f513bd80SPeter Klausler     return true;
1620f513bd80SPeter Klausler   }
16213d115700SPeter Klausler   // If there are no optional or unlimited polymorphic dummy arguments,
16223d115700SPeter Klausler   // then we know the result for sure; otherwise, it's possible for
16233d115700SPeter Klausler   // the procedures to be unambiguous.
16243d115700SPeter Klausler   if ((AnyOptionalData(args1) || AnyUnlimitedPolymorphicData(args1)) &&
16253d115700SPeter Klausler       (AnyOptionalData(args2) || AnyUnlimitedPolymorphicData(args2))) {
16263d115700SPeter Klausler     return std::nullopt; // meaning "maybe"
16273d115700SPeter Klausler   } else {
16283d115700SPeter Klausler     return false;
16293d115700SPeter Klausler   }
16303d115700SPeter Klausler }
16313d115700SPeter Klausler 
16323d115700SPeter Klausler bool DistinguishUtils::AnyOptionalData(const DummyArguments &args) const {
16333d115700SPeter Klausler   for (const auto &arg : args) {
16343d115700SPeter Klausler     if (std::holds_alternative<DummyDataObject>(arg.u) && arg.IsOptional()) {
16353d115700SPeter Klausler       return true;
16363d115700SPeter Klausler     }
16373d115700SPeter Klausler   }
16383d115700SPeter Klausler   return false;
16393d115700SPeter Klausler }
16403d115700SPeter Klausler 
16413d115700SPeter Klausler bool DistinguishUtils::AnyUnlimitedPolymorphicData(
16423d115700SPeter Klausler     const DummyArguments &args) const {
16433d115700SPeter Klausler   for (const auto &arg : args) {
16443d115700SPeter Klausler     if (const auto *object{std::get_if<DummyDataObject>(&arg.u)}) {
16453d115700SPeter Klausler       if (object->type.type().IsUnlimitedPolymorphic()) {
16463d115700SPeter Klausler         return true;
16473d115700SPeter Klausler       }
16483d115700SPeter Klausler     }
16493d115700SPeter Klausler   }
165064ab3302SCarolineConcatto   return false;
165164ab3302SCarolineConcatto }
165264ab3302SCarolineConcatto 
165364ab3302SCarolineConcatto // C1514 rule 3: Procedures are distinguishable if both have a passed-object
165464ab3302SCarolineConcatto // dummy argument and those are distinguishable.
165564ab3302SCarolineConcatto bool DistinguishUtils::Rule3Distinguishable(
1656c4ba1108Speter klausler     const Procedure &proc1, const Procedure &proc2) const {
165764ab3302SCarolineConcatto   const DummyArgument *pass1{GetPassArg(proc1)};
165864ab3302SCarolineConcatto   const DummyArgument *pass2{GetPassArg(proc2)};
165964ab3302SCarolineConcatto   return pass1 && pass2 && Distinguishable(*pass1, *pass2);
166064ab3302SCarolineConcatto }
166164ab3302SCarolineConcatto 
166264ab3302SCarolineConcatto // Find a non-passed-object dummy data object in one of the argument lists
166364ab3302SCarolineConcatto // that satisfies C1514 rule 1. I.e. x such that:
166464ab3302SCarolineConcatto // - m is the number of dummy data objects in one that are nonoptional,
166564ab3302SCarolineConcatto //   are not passed-object, that x is TKR compatible with
166664ab3302SCarolineConcatto // - n is the number of non-passed-object dummy data objects, in the other
166764ab3302SCarolineConcatto //   that are not distinguishable from x
166864ab3302SCarolineConcatto // - m is greater than n
166964ab3302SCarolineConcatto const DummyArgument *DistinguishUtils::Rule1DistinguishingArg(
1670c4ba1108Speter klausler     const DummyArguments &args1, const DummyArguments &args2) const {
167164ab3302SCarolineConcatto   auto size1{args1.size()};
167264ab3302SCarolineConcatto   auto size2{args2.size()};
167364ab3302SCarolineConcatto   for (std::size_t i{0}; i < size1 + size2; ++i) {
167464ab3302SCarolineConcatto     const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]};
167564ab3302SCarolineConcatto     if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) {
167664ab3302SCarolineConcatto       if (CountCompatibleWith(x, args1) >
167764ab3302SCarolineConcatto               CountNotDistinguishableFrom(x, args2) ||
167864ab3302SCarolineConcatto           CountCompatibleWith(x, args2) >
167964ab3302SCarolineConcatto               CountNotDistinguishableFrom(x, args1)) {
168064ab3302SCarolineConcatto         return &x;
168164ab3302SCarolineConcatto       }
168264ab3302SCarolineConcatto     }
168364ab3302SCarolineConcatto   }
168464ab3302SCarolineConcatto   return nullptr;
168564ab3302SCarolineConcatto }
168664ab3302SCarolineConcatto 
168764ab3302SCarolineConcatto // Find the index of the first nonoptional non-passed-object dummy argument
168864ab3302SCarolineConcatto // in args1 at an effective position such that either:
168964ab3302SCarolineConcatto // - args2 has no dummy argument at that effective position
169064ab3302SCarolineConcatto // - the dummy argument at that position is distinguishable from it
169164ab3302SCarolineConcatto int DistinguishUtils::FindFirstToDistinguishByPosition(
1692c4ba1108Speter klausler     const DummyArguments &args1, const DummyArguments &args2) const {
169364ab3302SCarolineConcatto   int effective{0}; // position of arg1 in list, ignoring passed arg
169464ab3302SCarolineConcatto   for (std::size_t i{0}; i < args1.size(); ++i) {
169564ab3302SCarolineConcatto     const DummyArgument &arg1{args1.at(i)};
169664ab3302SCarolineConcatto     if (!arg1.pass && !arg1.IsOptional()) {
169764ab3302SCarolineConcatto       const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)};
169864ab3302SCarolineConcatto       if (!arg2 || Distinguishable(arg1, *arg2)) {
169964ab3302SCarolineConcatto         return i;
170064ab3302SCarolineConcatto       }
170164ab3302SCarolineConcatto     }
170264ab3302SCarolineConcatto     effective += !arg1.pass;
170364ab3302SCarolineConcatto   }
170464ab3302SCarolineConcatto   return -1;
170564ab3302SCarolineConcatto }
170664ab3302SCarolineConcatto 
170764ab3302SCarolineConcatto // Find the index of the last nonoptional non-passed-object dummy argument
170864ab3302SCarolineConcatto // in args1 whose name is such that either:
170964ab3302SCarolineConcatto // - args2 has no dummy argument with that name
171064ab3302SCarolineConcatto // - the dummy argument with that name is distinguishable from it
171164ab3302SCarolineConcatto int DistinguishUtils::FindLastToDistinguishByName(
1712c4ba1108Speter klausler     const DummyArguments &args1, const DummyArguments &args2) const {
171364ab3302SCarolineConcatto   std::map<std::string, const DummyArgument *> nameToArg;
171464ab3302SCarolineConcatto   for (const auto &arg2 : args2) {
171564ab3302SCarolineConcatto     nameToArg.emplace(arg2.name, &arg2);
171664ab3302SCarolineConcatto   }
171764ab3302SCarolineConcatto   for (int i = args1.size() - 1; i >= 0; --i) {
171864ab3302SCarolineConcatto     const DummyArgument &arg1{args1.at(i)};
171964ab3302SCarolineConcatto     if (!arg1.pass && !arg1.IsOptional()) {
172064ab3302SCarolineConcatto       auto it{nameToArg.find(arg1.name)};
172164ab3302SCarolineConcatto       if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) {
172264ab3302SCarolineConcatto         return i;
172364ab3302SCarolineConcatto       }
172464ab3302SCarolineConcatto     }
172564ab3302SCarolineConcatto   }
172664ab3302SCarolineConcatto   return -1;
172764ab3302SCarolineConcatto }
172864ab3302SCarolineConcatto 
172964ab3302SCarolineConcatto // Count the dummy data objects in args that are nonoptional, are not
173064ab3302SCarolineConcatto // passed-object, and that x is TKR compatible with
173164ab3302SCarolineConcatto int DistinguishUtils::CountCompatibleWith(
1732c4ba1108Speter klausler     const DummyArgument &x, const DummyArguments &args) const {
17333850edd9SKazu Hirata   return llvm::count_if(args, [&](const DummyArgument &y) {
173464ab3302SCarolineConcatto     return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y);
173564ab3302SCarolineConcatto   });
173664ab3302SCarolineConcatto }
173764ab3302SCarolineConcatto 
173864ab3302SCarolineConcatto // Return the number of dummy data objects in args that are not
173964ab3302SCarolineConcatto // distinguishable from x and not passed-object.
174064ab3302SCarolineConcatto int DistinguishUtils::CountNotDistinguishableFrom(
1741c4ba1108Speter klausler     const DummyArgument &x, const DummyArguments &args) const {
17423850edd9SKazu Hirata   return llvm::count_if(args, [&](const DummyArgument &y) {
174364ab3302SCarolineConcatto     return !y.pass && std::holds_alternative<DummyDataObject>(y.u) &&
174464ab3302SCarolineConcatto         !Distinguishable(y, x);
174564ab3302SCarolineConcatto   });
174664ab3302SCarolineConcatto }
174764ab3302SCarolineConcatto 
174864ab3302SCarolineConcatto bool DistinguishUtils::Distinguishable(
1749c4ba1108Speter klausler     const DummyArgument &x, const DummyArgument &y) const {
175064ab3302SCarolineConcatto   if (x.u.index() != y.u.index()) {
175164ab3302SCarolineConcatto     return true; // different kind: data/proc/alt-return
175264ab3302SCarolineConcatto   }
1753cd03e96fSPeter Klausler   return common::visit(
175464ab3302SCarolineConcatto       common::visitors{
175564ab3302SCarolineConcatto           [&](const DummyDataObject &z) {
175664ab3302SCarolineConcatto             return Distinguishable(z, std::get<DummyDataObject>(y.u));
175764ab3302SCarolineConcatto           },
175864ab3302SCarolineConcatto           [&](const DummyProcedure &z) {
175964ab3302SCarolineConcatto             return Distinguishable(z, std::get<DummyProcedure>(y.u));
176064ab3302SCarolineConcatto           },
176164ab3302SCarolineConcatto           [&](const AlternateReturn &) { return false; },
176264ab3302SCarolineConcatto       },
176364ab3302SCarolineConcatto       x.u);
176464ab3302SCarolineConcatto }
176564ab3302SCarolineConcatto 
176664ab3302SCarolineConcatto bool DistinguishUtils::Distinguishable(
1767c4ba1108Speter klausler     const DummyDataObject &x, const DummyDataObject &y) const {
176864ab3302SCarolineConcatto   using Attr = DummyDataObject::Attr;
1769864cb2aaSPeter Klausler   if (Distinguishable(x.type, y.type, x.ignoreTKR | y.ignoreTKR)) {
177064ab3302SCarolineConcatto     return true;
177164ab3302SCarolineConcatto   } else if (x.attrs.test(Attr::Allocatable) && y.attrs.test(Attr::Pointer) &&
177264ab3302SCarolineConcatto       y.intent != common::Intent::In) {
177364ab3302SCarolineConcatto     return true;
177464ab3302SCarolineConcatto   } else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) &&
177564ab3302SCarolineConcatto       x.intent != common::Intent::In) {
177664ab3302SCarolineConcatto     return true;
17773e930864SValentin Clement   } else if (!common::AreCompatibleCUDADataAttrs(x.cudaDataAttr, y.cudaDataAttr,
177830d80009SValentin Clement (バレンタイン クレメン)                  x.ignoreTKR | y.ignoreTKR, nullptr,
17793e930864SValentin Clement                  /*allowUnifiedMatchingRule=*/false)) {
1780f513bd80SPeter Klausler     return true;
1781c4ba1108Speter klausler   } else if (features_.IsEnabled(
1782c4ba1108Speter klausler                  common::LanguageFeature::DistinguishableSpecifics) &&
1783c4ba1108Speter klausler       (x.attrs.test(Attr::Allocatable) || x.attrs.test(Attr::Pointer)) &&
1784c4ba1108Speter klausler       (y.attrs.test(Attr::Allocatable) || y.attrs.test(Attr::Pointer)) &&
1785c4ba1108Speter klausler       (x.type.type().IsUnlimitedPolymorphic() !=
1786c4ba1108Speter klausler               y.type.type().IsUnlimitedPolymorphic() ||
1787c4ba1108Speter klausler           x.type.type().IsPolymorphic() != y.type.type().IsPolymorphic())) {
1788c4ba1108Speter klausler     // Extension: Per 15.5.2.5(2), an allocatable/pointer dummy and its
1789c4ba1108Speter klausler     // corresponding actual argument must both or neither be polymorphic,
1790c4ba1108Speter klausler     // and must both or neither be unlimited polymorphic.  So when exactly
1791c4ba1108Speter klausler     // one of two dummy arguments is polymorphic or unlimited polymorphic,
1792c4ba1108Speter klausler     // any actual argument that is admissible to one of them cannot also match
1793c4ba1108Speter klausler     // the other one.
1794c4ba1108Speter klausler     return true;
179564ab3302SCarolineConcatto   } else {
179664ab3302SCarolineConcatto     return false;
179764ab3302SCarolineConcatto   }
179864ab3302SCarolineConcatto }
179964ab3302SCarolineConcatto 
180064ab3302SCarolineConcatto bool DistinguishUtils::Distinguishable(
1801c4ba1108Speter klausler     const DummyProcedure &x, const DummyProcedure &y) const {
180264ab3302SCarolineConcatto   const Procedure &xProc{x.procedure.value()};
180364ab3302SCarolineConcatto   const Procedure &yProc{y.procedure.value()};
18043d115700SPeter Klausler   if (Distinguishable(xProc, yProc).value_or(false)) {
180564ab3302SCarolineConcatto     return true;
180664ab3302SCarolineConcatto   } else {
180764ab3302SCarolineConcatto     const std::optional<FunctionResult> &xResult{xProc.functionResult};
180864ab3302SCarolineConcatto     const std::optional<FunctionResult> &yResult{yProc.functionResult};
180964ab3302SCarolineConcatto     return xResult ? !yResult || Distinguishable(*xResult, *yResult)
181064ab3302SCarolineConcatto                    : yResult.has_value();
181164ab3302SCarolineConcatto   }
181264ab3302SCarolineConcatto }
181364ab3302SCarolineConcatto 
181464ab3302SCarolineConcatto bool DistinguishUtils::Distinguishable(
1815c4ba1108Speter klausler     const FunctionResult &x, const FunctionResult &y) const {
181664ab3302SCarolineConcatto   if (x.u.index() != y.u.index()) {
181764ab3302SCarolineConcatto     return true; // one is data object, one is procedure
181864ab3302SCarolineConcatto   }
1819f513bd80SPeter Klausler   if (x.cudaDataAttr != y.cudaDataAttr) {
1820f513bd80SPeter Klausler     return true;
1821f513bd80SPeter Klausler   }
1822cd03e96fSPeter Klausler   return common::visit(
182364ab3302SCarolineConcatto       common::visitors{
182464ab3302SCarolineConcatto           [&](const TypeAndShape &z) {
1825864cb2aaSPeter Klausler             return Distinguishable(
1826864cb2aaSPeter Klausler                 z, std::get<TypeAndShape>(y.u), common::IgnoreTKRSet{});
182764ab3302SCarolineConcatto           },
182864ab3302SCarolineConcatto           [&](const CopyableIndirection<Procedure> &z) {
182964ab3302SCarolineConcatto             return Distinguishable(z.value(),
18303d115700SPeter Klausler                 std::get<CopyableIndirection<Procedure>>(y.u).value())
18313d115700SPeter Klausler                 .value_or(false);
183264ab3302SCarolineConcatto           },
183364ab3302SCarolineConcatto       },
183464ab3302SCarolineConcatto       x.u);
183564ab3302SCarolineConcatto }
183664ab3302SCarolineConcatto 
1837864cb2aaSPeter Klausler bool DistinguishUtils::Distinguishable(const TypeAndShape &x,
1838864cb2aaSPeter Klausler     const TypeAndShape &y, common::IgnoreTKRSet ignoreTKR) const {
1839864cb2aaSPeter Klausler   if (!x.type().IsTkCompatibleWith(y.type(), ignoreTKR) &&
1840864cb2aaSPeter Klausler       !y.type().IsTkCompatibleWith(x.type(), ignoreTKR)) {
1841864cb2aaSPeter Klausler     return true;
1842864cb2aaSPeter Klausler   }
1843864cb2aaSPeter Klausler   if (ignoreTKR.test(common::IgnoreTKR::Rank)) {
1844864cb2aaSPeter Klausler   } else if (x.attrs().test(TypeAndShape::Attr::AssumedRank) ||
1845864cb2aaSPeter Klausler       y.attrs().test(TypeAndShape::Attr::AssumedRank)) {
1846864cb2aaSPeter Klausler   } else if (x.Rank() != y.Rank()) {
1847864cb2aaSPeter Klausler     return true;
1848864cb2aaSPeter Klausler   }
1849864cb2aaSPeter Klausler   return false;
185064ab3302SCarolineConcatto }
185164ab3302SCarolineConcatto 
185264ab3302SCarolineConcatto // Compatibility based on type, kind, and rank
1853864cb2aaSPeter Klausler 
185464ab3302SCarolineConcatto bool DistinguishUtils::IsTkrCompatible(
1855c4ba1108Speter klausler     const DummyArgument &x, const DummyArgument &y) const {
185664ab3302SCarolineConcatto   const auto *obj1{std::get_if<DummyDataObject>(&x.u)};
185764ab3302SCarolineConcatto   const auto *obj2{std::get_if<DummyDataObject>(&y.u)};
1858864cb2aaSPeter Klausler   return obj1 && obj2 && IsTkCompatible(*obj1, *obj2) &&
1859864cb2aaSPeter Klausler       (obj1->type.Rank() == obj2->type.Rank() ||
1860864cb2aaSPeter Klausler           obj1->type.attrs().test(TypeAndShape::Attr::AssumedRank) ||
1861864cb2aaSPeter Klausler           obj2->type.attrs().test(TypeAndShape::Attr::AssumedRank) ||
1862864cb2aaSPeter Klausler           obj1->ignoreTKR.test(common::IgnoreTKR::Rank) ||
1863864cb2aaSPeter Klausler           obj2->ignoreTKR.test(common::IgnoreTKR::Rank));
186464ab3302SCarolineConcatto }
1865864cb2aaSPeter Klausler 
1866864cb2aaSPeter Klausler bool DistinguishUtils::IsTkCompatible(
1867864cb2aaSPeter Klausler     const DummyDataObject &x, const DummyDataObject &y) const {
1868864cb2aaSPeter Klausler   return x.type.type().IsTkCompatibleWith(
1869864cb2aaSPeter Klausler       y.type.type(), x.ignoreTKR | y.ignoreTKR);
187064ab3302SCarolineConcatto }
187164ab3302SCarolineConcatto 
187264ab3302SCarolineConcatto // Return the argument at the given index, ignoring the passed arg
187364ab3302SCarolineConcatto const DummyArgument *DistinguishUtils::GetAtEffectivePosition(
1874c4ba1108Speter klausler     const DummyArguments &args, int index) const {
187564ab3302SCarolineConcatto   for (const DummyArgument &arg : args) {
187664ab3302SCarolineConcatto     if (!arg.pass) {
187764ab3302SCarolineConcatto       if (index == 0) {
187864ab3302SCarolineConcatto         return &arg;
187964ab3302SCarolineConcatto       }
188064ab3302SCarolineConcatto       --index;
188164ab3302SCarolineConcatto     }
188264ab3302SCarolineConcatto   }
188364ab3302SCarolineConcatto   return nullptr;
188464ab3302SCarolineConcatto }
188564ab3302SCarolineConcatto 
188664ab3302SCarolineConcatto // Return the passed-object dummy argument of this procedure, if any
1887c4ba1108Speter klausler const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) const {
188864ab3302SCarolineConcatto   for (const auto &arg : proc.dummyArguments) {
188964ab3302SCarolineConcatto     if (arg.pass) {
189064ab3302SCarolineConcatto       return &arg;
189164ab3302SCarolineConcatto     }
189264ab3302SCarolineConcatto   }
189364ab3302SCarolineConcatto   return nullptr;
189464ab3302SCarolineConcatto }
189564ab3302SCarolineConcatto 
18963d115700SPeter Klausler std::optional<bool> Distinguishable(
18973d115700SPeter Klausler     const common::LanguageFeatureControl &features, const Procedure &x,
18983d115700SPeter Klausler     const Procedure &y) {
1899c4ba1108Speter klausler   return DistinguishUtils{features}.Distinguishable(x, y);
190064ab3302SCarolineConcatto }
190164ab3302SCarolineConcatto 
19023d115700SPeter Klausler std::optional<bool> DistinguishableOpOrAssign(
19033d115700SPeter Klausler     const common::LanguageFeatureControl &features, const Procedure &x,
19043d115700SPeter Klausler     const Procedure &y) {
1905c4ba1108Speter klausler   return DistinguishUtils{features}.DistinguishableOpOrAssign(x, y);
190664ab3302SCarolineConcatto }
190764ab3302SCarolineConcatto 
190864ab3302SCarolineConcatto DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument)
190964ab3302SCarolineConcatto DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure)
191064ab3302SCarolineConcatto DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult)
191164ab3302SCarolineConcatto DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure)
19121f879005STim Keith } // namespace Fortran::evaluate::characteristics
191364ab3302SCarolineConcatto 
191464ab3302SCarolineConcatto template class Fortran::common::Indirection<
191564ab3302SCarolineConcatto     Fortran::evaluate::characteristics::Procedure, true>;
1916