LAA: improve code in getStrideFromPointer (NFC) (#124780)
[llvm-project.git] / flang / lib / Evaluate / characteristics.cpp
blobc5470df2622a59fb1232e17a4b9c7957c16f2570
1 //===-- lib/Evaluate/characteristics.cpp ----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "flang/Evaluate/characteristics.h"
10 #include "flang/Common/indirection.h"
11 #include "flang/Evaluate/check-expression.h"
12 #include "flang/Evaluate/fold.h"
13 #include "flang/Evaluate/intrinsics.h"
14 #include "flang/Evaluate/tools.h"
15 #include "flang/Evaluate/type.h"
16 #include "flang/Parser/message.h"
17 #include "flang/Semantics/scope.h"
18 #include "flang/Semantics/symbol.h"
19 #include "flang/Semantics/tools.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include <initializer_list>
23 using namespace Fortran::parser::literals;
25 namespace Fortran::evaluate::characteristics {
27 // Copy attributes from a symbol to dst based on the mapping in pairs.
28 // An ASYNCHRONOUS attribute counts even if it is implied.
29 template <typename A, typename B>
30 static void CopyAttrs(const semantics::Symbol &src, A &dst,
31 const std::initializer_list<std::pair<semantics::Attr, B>> &pairs) {
32 for (const auto &pair : pairs) {
33 if (src.attrs().test(pair.first)) {
34 dst.attrs.set(pair.second);
39 // Shapes of function results and dummy arguments have to have
40 // the same rank, the same deferred dimensions, and the same
41 // values for explicit dimensions when constant.
42 bool ShapesAreCompatible(const std::optional<Shape> &x,
43 const std::optional<Shape> &y, bool *possibleWarning) {
44 if (!x || !y) {
45 return !x && !y;
47 if (x->size() != y->size()) {
48 return false;
50 auto yIter{y->begin()};
51 for (const auto &xDim : *x) {
52 const auto &yDim{*yIter++};
53 if (xDim && yDim) {
54 if (auto equiv{AreEquivalentInInterface(*xDim, *yDim)}) {
55 if (!*equiv) {
56 return false;
58 } else if (possibleWarning) {
59 *possibleWarning = true;
61 } else if (xDim || yDim) {
62 return false;
65 return true;
68 bool TypeAndShape::operator==(const TypeAndShape &that) const {
69 return type_.IsEquivalentTo(that.type_) &&
70 ShapesAreCompatible(shape_, that.shape_) && attrs_ == that.attrs_ &&
71 corank_ == that.corank_;
74 TypeAndShape &TypeAndShape::Rewrite(FoldingContext &context) {
75 LEN_ = Fold(context, std::move(LEN_));
76 if (LEN_) {
77 if (auto n{ToInt64(*LEN_)}) {
78 type_ = DynamicType{type_.kind(), *n};
81 shape_ = Fold(context, std::move(shape_));
82 return *this;
85 std::optional<TypeAndShape> TypeAndShape::Characterize(
86 const semantics::Symbol &symbol, FoldingContext &context,
87 bool invariantOnly) {
88 const auto &ultimate{symbol.GetUltimate()};
89 return common::visit(
90 common::visitors{
91 [&](const semantics::ProcEntityDetails &proc) {
92 if (proc.procInterface()) {
93 return Characterize(
94 *proc.procInterface(), context, invariantOnly);
95 } else if (proc.type()) {
96 return Characterize(*proc.type(), context, invariantOnly);
97 } else {
98 return std::optional<TypeAndShape>{};
101 [&](const semantics::AssocEntityDetails &assoc) {
102 return Characterize(assoc, context, invariantOnly);
104 [&](const semantics::ProcBindingDetails &binding) {
105 return Characterize(binding.symbol(), context, invariantOnly);
107 [&](const auto &x) -> std::optional<TypeAndShape> {
108 using Ty = std::decay_t<decltype(x)>;
109 if constexpr (std::is_same_v<Ty, semantics::EntityDetails> ||
110 std::is_same_v<Ty, semantics::ObjectEntityDetails> ||
111 std::is_same_v<Ty, semantics::TypeParamDetails>) {
112 if (const semantics::DeclTypeSpec * type{ultimate.GetType()}) {
113 if (auto dyType{DynamicType::From(*type)}) {
114 TypeAndShape result{std::move(*dyType),
115 GetShape(context, ultimate, invariantOnly)};
116 result.AcquireAttrs(ultimate);
117 result.AcquireLEN(ultimate);
118 return std::move(result.Rewrite(context));
122 return std::nullopt;
125 // GetUltimate() used here, not ResolveAssociations(), because
126 // we need the type/rank of an associate entity from TYPE IS,
127 // CLASS IS, or RANK statement.
128 ultimate.details());
131 std::optional<TypeAndShape> TypeAndShape::Characterize(
132 const semantics::AssocEntityDetails &assoc, FoldingContext &context,
133 bool invariantOnly) {
134 std::optional<TypeAndShape> result;
135 if (auto type{DynamicType::From(assoc.type())}) {
136 if (auto rank{assoc.rank()}) {
137 if (*rank >= 0 && *rank <= common::maxRank) {
138 result = TypeAndShape{std::move(*type), Shape(*rank)};
140 } else if (auto shape{GetShape(context, assoc.expr(), invariantOnly)}) {
141 result = TypeAndShape{std::move(*type), std::move(*shape)};
143 if (result && type->category() == TypeCategory::Character) {
144 if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(assoc.expr())}) {
145 if (auto len{chExpr->LEN()}) {
146 result->set_LEN(std::move(*len));
151 return Fold(context, std::move(result));
154 std::optional<TypeAndShape> TypeAndShape::Characterize(
155 const semantics::DeclTypeSpec &spec, FoldingContext &context,
156 bool /*invariantOnly=*/) {
157 if (auto type{DynamicType::From(spec)}) {
158 return Fold(context, TypeAndShape{std::move(*type)});
159 } else {
160 return std::nullopt;
164 std::optional<TypeAndShape> TypeAndShape::Characterize(
165 const ActualArgument &arg, FoldingContext &context, bool invariantOnly) {
166 if (const auto *expr{arg.UnwrapExpr()}) {
167 return Characterize(*expr, context, invariantOnly);
168 } else if (const Symbol * assumed{arg.GetAssumedTypeDummy()}) {
169 return Characterize(*assumed, context, invariantOnly);
170 } else {
171 return std::nullopt;
175 bool TypeAndShape::IsCompatibleWith(parser::ContextualMessages &messages,
176 const TypeAndShape &that, const char *thisIs, const char *thatIs,
177 bool omitShapeConformanceCheck,
178 enum CheckConformanceFlags::Flags flags) const {
179 if (!type_.IsTkCompatibleWith(that.type_)) {
180 messages.Say(
181 "%1$s type '%2$s' is not compatible with %3$s type '%4$s'"_err_en_US,
182 thatIs, that.AsFortran(), thisIs, AsFortran());
183 return false;
185 return omitShapeConformanceCheck || (!shape_ && !that.shape_) ||
186 (shape_ && that.shape_ &&
187 CheckConformance(
188 messages, *shape_, *that.shape_, flags, thisIs, thatIs)
189 .value_or(true /*fail only when nonconformance is known now*/));
192 std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureElementSizeInBytes(
193 FoldingContext &foldingContext, bool align) const {
194 if (LEN_) {
195 CHECK(type_.category() == TypeCategory::Character);
196 return Fold(foldingContext,
197 Expr<SubscriptInteger>{
198 foldingContext.targetCharacteristics().GetByteSize(
199 type_.category(), type_.kind())} *
200 Expr<SubscriptInteger>{*LEN_});
202 if (auto elementBytes{type_.MeasureSizeInBytes(foldingContext, align)}) {
203 return Fold(foldingContext, std::move(*elementBytes));
205 return std::nullopt;
208 std::optional<Expr<SubscriptInteger>> TypeAndShape::MeasureSizeInBytes(
209 FoldingContext &foldingContext) const {
210 if (auto elements{GetSize(shape_)}) {
211 // Sizes of arrays (even with single elements) are multiples of
212 // their alignments.
213 if (auto elementBytes{
214 MeasureElementSizeInBytes(foldingContext, Rank() > 0)}) {
215 return Fold(
216 foldingContext, std::move(*elements) * std::move(*elementBytes));
219 return std::nullopt;
222 void TypeAndShape::AcquireAttrs(const semantics::Symbol &symbol) {
223 if (IsAssumedShape(symbol)) {
224 attrs_.set(Attr::AssumedShape);
225 } else if (IsDeferredShape(symbol)) {
226 attrs_.set(Attr::DeferredShape);
227 } else if (semantics::IsAssumedSizeArray(symbol)) {
228 attrs_.set(Attr::AssumedSize);
230 if (int corank{GetCorank(symbol)}; corank > 0) {
231 corank_ = corank;
233 if (const auto *object{
234 symbol.GetUltimate().detailsIf<semantics::ObjectEntityDetails>()};
235 object && object->IsAssumedRank()) {
236 attrs_.set(Attr::AssumedRank);
240 void TypeAndShape::AcquireLEN() {
241 if (auto len{type_.GetCharLength()}) {
242 LEN_ = std::move(len);
246 void TypeAndShape::AcquireLEN(const semantics::Symbol &symbol) {
247 if (type_.category() == TypeCategory::Character) {
248 if (auto len{DataRef{symbol}.LEN()}) {
249 LEN_ = std::move(*len);
254 std::string TypeAndShape::AsFortran() const {
255 return type_.AsFortran(LEN_ ? LEN_->AsFortran() : "");
258 llvm::raw_ostream &TypeAndShape::Dump(llvm::raw_ostream &o) const {
259 o << type_.AsFortran(LEN_ ? LEN_->AsFortran() : "");
260 attrs_.Dump(o, EnumToString);
261 if (!shape_) {
262 o << " dimension(..)";
263 } else if (!shape_->empty()) {
264 o << " dimension";
265 char sep{'('};
266 for (const auto &expr : *shape_) {
267 o << sep;
268 sep = ',';
269 if (expr) {
270 expr->AsFortran(o);
271 } else {
272 o << ':';
275 o << ')';
277 return o;
280 bool DummyDataObject::operator==(const DummyDataObject &that) const {
281 return type == that.type && attrs == that.attrs && intent == that.intent &&
282 coshape == that.coshape && cudaDataAttr == that.cudaDataAttr;
285 bool DummyDataObject::IsCompatibleWith(const DummyDataObject &actual,
286 std::string *whyNot, std::optional<std::string> *warning) const {
287 bool possibleWarning{false};
288 if (!ShapesAreCompatible(
289 type.shape(), actual.type.shape(), &possibleWarning)) {
290 if (whyNot) {
291 *whyNot = "incompatible dummy data object shapes";
293 return false;
294 } else if (warning && possibleWarning) {
295 *warning = "distinct dummy data object shapes";
297 // Treat deduced dummy character type as if it were assumed-length character
298 // to avoid useless "implicit interfaces have distinct type" warnings from
299 // CALL FOO('abc'); CALL FOO('abcd').
300 bool deducedAssumedLength{type.type().category() == TypeCategory::Character &&
301 attrs.test(Attr::DeducedFromActual)};
302 bool compatibleTypes{deducedAssumedLength
303 ? type.type().IsTkCompatibleWith(actual.type.type())
304 : type.type().IsTkLenCompatibleWith(actual.type.type())};
305 if (!compatibleTypes) {
306 if (whyNot) {
307 *whyNot = "incompatible dummy data object types: "s +
308 type.type().AsFortran() + " vs " + actual.type.type().AsFortran();
310 return false;
312 if (type.type().IsPolymorphic() != actual.type.type().IsPolymorphic()) {
313 if (whyNot) {
314 *whyNot = "incompatible dummy data object polymorphism: "s +
315 type.type().AsFortran() + " vs " + actual.type.type().AsFortran();
317 return false;
319 if (type.type().category() == TypeCategory::Character &&
320 !deducedAssumedLength) {
321 if (actual.type.type().IsAssumedLengthCharacter() !=
322 type.type().IsAssumedLengthCharacter()) {
323 if (whyNot) {
324 *whyNot = "assumed-length character vs explicit-length character";
326 return false;
328 if (!type.type().IsAssumedLengthCharacter() && type.LEN() &&
329 actual.type.LEN()) {
330 auto len{ToInt64(*type.LEN())};
331 auto actualLen{ToInt64(*actual.type.LEN())};
332 if (len.has_value() != actualLen.has_value()) {
333 if (whyNot) {
334 *whyNot = "constant-length vs non-constant-length character dummy "
335 "arguments";
337 return false;
338 } else if (len && *len != *actualLen) {
339 if (whyNot) {
340 *whyNot = "character dummy arguments with distinct lengths";
342 return false;
346 if (!IdenticalSignificantAttrs(attrs, actual.attrs) ||
347 type.attrs() != actual.type.attrs()) {
348 if (whyNot) {
349 *whyNot = "incompatible dummy data object attributes";
351 return false;
353 if (intent != actual.intent) {
354 if (whyNot) {
355 *whyNot = "incompatible dummy data object intents";
357 return false;
359 if (coshape != actual.coshape) {
360 if (whyNot) {
361 *whyNot = "incompatible dummy data object coshapes";
363 return false;
365 if (ignoreTKR != actual.ignoreTKR) {
366 if (whyNot) {
367 *whyNot = "incompatible !DIR$ IGNORE_TKR directives";
370 if (!attrs.test(Attr::Value) &&
371 !common::AreCompatibleCUDADataAttrs(cudaDataAttr, actual.cudaDataAttr,
372 ignoreTKR, warning,
373 /*allowUnifiedMatchingRule=*/false)) {
374 if (whyNot) {
375 *whyNot = "incompatible CUDA data attributes";
378 return true;
381 static common::Intent GetIntent(const semantics::Attrs &attrs) {
382 if (attrs.test(semantics::Attr::INTENT_IN)) {
383 return common::Intent::In;
384 } else if (attrs.test(semantics::Attr::INTENT_OUT)) {
385 return common::Intent::Out;
386 } else if (attrs.test(semantics::Attr::INTENT_INOUT)) {
387 return common::Intent::InOut;
388 } else {
389 return common::Intent::Default;
393 std::optional<DummyDataObject> DummyDataObject::Characterize(
394 const semantics::Symbol &symbol, FoldingContext &context) {
395 if (const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()};
396 object || symbol.has<semantics::EntityDetails>()) {
397 if (auto type{TypeAndShape::Characterize(
398 symbol, context, /*invariantOnly=*/false)}) {
399 std::optional<DummyDataObject> result{std::move(*type)};
400 using semantics::Attr;
401 CopyAttrs<DummyDataObject, DummyDataObject::Attr>(symbol, *result,
403 {Attr::OPTIONAL, DummyDataObject::Attr::Optional},
404 {Attr::ALLOCATABLE, DummyDataObject::Attr::Allocatable},
405 {Attr::ASYNCHRONOUS, DummyDataObject::Attr::Asynchronous},
406 {Attr::CONTIGUOUS, DummyDataObject::Attr::Contiguous},
407 {Attr::VALUE, DummyDataObject::Attr::Value},
408 {Attr::VOLATILE, DummyDataObject::Attr::Volatile},
409 {Attr::POINTER, DummyDataObject::Attr::Pointer},
410 {Attr::TARGET, DummyDataObject::Attr::Target},
412 result->intent = GetIntent(symbol.attrs());
413 result->ignoreTKR = GetIgnoreTKR(symbol);
414 if (object) {
415 result->cudaDataAttr = object->cudaDataAttr();
416 if (!result->cudaDataAttr &&
417 !result->attrs.test(DummyDataObject::Attr::Value) &&
418 semantics::IsCUDADeviceContext(&symbol.owner())) {
419 result->cudaDataAttr = common::CUDADataAttr::Device;
422 return result;
425 return std::nullopt;
428 bool DummyDataObject::CanBePassedViaImplicitInterface(
429 std::string *whyNot) const {
430 if ((attrs &
431 Attrs{Attr::Allocatable, Attr::Asynchronous, Attr::Optional,
432 Attr::Pointer, Attr::Target, Attr::Value, Attr::Volatile})
433 .any()) {
434 if (whyNot) {
435 *whyNot = "a dummy argument has the allocatable, asynchronous, optional, "
436 "pointer, target, value, or volatile attribute";
438 return false; // 15.4.2.2(3)(a)
439 } else if ((type.attrs() &
440 TypeAndShape::Attrs{TypeAndShape::Attr::AssumedShape,
441 TypeAndShape::Attr::AssumedRank})
442 .any() ||
443 type.corank() > 0) {
444 if (whyNot) {
445 *whyNot = "a dummy argument is assumed-shape, assumed-rank, or a coarray";
447 return false; // 15.4.2.2(3)(b-d)
448 } else if (type.type().IsPolymorphic()) {
449 if (whyNot) {
450 *whyNot = "a dummy argument is polymorphic";
452 return false; // 15.4.2.2(3)(f)
453 } else if (cudaDataAttr) {
454 if (whyNot) {
455 *whyNot = "a dummy argument has a CUDA data attribute";
457 return false;
458 } else if (const auto *derived{GetDerivedTypeSpec(type.type())}) {
459 if (derived->parameters().empty()) { // 15.4.2.2(3)(e)
460 return true;
461 } else {
462 if (whyNot) {
463 *whyNot = "a dummy argument has derived type parameters";
465 return false;
467 } else {
468 return true;
472 bool DummyDataObject::IsPassedByDescriptor(bool isBindC) const {
473 constexpr TypeAndShape::Attrs shapeRequiringBox{
474 TypeAndShape::Attr::AssumedShape, TypeAndShape::Attr::DeferredShape,
475 TypeAndShape::Attr::AssumedRank};
476 if ((attrs & Attrs{Attr::Allocatable, Attr::Pointer}).any()) {
477 return true;
478 } else if ((type.attrs() & shapeRequiringBox).any()) {
479 return true; // pass shape in descriptor
480 } else if (type.corank() > 0) {
481 return true; // pass coshape in descriptor
482 } else if (type.type().IsPolymorphic() && !type.type().IsAssumedType()) {
483 // Need to pass dynamic type info in a descriptor.
484 return true;
485 } else if (const auto *derived{GetDerivedTypeSpec(type.type())}) {
486 if (!derived->parameters().empty()) {
487 for (const auto &param : derived->parameters()) {
488 if (param.second.isLen()) {
489 // Need to pass length type parameters in a descriptor.
490 return true;
494 } else if (isBindC && type.type().IsAssumedLengthCharacter()) {
495 // Fortran 2018 18.3.6 point 2 (5)
496 return true;
498 return false;
501 llvm::raw_ostream &DummyDataObject::Dump(llvm::raw_ostream &o) const {
502 attrs.Dump(o, EnumToString);
503 if (intent != common::Intent::Default) {
504 o << "INTENT(" << common::EnumToString(intent) << ')';
506 type.Dump(o);
507 if (!coshape.empty()) {
508 char sep{'['};
509 for (const auto &expr : coshape) {
510 expr.AsFortran(o << sep);
511 sep = ',';
514 if (cudaDataAttr) {
515 o << " cudaDataAttr: " << common::EnumToString(*cudaDataAttr);
517 if (!ignoreTKR.empty()) {
518 ignoreTKR.Dump(o << ' ', common::EnumToString);
520 return o;
523 DummyProcedure::DummyProcedure(Procedure &&p)
524 : procedure{new Procedure{std::move(p)}} {}
526 bool DummyProcedure::operator==(const DummyProcedure &that) const {
527 return attrs == that.attrs && intent == that.intent &&
528 procedure.value() == that.procedure.value();
531 bool DummyProcedure::IsCompatibleWith(
532 const DummyProcedure &actual, std::string *whyNot) const {
533 if (attrs != actual.attrs) {
534 if (whyNot) {
535 *whyNot = "incompatible dummy procedure attributes";
537 return false;
539 if (intent != actual.intent) {
540 if (whyNot) {
541 *whyNot = "incompatible dummy procedure intents";
543 return false;
545 if (!procedure.value().IsCompatibleWith(actual.procedure.value(),
546 /*ignoreImplicitVsExplicit=*/false, whyNot)) {
547 if (whyNot) {
548 *whyNot = "incompatible dummy procedure interfaces: "s + *whyNot;
550 return false;
552 return true;
555 bool DummyProcedure::CanBePassedViaImplicitInterface(
556 std::string *whyNot) const {
557 if ((attrs & Attrs{Attr::Optional, Attr::Pointer}).any()) {
558 if (whyNot) {
559 *whyNot = "a dummy procedure is optional or a pointer";
561 return false; // 15.4.2.2(3)(a)
563 return true;
566 static std::string GetSeenProcs(
567 const semantics::UnorderedSymbolSet &seenProcs) {
568 // Sort the symbols so that they appear in the same order on all platforms
569 auto ordered{semantics::OrderBySourcePosition(seenProcs)};
570 std::string result;
571 llvm::interleave(
572 ordered,
573 [&](const SymbolRef p) { result += '\'' + p->name().ToString() + '\''; },
574 [&]() { result += ", "; });
575 return result;
578 // These functions with arguments of type UnorderedSymbolSet are used with
579 // mutually recursive calls when characterizing a Procedure, a DummyArgument,
580 // or a DummyProcedure to detect circularly defined procedures as required by
581 // 15.4.3.6, paragraph 2.
582 static std::optional<DummyArgument> CharacterizeDummyArgument(
583 const semantics::Symbol &symbol, FoldingContext &context,
584 semantics::UnorderedSymbolSet seenProcs);
585 static std::optional<FunctionResult> CharacterizeFunctionResult(
586 const semantics::Symbol &symbol, FoldingContext &context,
587 semantics::UnorderedSymbolSet seenProcs, bool emitError);
589 static std::optional<Procedure> CharacterizeProcedure(
590 const semantics::Symbol &original, FoldingContext &context,
591 semantics::UnorderedSymbolSet seenProcs, bool emitError) {
592 const auto &symbol{ResolveAssociations(original)};
593 if (seenProcs.find(symbol) != seenProcs.end()) {
594 std::string procsList{GetSeenProcs(seenProcs)};
595 context.messages().Say(symbol.name(),
596 "Procedure '%s' is recursively defined. Procedures in the cycle:"
597 " %s"_err_en_US,
598 symbol.name(), procsList);
599 return std::nullopt;
601 seenProcs.insert(symbol);
602 auto CheckForNested{[&](const Symbol &symbol) {
603 if (emitError) {
604 context.messages().Say(
605 "Procedure '%s' is referenced before being sufficiently defined in a context where it must be so"_err_en_US,
606 symbol.name());
609 auto result{common::visit(
610 common::visitors{
611 [&](const semantics::SubprogramDetails &subp)
612 -> std::optional<Procedure> {
613 Procedure result;
614 if (subp.isFunction()) {
615 if (auto fr{CharacterizeFunctionResult(
616 subp.result(), context, seenProcs, emitError)}) {
617 result.functionResult = std::move(fr);
618 } else {
619 return std::nullopt;
621 } else {
622 result.attrs.set(Procedure::Attr::Subroutine);
624 for (const semantics::Symbol *arg : subp.dummyArgs()) {
625 if (!arg) {
626 if (subp.isFunction()) {
627 return std::nullopt;
628 } else {
629 result.dummyArguments.emplace_back(AlternateReturn{});
631 } else if (auto argCharacteristics{CharacterizeDummyArgument(
632 *arg, context, seenProcs)}) {
633 result.dummyArguments.emplace_back(
634 std::move(argCharacteristics.value()));
635 } else {
636 return std::nullopt;
639 result.cudaSubprogramAttrs = subp.cudaSubprogramAttrs();
640 return std::move(result);
642 [&](const semantics::ProcEntityDetails &proc)
643 -> std::optional<Procedure> {
644 if (symbol.attrs().test(semantics::Attr::INTRINSIC)) {
645 // Fails when the intrinsic is not a specific intrinsic function
646 // from F'2018 table 16.2. In order to handle forward references,
647 // attempts to use impermissible intrinsic procedures as the
648 // interfaces of procedure pointers are caught and flagged in
649 // declaration checking in Semantics.
650 auto intrinsic{context.intrinsics().IsSpecificIntrinsicFunction(
651 symbol.name().ToString())};
652 if (intrinsic && intrinsic->isRestrictedSpecific) {
653 intrinsic.reset(); // Exclude intrinsics from table 16.3.
655 return intrinsic;
657 if (const semantics::Symbol *
658 interfaceSymbol{proc.procInterface()}) {
659 auto result{CharacterizeProcedure(
660 *interfaceSymbol, context, seenProcs, /*emitError=*/false)};
661 if (result && (IsDummy(symbol) || IsPointer(symbol))) {
662 // Dummy procedures and procedure pointers may not be
663 // ELEMENTAL, but we do accept the use of elemental intrinsic
664 // functions as their interfaces.
665 result->attrs.reset(Procedure::Attr::Elemental);
667 return result;
668 } else {
669 Procedure result;
670 result.attrs.set(Procedure::Attr::ImplicitInterface);
671 const semantics::DeclTypeSpec *type{proc.type()};
672 if (symbol.test(semantics::Symbol::Flag::Subroutine)) {
673 // ignore any implicit typing
674 result.attrs.set(Procedure::Attr::Subroutine);
675 if (proc.isCUDAKernel()) {
676 result.cudaSubprogramAttrs =
677 common::CUDASubprogramAttrs::Global;
679 } else if (type) {
680 if (auto resultType{DynamicType::From(*type)}) {
681 result.functionResult = FunctionResult{*resultType};
682 } else {
683 return std::nullopt;
685 } else if (symbol.test(semantics::Symbol::Flag::Function)) {
686 return std::nullopt;
688 // The PASS name, if any, is not a characteristic.
689 return std::move(result);
692 [&](const semantics::ProcBindingDetails &binding) {
693 if (auto result{CharacterizeProcedure(binding.symbol(), context,
694 seenProcs, /*emitError=*/false)}) {
695 if (binding.symbol().attrs().test(semantics::Attr::INTRINSIC)) {
696 result->attrs.reset(Procedure::Attr::Elemental);
698 if (!symbol.attrs().test(semantics::Attr::NOPASS)) {
699 auto passName{binding.passName()};
700 for (auto &dummy : result->dummyArguments) {
701 if (!passName || dummy.name.c_str() == *passName) {
702 dummy.pass = true;
703 break;
707 return result;
708 } else {
709 return std::optional<Procedure>{};
712 [&](const semantics::UseDetails &use) {
713 return CharacterizeProcedure(
714 use.symbol(), context, seenProcs, /*emitError=*/false);
716 [](const semantics::UseErrorDetails &) {
717 // Ambiguous use-association will be handled later during symbol
718 // checks, ignore UseErrorDetails here without actual symbol usage.
719 return std::optional<Procedure>{};
721 [&](const semantics::HostAssocDetails &assoc) {
722 return CharacterizeProcedure(
723 assoc.symbol(), context, seenProcs, /*emitError=*/false);
725 [&](const semantics::GenericDetails &generic) {
726 if (const semantics::Symbol * specific{generic.specific()}) {
727 return CharacterizeProcedure(
728 *specific, context, seenProcs, emitError);
729 } else {
730 return std::optional<Procedure>{};
733 [&](const semantics::EntityDetails &x) {
734 CheckForNested(symbol);
735 return std::optional<Procedure>{};
737 [&](const semantics::SubprogramNameDetails &) {
738 if (const semantics::Symbol *
739 ancestor{FindAncestorModuleProcedure(&symbol)}) {
740 return CharacterizeProcedure(
741 *ancestor, context, seenProcs, emitError);
743 CheckForNested(symbol);
744 return std::optional<Procedure>{};
746 [&](const auto &) {
747 context.messages().Say(
748 "'%s' is not a procedure"_err_en_US, symbol.name());
749 return std::optional<Procedure>{};
752 symbol.details())};
753 if (result && !symbol.has<semantics::ProcBindingDetails>()) {
754 CopyAttrs<Procedure, Procedure::Attr>(symbol, *result,
756 {semantics::Attr::BIND_C, Procedure::Attr::BindC},
758 CopyAttrs<Procedure, Procedure::Attr>(DEREF(GetMainEntry(&symbol)), *result,
760 {semantics::Attr::ELEMENTAL, Procedure::Attr::Elemental},
762 if (IsPureProcedure(symbol) || // works for ENTRY too
763 (!IsExplicitlyImpureProcedure(symbol) &&
764 result->attrs.test(Procedure::Attr::Elemental))) {
765 result->attrs.set(Procedure::Attr::Pure);
768 return result;
771 static std::optional<DummyProcedure> CharacterizeDummyProcedure(
772 const semantics::Symbol &symbol, FoldingContext &context,
773 semantics::UnorderedSymbolSet seenProcs) {
774 if (auto procedure{CharacterizeProcedure(
775 symbol, context, seenProcs, /*emitError=*/true)}) {
776 // Dummy procedures may not be elemental. Elemental dummy procedure
777 // interfaces are errors when the interface is not intrinsic, and that
778 // error is caught elsewhere. Elemental intrinsic interfaces are
779 // made non-elemental.
780 procedure->attrs.reset(Procedure::Attr::Elemental);
781 DummyProcedure result{std::move(procedure.value())};
782 CopyAttrs<DummyProcedure, DummyProcedure::Attr>(symbol, result,
784 {semantics::Attr::OPTIONAL, DummyProcedure::Attr::Optional},
785 {semantics::Attr::POINTER, DummyProcedure::Attr::Pointer},
787 result.intent = GetIntent(symbol.attrs());
788 return result;
789 } else {
790 return std::nullopt;
794 llvm::raw_ostream &DummyProcedure::Dump(llvm::raw_ostream &o) const {
795 attrs.Dump(o, EnumToString);
796 if (intent != common::Intent::Default) {
797 o << "INTENT(" << common::EnumToString(intent) << ')';
799 procedure.value().Dump(o);
800 return o;
803 llvm::raw_ostream &AlternateReturn::Dump(llvm::raw_ostream &o) const {
804 return o << '*';
807 DummyArgument::~DummyArgument() {}
809 bool DummyArgument::operator==(const DummyArgument &that) const {
810 return u == that.u; // name and passed-object usage are not characteristics
813 bool DummyArgument::IsCompatibleWith(const DummyArgument &actual,
814 std::string *whyNot, std::optional<std::string> *warning) const {
815 if (const auto *ifaceData{std::get_if<DummyDataObject>(&u)}) {
816 if (const auto *actualData{std::get_if<DummyDataObject>(&actual.u)}) {
817 return ifaceData->IsCompatibleWith(*actualData, whyNot, warning);
819 if (whyNot) {
820 *whyNot = "one dummy argument is an object, the other is not";
822 } else if (const auto *ifaceProc{std::get_if<DummyProcedure>(&u)}) {
823 if (const auto *actualProc{std::get_if<DummyProcedure>(&actual.u)}) {
824 return ifaceProc->IsCompatibleWith(*actualProc, whyNot);
826 if (whyNot) {
827 *whyNot = "one dummy argument is a procedure, the other is not";
829 } else {
830 CHECK(std::holds_alternative<AlternateReturn>(u));
831 if (std::holds_alternative<AlternateReturn>(actual.u)) {
832 return true;
834 if (whyNot) {
835 *whyNot = "one dummy argument is an alternate return, the other is not";
838 return false;
841 static std::optional<DummyArgument> CharacterizeDummyArgument(
842 const semantics::Symbol &symbol, FoldingContext &context,
843 semantics::UnorderedSymbolSet seenProcs) {
844 auto name{symbol.name().ToString()};
845 if (symbol.has<semantics::ObjectEntityDetails>() ||
846 symbol.has<semantics::EntityDetails>()) {
847 if (auto obj{DummyDataObject::Characterize(symbol, context)}) {
848 return DummyArgument{std::move(name), std::move(obj.value())};
850 } else if (auto proc{
851 CharacterizeDummyProcedure(symbol, context, seenProcs)}) {
852 return DummyArgument{std::move(name), std::move(proc.value())};
854 return std::nullopt;
857 std::optional<DummyArgument> DummyArgument::FromActual(std::string &&name,
858 const Expr<SomeType> &expr, FoldingContext &context,
859 bool forImplicitInterface) {
860 return common::visit(
861 common::visitors{
862 [&](const BOZLiteralConstant &) {
863 DummyDataObject obj{
864 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}};
865 obj.attrs.set(DummyDataObject::Attr::DeducedFromActual);
866 return std::make_optional<DummyArgument>(
867 std::move(name), std::move(obj));
869 [&](const NullPointer &) {
870 DummyDataObject obj{
871 TypeAndShape{DynamicType::TypelessIntrinsicArgument()}};
872 obj.attrs.set(DummyDataObject::Attr::DeducedFromActual);
873 return std::make_optional<DummyArgument>(
874 std::move(name), std::move(obj));
876 [&](const ProcedureDesignator &designator) {
877 if (auto proc{Procedure::Characterize(
878 designator, context, /*emitError=*/true)}) {
879 return std::make_optional<DummyArgument>(
880 std::move(name), DummyProcedure{std::move(*proc)});
881 } else {
882 return std::optional<DummyArgument>{};
885 [&](const ProcedureRef &call) {
886 if (auto proc{Procedure::Characterize(call, context)}) {
887 return std::make_optional<DummyArgument>(
888 std::move(name), DummyProcedure{std::move(*proc)});
889 } else {
890 return std::optional<DummyArgument>{};
893 [&](const auto &) {
894 if (auto type{TypeAndShape::Characterize(expr, context)}) {
895 if (forImplicitInterface &&
896 !type->type().IsUnlimitedPolymorphic() &&
897 type->type().IsPolymorphic()) {
898 // Pass the monomorphic declared type to an implicit interface
899 type->set_type(DynamicType{
900 type->type().GetDerivedTypeSpec(), /*poly=*/false});
902 DummyDataObject obj{std::move(*type)};
903 obj.attrs.set(DummyDataObject::Attr::DeducedFromActual);
904 return std::make_optional<DummyArgument>(
905 std::move(name), std::move(obj));
906 } else {
907 return std::optional<DummyArgument>{};
911 expr.u);
914 std::optional<DummyArgument> DummyArgument::FromActual(std::string &&name,
915 const ActualArgument &arg, FoldingContext &context,
916 bool forImplicitInterface) {
917 if (const auto *expr{arg.UnwrapExpr()}) {
918 return FromActual(std::move(name), *expr, context, forImplicitInterface);
919 } else if (arg.GetAssumedTypeDummy()) {
920 return std::nullopt;
921 } else {
922 return DummyArgument{AlternateReturn{}};
926 bool DummyArgument::IsOptional() const {
927 return common::visit(
928 common::visitors{
929 [](const DummyDataObject &data) {
930 return data.attrs.test(DummyDataObject::Attr::Optional);
932 [](const DummyProcedure &proc) {
933 return proc.attrs.test(DummyProcedure::Attr::Optional);
935 [](const AlternateReturn &) { return false; },
940 void DummyArgument::SetOptional(bool value) {
941 common::visit(common::visitors{
942 [value](DummyDataObject &data) {
943 data.attrs.set(DummyDataObject::Attr::Optional, value);
945 [value](DummyProcedure &proc) {
946 proc.attrs.set(DummyProcedure::Attr::Optional, value);
948 [](AlternateReturn &) { DIE("cannot set optional"); },
953 void DummyArgument::SetIntent(common::Intent intent) {
954 common::visit(common::visitors{
955 [intent](DummyDataObject &data) { data.intent = intent; },
956 [intent](DummyProcedure &proc) { proc.intent = intent; },
957 [](AlternateReturn &) { DIE("cannot set intent"); },
962 common::Intent DummyArgument::GetIntent() const {
963 return common::visit(
964 common::visitors{
965 [](const DummyDataObject &data) { return data.intent; },
966 [](const DummyProcedure &proc) { return proc.intent; },
967 [](const AlternateReturn &) -> common::Intent {
968 DIE("Alternate returns have no intent");
974 bool DummyArgument::CanBePassedViaImplicitInterface(std::string *whyNot) const {
975 if (const auto *object{std::get_if<DummyDataObject>(&u)}) {
976 return object->CanBePassedViaImplicitInterface(whyNot);
977 } else if (const auto *proc{std::get_if<DummyProcedure>(&u)}) {
978 return proc->CanBePassedViaImplicitInterface(whyNot);
979 } else {
980 return true;
984 bool DummyArgument::IsTypelessIntrinsicDummy() const {
985 const auto *argObj{std::get_if<characteristics::DummyDataObject>(&u)};
986 return argObj && argObj->type.type().IsTypelessIntrinsicArgument();
989 llvm::raw_ostream &DummyArgument::Dump(llvm::raw_ostream &o) const {
990 if (!name.empty()) {
991 o << name << '=';
993 if (pass) {
994 o << " PASS";
996 common::visit([&](const auto &x) { x.Dump(o); }, u);
997 return o;
1000 FunctionResult::FunctionResult(DynamicType t) : u{TypeAndShape{t}} {}
1001 FunctionResult::FunctionResult(TypeAndShape &&t) : u{std::move(t)} {}
1002 FunctionResult::FunctionResult(Procedure &&p) : u{std::move(p)} {}
1003 FunctionResult::~FunctionResult() {}
1005 bool FunctionResult::operator==(const FunctionResult &that) const {
1006 return attrs == that.attrs && cudaDataAttr == that.cudaDataAttr &&
1007 u == that.u;
1010 static std::optional<FunctionResult> CharacterizeFunctionResult(
1011 const semantics::Symbol &symbol, FoldingContext &context,
1012 semantics::UnorderedSymbolSet seenProcs, bool emitError) {
1013 if (const auto *object{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
1014 if (auto type{TypeAndShape::Characterize(
1015 symbol, context, /*invariantOnly=*/false)}) {
1016 FunctionResult result{std::move(*type)};
1017 CopyAttrs<FunctionResult, FunctionResult::Attr>(symbol, result,
1019 {semantics::Attr::ALLOCATABLE, FunctionResult::Attr::Allocatable},
1020 {semantics::Attr::CONTIGUOUS, FunctionResult::Attr::Contiguous},
1021 {semantics::Attr::POINTER, FunctionResult::Attr::Pointer},
1023 result.cudaDataAttr = object->cudaDataAttr();
1024 return result;
1026 } else if (auto maybeProc{CharacterizeProcedure(
1027 symbol, context, seenProcs, emitError)}) {
1028 FunctionResult result{std::move(*maybeProc)};
1029 result.attrs.set(FunctionResult::Attr::Pointer);
1030 return result;
1032 return std::nullopt;
1035 std::optional<FunctionResult> FunctionResult::Characterize(
1036 const Symbol &symbol, FoldingContext &context) {
1037 semantics::UnorderedSymbolSet seenProcs;
1038 return CharacterizeFunctionResult(
1039 symbol, context, seenProcs, /*emitError=*/false);
1042 bool FunctionResult::IsAssumedLengthCharacter() const {
1043 if (const auto *ts{std::get_if<TypeAndShape>(&u)}) {
1044 return ts->type().IsAssumedLengthCharacter();
1045 } else {
1046 return false;
1050 bool FunctionResult::CanBeReturnedViaImplicitInterface(
1051 std::string *whyNot) const {
1052 if (attrs.test(Attr::Pointer) || attrs.test(Attr::Allocatable)) {
1053 if (whyNot) {
1054 *whyNot = "the function result is a pointer or allocatable";
1056 return false; // 15.4.2.2(4)(b)
1057 } else if (cudaDataAttr) {
1058 if (whyNot) {
1059 *whyNot = "the function result has CUDA attributes";
1061 return false;
1062 } else if (const auto *typeAndShape{GetTypeAndShape()}) {
1063 if (typeAndShape->Rank() > 0) {
1064 if (whyNot) {
1065 *whyNot = "the function result is an array";
1067 return false; // 15.4.2.2(4)(a)
1068 } else {
1069 const DynamicType &type{typeAndShape->type()};
1070 switch (type.category()) {
1071 case TypeCategory::Character:
1072 if (type.knownLength()) {
1073 return true;
1074 } else if (const auto *param{type.charLengthParamValue()}) {
1075 if (const auto &expr{param->GetExplicit()}) {
1076 if (IsConstantExpr(*expr)) { // 15.4.2.2(4)(c)
1077 return true;
1078 } else {
1079 if (whyNot) {
1080 *whyNot = "the function result's length is not constant";
1082 return false;
1084 } else if (param->isAssumed()) {
1085 return true;
1088 if (whyNot) {
1089 *whyNot = "the function result's length is not known to the caller";
1091 return false;
1092 case TypeCategory::Derived:
1093 if (type.IsPolymorphic()) {
1094 if (whyNot) {
1095 *whyNot = "the function result is polymorphic";
1097 return false;
1098 } else {
1099 const auto &spec{type.GetDerivedTypeSpec()};
1100 for (const auto &pair : spec.parameters()) {
1101 if (const auto &expr{pair.second.GetExplicit()}) {
1102 if (!IsConstantExpr(*expr)) {
1103 if (whyNot) {
1104 *whyNot = "the function result's derived type has a "
1105 "non-constant parameter";
1107 return false; // 15.4.2.2(4)(c)
1111 return true;
1113 default:
1114 return true;
1117 } else {
1118 if (whyNot) {
1119 *whyNot = "the function result has unknown type or shape";
1121 return false; // 15.4.2.2(4)(b) - procedure pointer?
1125 static std::optional<std::string> AreIncompatibleFunctionResultShapes(
1126 const Shape &x, const Shape &y) {
1127 // Function results cannot be assumed-rank, hence the non optional arguments.
1128 int rank{GetRank(x)};
1129 if (int yrank{GetRank(y)}; yrank != rank) {
1130 return "rank "s + std::to_string(rank) + " vs " + std::to_string(yrank);
1132 for (int j{0}; j < rank; ++j) {
1133 if (x[j] && y[j] && !(*x[j] == *y[j])) {
1134 return x[j]->AsFortran() + " vs " + y[j]->AsFortran();
1137 return std::nullopt;
1140 bool FunctionResult::IsCompatibleWith(
1141 const FunctionResult &actual, std::string *whyNot) const {
1142 Attrs actualAttrs{actual.attrs};
1143 if (!attrs.test(Attr::Contiguous)) {
1144 actualAttrs.reset(Attr::Contiguous);
1146 if (attrs != actualAttrs) {
1147 if (whyNot) {
1148 *whyNot = "function results have incompatible attributes";
1150 } else if (cudaDataAttr != actual.cudaDataAttr) {
1151 if (whyNot) {
1152 *whyNot = "function results have incompatible CUDA data attributes";
1154 } else if (const auto *ifaceTypeShape{std::get_if<TypeAndShape>(&u)}) {
1155 if (const auto *actualTypeShape{std::get_if<TypeAndShape>(&actual.u)}) {
1156 std::optional<std::string> details;
1157 if (ifaceTypeShape->Rank() != actualTypeShape->Rank()) {
1158 if (whyNot) {
1159 *whyNot = "function results have distinct ranks";
1161 } else if (!attrs.test(Attr::Allocatable) && !attrs.test(Attr::Pointer) &&
1162 (details = AreIncompatibleFunctionResultShapes(
1163 ifaceTypeShape->shape().value(),
1164 actualTypeShape->shape().value()))) {
1165 if (whyNot) {
1166 *whyNot = "function results have distinct extents (" + *details + ')';
1168 } else if (ifaceTypeShape->type() != actualTypeShape->type()) {
1169 if (ifaceTypeShape->type().category() !=
1170 actualTypeShape->type().category()) {
1171 } else if (ifaceTypeShape->type().category() ==
1172 TypeCategory::Character) {
1173 if (ifaceTypeShape->type().kind() == actualTypeShape->type().kind()) {
1174 if (IsAssumedLengthCharacter() ||
1175 actual.IsAssumedLengthCharacter()) {
1176 return true;
1177 } else {
1178 auto len{ToInt64(ifaceTypeShape->LEN())};
1179 auto actualLen{ToInt64(actualTypeShape->LEN())};
1180 if (len.has_value() != actualLen.has_value()) {
1181 if (whyNot) {
1182 *whyNot = "constant-length vs non-constant-length character "
1183 "results";
1185 } else if (len && *len != *actualLen) {
1186 if (whyNot) {
1187 *whyNot = "character results with distinct lengths";
1189 } else {
1190 const auto *ifaceLenParam{
1191 ifaceTypeShape->type().charLengthParamValue()};
1192 const auto *actualLenParam{
1193 actualTypeShape->type().charLengthParamValue()};
1194 if (ifaceLenParam && actualLenParam &&
1195 ifaceLenParam->isExplicit() !=
1196 actualLenParam->isExplicit()) {
1197 if (whyNot) {
1198 *whyNot =
1199 "explicit-length vs deferred-length character results";
1201 } else {
1202 return true;
1207 } else if (ifaceTypeShape->type().category() == TypeCategory::Derived) {
1208 if (ifaceTypeShape->type().IsPolymorphic() ==
1209 actualTypeShape->type().IsPolymorphic() &&
1210 !ifaceTypeShape->type().IsUnlimitedPolymorphic() &&
1211 !actualTypeShape->type().IsUnlimitedPolymorphic() &&
1212 AreSameDerivedType(ifaceTypeShape->type().GetDerivedTypeSpec(),
1213 actualTypeShape->type().GetDerivedTypeSpec())) {
1214 return true;
1217 if (whyNot) {
1218 *whyNot = "function results have distinct types: "s +
1219 ifaceTypeShape->type().AsFortran() + " vs "s +
1220 actualTypeShape->type().AsFortran();
1222 } else {
1223 return true;
1225 } else {
1226 if (whyNot) {
1227 *whyNot = "function result type and shape are not known";
1230 } else {
1231 const auto *ifaceProc{std::get_if<CopyableIndirection<Procedure>>(&u)};
1232 CHECK(ifaceProc != nullptr);
1233 if (const auto *actualProc{
1234 std::get_if<CopyableIndirection<Procedure>>(&actual.u)}) {
1235 if (ifaceProc->value().IsCompatibleWith(actualProc->value(),
1236 /*ignoreImplicitVsExplicit=*/false, whyNot)) {
1237 return true;
1239 if (whyNot) {
1240 *whyNot =
1241 "function results are incompatible procedure pointers: "s + *whyNot;
1243 } else {
1244 if (whyNot) {
1245 *whyNot =
1246 "one function result is a procedure pointer, the other is not";
1250 return false;
1253 llvm::raw_ostream &FunctionResult::Dump(llvm::raw_ostream &o) const {
1254 attrs.Dump(o, EnumToString);
1255 common::visit(common::visitors{
1256 [&](const TypeAndShape &ts) { ts.Dump(o); },
1257 [&](const CopyableIndirection<Procedure> &p) {
1258 p.value().Dump(o << " procedure(") << ')';
1262 if (cudaDataAttr) {
1263 o << " cudaDataAttr: " << common::EnumToString(*cudaDataAttr);
1265 return o;
1268 Procedure::Procedure(FunctionResult &&fr, DummyArguments &&args, Attrs a)
1269 : functionResult{std::move(fr)}, dummyArguments{std::move(args)}, attrs{a} {
1271 Procedure::Procedure(DummyArguments &&args, Attrs a)
1272 : dummyArguments{std::move(args)}, attrs{a} {}
1273 Procedure::~Procedure() {}
1275 bool Procedure::operator==(const Procedure &that) const {
1276 return attrs == that.attrs && functionResult == that.functionResult &&
1277 dummyArguments == that.dummyArguments &&
1278 cudaSubprogramAttrs == that.cudaSubprogramAttrs;
1281 bool Procedure::IsCompatibleWith(const Procedure &actual,
1282 bool ignoreImplicitVsExplicit, std::string *whyNot,
1283 const SpecificIntrinsic *specificIntrinsic,
1284 std::optional<std::string> *warning) const {
1285 // 15.5.2.9(1): if dummy is not pure, actual need not be.
1286 // Ditto with elemental.
1287 Attrs actualAttrs{actual.attrs};
1288 if (!attrs.test(Attr::Pure)) {
1289 actualAttrs.reset(Attr::Pure);
1291 if (!attrs.test(Attr::Elemental) && specificIntrinsic) {
1292 actualAttrs.reset(Attr::Elemental);
1294 Attrs differences{attrs ^ actualAttrs};
1295 differences.reset(Attr::Subroutine); // dealt with specifically later
1296 if (ignoreImplicitVsExplicit) {
1297 differences.reset(Attr::ImplicitInterface);
1299 if (!differences.empty()) {
1300 if (whyNot) {
1301 auto sep{": "s};
1302 *whyNot = "incompatible procedure attributes";
1303 differences.IterateOverMembers([&](Attr x) {
1304 *whyNot += sep + std::string{EnumToString(x)};
1305 sep = ", ";
1308 } else if ((IsFunction() && actual.IsSubroutine()) ||
1309 (IsSubroutine() && actual.IsFunction())) {
1310 if (whyNot) {
1311 *whyNot =
1312 "incompatible procedures: one is a function, the other a subroutine";
1314 } else if (functionResult && actual.functionResult &&
1315 !functionResult->IsCompatibleWith(*actual.functionResult, whyNot)) {
1316 } else if (cudaSubprogramAttrs != actual.cudaSubprogramAttrs) {
1317 if (whyNot) {
1318 *whyNot = "incompatible CUDA subprogram attributes";
1320 } else if (dummyArguments.size() != actual.dummyArguments.size()) {
1321 if (whyNot) {
1322 *whyNot = "distinct numbers of dummy arguments";
1324 } else {
1325 for (std::size_t j{0}; j < dummyArguments.size(); ++j) {
1326 // Subtlety: the dummy/actual distinction must be reversed for this
1327 // compatibility test in order to correctly check extended vs.
1328 // base types. Example:
1329 // subroutine s1(base); subroutine s2(extended)
1330 // procedure(s1), pointer :: p
1331 // p => s2 ! an error, s2 is more restricted, can't handle "base"
1332 std::optional<std::string> gotWarning;
1333 if (!actual.dummyArguments[j].IsCompatibleWith(
1334 dummyArguments[j], whyNot, warning ? &gotWarning : nullptr)) {
1335 if (whyNot) {
1336 *whyNot = "incompatible dummy argument #"s + std::to_string(j + 1) +
1337 ": "s + *whyNot;
1339 return false;
1340 } else if (warning && !*warning && gotWarning) {
1341 *warning = "possibly incompatible dummy argument #"s +
1342 std::to_string(j + 1) + ": "s + std::move(*gotWarning);
1345 return true;
1347 return false;
1350 std::optional<int> Procedure::FindPassIndex(
1351 std::optional<parser::CharBlock> name) const {
1352 int argCount{static_cast<int>(dummyArguments.size())};
1353 if (name) {
1354 for (int index{0}; index < argCount; ++index) {
1355 if (*name == dummyArguments[index].name.c_str()) {
1356 return index;
1359 return std::nullopt;
1360 } else if (argCount > 0) {
1361 return 0;
1362 } else {
1363 return std::nullopt;
1367 bool Procedure::CanOverride(
1368 const Procedure &that, std::optional<int> passIndex) const {
1369 // A pure procedure may override an impure one (7.5.7.3(2))
1370 if ((that.attrs.test(Attr::Pure) && !attrs.test(Attr::Pure)) ||
1371 that.attrs.test(Attr::Elemental) != attrs.test(Attr::Elemental) ||
1372 functionResult != that.functionResult) {
1373 return false;
1375 int argCount{static_cast<int>(dummyArguments.size())};
1376 if (argCount != static_cast<int>(that.dummyArguments.size())) {
1377 return false;
1379 for (int j{0}; j < argCount; ++j) {
1380 if (passIndex && j == *passIndex) {
1381 if (!that.dummyArguments[j].IsCompatibleWith(dummyArguments[j])) {
1382 return false;
1384 } else if (dummyArguments[j] != that.dummyArguments[j]) {
1385 return false;
1388 return true;
1391 std::optional<Procedure> Procedure::Characterize(
1392 const semantics::Symbol &symbol, FoldingContext &context) {
1393 semantics::UnorderedSymbolSet seenProcs;
1394 return CharacterizeProcedure(symbol, context, seenProcs, /*emitError=*/true);
1397 std::optional<Procedure> Procedure::Characterize(
1398 const ProcedureDesignator &proc, FoldingContext &context, bool emitError) {
1399 if (const auto *symbol{proc.GetSymbol()}) {
1400 semantics::UnorderedSymbolSet seenProcs;
1401 return CharacterizeProcedure(*symbol, context, seenProcs, emitError);
1402 } else if (const auto *intrinsic{proc.GetSpecificIntrinsic()}) {
1403 return intrinsic->characteristics.value();
1404 } else {
1405 return std::nullopt;
1409 std::optional<Procedure> Procedure::Characterize(
1410 const ProcedureRef &ref, FoldingContext &context) {
1411 if (auto callee{Characterize(ref.proc(), context, /*emitError=*/true)}) {
1412 if (callee->functionResult) {
1413 if (const Procedure *
1414 proc{callee->functionResult->IsProcedurePointer()}) {
1415 return {*proc};
1419 return std::nullopt;
1422 std::optional<Procedure> Procedure::Characterize(
1423 const Expr<SomeType> &expr, FoldingContext &context) {
1424 if (const auto *procRef{UnwrapProcedureRef(expr)}) {
1425 return Characterize(*procRef, context);
1426 } else if (const auto *procDesignator{
1427 std::get_if<ProcedureDesignator>(&expr.u)}) {
1428 return Characterize(*procDesignator, context, /*emitError=*/true);
1429 } else if (const Symbol * symbol{UnwrapWholeSymbolOrComponentDataRef(expr)}) {
1430 return Characterize(*symbol, context);
1431 } else {
1432 context.messages().Say(
1433 "Expression '%s' is not a procedure"_err_en_US, expr.AsFortran());
1434 return std::nullopt;
1438 std::optional<Procedure> Procedure::FromActuals(const ProcedureDesignator &proc,
1439 const ActualArguments &args, FoldingContext &context) {
1440 auto callee{Characterize(proc, context, /*emitError=*/true)};
1441 if (callee) {
1442 if (callee->dummyArguments.empty() &&
1443 callee->attrs.test(Procedure::Attr::ImplicitInterface)) {
1444 int j{0};
1445 for (const auto &arg : args) {
1446 ++j;
1447 if (arg) {
1448 if (auto dummy{DummyArgument::FromActual("x"s + std::to_string(j),
1449 *arg, context,
1450 /*forImplicitInterface=*/true)}) {
1451 callee->dummyArguments.emplace_back(std::move(*dummy));
1452 continue;
1455 callee.reset();
1456 break;
1460 return callee;
1463 bool Procedure::CanBeCalledViaImplicitInterface(std::string *whyNot) const {
1464 if (attrs.test(Attr::Elemental)) {
1465 if (whyNot) {
1466 *whyNot = "the procedure is elemental";
1468 return false; // 15.4.2.2(5,6)
1469 } else if (attrs.test(Attr::BindC)) {
1470 if (whyNot) {
1471 *whyNot = "the procedure is BIND(C)";
1473 return false; // 15.4.2.2(5,6)
1474 } else if (cudaSubprogramAttrs &&
1475 *cudaSubprogramAttrs != common::CUDASubprogramAttrs::Host &&
1476 *cudaSubprogramAttrs != common::CUDASubprogramAttrs::Global) {
1477 if (whyNot) {
1478 *whyNot = "the procedure is CUDA but neither HOST nor GLOBAL";
1480 return false;
1481 } else if (IsFunction() &&
1482 !functionResult->CanBeReturnedViaImplicitInterface(whyNot)) {
1483 return false;
1484 } else {
1485 for (const DummyArgument &arg : dummyArguments) {
1486 if (!arg.CanBePassedViaImplicitInterface(whyNot)) {
1487 return false;
1490 return true;
1494 llvm::raw_ostream &Procedure::Dump(llvm::raw_ostream &o) const {
1495 attrs.Dump(o, EnumToString);
1496 if (functionResult) {
1497 functionResult->Dump(o << "TYPE(") << ") FUNCTION";
1498 } else if (attrs.test(Attr::Subroutine)) {
1499 o << "SUBROUTINE";
1500 } else {
1501 o << "EXTERNAL";
1503 char sep{'('};
1504 for (const auto &dummy : dummyArguments) {
1505 dummy.Dump(o << sep);
1506 sep = ',';
1508 o << (sep == '(' ? "()" : ")");
1509 if (cudaSubprogramAttrs) {
1510 o << " cudaSubprogramAttrs: " << common::EnumToString(*cudaSubprogramAttrs);
1512 return o;
1515 // Utility class to determine if Procedures, etc. are distinguishable
1516 class DistinguishUtils {
1517 public:
1518 explicit DistinguishUtils(const common::LanguageFeatureControl &features)
1519 : features_{features} {}
1521 // Are these procedures distinguishable for a generic name?
1522 std::optional<bool> Distinguishable(
1523 const Procedure &, const Procedure &) const;
1524 // Are these procedures distinguishable for a generic operator or assignment?
1525 std::optional<bool> DistinguishableOpOrAssign(
1526 const Procedure &, const Procedure &) const;
1528 private:
1529 struct CountDummyProcedures {
1530 CountDummyProcedures(const DummyArguments &args) {
1531 for (const DummyArgument &arg : args) {
1532 if (std::holds_alternative<DummyProcedure>(arg.u)) {
1533 total += 1;
1534 notOptional += !arg.IsOptional();
1538 int total{0};
1539 int notOptional{0};
1542 bool AnyOptionalData(const DummyArguments &) const;
1543 bool AnyUnlimitedPolymorphicData(const DummyArguments &) const;
1544 bool Rule3Distinguishable(const Procedure &, const Procedure &) const;
1545 const DummyArgument *Rule1DistinguishingArg(
1546 const DummyArguments &, const DummyArguments &) const;
1547 int FindFirstToDistinguishByPosition(
1548 const DummyArguments &, const DummyArguments &) const;
1549 int FindLastToDistinguishByName(
1550 const DummyArguments &, const DummyArguments &) const;
1551 int CountCompatibleWith(const DummyArgument &, const DummyArguments &) const;
1552 int CountNotDistinguishableFrom(
1553 const DummyArgument &, const DummyArguments &) const;
1554 bool Distinguishable(const DummyArgument &, const DummyArgument &) const;
1555 bool Distinguishable(const DummyDataObject &, const DummyDataObject &) const;
1556 bool Distinguishable(const DummyProcedure &, const DummyProcedure &) const;
1557 bool Distinguishable(const FunctionResult &, const FunctionResult &) const;
1558 bool Distinguishable(
1559 const TypeAndShape &, const TypeAndShape &, common::IgnoreTKRSet) const;
1560 bool IsTkrCompatible(const DummyArgument &, const DummyArgument &) const;
1561 bool IsTkCompatible(const DummyDataObject &, const DummyDataObject &) const;
1562 const DummyArgument *GetAtEffectivePosition(
1563 const DummyArguments &, int) const;
1564 const DummyArgument *GetPassArg(const Procedure &) const;
1566 const common::LanguageFeatureControl &features_;
1569 // Simpler distinguishability rules for operators and assignment
1570 std::optional<bool> DistinguishUtils::DistinguishableOpOrAssign(
1571 const Procedure &proc1, const Procedure &proc2) const {
1572 if ((proc1.IsFunction() && proc2.IsSubroutine()) ||
1573 (proc1.IsSubroutine() && proc2.IsFunction())) {
1574 return true;
1576 auto &args1{proc1.dummyArguments};
1577 auto &args2{proc2.dummyArguments};
1578 if (args1.size() != args2.size()) {
1579 return true; // C1511: distinguishable based on number of arguments
1581 for (std::size_t i{0}; i < args1.size(); ++i) {
1582 if (Distinguishable(args1[i], args2[i])) {
1583 return true; // C1511, C1512: distinguishable based on this arg
1586 return false;
1589 std::optional<bool> DistinguishUtils::Distinguishable(
1590 const Procedure &proc1, const Procedure &proc2) const {
1591 if ((proc1.IsFunction() && proc2.IsSubroutine()) ||
1592 (proc1.IsSubroutine() && proc2.IsFunction())) {
1593 return true;
1595 auto &args1{proc1.dummyArguments};
1596 auto &args2{proc2.dummyArguments};
1597 auto count1{CountDummyProcedures(args1)};
1598 auto count2{CountDummyProcedures(args2)};
1599 if (count1.notOptional > count2.total || count2.notOptional > count1.total) {
1600 return true; // distinguishable based on C1514 rule 2
1602 if (Rule3Distinguishable(proc1, proc2)) {
1603 return true; // distinguishable based on C1514 rule 3
1605 if (Rule1DistinguishingArg(args1, args2)) {
1606 return true; // distinguishable based on C1514 rule 1
1608 int pos1{FindFirstToDistinguishByPosition(args1, args2)};
1609 int name1{FindLastToDistinguishByName(args1, args2)};
1610 if (pos1 >= 0 && pos1 <= name1) {
1611 return true; // distinguishable based on C1514 rule 4
1613 int pos2{FindFirstToDistinguishByPosition(args2, args1)};
1614 int name2{FindLastToDistinguishByName(args2, args1)};
1615 if (pos2 >= 0 && pos2 <= name2) {
1616 return true; // distinguishable based on C1514 rule 4
1618 if (proc1.cudaSubprogramAttrs != proc2.cudaSubprogramAttrs) {
1619 return true;
1621 // If there are no optional or unlimited polymorphic dummy arguments,
1622 // then we know the result for sure; otherwise, it's possible for
1623 // the procedures to be unambiguous.
1624 if ((AnyOptionalData(args1) || AnyUnlimitedPolymorphicData(args1)) &&
1625 (AnyOptionalData(args2) || AnyUnlimitedPolymorphicData(args2))) {
1626 return std::nullopt; // meaning "maybe"
1627 } else {
1628 return false;
1632 bool DistinguishUtils::AnyOptionalData(const DummyArguments &args) const {
1633 for (const auto &arg : args) {
1634 if (std::holds_alternative<DummyDataObject>(arg.u) && arg.IsOptional()) {
1635 return true;
1638 return false;
1641 bool DistinguishUtils::AnyUnlimitedPolymorphicData(
1642 const DummyArguments &args) const {
1643 for (const auto &arg : args) {
1644 if (const auto *object{std::get_if<DummyDataObject>(&arg.u)}) {
1645 if (object->type.type().IsUnlimitedPolymorphic()) {
1646 return true;
1650 return false;
1653 // C1514 rule 3: Procedures are distinguishable if both have a passed-object
1654 // dummy argument and those are distinguishable.
1655 bool DistinguishUtils::Rule3Distinguishable(
1656 const Procedure &proc1, const Procedure &proc2) const {
1657 const DummyArgument *pass1{GetPassArg(proc1)};
1658 const DummyArgument *pass2{GetPassArg(proc2)};
1659 return pass1 && pass2 && Distinguishable(*pass1, *pass2);
1662 // Find a non-passed-object dummy data object in one of the argument lists
1663 // that satisfies C1514 rule 1. I.e. x such that:
1664 // - m is the number of dummy data objects in one that are nonoptional,
1665 // are not passed-object, that x is TKR compatible with
1666 // - n is the number of non-passed-object dummy data objects, in the other
1667 // that are not distinguishable from x
1668 // - m is greater than n
1669 const DummyArgument *DistinguishUtils::Rule1DistinguishingArg(
1670 const DummyArguments &args1, const DummyArguments &args2) const {
1671 auto size1{args1.size()};
1672 auto size2{args2.size()};
1673 for (std::size_t i{0}; i < size1 + size2; ++i) {
1674 const DummyArgument &x{i < size1 ? args1[i] : args2[i - size1]};
1675 if (!x.pass && std::holds_alternative<DummyDataObject>(x.u)) {
1676 if (CountCompatibleWith(x, args1) >
1677 CountNotDistinguishableFrom(x, args2) ||
1678 CountCompatibleWith(x, args2) >
1679 CountNotDistinguishableFrom(x, args1)) {
1680 return &x;
1684 return nullptr;
1687 // Find the index of the first nonoptional non-passed-object dummy argument
1688 // in args1 at an effective position such that either:
1689 // - args2 has no dummy argument at that effective position
1690 // - the dummy argument at that position is distinguishable from it
1691 int DistinguishUtils::FindFirstToDistinguishByPosition(
1692 const DummyArguments &args1, const DummyArguments &args2) const {
1693 int effective{0}; // position of arg1 in list, ignoring passed arg
1694 for (std::size_t i{0}; i < args1.size(); ++i) {
1695 const DummyArgument &arg1{args1.at(i)};
1696 if (!arg1.pass && !arg1.IsOptional()) {
1697 const DummyArgument *arg2{GetAtEffectivePosition(args2, effective)};
1698 if (!arg2 || Distinguishable(arg1, *arg2)) {
1699 return i;
1702 effective += !arg1.pass;
1704 return -1;
1707 // Find the index of the last nonoptional non-passed-object dummy argument
1708 // in args1 whose name is such that either:
1709 // - args2 has no dummy argument with that name
1710 // - the dummy argument with that name is distinguishable from it
1711 int DistinguishUtils::FindLastToDistinguishByName(
1712 const DummyArguments &args1, const DummyArguments &args2) const {
1713 std::map<std::string, const DummyArgument *> nameToArg;
1714 for (const auto &arg2 : args2) {
1715 nameToArg.emplace(arg2.name, &arg2);
1717 for (int i = args1.size() - 1; i >= 0; --i) {
1718 const DummyArgument &arg1{args1.at(i)};
1719 if (!arg1.pass && !arg1.IsOptional()) {
1720 auto it{nameToArg.find(arg1.name)};
1721 if (it == nameToArg.end() || Distinguishable(arg1, *it->second)) {
1722 return i;
1726 return -1;
1729 // Count the dummy data objects in args that are nonoptional, are not
1730 // passed-object, and that x is TKR compatible with
1731 int DistinguishUtils::CountCompatibleWith(
1732 const DummyArgument &x, const DummyArguments &args) const {
1733 return llvm::count_if(args, [&](const DummyArgument &y) {
1734 return !y.pass && !y.IsOptional() && IsTkrCompatible(x, y);
1738 // Return the number of dummy data objects in args that are not
1739 // distinguishable from x and not passed-object.
1740 int DistinguishUtils::CountNotDistinguishableFrom(
1741 const DummyArgument &x, const DummyArguments &args) const {
1742 return llvm::count_if(args, [&](const DummyArgument &y) {
1743 return !y.pass && std::holds_alternative<DummyDataObject>(y.u) &&
1744 !Distinguishable(y, x);
1748 bool DistinguishUtils::Distinguishable(
1749 const DummyArgument &x, const DummyArgument &y) const {
1750 if (x.u.index() != y.u.index()) {
1751 return true; // different kind: data/proc/alt-return
1753 return common::visit(
1754 common::visitors{
1755 [&](const DummyDataObject &z) {
1756 return Distinguishable(z, std::get<DummyDataObject>(y.u));
1758 [&](const DummyProcedure &z) {
1759 return Distinguishable(z, std::get<DummyProcedure>(y.u));
1761 [&](const AlternateReturn &) { return false; },
1763 x.u);
1766 bool DistinguishUtils::Distinguishable(
1767 const DummyDataObject &x, const DummyDataObject &y) const {
1768 using Attr = DummyDataObject::Attr;
1769 if (Distinguishable(x.type, y.type, x.ignoreTKR | y.ignoreTKR)) {
1770 return true;
1771 } else if (x.attrs.test(Attr::Allocatable) && y.attrs.test(Attr::Pointer) &&
1772 y.intent != common::Intent::In) {
1773 return true;
1774 } else if (y.attrs.test(Attr::Allocatable) && x.attrs.test(Attr::Pointer) &&
1775 x.intent != common::Intent::In) {
1776 return true;
1777 } else if (!common::AreCompatibleCUDADataAttrs(x.cudaDataAttr, y.cudaDataAttr,
1778 x.ignoreTKR | y.ignoreTKR, nullptr,
1779 /*allowUnifiedMatchingRule=*/false)) {
1780 return true;
1781 } else if (features_.IsEnabled(
1782 common::LanguageFeature::DistinguishableSpecifics) &&
1783 (x.attrs.test(Attr::Allocatable) || x.attrs.test(Attr::Pointer)) &&
1784 (y.attrs.test(Attr::Allocatable) || y.attrs.test(Attr::Pointer)) &&
1785 (x.type.type().IsUnlimitedPolymorphic() !=
1786 y.type.type().IsUnlimitedPolymorphic() ||
1787 x.type.type().IsPolymorphic() != y.type.type().IsPolymorphic())) {
1788 // Extension: Per 15.5.2.5(2), an allocatable/pointer dummy and its
1789 // corresponding actual argument must both or neither be polymorphic,
1790 // and must both or neither be unlimited polymorphic. So when exactly
1791 // one of two dummy arguments is polymorphic or unlimited polymorphic,
1792 // any actual argument that is admissible to one of them cannot also match
1793 // the other one.
1794 return true;
1795 } else {
1796 return false;
1800 bool DistinguishUtils::Distinguishable(
1801 const DummyProcedure &x, const DummyProcedure &y) const {
1802 const Procedure &xProc{x.procedure.value()};
1803 const Procedure &yProc{y.procedure.value()};
1804 if (Distinguishable(xProc, yProc).value_or(false)) {
1805 return true;
1806 } else {
1807 const std::optional<FunctionResult> &xResult{xProc.functionResult};
1808 const std::optional<FunctionResult> &yResult{yProc.functionResult};
1809 return xResult ? !yResult || Distinguishable(*xResult, *yResult)
1810 : yResult.has_value();
1814 bool DistinguishUtils::Distinguishable(
1815 const FunctionResult &x, const FunctionResult &y) const {
1816 if (x.u.index() != y.u.index()) {
1817 return true; // one is data object, one is procedure
1819 if (x.cudaDataAttr != y.cudaDataAttr) {
1820 return true;
1822 return common::visit(
1823 common::visitors{
1824 [&](const TypeAndShape &z) {
1825 return Distinguishable(
1826 z, std::get<TypeAndShape>(y.u), common::IgnoreTKRSet{});
1828 [&](const CopyableIndirection<Procedure> &z) {
1829 return Distinguishable(z.value(),
1830 std::get<CopyableIndirection<Procedure>>(y.u).value())
1831 .value_or(false);
1834 x.u);
1837 bool DistinguishUtils::Distinguishable(const TypeAndShape &x,
1838 const TypeAndShape &y, common::IgnoreTKRSet ignoreTKR) const {
1839 if (!x.type().IsTkCompatibleWith(y.type(), ignoreTKR) &&
1840 !y.type().IsTkCompatibleWith(x.type(), ignoreTKR)) {
1841 return true;
1843 if (ignoreTKR.test(common::IgnoreTKR::Rank)) {
1844 } else if (x.attrs().test(TypeAndShape::Attr::AssumedRank) ||
1845 y.attrs().test(TypeAndShape::Attr::AssumedRank)) {
1846 } else if (x.Rank() != y.Rank()) {
1847 return true;
1849 return false;
1852 // Compatibility based on type, kind, and rank
1854 bool DistinguishUtils::IsTkrCompatible(
1855 const DummyArgument &x, const DummyArgument &y) const {
1856 const auto *obj1{std::get_if<DummyDataObject>(&x.u)};
1857 const auto *obj2{std::get_if<DummyDataObject>(&y.u)};
1858 return obj1 && obj2 && IsTkCompatible(*obj1, *obj2) &&
1859 (obj1->type.Rank() == obj2->type.Rank() ||
1860 obj1->type.attrs().test(TypeAndShape::Attr::AssumedRank) ||
1861 obj2->type.attrs().test(TypeAndShape::Attr::AssumedRank) ||
1862 obj1->ignoreTKR.test(common::IgnoreTKR::Rank) ||
1863 obj2->ignoreTKR.test(common::IgnoreTKR::Rank));
1866 bool DistinguishUtils::IsTkCompatible(
1867 const DummyDataObject &x, const DummyDataObject &y) const {
1868 return x.type.type().IsTkCompatibleWith(
1869 y.type.type(), x.ignoreTKR | y.ignoreTKR);
1872 // Return the argument at the given index, ignoring the passed arg
1873 const DummyArgument *DistinguishUtils::GetAtEffectivePosition(
1874 const DummyArguments &args, int index) const {
1875 for (const DummyArgument &arg : args) {
1876 if (!arg.pass) {
1877 if (index == 0) {
1878 return &arg;
1880 --index;
1883 return nullptr;
1886 // Return the passed-object dummy argument of this procedure, if any
1887 const DummyArgument *DistinguishUtils::GetPassArg(const Procedure &proc) const {
1888 for (const auto &arg : proc.dummyArguments) {
1889 if (arg.pass) {
1890 return &arg;
1893 return nullptr;
1896 std::optional<bool> Distinguishable(
1897 const common::LanguageFeatureControl &features, const Procedure &x,
1898 const Procedure &y) {
1899 return DistinguishUtils{features}.Distinguishable(x, y);
1902 std::optional<bool> DistinguishableOpOrAssign(
1903 const common::LanguageFeatureControl &features, const Procedure &x,
1904 const Procedure &y) {
1905 return DistinguishUtils{features}.DistinguishableOpOrAssign(x, y);
1908 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyArgument)
1909 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(DummyProcedure)
1910 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(FunctionResult)
1911 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(Procedure)
1912 } // namespace Fortran::evaluate::characteristics
1914 template class Fortran::common::Indirection<
1915 Fortran::evaluate::characteristics::Procedure, true>;