xref: /llvm-project/flang/lib/Semantics/check-call.cpp (revision d732c86c928271cf3a829d95a1fcc560894ab8e4)
164ab3302SCarolineConcatto //===-- lib/Semantics/check-call.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 "check-call.h"
10573fc618SPeter Klausler #include "definable.h"
1164ab3302SCarolineConcatto #include "pointer-assignment.h"
1264ab3302SCarolineConcatto #include "flang/Evaluate/characteristics.h"
1364ab3302SCarolineConcatto #include "flang/Evaluate/check-expression.h"
145718a425SPeter Klausler #include "flang/Evaluate/fold-designator.h"
1564ab3302SCarolineConcatto #include "flang/Evaluate/shape.h"
1664ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
1764ab3302SCarolineConcatto #include "flang/Parser/characters.h"
1864ab3302SCarolineConcatto #include "flang/Parser/message.h"
1964ab3302SCarolineConcatto #include "flang/Semantics/scope.h"
2064ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
2164ab3302SCarolineConcatto #include <map>
2264ab3302SCarolineConcatto #include <string>
2364ab3302SCarolineConcatto 
2464ab3302SCarolineConcatto using namespace Fortran::parser::literals;
2564ab3302SCarolineConcatto namespace characteristics = Fortran::evaluate::characteristics;
2664ab3302SCarolineConcatto 
2764ab3302SCarolineConcatto namespace Fortran::semantics {
2864ab3302SCarolineConcatto 
29303ecc42SPeixinQiao static void CheckImplicitInterfaceArg(evaluate::ActualArgument &arg,
3033c27f28SPeter Klausler     parser::ContextualMessages &messages, SemanticsContext &context) {
31fa0443f2SPeter Klausler   auto restorer{
32fa0443f2SPeter Klausler       messages.SetLocation(arg.sourceLocation().value_or(messages.at()))};
3364ab3302SCarolineConcatto   if (auto kw{arg.keyword()}) {
3464ab3302SCarolineConcatto     messages.Say(*kw,
3564ab3302SCarolineConcatto         "Keyword '%s=' may not appear in a reference to a procedure with an implicit interface"_err_en_US,
3664ab3302SCarolineConcatto         *kw);
3764ab3302SCarolineConcatto   }
38930c2d91SPeter Klausler   auto type{arg.GetType()};
39930c2d91SPeter Klausler   if (type) {
4064ab3302SCarolineConcatto     if (type->IsAssumedType()) {
4164ab3302SCarolineConcatto       messages.Say(
4229fd3e2aSPeter Klausler           "Assumed type actual argument requires an explicit interface"_err_en_US);
4329fd3e2aSPeter Klausler     } else if (type->IsUnlimitedPolymorphic()) {
4464ab3302SCarolineConcatto       messages.Say(
4529fd3e2aSPeter Klausler           "Unlimited polymorphic actual argument requires an explicit interface"_err_en_US);
4664ab3302SCarolineConcatto     } else if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(type)}) {
4764ab3302SCarolineConcatto       if (!derived->parameters().empty()) {
4864ab3302SCarolineConcatto         messages.Say(
4929fd3e2aSPeter Klausler             "Parameterized derived type actual argument requires an explicit interface"_err_en_US);
5064ab3302SCarolineConcatto       }
5164ab3302SCarolineConcatto     }
5264ab3302SCarolineConcatto   }
53930c2d91SPeter Klausler   if (arg.isPercentVal() &&
54930c2d91SPeter Klausler       (!type || !type->IsLengthlessIntrinsicType() || arg.Rank() != 0)) {
55930c2d91SPeter Klausler     messages.Say(
56930c2d91SPeter Klausler         "%VAL argument must be a scalar numeric or logical expression"_err_en_US);
57930c2d91SPeter Klausler   }
5864ab3302SCarolineConcatto   if (const auto *expr{arg.UnwrapExpr()}) {
59b52728d8SPeter Klausler     if (const Symbol * base{GetFirstSymbol(*expr)};
60b52728d8SPeter Klausler         base && IsFunctionResult(*base)) {
61b52728d8SPeter Klausler       context.NoteDefinedSymbol(*base);
62b52728d8SPeter Klausler     }
63571673ceSPeter Steinfeld     if (IsBOZLiteral(*expr)) {
648f414316SPeter Steinfeld       messages.Say("BOZ argument requires an explicit interface"_err_en_US);
65bcb2591bSpeter klausler     } else if (evaluate::IsNullPointer(*expr)) {
66bcb2591bSpeter klausler       messages.Say(
67bcb2591bSpeter klausler           "Null pointer argument requires an explicit interface"_err_en_US);
68bcb2591bSpeter klausler     } else if (auto named{evaluate::ExtractNamedEntity(*expr)}) {
6964ab3302SCarolineConcatto       const Symbol &symbol{named->GetLastSymbol()};
709ab292d7SPeter Klausler       if (evaluate::IsAssumedRank(symbol)) {
7164ab3302SCarolineConcatto         messages.Say(
7264ab3302SCarolineConcatto             "Assumed rank argument requires an explicit interface"_err_en_US);
7364ab3302SCarolineConcatto       }
7464ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::ASYNCHRONOUS)) {
7564ab3302SCarolineConcatto         messages.Say(
7664ab3302SCarolineConcatto             "ASYNCHRONOUS argument requires an explicit interface"_err_en_US);
7764ab3302SCarolineConcatto       }
7864ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::VOLATILE)) {
7964ab3302SCarolineConcatto         messages.Say(
8064ab3302SCarolineConcatto             "VOLATILE argument requires an explicit interface"_err_en_US);
8164ab3302SCarolineConcatto       }
82303ecc42SPeixinQiao     } else if (auto argChars{characteristics::DummyArgument::FromActual(
8333c27f28SPeter Klausler                    "actual argument", *expr, context.foldingContext(),
8429fd3e2aSPeter Klausler                    /*forImplicitInterface=*/true)}) {
85303ecc42SPeixinQiao       const auto *argProcDesignator{
86303ecc42SPeixinQiao           std::get_if<evaluate::ProcedureDesignator>(&expr->u)};
87bd28a0a5SPeter Klausler       if (const auto *argProcSymbol{
88bd28a0a5SPeter Klausler               argProcDesignator ? argProcDesignator->GetSymbol() : nullptr}) {
89bd28a0a5SPeter Klausler         if (!argChars->IsTypelessIntrinsicDummy() && argProcDesignator &&
90bd28a0a5SPeter Klausler             argProcDesignator->IsElemental()) { // C1533
91303ecc42SPeixinQiao           evaluate::SayWithDeclaration(messages, *argProcSymbol,
92303ecc42SPeixinQiao               "Non-intrinsic ELEMENTAL procedure '%s' may not be passed as an actual argument"_err_en_US,
93303ecc42SPeixinQiao               argProcSymbol->name());
94bd28a0a5SPeter Klausler         } else if (const auto *subp{argProcSymbol->GetUltimate()
95bd28a0a5SPeter Klausler                                         .detailsIf<SubprogramDetails>()}) {
96bd28a0a5SPeter Klausler           if (subp->stmtFunction()) {
97bd28a0a5SPeter Klausler             evaluate::SayWithDeclaration(messages, *argProcSymbol,
98bd28a0a5SPeter Klausler                 "Statement function '%s' may not be passed as an actual argument"_err_en_US,
99bd28a0a5SPeter Klausler                 argProcSymbol->name());
100bd28a0a5SPeter Klausler           }
101bd28a0a5SPeter Klausler         }
102303ecc42SPeixinQiao       }
10364ab3302SCarolineConcatto     }
10464ab3302SCarolineConcatto   }
10564ab3302SCarolineConcatto }
10664ab3302SCarolineConcatto 
1075718a425SPeter Klausler // F'2023 15.5.2.12p1: "Sequence association only applies when the dummy
1085718a425SPeter Klausler // argument is an explicit-shape or assumed-size array."
1095718a425SPeter Klausler static bool CanAssociateWithStorageSequence(
1105718a425SPeter Klausler     const characteristics::DummyDataObject &dummy) {
1115718a425SPeter Klausler   return !dummy.type.attrs().test(
1125718a425SPeter Klausler              characteristics::TypeAndShape::Attr::AssumedRank) &&
1135718a425SPeter Klausler       !dummy.type.attrs().test(
1145718a425SPeter Klausler           characteristics::TypeAndShape::Attr::AssumedShape) &&
1155718a425SPeter Klausler       !dummy.attrs.test(characteristics::DummyDataObject::Attr::Allocatable) &&
116*d732c86cSPeter Klausler       !dummy.attrs.test(characteristics::DummyDataObject::Attr::Pointer) &&
117*d732c86cSPeter Klausler       dummy.type.corank() == 0;
1185718a425SPeter Klausler }
1195718a425SPeter Klausler 
120f28c1a9dSPeter Klausler // When a CHARACTER actual argument is known to be short,
121f28c1a9dSPeter Klausler // we extend it on the right with spaces and a warning if
122f28c1a9dSPeter Klausler // possible.  When it is long, and not required to be equal,
123f28c1a9dSPeter Klausler // the usage conforms to the standard and no warning is needed.
124847c3983SPeter Klausler static void CheckCharacterActual(evaluate::Expr<evaluate::SomeType> &actual,
125f28c1a9dSPeter Klausler     const characteristics::DummyDataObject &dummy,
126191d4872SPeter Klausler     characteristics::TypeAndShape &actualType, SemanticsContext &context,
1275718a425SPeter Klausler     parser::ContextualMessages &messages, bool extentErrors,
1285718a425SPeter Klausler     const std::string &dummyName) {
129f28c1a9dSPeter Klausler   if (dummy.type.type().category() == TypeCategory::Character &&
13064ab3302SCarolineConcatto       actualType.type().category() == TypeCategory::Character &&
1315718a425SPeter Klausler       dummy.type.type().kind() == actualType.type().kind() &&
1325718a425SPeter Klausler       !dummy.attrs.test(
1335718a425SPeter Klausler           characteristics::DummyDataObject::Attr::DeducedFromActual)) {
134aa68dd57SPeter Klausler     bool actualIsAssumedRank{evaluate::IsAssumedRank(actual)};
135aa68dd57SPeter Klausler     if (actualIsAssumedRank &&
136aa68dd57SPeter Klausler         !dummy.type.attrs().test(
137aa68dd57SPeter Klausler             characteristics::TypeAndShape::Attr::AssumedRank)) {
138aa68dd57SPeter Klausler       if (!context.languageFeatures().IsEnabled(
139aa68dd57SPeter Klausler               common::LanguageFeature::AssumedRankPassedToNonAssumedRank)) {
140aa68dd57SPeter Klausler         messages.Say(
141aa68dd57SPeter Klausler             "Assumed-rank character array may not be associated with a dummy argument that is not assumed-rank"_err_en_US);
142aa68dd57SPeter Klausler       } else {
143aa68dd57SPeter Klausler         context.Warn(common::LanguageFeature::AssumedRankPassedToNonAssumedRank,
144aa68dd57SPeter Klausler             messages.at(),
145aa68dd57SPeter Klausler             "Assumed-rank character array should not be associated with a dummy argument that is not assumed-rank"_port_en_US);
146aa68dd57SPeter Klausler       }
147aa68dd57SPeter Klausler     }
148f28c1a9dSPeter Klausler     if (dummy.type.LEN() && actualType.LEN()) {
149191d4872SPeter Klausler       evaluate::FoldingContext &foldingContext{context.foldingContext()};
150f28c1a9dSPeter Klausler       auto dummyLength{
151191d4872SPeter Klausler           ToInt64(Fold(foldingContext, common::Clone(*dummy.type.LEN())))};
15253bf28b8Speter klausler       auto actualLength{
153191d4872SPeter Klausler           ToInt64(Fold(foldingContext, common::Clone(*actualType.LEN())))};
1545718a425SPeter Klausler       if (dummyLength && actualLength) {
1555718a425SPeter Klausler         bool canAssociate{CanAssociateWithStorageSequence(dummy)};
1565718a425SPeter Klausler         if (dummy.type.Rank() > 0 && canAssociate) {
1575718a425SPeter Klausler           // Character storage sequence association (F'2023 15.5.2.12p4)
15873cf0142SjeanPerier           if (auto dummySize{evaluate::ToInt64(evaluate::Fold(
15973cf0142SjeanPerier                   foldingContext, evaluate::GetSize(dummy.type.shape())))}) {
1605718a425SPeter Klausler             auto dummyChars{*dummySize * *dummyLength};
161aa68dd57SPeter Klausler             if (actualType.Rank() == 0 && !actualIsAssumedRank) {
1625718a425SPeter Klausler               evaluate::DesignatorFolder folder{
1635718a425SPeter Klausler                   context.foldingContext(), /*getLastComponent=*/true};
1645718a425SPeter Klausler               if (auto actualOffset{folder.FoldDesignator(actual)}) {
1655718a425SPeter Klausler                 std::int64_t actualChars{*actualLength};
166b0fab14eSPeter Klausler                 if (IsAllocatableOrPointer(actualOffset->symbol())) {
167b0fab14eSPeter Klausler                   // don't use actualOffset->symbol().size()!
168b0fab14eSPeter Klausler                 } else if (static_cast<std::size_t>(actualOffset->offset()) >=
1695718a425SPeter Klausler                         actualOffset->symbol().size() ||
1705718a425SPeter Klausler                     !evaluate::IsContiguous(
1715718a425SPeter Klausler                         actualOffset->symbol(), foldingContext)) {
1725718a425SPeter Klausler                   // If substring, take rest of substring
1735718a425SPeter Klausler                   if (*actualLength > 0) {
1745718a425SPeter Klausler                     actualChars -=
1755718a425SPeter Klausler                         (actualOffset->offset() / actualType.type().kind()) %
1765718a425SPeter Klausler                         *actualLength;
1775718a425SPeter Klausler                   }
1785718a425SPeter Klausler                 } else {
1795718a425SPeter Klausler                   actualChars = (static_cast<std::int64_t>(
1805718a425SPeter Klausler                                      actualOffset->symbol().size()) -
1815718a425SPeter Klausler                                     actualOffset->offset()) /
1825718a425SPeter Klausler                       actualType.type().kind();
1835718a425SPeter Klausler                 }
1840f973ac7SPeter Klausler                 if (actualChars < dummyChars) {
1855718a425SPeter Klausler                   if (extentErrors) {
1860f973ac7SPeter Klausler                     messages.Say(
1870f973ac7SPeter Klausler                         "Actual argument has fewer characters remaining in storage sequence (%jd) than %s (%jd)"_err_en_US,
1885718a425SPeter Klausler                         static_cast<std::intmax_t>(actualChars), dummyName,
1895718a425SPeter Klausler                         static_cast<std::intmax_t>(dummyChars));
1900f973ac7SPeter Klausler                   } else if (context.ShouldWarn(
1910f973ac7SPeter Klausler                                  common::UsageWarning::ShortCharacterActual)) {
1920f973ac7SPeter Klausler                     messages.Say(common::UsageWarning::ShortCharacterActual,
1930f973ac7SPeter Klausler                         "Actual argument has fewer characters remaining in storage sequence (%jd) than %s (%jd)"_warn_en_US,
1940f973ac7SPeter Klausler                         static_cast<std::intmax_t>(actualChars), dummyName,
1950f973ac7SPeter Klausler                         static_cast<std::intmax_t>(dummyChars));
1960f973ac7SPeter Klausler                   }
1975718a425SPeter Klausler                 }
1985718a425SPeter Klausler               }
1995718a425SPeter Klausler             } else { // actual.type.Rank() > 0
2005718a425SPeter Klausler               if (auto actualSize{evaluate::ToInt64(evaluate::Fold(
20173cf0142SjeanPerier                       foldingContext, evaluate::GetSize(actualType.shape())))};
2025718a425SPeter Klausler                   actualSize &&
2030f973ac7SPeter Klausler                   *actualSize * *actualLength < *dummySize * *dummyLength) {
2045718a425SPeter Klausler                 if (extentErrors) {
2050f973ac7SPeter Klausler                   messages.Say(
2060f973ac7SPeter Klausler                       "Actual argument array has fewer characters (%jd) than %s array (%jd)"_err_en_US,
2075718a425SPeter Klausler                       static_cast<std::intmax_t>(*actualSize * *actualLength),
2085718a425SPeter Klausler                       dummyName,
2095718a425SPeter Klausler                       static_cast<std::intmax_t>(*dummySize * *dummyLength));
2100f973ac7SPeter Klausler                 } else if (context.ShouldWarn(
2110f973ac7SPeter Klausler                                common::UsageWarning::ShortCharacterActual)) {
2120f973ac7SPeter Klausler                   messages.Say(common::UsageWarning::ShortCharacterActual,
2130f973ac7SPeter Klausler                       "Actual argument array has fewer characters (%jd) than %s array (%jd)"_warn_en_US,
2140f973ac7SPeter Klausler                       static_cast<std::intmax_t>(*actualSize * *actualLength),
2150f973ac7SPeter Klausler                       dummyName,
2160f973ac7SPeter Klausler                       static_cast<std::intmax_t>(*dummySize * *dummyLength));
2170f973ac7SPeter Klausler                 }
2185718a425SPeter Klausler               }
2195718a425SPeter Klausler             }
2205718a425SPeter Klausler           }
2215718a425SPeter Klausler         } else if (*actualLength != *dummyLength) {
2225718a425SPeter Klausler           // Not using storage sequence association, and the lengths don't
2235718a425SPeter Klausler           // match.
2245718a425SPeter Klausler           if (!canAssociate) {
2255718a425SPeter Klausler             // F'2023 15.5.2.5 paragraph 4
226f28c1a9dSPeter Klausler             messages.Say(
227f28c1a9dSPeter Klausler                 "Actual argument variable length '%jd' does not match the expected length '%jd'"_err_en_US,
228f28c1a9dSPeter Klausler                 *actualLength, *dummyLength);
229ede83e0eSPeter Klausler           } else if (*actualLength < *dummyLength) {
2305718a425SPeter Klausler             CHECK(dummy.type.Rank() == 0);
231ede83e0eSPeter Klausler             bool isVariable{evaluate::IsVariable(actual)};
2325718a425SPeter Klausler             if (context.ShouldWarn(
2335718a425SPeter Klausler                     common::UsageWarning::ShortCharacterActual)) {
234ede83e0eSPeter Klausler               if (isVariable) {
2350f973ac7SPeter Klausler                 messages.Say(common::UsageWarning::ShortCharacterActual,
236f28c1a9dSPeter Klausler                     "Actual argument variable length '%jd' is less than expected length '%jd'"_warn_en_US,
23753bf28b8Speter klausler                     *actualLength, *dummyLength);
238be768164SPeter Klausler               } else {
2390f973ac7SPeter Klausler                 messages.Say(common::UsageWarning::ShortCharacterActual,
240be768164SPeter Klausler                     "Actual argument expression length '%jd' is less than expected length '%jd'"_warn_en_US,
241be768164SPeter Klausler                     *actualLength, *dummyLength);
242ede83e0eSPeter Klausler               }
243ede83e0eSPeter Klausler             }
244ede83e0eSPeter Klausler             if (!isVariable) {
2455718a425SPeter Klausler               auto converted{
2465718a425SPeter Klausler                   ConvertToType(dummy.type.type(), std::move(actual))};
24764ab3302SCarolineConcatto               CHECK(converted);
24864ab3302SCarolineConcatto               actual = std::move(*converted);
24953bf28b8Speter klausler               actualType.set_LEN(SubscriptIntExpr{*dummyLength});
250be768164SPeter Klausler             }
25164ab3302SCarolineConcatto           }
25264ab3302SCarolineConcatto         }
25364ab3302SCarolineConcatto       }
25464ab3302SCarolineConcatto     }
255f28c1a9dSPeter Klausler   }
2565718a425SPeter Klausler }
25764ab3302SCarolineConcatto 
25864ab3302SCarolineConcatto // Automatic conversion of different-kind INTEGER scalar actual
25964ab3302SCarolineConcatto // argument expressions (not variables) to INTEGER scalar dummies.
26064ab3302SCarolineConcatto // We return nonstandard INTEGER(8) results from intrinsic functions
26164ab3302SCarolineConcatto // like SIZE() by default in order to facilitate the use of large
26264ab3302SCarolineConcatto // arrays.  Emit a warning when downconverting.
26364ab3302SCarolineConcatto static void ConvertIntegerActual(evaluate::Expr<evaluate::SomeType> &actual,
26464ab3302SCarolineConcatto     const characteristics::TypeAndShape &dummyType,
26564ab3302SCarolineConcatto     characteristics::TypeAndShape &actualType,
2661c91d9bdSPeter Klausler     parser::ContextualMessages &messages, SemanticsContext &semanticsContext) {
26764ab3302SCarolineConcatto   if (dummyType.type().category() == TypeCategory::Integer &&
26864ab3302SCarolineConcatto       actualType.type().category() == TypeCategory::Integer &&
26964ab3302SCarolineConcatto       dummyType.type().kind() != actualType.type().kind() &&
27073cf0142SjeanPerier       dummyType.Rank() == 0 && actualType.Rank() == 0 &&
27164ab3302SCarolineConcatto       !evaluate::IsVariable(actual)) {
27264ab3302SCarolineConcatto     auto converted{
27364ab3302SCarolineConcatto         evaluate::ConvertToType(dummyType.type(), std::move(actual))};
27464ab3302SCarolineConcatto     CHECK(converted);
27564ab3302SCarolineConcatto     actual = std::move(*converted);
27664ab3302SCarolineConcatto     if (dummyType.type().kind() < actualType.type().kind()) {
2771c91d9bdSPeter Klausler       if (!semanticsContext.IsEnabled(
2781c91d9bdSPeter Klausler               common::LanguageFeature::ActualIntegerConvertedToSmallerKind)) {
2790f973ac7SPeter Klausler         messages.Say(
2800f973ac7SPeter Klausler             "Actual argument scalar expression of type INTEGER(%d) cannot be implicitly converted to smaller dummy argument type INTEGER(%d)"_err_en_US,
2810f973ac7SPeter Klausler             actualType.type().kind(), dummyType.type().kind());
2820f973ac7SPeter Klausler       } else if (semanticsContext.ShouldWarn(common::LanguageFeature::
2830f973ac7SPeter Klausler                          ActualIntegerConvertedToSmallerKind)) {
2840f973ac7SPeter Klausler         messages.Say(
2850f973ac7SPeter Klausler             common::LanguageFeature::ActualIntegerConvertedToSmallerKind,
2860f973ac7SPeter Klausler             "Actual argument scalar expression of type INTEGER(%d) was converted to smaller dummy argument type INTEGER(%d)"_port_en_US,
2870f973ac7SPeter Klausler             actualType.type().kind(), dummyType.type().kind());
28864ab3302SCarolineConcatto       }
289505f6da1SPeter Klausler     }
29064ab3302SCarolineConcatto     actualType = dummyType;
29164ab3302SCarolineConcatto   }
29264ab3302SCarolineConcatto }
29364ab3302SCarolineConcatto 
294e6338254SDavid Truby // Automatic conversion of different-kind LOGICAL scalar actual argument
295e6338254SDavid Truby // expressions (not variables) to LOGICAL scalar dummies when the dummy is of
296e6338254SDavid Truby // default logical kind. This allows expressions in dummy arguments to work when
297e6338254SDavid Truby // the default logical kind is not the one used in LogicalResult. This will
298e6338254SDavid Truby // always be safe even when downconverting so no warning is needed.
299e6338254SDavid Truby static void ConvertLogicalActual(evaluate::Expr<evaluate::SomeType> &actual,
300e6338254SDavid Truby     const characteristics::TypeAndShape &dummyType,
301e6338254SDavid Truby     characteristics::TypeAndShape &actualType) {
302e6338254SDavid Truby   if (dummyType.type().category() == TypeCategory::Logical &&
303e6338254SDavid Truby       actualType.type().category() == TypeCategory::Logical &&
304e6338254SDavid Truby       dummyType.type().kind() != actualType.type().kind() &&
305e6338254SDavid Truby       !evaluate::IsVariable(actual)) {
306e6338254SDavid Truby     auto converted{
307e6338254SDavid Truby         evaluate::ConvertToType(dummyType.type(), std::move(actual))};
308e6338254SDavid Truby     CHECK(converted);
309e6338254SDavid Truby     actual = std::move(*converted);
310e6338254SDavid Truby     actualType = dummyType;
311e6338254SDavid Truby   }
312e6338254SDavid Truby }
313e6338254SDavid Truby 
31464ab3302SCarolineConcatto static bool DefersSameTypeParameters(
31570cbedcdSPeter Klausler     const DerivedTypeSpec *actual, const DerivedTypeSpec *dummy) {
31670cbedcdSPeter Klausler   if (actual && dummy) {
31770cbedcdSPeter Klausler     for (const auto &pair : actual->parameters()) {
31864ab3302SCarolineConcatto       const ParamValue &actualValue{pair.second};
31970cbedcdSPeter Klausler       const ParamValue *dummyValue{dummy->FindParameter(pair.first)};
32070cbedcdSPeter Klausler       if (!dummyValue ||
32170cbedcdSPeter Klausler           (actualValue.isDeferred() != dummyValue->isDeferred())) {
32264ab3302SCarolineConcatto         return false;
32364ab3302SCarolineConcatto       }
32464ab3302SCarolineConcatto     }
32570cbedcdSPeter Klausler   }
32664ab3302SCarolineConcatto   return true;
32764ab3302SCarolineConcatto }
32864ab3302SCarolineConcatto 
32964ab3302SCarolineConcatto static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
33064ab3302SCarolineConcatto     const std::string &dummyName, evaluate::Expr<evaluate::SomeType> &actual,
33164ab3302SCarolineConcatto     characteristics::TypeAndShape &actualType, bool isElemental,
332191d4872SPeter Klausler     SemanticsContext &context, evaluate::FoldingContext &foldingContext,
333191d4872SPeter Klausler     const Scope *scope, const evaluate::SpecificIntrinsic *intrinsic,
3346ceba01aSPeter Klausler     bool allowActualArgumentConversions, bool extentErrors,
335930c2d91SPeter Klausler     const characteristics::Procedure &procedure,
336930c2d91SPeter Klausler     const evaluate::ActualArgument &arg) {
33764ab3302SCarolineConcatto 
33864ab3302SCarolineConcatto   // Basic type & rank checking
339191d4872SPeter Klausler   parser::ContextualMessages &messages{foldingContext.messages()};
3405718a425SPeter Klausler   CheckCharacterActual(
3415718a425SPeter Klausler       actual, dummy, actualType, context, messages, extentErrors, dummyName);
342016d5a0aSPeter Klausler   bool dummyIsAllocatable{
343016d5a0aSPeter Klausler       dummy.attrs.test(characteristics::DummyDataObject::Attr::Allocatable)};
344016d5a0aSPeter Klausler   bool dummyIsPointer{
345016d5a0aSPeter Klausler       dummy.attrs.test(characteristics::DummyDataObject::Attr::Pointer)};
346016d5a0aSPeter Klausler   bool dummyIsAllocatableOrPointer{dummyIsAllocatable || dummyIsPointer};
347016d5a0aSPeter Klausler   allowActualArgumentConversions &= !dummyIsAllocatableOrPointer;
348f6e8b3ecSKelvin Li   bool typesCompatibleWithIgnoreTKR{
349864cb2aaSPeter Klausler       (dummy.ignoreTKR.test(common::IgnoreTKR::Type) &&
350864cb2aaSPeter Klausler           (dummy.type.type().category() == TypeCategory::Derived ||
351864cb2aaSPeter Klausler               actualType.type().category() == TypeCategory::Derived ||
352864cb2aaSPeter Klausler               dummy.type.type().category() != actualType.type().category())) ||
353864cb2aaSPeter Klausler       (dummy.ignoreTKR.test(common::IgnoreTKR::Kind) &&
354f6e8b3ecSKelvin Li           dummy.type.type().category() == actualType.type().category())};
355f6e8b3ecSKelvin Li   allowActualArgumentConversions &= !typesCompatibleWithIgnoreTKR;
356f6e8b3ecSKelvin Li   if (allowActualArgumentConversions) {
3571c91d9bdSPeter Klausler     ConvertIntegerActual(actual, dummy.type, actualType, messages, context);
358e6338254SDavid Truby     ConvertLogicalActual(actual, dummy.type, actualType);
359a519d76aSDavid Truby   }
360f6e8b3ecSKelvin Li   bool typesCompatible{typesCompatibleWithIgnoreTKR ||
361864cb2aaSPeter Klausler       dummy.type.type().IsTkCompatibleWith(actualType.type())};
3625718a425SPeter Klausler   int dummyRank{dummy.type.Rank()};
36303e50c45SPeter Klausler   if (typesCompatible) {
36403e50c45SPeter Klausler     if (const auto *constantChar{
36503e50c45SPeter Klausler             evaluate::UnwrapConstantValue<evaluate::Ascii>(actual)};
36603e50c45SPeter Klausler         constantChar && constantChar->wasHollerith() &&
367505f6da1SPeter Klausler         dummy.type.type().IsUnlimitedPolymorphic() &&
368505f6da1SPeter Klausler         context.ShouldWarn(common::LanguageFeature::HollerithPolymorphic)) {
3690f973ac7SPeter Klausler       messages.Say(common::LanguageFeature::HollerithPolymorphic,
37003e50c45SPeter Klausler           "passing Hollerith to unlimited polymorphic as if it were CHARACTER"_port_en_US);
37103e50c45SPeter Klausler     }
37203e50c45SPeter Klausler   } else if (dummyRank == 0 && allowActualArgumentConversions) {
373574f9dfeSPeter Klausler     // Extension: pass Hollerith literal to scalar as if it had been BOZ
374191d4872SPeter Klausler     if (auto converted{evaluate::HollerithToBOZ(
375191d4872SPeter Klausler             foldingContext, actual, dummy.type.type())}) {
3761c91d9bdSPeter Klausler       if (context.ShouldWarn(
3771c91d9bdSPeter Klausler               common::LanguageFeature::HollerithOrCharacterAsBOZ)) {
3780f973ac7SPeter Klausler         messages.Say(common::LanguageFeature::HollerithOrCharacterAsBOZ,
379574f9dfeSPeter Klausler             "passing Hollerith or character literal as if it were BOZ"_port_en_US);
3801c91d9bdSPeter Klausler       }
381574f9dfeSPeter Klausler       actual = *converted;
382574f9dfeSPeter Klausler       actualType.type() = dummy.type.type();
383574f9dfeSPeter Klausler       typesCompatible = true;
384574f9dfeSPeter Klausler     }
385574f9dfeSPeter Klausler   }
386f82ee155SPeter Klausler   bool dummyIsAssumedRank{dummy.type.attrs().test(
387f82ee155SPeter Klausler       characteristics::TypeAndShape::Attr::AssumedRank)};
38870cbedcdSPeter Klausler   bool actualIsAssumedSize{actualType.attrs().test(
38970cbedcdSPeter Klausler       characteristics::TypeAndShape::Attr::AssumedSize)};
39070cbedcdSPeter Klausler   bool actualIsAssumedRank{evaluate::IsAssumedRank(actual)};
39170cbedcdSPeter Klausler   bool actualIsPointer{evaluate::IsObjectPointer(actual)};
39270cbedcdSPeter Klausler   bool actualIsAllocatable{evaluate::IsAllocatableDesignator(actual)};
39370cbedcdSPeter Klausler   bool actualMayBeAssumedSize{actualIsAssumedSize ||
39470cbedcdSPeter Klausler       (actualIsAssumedRank && !actualIsPointer && !actualIsAllocatable)};
39570cbedcdSPeter Klausler   bool actualIsPolymorphic{actualType.type().IsPolymorphic()};
39670cbedcdSPeter Klausler   const auto *actualDerived{evaluate::GetDerivedTypeSpec(actualType.type())};
39764ab3302SCarolineConcatto   if (typesCompatible) {
39864ab3302SCarolineConcatto     if (isElemental) {
399f82ee155SPeter Klausler     } else if (dummyIsAssumedRank) {
40070cbedcdSPeter Klausler       if (actualMayBeAssumedSize && dummy.intent == common::Intent::Out) {
40170cbedcdSPeter Klausler         // An INTENT(OUT) dummy might be a no-op at run time
40270cbedcdSPeter Klausler         bool dummyHasSignificantIntentOut{actualIsPolymorphic ||
40370cbedcdSPeter Klausler             (actualDerived &&
40470cbedcdSPeter Klausler                 (actualDerived->HasDefaultInitialization(
40570cbedcdSPeter Klausler                      /*ignoreAllocatable=*/false, /*ignorePointer=*/true) ||
40670cbedcdSPeter Klausler                     actualDerived->HasDestruction()))};
40770cbedcdSPeter Klausler         const char *actualDesc{
40870cbedcdSPeter Klausler             actualIsAssumedSize ? "Assumed-size" : "Assumed-rank"};
40970cbedcdSPeter Klausler         if (dummyHasSignificantIntentOut) {
41070cbedcdSPeter Klausler           messages.Say(
41170cbedcdSPeter Klausler               "%s actual argument may not be associated with INTENT(OUT) assumed-rank dummy argument requiring finalization, destruction, or initialization"_err_en_US,
41270cbedcdSPeter Klausler               actualDesc);
41370cbedcdSPeter Klausler         } else {
41470cbedcdSPeter Klausler           context.Warn(common::UsageWarning::Portability, messages.at(),
41570cbedcdSPeter Klausler               "%s actual argument should not be associated with INTENT(OUT) assumed-rank dummy argument"_port_en_US,
41670cbedcdSPeter Klausler               actualDesc);
41770cbedcdSPeter Klausler         }
41870cbedcdSPeter Klausler       }
419864cb2aaSPeter Klausler     } else if (dummy.ignoreTKR.test(common::IgnoreTKR::Rank)) {
4205718a425SPeter Klausler     } else if (dummyRank > 0 && !dummyIsAllocatableOrPointer &&
42195f4ca7fSPeter Klausler         !dummy.type.attrs().test(
42264ab3302SCarolineConcatto             characteristics::TypeAndShape::Attr::AssumedShape) &&
42344bc97c8SPeter Klausler         !dummy.type.attrs().test(
42444bc97c8SPeter Klausler             characteristics::TypeAndShape::Attr::DeferredShape) &&
4250cfadb37Speter klausler         (actualType.Rank() > 0 || IsArrayElement(actual))) {
42664ab3302SCarolineConcatto       // Sequence association (15.5.2.11) applies -- rank need not match
42744bc97c8SPeter Klausler       // if the actual argument is an array or array element designator,
42895f4ca7fSPeter Klausler       // and the dummy is an array, but not assumed-shape or an INTENT(IN)
42995f4ca7fSPeter Klausler       // pointer that's standing in for an assumed-shape dummy.
43073cf0142SjeanPerier     } else if (dummy.type.shape() && actualType.shape()) {
43195f4ca7fSPeter Klausler       // Let CheckConformance accept actual scalars; storage association
432641ede93Speter klausler       // cases are checked here below.
43373cf0142SjeanPerier       CheckConformance(messages, *dummy.type.shape(), *actualType.shape(),
434016d5a0aSPeter Klausler           dummyIsAllocatableOrPointer
435016d5a0aSPeter Klausler               ? evaluate::CheckConformanceFlags::None
436016d5a0aSPeter Klausler               : evaluate::CheckConformanceFlags::RightScalarExpandable,
437dfecbcaeSpeter klausler           "dummy argument", "actual argument");
43864ab3302SCarolineConcatto     }
43964ab3302SCarolineConcatto   } else {
44064ab3302SCarolineConcatto     const auto &len{actualType.LEN()};
44164ab3302SCarolineConcatto     messages.Say(
44264ab3302SCarolineConcatto         "Actual argument type '%s' is not compatible with dummy argument type '%s'"_err_en_US,
44364ab3302SCarolineConcatto         actualType.type().AsFortran(len ? len->AsFortran() : ""),
44464ab3302SCarolineConcatto         dummy.type.type().AsFortran());
44564ab3302SCarolineConcatto   }
44664ab3302SCarolineConcatto 
44764ab3302SCarolineConcatto   bool actualIsCoindexed{ExtractCoarrayRef(actual).has_value()};
44864ab3302SCarolineConcatto   bool dummyIsAssumedSize{dummy.type.attrs().test(
44964ab3302SCarolineConcatto       characteristics::TypeAndShape::Attr::AssumedSize)};
45064ab3302SCarolineConcatto   bool dummyIsAsynchronous{
45164ab3302SCarolineConcatto       dummy.attrs.test(characteristics::DummyDataObject::Attr::Asynchronous)};
45264ab3302SCarolineConcatto   bool dummyIsVolatile{
45364ab3302SCarolineConcatto       dummy.attrs.test(characteristics::DummyDataObject::Attr::Volatile)};
45464ab3302SCarolineConcatto   bool dummyIsValue{
45564ab3302SCarolineConcatto       dummy.attrs.test(characteristics::DummyDataObject::Attr::Value)};
45670cbedcdSPeter Klausler   bool dummyIsPolymorphic{dummy.type.type().IsPolymorphic()};
45764ab3302SCarolineConcatto   if (actualIsPolymorphic && dummyIsPolymorphic &&
45864ab3302SCarolineConcatto       actualIsCoindexed) { // 15.5.2.4(2)
45964ab3302SCarolineConcatto     messages.Say(
46064ab3302SCarolineConcatto         "Coindexed polymorphic object may not be associated with a polymorphic %s"_err_en_US,
46164ab3302SCarolineConcatto         dummyName);
46264ab3302SCarolineConcatto   }
46364ab3302SCarolineConcatto   if (actualIsPolymorphic && !dummyIsPolymorphic &&
46464ab3302SCarolineConcatto       actualIsAssumedSize) { // 15.5.2.4(2)
46564ab3302SCarolineConcatto     messages.Say(
46664ab3302SCarolineConcatto         "Assumed-size polymorphic array may not be associated with a monomorphic %s"_err_en_US,
46764ab3302SCarolineConcatto         dummyName);
46864ab3302SCarolineConcatto   }
46964ab3302SCarolineConcatto 
47064ab3302SCarolineConcatto   // Derived type actual argument checks
47164ab3302SCarolineConcatto   const Symbol *actualFirstSymbol{evaluate::GetFirstSymbol(actual)};
47264ab3302SCarolineConcatto   bool actualIsAsynchronous{
47364ab3302SCarolineConcatto       actualFirstSymbol && actualFirstSymbol->attrs().test(Attr::ASYNCHRONOUS)};
47464ab3302SCarolineConcatto   bool actualIsVolatile{
47564ab3302SCarolineConcatto       actualFirstSymbol && actualFirstSymbol->attrs().test(Attr::VOLATILE)};
47670cbedcdSPeter Klausler   if (actualDerived && !actualDerived->IsVectorType()) {
47764ab3302SCarolineConcatto     if (dummy.type.type().IsAssumedType()) {
47870cbedcdSPeter Klausler       if (!actualDerived->parameters().empty()) { // 15.5.2.4(2)
47964ab3302SCarolineConcatto         messages.Say(
48064ab3302SCarolineConcatto             "Actual argument associated with TYPE(*) %s may not have a parameterized derived type"_err_en_US,
48164ab3302SCarolineConcatto             dummyName);
48264ab3302SCarolineConcatto       }
48364ab3302SCarolineConcatto       if (const Symbol *
48470cbedcdSPeter Klausler           tbp{FindImmediateComponent(*actualDerived, [](const Symbol &symbol) {
48564ab3302SCarolineConcatto             return symbol.has<ProcBindingDetails>();
48664ab3302SCarolineConcatto           })}) { // 15.5.2.4(2)
48764ab3302SCarolineConcatto         evaluate::SayWithDeclaration(messages, *tbp,
48864ab3302SCarolineConcatto             "Actual argument associated with TYPE(*) %s may not have type-bound procedure '%s'"_err_en_US,
48964ab3302SCarolineConcatto             dummyName, tbp->name());
49064ab3302SCarolineConcatto       }
49170cbedcdSPeter Klausler       auto finals{FinalsForDerivedTypeInstantiation(*actualDerived)};
49237b2e2b0Speter klausler       if (!finals.empty()) { // 15.5.2.4(2)
493d84faa42SPeter Klausler         SourceName name{finals.front()->name()};
49437b2e2b0Speter klausler         if (auto *msg{messages.Say(
49537b2e2b0Speter klausler                 "Actual argument associated with TYPE(*) %s may not have derived type '%s' with FINAL subroutine '%s'"_err_en_US,
49670cbedcdSPeter Klausler                 dummyName, actualDerived->typeSymbol().name(), name)}) {
497d84faa42SPeter Klausler           msg->Attach(name, "FINAL subroutine '%s' in derived type '%s'"_en_US,
49870cbedcdSPeter Klausler               name, actualDerived->typeSymbol().name());
49937b2e2b0Speter klausler         }
50064ab3302SCarolineConcatto       }
50164ab3302SCarolineConcatto     }
50264ab3302SCarolineConcatto     if (actualIsCoindexed) {
50364ab3302SCarolineConcatto       if (dummy.intent != common::Intent::In && !dummyIsValue) {
50470cbedcdSPeter Klausler         if (auto bad{FindAllocatableUltimateComponent(
50570cbedcdSPeter Klausler                 *actualDerived)}) { // 15.5.2.4(6)
50664ab3302SCarolineConcatto           evaluate::SayWithDeclaration(messages, *bad,
50764ab3302SCarolineConcatto               "Coindexed actual argument with ALLOCATABLE ultimate component '%s' must be associated with a %s with VALUE or INTENT(IN) attributes"_err_en_US,
50864ab3302SCarolineConcatto               bad.BuildResultDesignatorName(), dummyName);
50964ab3302SCarolineConcatto         }
51064ab3302SCarolineConcatto       }
51164ab3302SCarolineConcatto       if (auto coarrayRef{evaluate::ExtractCoarrayRef(actual)}) { // C1537
51264ab3302SCarolineConcatto         const Symbol &coarray{coarrayRef->GetLastSymbol()};
51364ab3302SCarolineConcatto         if (const DeclTypeSpec * type{coarray.GetType()}) {
51464ab3302SCarolineConcatto           if (const DerivedTypeSpec * derived{type->AsDerived()}) {
51564ab3302SCarolineConcatto             if (auto bad{semantics::FindPointerUltimateComponent(*derived)}) {
51664ab3302SCarolineConcatto               evaluate::SayWithDeclaration(messages, coarray,
51764ab3302SCarolineConcatto                   "Coindexed object '%s' with POINTER ultimate component '%s' cannot be associated with %s"_err_en_US,
51864ab3302SCarolineConcatto                   coarray.name(), bad.BuildResultDesignatorName(), dummyName);
51964ab3302SCarolineConcatto             }
52064ab3302SCarolineConcatto           }
52164ab3302SCarolineConcatto         }
52264ab3302SCarolineConcatto       }
52364ab3302SCarolineConcatto     }
52464ab3302SCarolineConcatto     if (actualIsVolatile != dummyIsVolatile) { // 15.5.2.4(22)
52570cbedcdSPeter Klausler       if (auto bad{semantics::FindCoarrayUltimateComponent(*actualDerived)}) {
52664ab3302SCarolineConcatto         evaluate::SayWithDeclaration(messages, *bad,
52764ab3302SCarolineConcatto             "VOLATILE attribute must match for %s when actual argument has a coarray ultimate component '%s'"_err_en_US,
52864ab3302SCarolineConcatto             dummyName, bad.BuildResultDesignatorName());
52964ab3302SCarolineConcatto       }
53064ab3302SCarolineConcatto     }
53164ab3302SCarolineConcatto   }
53264ab3302SCarolineConcatto 
53364ab3302SCarolineConcatto   // Rank and shape checks
53464ab3302SCarolineConcatto   const auto *actualLastSymbol{evaluate::GetLastSymbol(actual)};
53564ab3302SCarolineConcatto   if (actualLastSymbol) {
536a50bb84eSpeter klausler     actualLastSymbol = &ResolveAssociations(*actualLastSymbol);
53764ab3302SCarolineConcatto   }
53864ab3302SCarolineConcatto   const ObjectEntityDetails *actualLastObject{actualLastSymbol
539a50bb84eSpeter klausler           ? actualLastSymbol->detailsIf<ObjectEntityDetails>()
54064ab3302SCarolineConcatto           : nullptr};
5415718a425SPeter Klausler   int actualRank{actualType.Rank()};
54264ab3302SCarolineConcatto   if (dummy.type.attrs().test(
54364ab3302SCarolineConcatto           characteristics::TypeAndShape::Attr::AssumedShape)) {
54464ab3302SCarolineConcatto     // 15.5.2.4(16)
5459652e9b7SPeter Klausler     if (actualIsAssumedRank) {
5469652e9b7SPeter Klausler       messages.Say(
5479652e9b7SPeter Klausler           "Assumed-rank actual argument may not be associated with assumed-shape %s"_err_en_US,
5489652e9b7SPeter Klausler           dummyName);
5499652e9b7SPeter Klausler     } else if (actualRank == 0) {
55064ab3302SCarolineConcatto       messages.Say(
55164ab3302SCarolineConcatto           "Scalar actual argument may not be associated with assumed-shape %s"_err_en_US,
55264ab3302SCarolineConcatto           dummyName);
5539652e9b7SPeter Klausler     } else if (actualIsAssumedSize && actualLastSymbol) {
55464ab3302SCarolineConcatto       evaluate::SayWithDeclaration(messages, *actualLastSymbol,
55564ab3302SCarolineConcatto           "Assumed-size array may not be associated with assumed-shape %s"_err_en_US,
55664ab3302SCarolineConcatto           dummyName);
55764ab3302SCarolineConcatto     }
5585718a425SPeter Klausler   } else if (dummyRank > 0) {
5595718a425SPeter Klausler     bool basicError{false};
5609652e9b7SPeter Klausler     if (actualRank == 0 && !actualIsAssumedRank &&
5619652e9b7SPeter Klausler         !dummyIsAllocatableOrPointer) {
5625718a425SPeter Klausler       // Actual is scalar, dummy is an array.  F'2023 15.5.2.5p14
56364ab3302SCarolineConcatto       if (actualIsCoindexed) {
5645718a425SPeter Klausler         basicError = true;
56564ab3302SCarolineConcatto         messages.Say(
56664ab3302SCarolineConcatto             "Coindexed scalar actual argument must be associated with a scalar %s"_err_en_US,
56764ab3302SCarolineConcatto             dummyName);
56864ab3302SCarolineConcatto       }
569a7802a80SJean Perier       bool actualIsArrayElement{IsArrayElement(actual)};
570a7802a80SJean Perier       bool actualIsCKindCharacter{
571a7802a80SJean Perier           actualType.type().category() == TypeCategory::Character &&
572a7802a80SJean Perier           actualType.type().kind() == 1};
573a7802a80SJean Perier       if (!actualIsCKindCharacter) {
574a7802a80SJean Perier         if (!actualIsArrayElement &&
5756f3d322fSpeter klausler             !(dummy.type.type().IsAssumedType() && dummyIsAssumedSize) &&
576864cb2aaSPeter Klausler             !dummyIsAssumedRank &&
577864cb2aaSPeter Klausler             !dummy.ignoreTKR.test(common::IgnoreTKR::Rank)) {
5785718a425SPeter Klausler           basicError = true;
57964ab3302SCarolineConcatto           messages.Say(
58064ab3302SCarolineConcatto               "Whole scalar actual argument may not be associated with a %s array"_err_en_US,
58164ab3302SCarolineConcatto               dummyName);
58264ab3302SCarolineConcatto         }
58364ab3302SCarolineConcatto         if (actualIsPolymorphic) {
5845718a425SPeter Klausler           basicError = true;
58564ab3302SCarolineConcatto           messages.Say(
58664ab3302SCarolineConcatto               "Polymorphic scalar may not be associated with a %s array"_err_en_US,
58764ab3302SCarolineConcatto               dummyName);
58864ab3302SCarolineConcatto         }
589a7802a80SJean Perier         if (actualIsArrayElement && actualLastSymbol &&
59050e2581aSTom Eccles             !evaluate::IsContiguous(*actualLastSymbol, foldingContext) &&
59150e2581aSTom Eccles             !dummy.ignoreTKR.test(common::IgnoreTKR::Contiguous)) {
592828bfbefSPeter Klausler           if (IsPointer(*actualLastSymbol)) {
5935718a425SPeter Klausler             basicError = true;
59464ab3302SCarolineConcatto             messages.Say(
595a7802a80SJean Perier                 "Element of pointer array may not be associated with a %s array"_err_en_US,
59664ab3302SCarolineConcatto                 dummyName);
59750e2581aSTom Eccles           } else if (IsAssumedShape(*actualLastSymbol) &&
59850e2581aSTom Eccles               !dummy.ignoreTKR.test(common::IgnoreTKR::Contiguous)) {
5995718a425SPeter Klausler             basicError = true;
60064ab3302SCarolineConcatto             messages.Say(
60164ab3302SCarolineConcatto                 "Element of assumed-shape array may not be associated with a %s array"_err_en_US,
60264ab3302SCarolineConcatto                 dummyName);
60364ab3302SCarolineConcatto           }
60464ab3302SCarolineConcatto         }
6055718a425SPeter Klausler       }
606828bfbefSPeter Klausler     }
6075718a425SPeter Klausler     // Storage sequence association (F'2023 15.5.2.12p3) checks.
6085718a425SPeter Klausler     // Character storage sequence association is checked in
6095718a425SPeter Klausler     // CheckCharacterActual().
6105718a425SPeter Klausler     if (!basicError &&
6115718a425SPeter Klausler         actualType.type().category() != TypeCategory::Character &&
6125718a425SPeter Klausler         CanAssociateWithStorageSequence(dummy) &&
6135718a425SPeter Klausler         !dummy.attrs.test(
6145718a425SPeter Klausler             characteristics::DummyDataObject::Attr::DeducedFromActual)) {
61573cf0142SjeanPerier       if (auto dummySize{evaluate::ToInt64(evaluate::Fold(
61673cf0142SjeanPerier               foldingContext, evaluate::GetSize(dummy.type.shape())))}) {
617aa68dd57SPeter Klausler         if (actualIsAssumedRank) {
618aa68dd57SPeter Klausler           if (!context.languageFeatures().IsEnabled(
619aa68dd57SPeter Klausler                   common::LanguageFeature::AssumedRankPassedToNonAssumedRank)) {
620aa68dd57SPeter Klausler             messages.Say(
621aa68dd57SPeter Klausler                 "Assumed-rank array may not be associated with a dummy argument that is not assumed-rank"_err_en_US);
622aa68dd57SPeter Klausler           } else {
623aa68dd57SPeter Klausler             context.Warn(
624aa68dd57SPeter Klausler                 common::LanguageFeature::AssumedRankPassedToNonAssumedRank,
625aa68dd57SPeter Klausler                 messages.at(),
626aa68dd57SPeter Klausler                 "Assumed-rank array should not be associated with a dummy argument that is not assumed-rank"_port_en_US);
627aa68dd57SPeter Klausler           }
628aa68dd57SPeter Klausler         } else if (actualRank == 0) {
6295718a425SPeter Klausler           if (evaluate::IsArrayElement(actual)) {
6305718a425SPeter Klausler             // Actual argument is a scalar array element
6315718a425SPeter Klausler             evaluate::DesignatorFolder folder{
6325718a425SPeter Klausler                 context.foldingContext(), /*getLastComponent=*/true};
6335718a425SPeter Klausler             if (auto actualOffset{folder.FoldDesignator(actual)}) {
6345718a425SPeter Klausler               std::optional<std::int64_t> actualElements;
635b0fab14eSPeter Klausler               if (IsAllocatableOrPointer(actualOffset->symbol())) {
636b0fab14eSPeter Klausler                 // don't use actualOffset->symbol().size()!
637b0fab14eSPeter Klausler               } else if (static_cast<std::size_t>(actualOffset->offset()) >=
6385718a425SPeter Klausler                       actualOffset->symbol().size() ||
6395718a425SPeter Klausler                   !evaluate::IsContiguous(
6405718a425SPeter Klausler                       actualOffset->symbol(), foldingContext)) {
6415718a425SPeter Klausler                 actualElements = 1;
6425718a425SPeter Klausler               } else if (auto actualSymType{evaluate::DynamicType::From(
6435718a425SPeter Klausler                              actualOffset->symbol())}) {
6445718a425SPeter Klausler                 if (auto actualSymTypeBytes{
6455718a425SPeter Klausler                         evaluate::ToInt64(evaluate::Fold(foldingContext,
6465718a425SPeter Klausler                             actualSymType->MeasureSizeInBytes(
6475718a425SPeter Klausler                                 foldingContext, false)))};
6485718a425SPeter Klausler                     actualSymTypeBytes && *actualSymTypeBytes > 0) {
6495718a425SPeter Klausler                   actualElements = (static_cast<std::int64_t>(
6505718a425SPeter Klausler                                         actualOffset->symbol().size()) -
6515718a425SPeter Klausler                                        actualOffset->offset()) /
6525718a425SPeter Klausler                       *actualSymTypeBytes;
6535718a425SPeter Klausler                 }
6545718a425SPeter Klausler               }
6550f973ac7SPeter Klausler               if (actualElements && *actualElements < *dummySize) {
6566ceba01aSPeter Klausler                 if (extentErrors) {
6570f973ac7SPeter Klausler                   messages.Say(
6580f973ac7SPeter Klausler                       "Actual argument has fewer elements remaining in storage sequence (%jd) than %s array (%jd)"_err_en_US,
6595718a425SPeter Klausler                       static_cast<std::intmax_t>(*actualElements), dummyName,
6605718a425SPeter Klausler                       static_cast<std::intmax_t>(*dummySize));
6610f973ac7SPeter Klausler                 } else if (context.ShouldWarn(
6620f973ac7SPeter Klausler                                common::UsageWarning::ShortArrayActual)) {
6630f973ac7SPeter Klausler                   messages.Say(common::UsageWarning::ShortArrayActual,
6640f973ac7SPeter Klausler                       "Actual argument has fewer elements remaining in storage sequence (%jd) than %s array (%jd)"_warn_en_US,
6650f973ac7SPeter Klausler                       static_cast<std::intmax_t>(*actualElements), dummyName,
6660f973ac7SPeter Klausler                       static_cast<std::intmax_t>(*dummySize));
6670f973ac7SPeter Klausler                 }
6685718a425SPeter Klausler               }
6695718a425SPeter Klausler             }
6705718a425SPeter Klausler           }
671aa68dd57SPeter Klausler         } else {
67273cf0142SjeanPerier           if (auto actualSize{evaluate::ToInt64(evaluate::Fold(
67373cf0142SjeanPerier                   foldingContext, evaluate::GetSize(actualType.shape())))};
6740f973ac7SPeter Klausler               actualSize && *actualSize < *dummySize) {
6755718a425SPeter Klausler             if (extentErrors) {
6760f973ac7SPeter Klausler               messages.Say(
6770f973ac7SPeter Klausler                   "Actual argument array has fewer elements (%jd) than %s array (%jd)"_err_en_US,
6785718a425SPeter Klausler                   static_cast<std::intmax_t>(*actualSize), dummyName,
6795718a425SPeter Klausler                   static_cast<std::intmax_t>(*dummySize));
6800f973ac7SPeter Klausler             } else if (context.ShouldWarn(
6810f973ac7SPeter Klausler                            common::UsageWarning::ShortArrayActual)) {
6820f973ac7SPeter Klausler               messages.Say(common::UsageWarning::ShortArrayActual,
6830f973ac7SPeter Klausler                   "Actual argument array has fewer elements (%jd) than %s array (%jd)"_warn_en_US,
6840f973ac7SPeter Klausler                   static_cast<std::intmax_t>(*actualSize), dummyName,
6850f973ac7SPeter Klausler                   static_cast<std::intmax_t>(*dummySize));
6860f973ac7SPeter Klausler             }
6875718a425SPeter Klausler           }
6886ceba01aSPeter Klausler         }
6896ceba01aSPeter Klausler       }
6906ceba01aSPeter Klausler     }
691a7802a80SJean Perier   }
69264ab3302SCarolineConcatto   if (actualLastObject && actualLastObject->IsCoarray() &&
693510285cdSPeter Klausler       dummy.attrs.test(characteristics::DummyDataObject::Attr::Allocatable) &&
694510285cdSPeter Klausler       dummy.intent == common::Intent::Out &&
69529d1a494SJean Perier       !(intrinsic &&
69629d1a494SJean Perier           evaluate::AcceptsIntentOutAllocatableCoarray(
69729d1a494SJean Perier               intrinsic->name))) { // C846
69864ab3302SCarolineConcatto     messages.Say(
69964ab3302SCarolineConcatto         "ALLOCATABLE coarray '%s' may not be associated with INTENT(OUT) %s"_err_en_US,
70064ab3302SCarolineConcatto         actualLastSymbol->name(), dummyName);
70164ab3302SCarolineConcatto   }
70264ab3302SCarolineConcatto 
70333c27f28SPeter Klausler   // Definability checking
70433c27f28SPeter Klausler   // Problems with polymorphism are caught in the callee's definition.
705caa0a269SPeter Klausler   if (scope) {
706caa0a269SPeter Klausler     std::optional<parser::MessageFixedText> undefinableMessage;
70707b3bba9SPeter Klausler     DefinabilityFlags flags{DefinabilityFlag::PolymorphicOkInPure};
70807b3bba9SPeter Klausler     if (dummy.intent == common::Intent::InOut) {
70907b3bba9SPeter Klausler       flags.set(DefinabilityFlag::AllowEventLockOrNotifyType);
710caa0a269SPeter Klausler       undefinableMessage =
711caa0a269SPeter Klausler           "Actual argument associated with INTENT(IN OUT) %s is not definable"_err_en_US;
71207b3bba9SPeter Klausler     } else if (dummy.intent == common::Intent::Out) {
71307b3bba9SPeter Klausler       undefinableMessage =
71407b3bba9SPeter Klausler           "Actual argument associated with INTENT(OUT) %s is not definable"_err_en_US;
715caa0a269SPeter Klausler     } else if (context.ShouldWarn(common::LanguageFeature::
716caa0a269SPeter Klausler                        UndefinableAsynchronousOrVolatileActual)) {
717caa0a269SPeter Klausler       if (dummy.attrs.test(
718caa0a269SPeter Klausler               characteristics::DummyDataObject::Attr::Asynchronous)) {
719caa0a269SPeter Klausler         undefinableMessage =
720caa0a269SPeter Klausler             "Actual argument associated with ASYNCHRONOUS %s is not definable"_warn_en_US;
721caa0a269SPeter Klausler       } else if (dummy.attrs.test(
722caa0a269SPeter Klausler                      characteristics::DummyDataObject::Attr::Volatile)) {
723caa0a269SPeter Klausler         undefinableMessage =
724caa0a269SPeter Klausler             "Actual argument associated with VOLATILE %s is not definable"_warn_en_US;
72564ab3302SCarolineConcatto       }
726caa0a269SPeter Klausler     }
727caa0a269SPeter Klausler     if (undefinableMessage) {
72822ed61edSPeter Klausler       if (isElemental) { // 15.5.2.4(21)
729573fc618SPeter Klausler         flags.set(DefinabilityFlag::VectorSubscriptIsOk);
730573fc618SPeter Klausler       }
731066aecffSPeter Klausler       if (actualIsPointer && dummyIsPointer) { // 19.6.8
732066aecffSPeter Klausler         flags.set(DefinabilityFlag::PointerDefinition);
733066aecffSPeter Klausler       }
734573fc618SPeter Klausler       if (auto whyNot{WhyNotDefinable(messages.at(), *scope, flags, actual)}) {
735d5285fefSPeter Klausler         if (whyNot->IsFatal()) {
7360f973ac7SPeter Klausler           if (auto *msg{messages.Say(*undefinableMessage, dummyName)}) {
7370f973ac7SPeter Klausler             if (!msg->IsFatal()) {
7380f973ac7SPeter Klausler               msg->set_languageFeature(common::LanguageFeature::
7390f973ac7SPeter Klausler                       UndefinableAsynchronousOrVolatileActual);
7400f973ac7SPeter Klausler             }
741d5285fefSPeter Klausler             msg->Attach(
742d5285fefSPeter Klausler                 std::move(whyNot->set_severity(parser::Severity::Because)));
743d5285fefSPeter Klausler           }
744d5285fefSPeter Klausler         } else {
745d5285fefSPeter Klausler           messages.Say(std::move(*whyNot));
74664ab3302SCarolineConcatto         }
74764ab3302SCarolineConcatto       }
74833c27f28SPeter Klausler     } else if (dummy.intent != common::Intent::In ||
74933c27f28SPeter Klausler         (dummyIsPointer && !actualIsPointer)) {
75033c27f28SPeter Klausler       if (auto named{evaluate::ExtractNamedEntity(actual)}) {
75133c27f28SPeter Klausler         if (const Symbol & base{named->GetFirstSymbol()};
75233c27f28SPeter Klausler             IsFunctionResult(base)) {
75333c27f28SPeter Klausler           context.NoteDefinedSymbol(base);
75433c27f28SPeter Klausler         }
75533c27f28SPeter Klausler       }
75664ab3302SCarolineConcatto     }
7579c87746dSCabrera, Anthony   }
7589c87746dSCabrera, Anthony 
75964ab3302SCarolineConcatto   // Cases when temporaries might be needed but must not be permitted.
760191d4872SPeter Klausler   bool actualIsContiguous{IsSimplyContiguous(actual, foldingContext)};
76144bc97c8SPeter Klausler   bool dummyIsAssumedShape{dummy.type.attrs().test(
76244bc97c8SPeter Klausler       characteristics::TypeAndShape::Attr::AssumedShape)};
76364ab3302SCarolineConcatto   bool dummyIsContiguous{
76464ab3302SCarolineConcatto       dummy.attrs.test(characteristics::DummyDataObject::Attr::Contiguous)};
76564ab3302SCarolineConcatto   if ((actualIsAsynchronous || actualIsVolatile) &&
76664ab3302SCarolineConcatto       (dummyIsAsynchronous || dummyIsVolatile) && !dummyIsValue) {
76764ab3302SCarolineConcatto     if (actualIsCoindexed) { // C1538
76864ab3302SCarolineConcatto       messages.Say(
76964ab3302SCarolineConcatto           "Coindexed ASYNCHRONOUS or VOLATILE actual argument may not be associated with %s with ASYNCHRONOUS or VOLATILE attributes unless VALUE"_err_en_US,
77064ab3302SCarolineConcatto           dummyName);
77164ab3302SCarolineConcatto     }
7729652e9b7SPeter Klausler     if ((actualRank > 0 || actualIsAssumedRank) && !actualIsContiguous) {
77364ab3302SCarolineConcatto       if (dummyIsContiguous ||
77464ab3302SCarolineConcatto           !(dummyIsAssumedShape || dummyIsAssumedRank ||
77564ab3302SCarolineConcatto               (actualIsPointer && dummyIsPointer))) { // C1539 & C1540
77664ab3302SCarolineConcatto         messages.Say(
777f31ac3cbSPeter Klausler             "ASYNCHRONOUS or VOLATILE actual argument that is not simply contiguous may not be associated with a contiguous ASYNCHRONOUS or VOLATILE %s"_err_en_US,
77864ab3302SCarolineConcatto             dummyName);
77964ab3302SCarolineConcatto       }
78064ab3302SCarolineConcatto     }
78164ab3302SCarolineConcatto   }
78264ab3302SCarolineConcatto 
78364ab3302SCarolineConcatto   // 15.5.2.6 -- dummy is ALLOCATABLE
784b76e08dbSPeter Klausler   bool dummyIsOptional{
785b76e08dbSPeter Klausler       dummy.attrs.test(characteristics::DummyDataObject::Attr::Optional)};
786b76e08dbSPeter Klausler   bool actualIsNull{evaluate::IsNullPointer(actual)};
78764ab3302SCarolineConcatto   if (dummyIsAllocatable) {
78861d361d6SPeter Klausler     if (actualIsAllocatable) {
78961d361d6SPeter Klausler       if (actualIsCoindexed && dummy.intent != common::Intent::In) {
79064ab3302SCarolineConcatto         messages.Say(
79164ab3302SCarolineConcatto             "ALLOCATABLE %s must have INTENT(IN) to be associated with a coindexed actual argument"_err_en_US,
79264ab3302SCarolineConcatto             dummyName);
79364ab3302SCarolineConcatto       }
79461d361d6SPeter Klausler     } else if (actualIsNull) {
79561d361d6SPeter Klausler       if (dummyIsOptional) {
79661d361d6SPeter Klausler       } else if (dummy.intent == common::Intent::In) {
79761d361d6SPeter Klausler         // Extension (Intel, NAG, XLF): a NULL() pointer is an acceptable
79861d361d6SPeter Klausler         // actual argument for an INTENT(IN) allocatable dummy, and it
79961d361d6SPeter Klausler         // is treated as an unassociated allocatable.
800505f6da1SPeter Klausler         if (context.ShouldWarn(
80161d361d6SPeter Klausler                 common::LanguageFeature::NullActualForAllocatable)) {
8020f973ac7SPeter Klausler           messages.Say(common::LanguageFeature::NullActualForAllocatable,
80361d361d6SPeter Klausler               "Allocatable %s is associated with a null pointer"_port_en_US,
80461d361d6SPeter Klausler               dummyName);
80561d361d6SPeter Klausler         }
80661d361d6SPeter Klausler       } else {
80761d361d6SPeter Klausler         messages.Say(
80861d361d6SPeter Klausler             "A null pointer may not be associated with allocatable %s without INTENT(IN)"_err_en_US,
80961d361d6SPeter Klausler             dummyName);
81061d361d6SPeter Klausler       }
81161d361d6SPeter Klausler     } else {
81261d361d6SPeter Klausler       messages.Say(
81361d361d6SPeter Klausler           "ALLOCATABLE %s must be associated with an ALLOCATABLE actual argument"_err_en_US,
81461d361d6SPeter Klausler           dummyName);
81561d361d6SPeter Klausler     }
81664ab3302SCarolineConcatto     if (!actualIsCoindexed && actualLastSymbol &&
81764ab3302SCarolineConcatto         actualLastSymbol->Corank() != dummy.type.corank()) {
81864ab3302SCarolineConcatto       messages.Say(
81964ab3302SCarolineConcatto           "ALLOCATABLE %s has corank %d but actual argument has corank %d"_err_en_US,
82064ab3302SCarolineConcatto           dummyName, dummy.type.corank(), actualLastSymbol->Corank());
82164ab3302SCarolineConcatto     }
82264ab3302SCarolineConcatto   }
82364ab3302SCarolineConcatto 
82464ab3302SCarolineConcatto   // 15.5.2.7 -- dummy is POINTER
82564ab3302SCarolineConcatto   if (dummyIsPointer) {
8261fa9ef62SPeter Klausler     if (actualIsPointer || dummy.intent == common::Intent::In) {
827573fc618SPeter Klausler       if (scope) {
828f82ee155SPeter Klausler         semantics::CheckPointerAssignment(context, messages.at(), dummyName,
829f82ee155SPeter Klausler             dummy, actual, *scope,
830f82ee155SPeter Klausler             /*isAssumedRank=*/dummyIsAssumedRank);
831573fc618SPeter Klausler       }
8321fa9ef62SPeter Klausler     } else if (!actualIsPointer) {
83364ab3302SCarolineConcatto       messages.Say(
83464ab3302SCarolineConcatto           "Actual argument associated with POINTER %s must also be POINTER unless INTENT(IN)"_err_en_US,
83564ab3302SCarolineConcatto           dummyName);
83664ab3302SCarolineConcatto     }
83764ab3302SCarolineConcatto   }
83864ab3302SCarolineConcatto 
83964ab3302SCarolineConcatto   // 15.5.2.5 -- actual & dummy are both POINTER or both ALLOCATABLE
840dc78329dSPeter Klausler   // For INTENT(IN), and for a polymorphic actual being associated with a
841dc78329dSPeter Klausler   // monomorphic dummy, we relax two checks that are in Fortran to
8429299bde9SPeter Klausler   // prevent the callee from changing the type or to avoid having
8439299bde9SPeter Klausler   // to use a descriptor.
8449299bde9SPeter Klausler   if (!typesCompatible) {
8459299bde9SPeter Klausler     // Don't pile on the errors emitted above
8469299bde9SPeter Klausler   } else if ((actualIsPointer && dummyIsPointer) ||
84764ab3302SCarolineConcatto       (actualIsAllocatable && dummyIsAllocatable)) {
84864ab3302SCarolineConcatto     bool actualIsUnlimited{actualType.type().IsUnlimitedPolymorphic()};
84964ab3302SCarolineConcatto     bool dummyIsUnlimited{dummy.type.type().IsUnlimitedPolymorphic()};
850dc78329dSPeter Klausler     bool checkTypeCompatibility{true};
85164ab3302SCarolineConcatto     if (actualIsUnlimited != dummyIsUnlimited) {
852dc78329dSPeter Klausler       checkTypeCompatibility = false;
8539299bde9SPeter Klausler       if (dummyIsUnlimited && dummy.intent == common::Intent::In &&
8549299bde9SPeter Klausler           context.IsEnabled(common::LanguageFeature::RelaxedIntentInChecking)) {
8559299bde9SPeter Klausler         if (context.ShouldWarn(
8569299bde9SPeter Klausler                 common::LanguageFeature::RelaxedIntentInChecking)) {
8570f973ac7SPeter Klausler           messages.Say(common::LanguageFeature::RelaxedIntentInChecking,
8589299bde9SPeter Klausler               "If a POINTER or ALLOCATABLE dummy or actual argument is unlimited polymorphic, both should be so"_port_en_US);
8599299bde9SPeter Klausler         }
8609299bde9SPeter Klausler       } else {
86164ab3302SCarolineConcatto         messages.Say(
86264ab3302SCarolineConcatto             "If a POINTER or ALLOCATABLE dummy or actual argument is unlimited polymorphic, both must be so"_err_en_US);
86364ab3302SCarolineConcatto       }
86464ab3302SCarolineConcatto     } else if (dummyIsPolymorphic != actualIsPolymorphic) {
8659299bde9SPeter Klausler       if (dummyIsPolymorphic && dummy.intent == common::Intent::In &&
8669299bde9SPeter Klausler           context.IsEnabled(common::LanguageFeature::RelaxedIntentInChecking)) {
8679299bde9SPeter Klausler         if (context.ShouldWarn(
8689299bde9SPeter Klausler                 common::LanguageFeature::RelaxedIntentInChecking)) {
8690f973ac7SPeter Klausler           messages.Say(common::LanguageFeature::RelaxedIntentInChecking,
870a53967cdSPeter Klausler               "If a POINTER or ALLOCATABLE dummy or actual argument is polymorphic, both should be so"_port_en_US);
8719299bde9SPeter Klausler         }
872dc78329dSPeter Klausler       } else if (actualIsPolymorphic &&
873dc78329dSPeter Klausler           context.IsEnabled(common::LanguageFeature::
874dc78329dSPeter Klausler                   PolymorphicActualAllocatableOrPointerToMonomorphicDummy)) {
875dc78329dSPeter Klausler         if (context.ShouldWarn(common::LanguageFeature::
876dc78329dSPeter Klausler                     PolymorphicActualAllocatableOrPointerToMonomorphicDummy)) {
877dc78329dSPeter Klausler           messages.Say(
8780f973ac7SPeter Klausler               common::LanguageFeature::
8790f973ac7SPeter Klausler                   PolymorphicActualAllocatableOrPointerToMonomorphicDummy,
880dc78329dSPeter Klausler               "If a POINTER or ALLOCATABLE actual argument is polymorphic, the corresponding dummy argument should also be so"_port_en_US);
881dc78329dSPeter Klausler         }
88264ab3302SCarolineConcatto       } else {
883dc78329dSPeter Klausler         checkTypeCompatibility = false;
88464ab3302SCarolineConcatto         messages.Say(
88564ab3302SCarolineConcatto             "If a POINTER or ALLOCATABLE dummy or actual argument is polymorphic, both must be so"_err_en_US);
88664ab3302SCarolineConcatto       }
887dc78329dSPeter Klausler     }
888dc78329dSPeter Klausler     if (checkTypeCompatibility && !actualIsUnlimited) {
88937b2e2b0Speter klausler       if (!actualType.type().IsTkCompatibleWith(dummy.type.type())) {
8909299bde9SPeter Klausler         if (dummy.intent == common::Intent::In &&
8919299bde9SPeter Klausler             context.IsEnabled(
8929299bde9SPeter Klausler                 common::LanguageFeature::RelaxedIntentInChecking)) {
8939299bde9SPeter Klausler           if (context.ShouldWarn(
8949299bde9SPeter Klausler                   common::LanguageFeature::RelaxedIntentInChecking)) {
8950f973ac7SPeter Klausler             messages.Say(common::LanguageFeature::RelaxedIntentInChecking,
896a53967cdSPeter Klausler                 "POINTER or ALLOCATABLE dummy and actual arguments should have the same declared type and kind"_port_en_US);
8979299bde9SPeter Klausler           }
89864ab3302SCarolineConcatto         } else {
89964ab3302SCarolineConcatto           messages.Say(
90037b2e2b0Speter klausler               "POINTER or ALLOCATABLE dummy and actual arguments must have the same declared type and kind"_err_en_US);
90164ab3302SCarolineConcatto         }
90264ab3302SCarolineConcatto       }
903fbdcb3ceSPeixinQiao       // 15.5.2.5(4)
90470cbedcdSPeter Klausler       const auto *dummyDerived{evaluate::GetDerivedTypeSpec(dummy.type.type())};
90570cbedcdSPeter Klausler       if (!DefersSameTypeParameters(actualDerived, dummyDerived) ||
90650960e93SPeter Klausler           dummy.type.type().HasDeferredTypeParameter() !=
907fbdcb3ceSPeixinQiao               actualType.type().HasDeferredTypeParameter()) {
908fbdcb3ceSPeixinQiao         messages.Say(
909fbdcb3ceSPeixinQiao             "Dummy and actual arguments must defer the same type parameters when POINTER or ALLOCATABLE"_err_en_US);
91064ab3302SCarolineConcatto       }
91164ab3302SCarolineConcatto     }
91264ab3302SCarolineConcatto   }
91364ab3302SCarolineConcatto 
91464ab3302SCarolineConcatto   // 15.5.2.8 -- coarray dummy arguments
91564ab3302SCarolineConcatto   if (dummy.type.corank() > 0) {
91664ab3302SCarolineConcatto     if (actualType.corank() == 0) {
91764ab3302SCarolineConcatto       messages.Say(
91864ab3302SCarolineConcatto           "Actual argument associated with coarray %s must be a coarray"_err_en_US,
91964ab3302SCarolineConcatto           dummyName);
92064ab3302SCarolineConcatto     }
92164ab3302SCarolineConcatto     if (dummyIsVolatile) {
92264ab3302SCarolineConcatto       if (!actualIsVolatile) {
92364ab3302SCarolineConcatto         messages.Say(
92464ab3302SCarolineConcatto             "non-VOLATILE coarray may not be associated with VOLATILE coarray %s"_err_en_US,
92564ab3302SCarolineConcatto             dummyName);
92664ab3302SCarolineConcatto       }
92764ab3302SCarolineConcatto     } else {
92864ab3302SCarolineConcatto       if (actualIsVolatile) {
92964ab3302SCarolineConcatto         messages.Say(
93064ab3302SCarolineConcatto             "VOLATILE coarray may not be associated with non-VOLATILE coarray %s"_err_en_US,
93164ab3302SCarolineConcatto             dummyName);
93264ab3302SCarolineConcatto       }
93364ab3302SCarolineConcatto     }
9345718a425SPeter Klausler     if (actualRank == dummyRank && !actualIsContiguous) {
93564ab3302SCarolineConcatto       if (dummyIsContiguous) {
93664ab3302SCarolineConcatto         messages.Say(
93764ab3302SCarolineConcatto             "Actual argument associated with a CONTIGUOUS coarray %s must be simply contiguous"_err_en_US,
93864ab3302SCarolineConcatto             dummyName);
93964ab3302SCarolineConcatto       } else if (!dummyIsAssumedShape && !dummyIsAssumedRank) {
94064ab3302SCarolineConcatto         messages.Say(
94164ab3302SCarolineConcatto             "Actual argument associated with coarray %s (not assumed shape or rank) must be simply contiguous"_err_en_US,
94264ab3302SCarolineConcatto             dummyName);
94364ab3302SCarolineConcatto       }
94464ab3302SCarolineConcatto     }
94564ab3302SCarolineConcatto   }
946bcb2591bSpeter klausler 
947bcb2591bSpeter klausler   // NULL(MOLD=) checking for non-intrinsic procedures
94861d361d6SPeter Klausler   if (!intrinsic && !dummyIsAllocatableOrPointer && !dummyIsOptional &&
94961d361d6SPeter Klausler       actualIsNull) {
950bcb2591bSpeter klausler     messages.Say(
951bcb2591bSpeter klausler         "Actual argument associated with %s may not be null pointer %s"_err_en_US,
952bcb2591bSpeter klausler         dummyName, actual.AsFortran());
953bcb2591bSpeter klausler   }
954fee3f0bdSPeter Klausler 
955caa0a269SPeter Klausler   // Warn about dubious actual argument association with a TARGET dummy
956caa0a269SPeter Klausler   // argument
957191d4872SPeter Klausler   if (dummy.attrs.test(characteristics::DummyDataObject::Attr::Target) &&
958191d4872SPeter Klausler       context.ShouldWarn(common::UsageWarning::NonTargetPassedToTarget)) {
95933c27f28SPeter Klausler     bool actualIsVariable{evaluate::IsVariable(actual)};
960fee3f0bdSPeter Klausler     bool actualIsTemp{!actualIsVariable || HasVectorSubscript(actual) ||
961fee3f0bdSPeter Klausler         evaluate::ExtractCoarrayRef(actual)};
962fee3f0bdSPeter Klausler     if (actualIsTemp) {
9630f973ac7SPeter Klausler       messages.Say(common::UsageWarning::NonTargetPassedToTarget,
964fee3f0bdSPeter Klausler           "Any pointer associated with TARGET %s during this call will not be associated with the value of '%s' afterwards"_warn_en_US,
965fee3f0bdSPeter Klausler           dummyName, actual.AsFortran());
966fee3f0bdSPeter Klausler     } else {
967fee3f0bdSPeter Klausler       auto actualSymbolVector{GetSymbolVector(actual)};
968fee3f0bdSPeter Klausler       if (!evaluate::GetLastTarget(actualSymbolVector)) {
9690f973ac7SPeter Klausler         messages.Say(common::UsageWarning::NonTargetPassedToTarget,
970fee3f0bdSPeter Klausler             "Any pointer associated with TARGET %s during this call must not be used afterwards, as '%s' is not a target"_warn_en_US,
971fee3f0bdSPeter Klausler             dummyName, actual.AsFortran());
972fee3f0bdSPeter Klausler       }
973fee3f0bdSPeter Klausler     }
974fee3f0bdSPeter Klausler   }
975f513bd80SPeter Klausler 
97689165e8bSPeter Klausler   // CUDA specific checks
97789165e8bSPeter Klausler   // TODO: These are disabled in OpenACC constructs, which may not be
97889165e8bSPeter Klausler   // correct when the target is not a GPU.
979f513bd80SPeter Klausler   if (!intrinsic &&
98089165e8bSPeter Klausler       !dummy.attrs.test(characteristics::DummyDataObject::Attr::Value) &&
98189165e8bSPeter Klausler       !FindOpenACCConstructContaining(scope)) {
982f513bd80SPeter Klausler     std::optional<common::CUDADataAttr> actualDataAttr, dummyDataAttr;
983f513bd80SPeter Klausler     if (const auto *actualObject{actualLastSymbol
984f513bd80SPeter Klausler                 ? actualLastSymbol->detailsIf<ObjectEntityDetails>()
985f513bd80SPeter Klausler                 : nullptr}) {
986f513bd80SPeter Klausler       actualDataAttr = actualObject->cudaDataAttr();
987f513bd80SPeter Klausler     }
988f513bd80SPeter Klausler     dummyDataAttr = dummy.cudaDataAttr;
989f513bd80SPeter Klausler     // Treat MANAGED like DEVICE for nonallocatable nonpointer arguments to
990f513bd80SPeter Klausler     // device subprograms
991f513bd80SPeter Klausler     if (procedure.cudaSubprogramAttrs.value_or(
992f513bd80SPeter Klausler             common::CUDASubprogramAttrs::Host) !=
993f513bd80SPeter Klausler             common::CUDASubprogramAttrs::Host &&
994f513bd80SPeter Klausler         !dummy.attrs.test(
995f513bd80SPeter Klausler             characteristics::DummyDataObject::Attr::Allocatable) &&
996f513bd80SPeter Klausler         !dummy.attrs.test(characteristics::DummyDataObject::Attr::Pointer)) {
997f513bd80SPeter Klausler       if (!dummyDataAttr || *dummyDataAttr == common::CUDADataAttr::Managed) {
998f513bd80SPeter Klausler         dummyDataAttr = common::CUDADataAttr::Device;
999f513bd80SPeter Klausler       }
1000f513bd80SPeter Klausler       if ((!actualDataAttr && FindCUDADeviceContext(scope)) ||
1001f513bd80SPeter Klausler           (actualDataAttr &&
1002f513bd80SPeter Klausler               *actualDataAttr == common::CUDADataAttr::Managed)) {
1003f513bd80SPeter Klausler         actualDataAttr = common::CUDADataAttr::Device;
1004f513bd80SPeter Klausler       }
1005f513bd80SPeter Klausler     }
100630d80009SValentin Clement (バレンタイン クレメン)     std::optional<std::string> warning;
10073e930864SValentin Clement     if (!common::AreCompatibleCUDADataAttrs(dummyDataAttr, actualDataAttr,
100830d80009SValentin Clement (バレンタイン クレメン)             dummy.ignoreTKR, &warning,
1009e8eb52d1SValentin Clement (バレンタイン クレメン)             /*allowUnifiedMatchingRule=*/true, &context.languageFeatures())) {
1010f513bd80SPeter Klausler       auto toStr{[](std::optional<common::CUDADataAttr> x) {
1011f513bd80SPeter Klausler         return x ? "ATTRIBUTES("s +
1012f513bd80SPeter Klausler                 parser::ToUpperCaseLetters(common::EnumToString(*x)) + ")"s
1013f513bd80SPeter Klausler                  : "no CUDA data attribute"s;
1014f513bd80SPeter Klausler       }};
1015f513bd80SPeter Klausler       messages.Say(
1016f513bd80SPeter Klausler           "%s has %s but its associated actual argument has %s"_err_en_US,
1017f513bd80SPeter Klausler           dummyName, toStr(dummyDataAttr), toStr(actualDataAttr));
1018f513bd80SPeter Klausler     }
101930d80009SValentin Clement (バレンタイン クレメン)     if (warning && context.ShouldWarn(common::UsageWarning::CUDAUsage)) {
102030d80009SValentin Clement (バレンタイン クレメン)       messages.Say(common::UsageWarning::CUDAUsage, "%s"_warn_en_US,
102130d80009SValentin Clement (バレンタイン クレメン)           std::move(*warning));
102230d80009SValentin Clement (バレンタイン クレメン)     }
1023f513bd80SPeter Klausler   }
10247871deb8SPeter Klausler 
1025930c2d91SPeter Klausler   // Warning for breaking F'2023 change with character allocatables
10267871deb8SPeter Klausler   if (intrinsic && dummy.intent != common::Intent::In) {
10277871deb8SPeter Klausler     WarnOnDeferredLengthCharacterScalar(
10287871deb8SPeter Klausler         context, &actual, messages.at(), dummyName.c_str());
10297871deb8SPeter Klausler   }
1030930c2d91SPeter Klausler 
1031930c2d91SPeter Klausler   // %VAL() and %REF() checking for explicit interface
1032930c2d91SPeter Klausler   if ((arg.isPercentRef() || arg.isPercentVal()) &&
1033930c2d91SPeter Klausler       dummy.IsPassedByDescriptor(procedure.IsBindC())) {
1034930c2d91SPeter Klausler     messages.Say(
10356ac5047aSLeandro Lupori         "%%VAL or %%REF are not allowed for %s that must be passed by means of a descriptor"_err_en_US,
1036930c2d91SPeter Klausler         dummyName);
1037930c2d91SPeter Klausler   }
1038930c2d91SPeter Klausler   if (arg.isPercentVal() &&
1039930c2d91SPeter Klausler       (!actualType.type().IsLengthlessIntrinsicType() ||
1040930c2d91SPeter Klausler           actualType.Rank() != 0)) {
1041930c2d91SPeter Klausler     messages.Say(
1042930c2d91SPeter Klausler         "%VAL argument must be a scalar numeric or logical expression"_err_en_US);
1043930c2d91SPeter Klausler   }
104464ab3302SCarolineConcatto }
104564ab3302SCarolineConcatto 
104664ab3302SCarolineConcatto static void CheckProcedureArg(evaluate::ActualArgument &arg,
104720afd386Speter klausler     const characteristics::Procedure &proc,
104820afd386Speter klausler     const characteristics::DummyProcedure &dummy, const std::string &dummyName,
10491c530b3dSPeter Klausler     SemanticsContext &context, bool ignoreImplicitVsExplicit) {
1050191d4872SPeter Klausler   evaluate::FoldingContext &foldingContext{context.foldingContext()};
1051191d4872SPeter Klausler   parser::ContextualMessages &messages{foldingContext.messages()};
1052fa0443f2SPeter Klausler   auto restorer{
1053fa0443f2SPeter Klausler       messages.SetLocation(arg.sourceLocation().value_or(messages.at()))};
105420afd386Speter klausler   const characteristics::Procedure &interface { dummy.procedure.value() };
105564ab3302SCarolineConcatto   if (const auto *expr{arg.UnwrapExpr()}) {
105664ab3302SCarolineConcatto     bool dummyIsPointer{
105720afd386Speter klausler         dummy.attrs.test(characteristics::DummyProcedure::Attr::Pointer)};
105864ab3302SCarolineConcatto     const auto *argProcDesignator{
105964ab3302SCarolineConcatto         std::get_if<evaluate::ProcedureDesignator>(&expr->u)};
106064ab3302SCarolineConcatto     const auto *argProcSymbol{
106164ab3302SCarolineConcatto         argProcDesignator ? argProcDesignator->GetSymbol() : nullptr};
1062bd28a0a5SPeter Klausler     if (argProcSymbol) {
1063bd28a0a5SPeter Klausler       if (const auto *subp{
1064bd28a0a5SPeter Klausler               argProcSymbol->GetUltimate().detailsIf<SubprogramDetails>()}) {
1065bd28a0a5SPeter Klausler         if (subp->stmtFunction()) {
1066bd28a0a5SPeter Klausler           evaluate::SayWithDeclaration(messages, *argProcSymbol,
1067bd28a0a5SPeter Klausler               "Statement function '%s' may not be passed as an actual argument"_err_en_US,
1068bd28a0a5SPeter Klausler               argProcSymbol->name());
1069bd28a0a5SPeter Klausler           return;
1070bd28a0a5SPeter Klausler         }
1071fee041f6SPeter Klausler       } else if (argProcSymbol->has<ProcBindingDetails>()) {
10721c91d9bdSPeter Klausler         if (!context.IsEnabled(common::LanguageFeature::BindingAsProcedure)) {
10730f973ac7SPeter Klausler           evaluate::SayWithDeclaration(messages, *argProcSymbol,
10740f973ac7SPeter Klausler               "Procedure binding '%s' passed as an actual argument"_err_en_US,
10750f973ac7SPeter Klausler               argProcSymbol->name());
10760f973ac7SPeter Klausler         } else if (context.ShouldWarn(
10770f973ac7SPeter Klausler                        common::LanguageFeature::BindingAsProcedure)) {
10780f973ac7SPeter Klausler           evaluate::SayWithDeclaration(messages, *argProcSymbol,
10790f973ac7SPeter Klausler               common::LanguageFeature::BindingAsProcedure,
10800f973ac7SPeter Klausler               "Procedure binding '%s' passed as an actual argument"_port_en_US,
10810f973ac7SPeter Klausler               argProcSymbol->name());
10821c91d9bdSPeter Klausler         }
1083bd28a0a5SPeter Klausler       }
1084bd28a0a5SPeter Klausler     }
108564ab3302SCarolineConcatto     if (auto argChars{characteristics::DummyArgument::FromActual(
108629fd3e2aSPeter Klausler             "actual argument", *expr, foldingContext,
108729fd3e2aSPeter Klausler             /*forImplicitInterface=*/true)}) {
1088c7574188SPeter Steinfeld       if (!argChars->IsTypelessIntrinsicDummy()) {
108964ab3302SCarolineConcatto         if (auto *argProc{
109064ab3302SCarolineConcatto                 std::get_if<characteristics::DummyProcedure>(&argChars->u)}) {
109164ab3302SCarolineConcatto           characteristics::Procedure &argInterface{argProc->procedure.value()};
1092c7574188SPeter Steinfeld           argInterface.attrs.reset(
1093c7574188SPeter Steinfeld               characteristics::Procedure::Attr::NullPointer);
109464ab3302SCarolineConcatto           if (!argProcSymbol || argProcSymbol->attrs().test(Attr::INTRINSIC)) {
109564ab3302SCarolineConcatto             // It's ok to pass ELEMENTAL unrestricted intrinsic functions.
1096c7574188SPeter Steinfeld             argInterface.attrs.reset(
1097c7574188SPeter Steinfeld                 characteristics::Procedure::Attr::Elemental);
109864ab3302SCarolineConcatto           } else if (argInterface.attrs.test(
109964ab3302SCarolineConcatto                          characteristics::Procedure::Attr::Elemental)) {
110064ab3302SCarolineConcatto             if (argProcSymbol) { // C1533
110164ab3302SCarolineConcatto               evaluate::SayWithDeclaration(messages, *argProcSymbol,
110264ab3302SCarolineConcatto                   "Non-intrinsic ELEMENTAL procedure '%s' may not be passed as an actual argument"_err_en_US,
110364ab3302SCarolineConcatto                   argProcSymbol->name());
110464ab3302SCarolineConcatto               return; // avoid piling on with checks below
110564ab3302SCarolineConcatto             } else {
110664ab3302SCarolineConcatto               argInterface.attrs.reset(
110764ab3302SCarolineConcatto                   characteristics::Procedure::Attr::NullPointer);
110864ab3302SCarolineConcatto             }
110964ab3302SCarolineConcatto           }
111064ab3302SCarolineConcatto           if (interface.HasExplicitInterface()) {
111162d874f2SPeter Klausler             std::string whyNot;
1112e86591b3SPeter Klausler             std::optional<std::string> warning;
11131c530b3dSPeter Klausler             if (!interface.IsCompatibleWith(argInterface,
11141c530b3dSPeter Klausler                     ignoreImplicitVsExplicit, &whyNot,
1115e86591b3SPeter Klausler                     /*specificIntrinsic=*/nullptr, &warning)) {
11163de92ca7SPeter Steinfeld               // 15.5.2.9(1): Explicit interfaces must match
11173de92ca7SPeter Steinfeld               if (argInterface.HasExplicitInterface()) {
111864ab3302SCarolineConcatto                 messages.Say(
111962d874f2SPeter Klausler                     "Actual procedure argument has interface incompatible with %s: %s"_err_en_US,
112062d874f2SPeter Klausler                     dummyName, whyNot);
11213de92ca7SPeter Steinfeld                 return;
112220afd386Speter klausler               } else if (proc.IsPure()) {
112320afd386Speter klausler                 messages.Say(
112420afd386Speter klausler                     "Actual procedure argument for %s of a PURE procedure must have an explicit interface"_err_en_US,
112520afd386Speter klausler                     dummyName);
1126191d4872SPeter Klausler               } else if (context.ShouldWarn(
1127191d4872SPeter Klausler                              common::UsageWarning::ImplicitInterfaceActual)) {
11280f973ac7SPeter Klausler                 messages.Say(common::UsageWarning::ImplicitInterfaceActual,
1129191d4872SPeter Klausler                     "Actual procedure argument has an implicit interface which is not known to be compatible with %s which has an explicit interface"_warn_en_US,
11303de92ca7SPeter Steinfeld                     dummyName);
11313de92ca7SPeter Steinfeld               }
1132e86591b3SPeter Klausler             } else if (warning &&
1133e86591b3SPeter Klausler                 context.ShouldWarn(common::UsageWarning::ProcDummyArgShapes)) {
11340f973ac7SPeter Klausler               messages.Say(common::UsageWarning::ProcDummyArgShapes,
1135e86591b3SPeter Klausler                   "Actual procedure argument has possible interface incompatibility with %s: %s"_warn_en_US,
1136e86591b3SPeter Klausler                   dummyName, std::move(*warning));
113764ab3302SCarolineConcatto             }
113864ab3302SCarolineConcatto           } else { // 15.5.2.9(2,3)
113964ab3302SCarolineConcatto             if (interface.IsSubroutine() && argInterface.IsFunction()) {
114064ab3302SCarolineConcatto               messages.Say(
114164ab3302SCarolineConcatto                   "Actual argument associated with procedure %s is a function but must be a subroutine"_err_en_US,
114264ab3302SCarolineConcatto                   dummyName);
114364ab3302SCarolineConcatto             } else if (interface.IsFunction()) {
114464ab3302SCarolineConcatto               if (argInterface.IsFunction()) {
1145c6b9df0fSPeter Klausler                 std::string whyNot;
11463bfe9074SPeter Klausler                 if (!interface.functionResult->IsCompatibleWith(
1147c6b9df0fSPeter Klausler                         *argInterface.functionResult, &whyNot)) {
114864ab3302SCarolineConcatto                   messages.Say(
1149c6b9df0fSPeter Klausler                       "Actual argument function associated with procedure %s is not compatible: %s"_err_en_US,
1150c6b9df0fSPeter Klausler                       dummyName, whyNot);
115164ab3302SCarolineConcatto                 }
115264ab3302SCarolineConcatto               } else if (argInterface.IsSubroutine()) {
115364ab3302SCarolineConcatto                 messages.Say(
115464ab3302SCarolineConcatto                     "Actual argument associated with procedure %s is a subroutine but must be a function"_err_en_US,
115564ab3302SCarolineConcatto                     dummyName);
115664ab3302SCarolineConcatto               }
115764ab3302SCarolineConcatto             }
115864ab3302SCarolineConcatto           }
115964ab3302SCarolineConcatto         } else {
116064ab3302SCarolineConcatto           messages.Say(
116164ab3302SCarolineConcatto               "Actual argument associated with procedure %s is not a procedure"_err_en_US,
116264ab3302SCarolineConcatto               dummyName);
116364ab3302SCarolineConcatto         }
1164bbd0dc3dSpeter klausler       } else if (IsNullPointer(*expr)) {
116578a166b4SPeter Klausler         if (!dummyIsPointer &&
116678a166b4SPeter Klausler             !dummy.attrs.test(
116778a166b4SPeter Klausler                 characteristics::DummyProcedure::Attr::Optional)) {
116864ab3302SCarolineConcatto           messages.Say(
1169bbd0dc3dSpeter klausler               "Actual argument associated with procedure %s is a null pointer"_err_en_US,
1170bbd0dc3dSpeter klausler               dummyName);
1171bbd0dc3dSpeter klausler         }
1172bbd0dc3dSpeter klausler       } else {
1173bbd0dc3dSpeter klausler         messages.Say(
1174bbd0dc3dSpeter klausler             "Actual argument associated with procedure %s is typeless"_err_en_US,
117564ab3302SCarolineConcatto             dummyName);
117664ab3302SCarolineConcatto       }
1177c7574188SPeter Steinfeld     }
1178d9f85656SPeter Klausler     if (dummyIsPointer && dummy.intent != common::Intent::In) {
1179bbd0dc3dSpeter klausler       const Symbol *last{GetLastSymbol(*expr)};
1180066aecffSPeter Klausler       if (last && IsProcedurePointer(*last)) {
1181066aecffSPeter Klausler         if (dummy.intent != common::Intent::Default &&
1182066aecffSPeter Klausler             IsIntentIn(last->GetUltimate())) { // 19.6.8
1183066aecffSPeter Klausler           messages.Say(
1184066aecffSPeter Klausler               "Actual argument associated with procedure pointer %s may not be INTENT(IN)"_err_en_US,
1185066aecffSPeter Klausler               dummyName);
1186066aecffSPeter Klausler         }
1187066aecffSPeter Klausler       } else if (!(dummy.intent == common::Intent::Default &&
11884e3bf225SPeter Klausler                      IsNullProcedurePointer(*expr))) {
118964ab3302SCarolineConcatto         // 15.5.2.9(5) -- dummy procedure POINTER
11903bfe9074SPeter Klausler         // Interface compatibility has already been checked above
119164ab3302SCarolineConcatto         messages.Say(
1192f025e411SPeter Klausler             "Actual argument associated with procedure pointer %s must be a pointer unless INTENT(IN)"_err_en_US,
119364ab3302SCarolineConcatto             dummyName);
119464ab3302SCarolineConcatto       }
119564ab3302SCarolineConcatto     }
119664ab3302SCarolineConcatto   } else {
119764ab3302SCarolineConcatto     messages.Say(
119864ab3302SCarolineConcatto         "Assumed-type argument may not be forwarded as procedure %s"_err_en_US,
119964ab3302SCarolineConcatto         dummyName);
120064ab3302SCarolineConcatto   }
120164ab3302SCarolineConcatto }
120264ab3302SCarolineConcatto 
120302847775SPeter Klausler // Allow BOZ literal actual arguments when they can be converted to a known
120402847775SPeter Klausler // dummy argument type
120502847775SPeter Klausler static void ConvertBOZLiteralArg(
120602847775SPeter Klausler     evaluate::ActualArgument &arg, const evaluate::DynamicType &type) {
120702847775SPeter Klausler   if (auto *expr{arg.UnwrapExpr()}) {
120802847775SPeter Klausler     if (IsBOZLiteral(*expr)) {
120902847775SPeter Klausler       if (auto converted{evaluate::ConvertToType(type, SomeExpr{*expr})}) {
121002847775SPeter Klausler         arg = std::move(*converted);
121102847775SPeter Klausler       }
121202847775SPeter Klausler     }
121302847775SPeter Klausler   }
121402847775SPeter Klausler }
121502847775SPeter Klausler 
121664ab3302SCarolineConcatto static void CheckExplicitInterfaceArg(evaluate::ActualArgument &arg,
121764ab3302SCarolineConcatto     const characteristics::DummyArgument &dummy,
1218191d4872SPeter Klausler     const characteristics::Procedure &proc, SemanticsContext &context,
121914b90d1fSpeter klausler     const Scope *scope, const evaluate::SpecificIntrinsic *intrinsic,
12201c530b3dSPeter Klausler     bool allowActualArgumentConversions, bool extentErrors,
12211c530b3dSPeter Klausler     bool ignoreImplicitVsExplicit) {
1222191d4872SPeter Klausler   evaluate::FoldingContext &foldingContext{context.foldingContext()};
1223191d4872SPeter Klausler   auto &messages{foldingContext.messages()};
122464ab3302SCarolineConcatto   std::string dummyName{"dummy argument"};
122564ab3302SCarolineConcatto   if (!dummy.name.empty()) {
122664ab3302SCarolineConcatto     dummyName += " '"s + parser::ToLowerCaseLetters(dummy.name) + "='";
122764ab3302SCarolineConcatto   }
1228fa0443f2SPeter Klausler   auto restorer{
1229fa0443f2SPeter Klausler       messages.SetLocation(arg.sourceLocation().value_or(messages.at()))};
1230b8b90c2aSPeter Klausler   auto CheckActualArgForLabel = [&](evaluate::ActualArgument &arg) {
12318867e83dSDaniil Dudkin     if (arg.isAlternateReturn()) {
12328867e83dSDaniil Dudkin       messages.Say(
12338867e83dSDaniil Dudkin           "Alternate return label '%d' cannot be associated with %s"_err_en_US,
12348867e83dSDaniil Dudkin           arg.GetLabel(), dummyName);
12358867e83dSDaniil Dudkin       return false;
1236b8b90c2aSPeter Klausler     } else {
1237b8b90c2aSPeter Klausler       return true;
12388867e83dSDaniil Dudkin     }
12398867e83dSDaniil Dudkin   };
1240cd03e96fSPeter Klausler   common::visit(
124164ab3302SCarolineConcatto       common::visitors{
124264ab3302SCarolineConcatto           [&](const characteristics::DummyDataObject &object) {
1243b8b90c2aSPeter Klausler             if (CheckActualArgForLabel(arg)) {
124402847775SPeter Klausler               ConvertBOZLiteralArg(arg, object.type.type());
124564ab3302SCarolineConcatto               if (auto *expr{arg.UnwrapExpr()}) {
124664ab3302SCarolineConcatto                 if (auto type{characteristics::TypeAndShape::Characterize(
1247191d4872SPeter Klausler                         *expr, foldingContext)}) {
124864ab3302SCarolineConcatto                   arg.set_dummyIntent(object.intent);
12498867e83dSDaniil Dudkin                   bool isElemental{
12508867e83dSDaniil Dudkin                       object.type.Rank() == 0 && proc.IsElemental()};
125164ab3302SCarolineConcatto                   CheckExplicitDataArg(object, dummyName, *expr, *type,
1252191d4872SPeter Klausler                       isElemental, context, foldingContext, scope, intrinsic,
1253930c2d91SPeter Klausler                       allowActualArgumentConversions, extentErrors, proc, arg);
125464ab3302SCarolineConcatto                 } else if (object.type.type().IsTypelessIntrinsicArgument() &&
1255571673ceSPeter Steinfeld                     IsBOZLiteral(*expr)) {
125664ab3302SCarolineConcatto                   // ok
1257c7574188SPeter Steinfeld                 } else if (object.type.type().IsTypelessIntrinsicArgument() &&
12584e3bf225SPeter Klausler                     evaluate::IsNullObjectPointer(*expr)) {
12598ceba598SPeter Klausler                   // ok, ASSOCIATED(NULL(without MOLD=))
126036ad3431SPeter Klausler                 } else if (object.type.attrs().test(characteristics::
1261b8b90c2aSPeter Klausler                                    TypeAndShape::Attr::AssumedRank) &&
1262b8b90c2aSPeter Klausler                     evaluate::IsNullObjectPointer(*expr) &&
1263b8b90c2aSPeter Klausler                     (object.attrs.test(
1264b8b90c2aSPeter Klausler                          characteristics::DummyDataObject::Attr::Allocatable) ||
1265b8b90c2aSPeter Klausler                         object.attrs.test(
1266b8b90c2aSPeter Klausler                             characteristics::DummyDataObject::Attr::Pointer) ||
1267b8b90c2aSPeter Klausler                         !object.attrs.test(characteristics::DummyDataObject::
1268b8b90c2aSPeter Klausler                                 Attr::Optional))) {
126936ad3431SPeter Klausler                   messages.Say(
1270b8b90c2aSPeter Klausler                       "NULL() without MOLD= must not be associated with an assumed-rank dummy argument that is ALLOCATABLE, POINTER, or non-OPTIONAL"_err_en_US);
1271bcb2591bSpeter klausler                 } else if ((object.attrs.test(characteristics::DummyDataObject::
1272bcb2591bSpeter klausler                                     Attr::Pointer) ||
1273bcb2591bSpeter klausler                                object.attrs.test(characteristics::
1274bcb2591bSpeter klausler                                        DummyDataObject::Attr::Optional)) &&
12754e3bf225SPeter Klausler                     evaluate::IsNullObjectPointer(*expr)) {
12768ceba598SPeter Klausler                   // FOO(NULL(without MOLD=))
12778ceba598SPeter Klausler                   if (object.type.type().IsAssumedLengthCharacter()) {
12788ceba598SPeter Klausler                     messages.Say(
12798ceba598SPeter Klausler                         "Actual argument associated with %s is a NULL() pointer without a MOLD= to provide a character length"_err_en_US,
12808ceba598SPeter Klausler                         dummyName);
12818ceba598SPeter Klausler                   } else if (const DerivedTypeSpec *
12828ceba598SPeter Klausler                       derived{GetDerivedTypeSpec(object.type.type())}) {
12838ceba598SPeter Klausler                     for (const auto &[pName, pValue] : derived->parameters()) {
12848ceba598SPeter Klausler                       if (pValue.isAssumed()) {
12858ceba598SPeter Klausler                         messages.Say(
12868ceba598SPeter Klausler                             "Actual argument associated with %s is a NULL() pointer without a MOLD= to provide a value for the assumed type parameter '%s'"_err_en_US,
12878ceba598SPeter Klausler                             dummyName, pName.ToString());
12888ceba598SPeter Klausler                         break;
12898ceba598SPeter Klausler                       }
12908ceba598SPeter Klausler                     }
12918ceba598SPeter Klausler                   }
129285a40ce6SPeter Klausler                 } else if (object.attrs.test(characteristics::DummyDataObject::
129385a40ce6SPeter Klausler                                    Attr::Allocatable) &&
129485a40ce6SPeter Klausler                     evaluate::IsNullPointer(*expr)) {
129561d361d6SPeter Klausler                   if (object.intent == common::Intent::In) {
129661d361d6SPeter Klausler                     // Extension (Intel, NAG, XLF); see CheckExplicitDataArg.
1297505f6da1SPeter Klausler                     if (context.ShouldWarn(common::LanguageFeature::
1298505f6da1SPeter Klausler                                 NullActualForAllocatable)) {
129985a40ce6SPeter Klausler                       messages.Say(
13000f973ac7SPeter Klausler                           common::LanguageFeature::NullActualForAllocatable,
130161d361d6SPeter Klausler                           "Allocatable %s is associated with NULL()"_port_en_US,
130261d361d6SPeter Klausler                           dummyName);
130361d361d6SPeter Klausler                     }
130461d361d6SPeter Klausler                   } else {
130561d361d6SPeter Klausler                     messages.Say(
130661d361d6SPeter Klausler                         "NULL() actual argument '%s' may not be associated with allocatable %s without INTENT(IN)"_err_en_US,
130785a40ce6SPeter Klausler                         expr->AsFortran(), dummyName);
130861d361d6SPeter Klausler                   }
130964ab3302SCarolineConcatto                 } else {
131064ab3302SCarolineConcatto                   messages.Say(
131116d24e45Speter klausler                       "Actual argument '%s' associated with %s is not a variable or typed expression"_err_en_US,
131216d24e45Speter klausler                       expr->AsFortran(), dummyName);
131364ab3302SCarolineConcatto                 }
131464ab3302SCarolineConcatto               } else {
131564ab3302SCarolineConcatto                 const Symbol &assumed{DEREF(arg.GetAssumedTypeDummy())};
131664ab3302SCarolineConcatto                 if (!object.type.type().IsAssumedType()) {
131764ab3302SCarolineConcatto                   messages.Say(
13183b635714Speter klausler                       "Assumed-type '%s' may be associated only with an assumed-type %s"_err_en_US,
131964ab3302SCarolineConcatto                       assumed.name(), dummyName);
1320bf4a8763SPeter Klausler                 } else if (object.type.attrs().test(characteristics::
132159d38f1bSPeter Klausler                                    TypeAndShape::Attr::AssumedRank) &&
132259d38f1bSPeter Klausler                     !IsAssumedShape(assumed) &&
132359d38f1bSPeter Klausler                     !evaluate::IsAssumedRank(assumed)) {
132464ab3302SCarolineConcatto                   messages.Say( // C711
132559d38f1bSPeter Klausler                       "Assumed-type '%s' must be either assumed shape or assumed rank to be associated with assumed rank %s"_err_en_US,
132664ab3302SCarolineConcatto                       assumed.name(), dummyName);
132764ab3302SCarolineConcatto                 }
132864ab3302SCarolineConcatto               }
13298867e83dSDaniil Dudkin             }
133064ab3302SCarolineConcatto           },
133120afd386Speter klausler           [&](const characteristics::DummyProcedure &dummy) {
1332b8b90c2aSPeter Klausler             if (CheckActualArgForLabel(arg)) {
13331c530b3dSPeter Klausler               CheckProcedureArg(arg, proc, dummy, dummyName, context,
13341c530b3dSPeter Klausler                   ignoreImplicitVsExplicit);
13358867e83dSDaniil Dudkin             }
133664ab3302SCarolineConcatto           },
133764ab3302SCarolineConcatto           [&](const characteristics::AlternateReturn &) {
1338ae0d1d2eSPeter Steinfeld             // All semantic checking is done elsewhere
133964ab3302SCarolineConcatto           },
134064ab3302SCarolineConcatto       },
134164ab3302SCarolineConcatto       dummy.u);
134264ab3302SCarolineConcatto }
134364ab3302SCarolineConcatto 
134464ab3302SCarolineConcatto static void RearrangeArguments(const characteristics::Procedure &proc,
134564ab3302SCarolineConcatto     evaluate::ActualArguments &actuals, parser::ContextualMessages &messages) {
134664ab3302SCarolineConcatto   CHECK(proc.HasExplicitInterface());
134764ab3302SCarolineConcatto   if (actuals.size() < proc.dummyArguments.size()) {
134864ab3302SCarolineConcatto     actuals.resize(proc.dummyArguments.size());
134964ab3302SCarolineConcatto   } else if (actuals.size() > proc.dummyArguments.size()) {
135064ab3302SCarolineConcatto     messages.Say(
135164ab3302SCarolineConcatto         "Too many actual arguments (%zd) passed to procedure that expects only %zd"_err_en_US,
135264ab3302SCarolineConcatto         actuals.size(), proc.dummyArguments.size());
135364ab3302SCarolineConcatto   }
135464ab3302SCarolineConcatto   std::map<std::string, evaluate::ActualArgument> kwArgs;
1355e3b6b929SPeter Klausler   bool anyKeyword{false};
1356e3b6b929SPeter Klausler   int which{1};
135764ab3302SCarolineConcatto   for (auto &x : actuals) {
1358e3b6b929SPeter Klausler     if (!x) {
1359e3b6b929SPeter Klausler     } else if (x->keyword()) {
136064ab3302SCarolineConcatto       auto emplaced{
136164ab3302SCarolineConcatto           kwArgs.try_emplace(x->keyword()->ToString(), std::move(*x))};
136264ab3302SCarolineConcatto       if (!emplaced.second) {
136364ab3302SCarolineConcatto         messages.Say(*x->keyword(),
136464ab3302SCarolineConcatto             "Argument keyword '%s=' appears on more than one effective argument in this procedure reference"_err_en_US,
136564ab3302SCarolineConcatto             *x->keyword());
136664ab3302SCarolineConcatto       }
136764ab3302SCarolineConcatto       x.reset();
1368e3b6b929SPeter Klausler       anyKeyword = true;
1369e3b6b929SPeter Klausler     } else if (anyKeyword) {
1370e3b6b929SPeter Klausler       messages.Say(x ? x->sourceLocation() : std::nullopt,
1371e3b6b929SPeter Klausler           "Actual argument #%d without a keyword may not follow any actual argument with a keyword"_err_en_US,
1372e3b6b929SPeter Klausler           which);
137364ab3302SCarolineConcatto     }
1374e3b6b929SPeter Klausler     ++which;
137564ab3302SCarolineConcatto   }
137664ab3302SCarolineConcatto   if (!kwArgs.empty()) {
137764ab3302SCarolineConcatto     int index{0};
137864ab3302SCarolineConcatto     for (const auto &dummy : proc.dummyArguments) {
137964ab3302SCarolineConcatto       if (!dummy.name.empty()) {
138064ab3302SCarolineConcatto         auto iter{kwArgs.find(dummy.name)};
138164ab3302SCarolineConcatto         if (iter != kwArgs.end()) {
138264ab3302SCarolineConcatto           evaluate::ActualArgument &x{iter->second};
138364ab3302SCarolineConcatto           if (actuals[index]) {
138464ab3302SCarolineConcatto             messages.Say(*x.keyword(),
138564ab3302SCarolineConcatto                 "Keyword argument '%s=' has already been specified positionally (#%d) in this procedure reference"_err_en_US,
138664ab3302SCarolineConcatto                 *x.keyword(), index + 1);
138764ab3302SCarolineConcatto           } else {
138864ab3302SCarolineConcatto             actuals[index] = std::move(x);
138964ab3302SCarolineConcatto           }
139064ab3302SCarolineConcatto           kwArgs.erase(iter);
139164ab3302SCarolineConcatto         }
139264ab3302SCarolineConcatto       }
139364ab3302SCarolineConcatto       ++index;
139464ab3302SCarolineConcatto     }
139564ab3302SCarolineConcatto     for (auto &bad : kwArgs) {
139664ab3302SCarolineConcatto       evaluate::ActualArgument &x{bad.second};
139764ab3302SCarolineConcatto       messages.Say(*x.keyword(),
139864ab3302SCarolineConcatto           "Argument keyword '%s=' is not recognized for this procedure reference"_err_en_US,
139964ab3302SCarolineConcatto           *x.keyword());
140064ab3302SCarolineConcatto     }
140164ab3302SCarolineConcatto   }
140264ab3302SCarolineConcatto }
140364ab3302SCarolineConcatto 
14045646d828SPeixin-Qiao // 15.8.1(3) -- In a reference to an elemental procedure, if any argument is an
14055646d828SPeixin-Qiao // array, each actual argument that corresponds to an INTENT(OUT) or
14065646d828SPeixin-Qiao // INTENT(INOUT) dummy argument shall be an array. The actual argument to an
14075646d828SPeixin-Qiao // ELEMENTAL procedure must conform.
140819afc495Speter klausler static bool CheckElementalConformance(parser::ContextualMessages &messages,
140919afc495Speter klausler     const characteristics::Procedure &proc, evaluate::ActualArguments &actuals,
141019afc495Speter klausler     evaluate::FoldingContext &context) {
141119afc495Speter klausler   std::optional<evaluate::Shape> shape;
141219afc495Speter klausler   std::string shapeName;
141319afc495Speter klausler   int index{0};
14145646d828SPeixin-Qiao   bool hasArrayArg{false};
14155646d828SPeixin-Qiao   for (const auto &arg : actuals) {
1416290cc4feSPeter Klausler     if (arg && !arg->isAlternateReturn() && arg->Rank() > 0) {
14175646d828SPeixin-Qiao       hasArrayArg = true;
14185646d828SPeixin-Qiao       break;
14195646d828SPeixin-Qiao     }
14205646d828SPeixin-Qiao   }
142119afc495Speter klausler   for (const auto &arg : actuals) {
142219afc495Speter klausler     const auto &dummy{proc.dummyArguments.at(index++)};
142319afc495Speter klausler     if (arg) {
142419afc495Speter klausler       if (const auto *expr{arg->UnwrapExpr()}) {
14251595ca43SPeter Klausler         if (const auto *wholeSymbol{evaluate::UnwrapWholeSymbolDataRef(arg)}) {
14261595ca43SPeter Klausler           wholeSymbol = &ResolveAssociations(*wholeSymbol);
14271595ca43SPeter Klausler           if (IsAssumedSizeArray(*wholeSymbol)) {
14281595ca43SPeter Klausler             evaluate::SayWithDeclaration(messages, *wholeSymbol,
14291595ca43SPeter Klausler                 "Whole assumed-size array '%s' may not be used as an argument to an elemental procedure"_err_en_US,
14301595ca43SPeter Klausler                 wholeSymbol->name());
14311595ca43SPeter Klausler           }
14321595ca43SPeter Klausler         }
143319afc495Speter klausler         if (auto argShape{evaluate::GetShape(context, *expr)}) {
143419afc495Speter klausler           if (GetRank(*argShape) > 0) {
143519afc495Speter klausler             std::string argName{"actual argument ("s + expr->AsFortran() +
143619afc495Speter klausler                 ") corresponding to dummy argument #" + std::to_string(index) +
143719afc495Speter klausler                 " ('" + dummy.name + "')"};
143819afc495Speter klausler             if (shape) {
143919afc495Speter klausler               auto tristate{evaluate::CheckConformance(messages, *shape,
144019afc495Speter klausler                   *argShape, evaluate::CheckConformanceFlags::None,
144119afc495Speter klausler                   shapeName.c_str(), argName.c_str())};
144219afc495Speter klausler               if (tristate && !*tristate) {
144319afc495Speter klausler                 return false;
144419afc495Speter klausler               }
144519afc495Speter klausler             } else {
144619afc495Speter klausler               shape = std::move(argShape);
144719afc495Speter klausler               shapeName = argName;
144819afc495Speter klausler             }
14495646d828SPeixin-Qiao           } else if ((dummy.GetIntent() == common::Intent::Out ||
14505646d828SPeixin-Qiao                          dummy.GetIntent() == common::Intent::InOut) &&
14515646d828SPeixin-Qiao               hasArrayArg) {
14525646d828SPeixin-Qiao             messages.Say(
14533142c761SPeter Klausler                 "In an elemental procedure reference with at least one array argument, actual argument %s that corresponds to an INTENT(OUT) or INTENT(INOUT) dummy argument must be an array"_err_en_US,
14545646d828SPeixin-Qiao                 expr->AsFortran());
145519afc495Speter klausler           }
145619afc495Speter klausler         }
145719afc495Speter klausler       }
145819afc495Speter klausler     }
145919afc495Speter klausler   }
146019afc495Speter klausler   return true;
146119afc495Speter klausler }
146219afc495Speter klausler 
1463aad5984bSPeter Klausler // ASSOCIATED (16.9.16)
1464aad5984bSPeter Klausler static void CheckAssociated(evaluate::ActualArguments &arguments,
14651c91d9bdSPeter Klausler     SemanticsContext &semanticsContext, const Scope *scope) {
14661c91d9bdSPeter Klausler   evaluate::FoldingContext &foldingContext{semanticsContext.foldingContext()};
14671c91d9bdSPeter Klausler   parser::ContextualMessages &messages{foldingContext.messages()};
1468aad5984bSPeter Klausler   bool ok{true};
1469aad5984bSPeter Klausler   if (arguments.size() < 2) {
1470aad5984bSPeter Klausler     return;
1471aad5984bSPeter Klausler   }
1472aad5984bSPeter Klausler   if (const auto &pointerArg{arguments[0]}) {
1473aad5984bSPeter Klausler     if (const auto *pointerExpr{pointerArg->UnwrapExpr()}) {
1474f025e411SPeter Klausler       if (!IsPointer(*pointerExpr)) {
14751c91d9bdSPeter Klausler         messages.Say(pointerArg->sourceLocation(),
1476f025e411SPeter Klausler             "POINTER= argument of ASSOCIATED() must be a pointer"_err_en_US);
1477aad5984bSPeter Klausler         return;
1478aad5984bSPeter Klausler       }
1479aad5984bSPeter Klausler       if (const auto &targetArg{arguments[1]}) {
1480f2bf44b6SPeter Klausler         // The standard requires that the TARGET= argument, when present,
1481f2bf44b6SPeter Klausler         // be a valid RHS for a pointer assignment that has the POINTER=
1482f2bf44b6SPeter Klausler         // argument as its LHS.  Some popular compilers misinterpret this
1483f2bf44b6SPeter Klausler         // requirement more strongly than necessary, and actually validate
1484f2bf44b6SPeter Klausler         // the POINTER= argument as if it were serving as the LHS of a pointer
1485f2bf44b6SPeter Klausler         // assignment.  This, perhaps unintentionally, excludes function
1486f2bf44b6SPeter Klausler         // results, including NULL(), from being used there, as well as
1487f2bf44b6SPeter Klausler         // INTENT(IN) dummy pointers.  Detect these conditions and emit
1488f2bf44b6SPeter Klausler         // portability warnings.
14891c91d9bdSPeter Klausler         if (semanticsContext.ShouldWarn(common::UsageWarning::Portability)) {
1490aad5984bSPeter Klausler           if (!evaluate::ExtractDataRef(*pointerExpr) &&
1491aad5984bSPeter Klausler               !evaluate::IsProcedurePointer(*pointerExpr)) {
14920f973ac7SPeter Klausler             messages.Say(common::UsageWarning::Portability,
14930f973ac7SPeter Klausler                 pointerArg->sourceLocation(),
14941c91d9bdSPeter Klausler                 "POINTER= argument of ASSOCIATED() is required by some other compilers to be a pointer"_port_en_US);
1495f025e411SPeter Klausler           } else if (scope && !evaluate::UnwrapProcedureRef(*pointerExpr)) {
14961c91d9bdSPeter Klausler             if (auto whyNot{WhyNotDefinable(
14971c91d9bdSPeter Klausler                     pointerArg->sourceLocation().value_or(messages.at()),
1498aad5984bSPeter Klausler                     *scope,
149933c27f28SPeter Klausler                     DefinabilityFlags{DefinabilityFlag::PointerDefinition,
150033c27f28SPeter Klausler                         DefinabilityFlag::DoNotNoteDefinition},
1501aad5984bSPeter Klausler                     *pointerExpr)}) {
1502d5285fefSPeter Klausler               if (whyNot->IsFatal()) {
15030f973ac7SPeter Klausler                 if (auto *msg{messages.Say(common::UsageWarning::Portability,
15040f973ac7SPeter Klausler                         pointerArg->sourceLocation(),
15051c91d9bdSPeter Klausler                         "POINTER= argument of ASSOCIATED() is required by some other compilers to be a valid left-hand side of a pointer assignment statement"_port_en_US)}) {
1506d5285fefSPeter Klausler                   msg->Attach(std::move(
1507d5285fefSPeter Klausler                       whyNot->set_severity(parser::Severity::Because)));
1508d5285fefSPeter Klausler                 }
1509d5285fefSPeter Klausler               } else {
1510d5285fefSPeter Klausler                 messages.Say(std::move(*whyNot));
1511aad5984bSPeter Klausler               }
1512aad5984bSPeter Klausler             }
1513aad5984bSPeter Klausler           }
15141c91d9bdSPeter Klausler         }
1515f025e411SPeter Klausler         if (const auto *targetExpr{targetArg->UnwrapExpr()}) {
1516f025e411SPeter Klausler           if (IsProcedurePointer(*pointerExpr) &&
1517f025e411SPeter Klausler               !IsBareNullPointer(pointerExpr)) { // POINTER= is a procedure
1518458d9fbdSPeter Klausler             if (auto pointerProc{characteristics::Procedure::Characterize(
15191c91d9bdSPeter Klausler                     *pointerExpr, foldingContext)}) {
1520f025e411SPeter Klausler               if (IsBareNullPointer(targetExpr)) {
1521f025e411SPeter Klausler               } else if (IsProcedurePointerTarget(*targetExpr)) {
1522f025e411SPeter Klausler                 if (auto targetProc{characteristics::Procedure::Characterize(
15231c91d9bdSPeter Klausler                         *targetExpr, foldingContext)}) {
1524f025e411SPeter Klausler                   bool isCall{!!UnwrapProcedureRef(*targetExpr)};
1525f025e411SPeter Klausler                   std::string whyNot;
1526e86591b3SPeter Klausler                   std::optional<std::string> warning;
1527aad5984bSPeter Klausler                   const auto *targetProcDesignator{
1528458d9fbdSPeter Klausler                       evaluate::UnwrapExpr<evaluate::ProcedureDesignator>(
1529458d9fbdSPeter Klausler                           *targetExpr)};
1530458d9fbdSPeter Klausler                   const evaluate::SpecificIntrinsic *specificIntrinsic{
1531458d9fbdSPeter Klausler                       targetProcDesignator
1532458d9fbdSPeter Klausler                           ? targetProcDesignator->GetSpecificIntrinsic()
1533458d9fbdSPeter Klausler                           : nullptr};
1534e86591b3SPeter Klausler                   std::optional<parser::MessageFixedText> msg{
1535e86591b3SPeter Klausler                       CheckProcCompatibility(isCall, pointerProc, &*targetProc,
15361c530b3dSPeter Klausler                           specificIntrinsic, whyNot, warning,
15371c530b3dSPeter Klausler                           /*ignoreImplicitVsExplicit=*/false)};
15380f973ac7SPeter Klausler                   std::optional<common::UsageWarning> whichWarning;
1539e86591b3SPeter Klausler                   if (!msg && warning &&
1540e86591b3SPeter Klausler                       semanticsContext.ShouldWarn(
1541e86591b3SPeter Klausler                           common::UsageWarning::ProcDummyArgShapes)) {
15420f973ac7SPeter Klausler                     whichWarning = common::UsageWarning::ProcDummyArgShapes;
1543e86591b3SPeter Klausler                     msg =
1544e86591b3SPeter Klausler                         "Procedures '%s' and '%s' may not be completely compatible: %s"_warn_en_US;
1545e86591b3SPeter Klausler                     whyNot = std::move(*warning);
15460f973ac7SPeter Klausler                   } else if (msg && !msg->IsFatal() &&
15470f973ac7SPeter Klausler                       semanticsContext.ShouldWarn(
1548505f6da1SPeter Klausler                           common::UsageWarning::ProcPointerCompatibility)) {
15490f973ac7SPeter Klausler                     whichWarning =
15500f973ac7SPeter Klausler                         common::UsageWarning::ProcPointerCompatibility;
1551e86591b3SPeter Klausler                   }
15520f973ac7SPeter Klausler                   if (msg && (msg->IsFatal() || whichWarning)) {
15530f973ac7SPeter Klausler                     if (auto *said{messages.Say(std::move(*msg),
1554f025e411SPeter Klausler                             "pointer '" + pointerExpr->AsFortran() + "'",
15550f973ac7SPeter Klausler                             targetExpr->AsFortran(), whyNot)};
15560f973ac7SPeter Klausler                         said && whichWarning) {
15570f973ac7SPeter Klausler                       said->set_usageWarning(*whichWarning);
15580f973ac7SPeter Klausler                     }
1559f025e411SPeter Klausler                   }
1560aad5984bSPeter Klausler                 }
1561aad5984bSPeter Klausler               } else if (!IsNullProcedurePointer(*targetExpr)) {
15621c91d9bdSPeter Klausler                 messages.Say(
1563aad5984bSPeter Klausler                     "POINTER= argument '%s' is a procedure pointer but the TARGET= argument '%s' is not a procedure or procedure pointer"_err_en_US,
1564f025e411SPeter Klausler                     pointerExpr->AsFortran(), targetExpr->AsFortran());
1565aad5984bSPeter Klausler               }
1566458d9fbdSPeter Klausler             }
1567458d9fbdSPeter Klausler           } else if (IsVariable(*targetExpr) || IsNullPointer(*targetExpr)) {
1568458d9fbdSPeter Klausler             // Object pointer and target
1569458d9fbdSPeter Klausler             if (ExtractDataRef(*targetExpr)) {
1570458d9fbdSPeter Klausler               if (SymbolVector symbols{GetSymbolVector(*targetExpr)};
1571458d9fbdSPeter Klausler                   !evaluate::GetLastTarget(symbols)) {
15721c91d9bdSPeter Klausler                 parser::Message *msg{messages.Say(targetArg->sourceLocation(),
1573aad5984bSPeter Klausler                     "TARGET= argument '%s' must have either the POINTER or the TARGET attribute"_err_en_US,
1574aad5984bSPeter Klausler                     targetExpr->AsFortran())};
1575aad5984bSPeter Klausler                 for (SymbolRef ref : symbols) {
1576aad5984bSPeter Klausler                   msg = evaluate::AttachDeclaration(msg, *ref);
1577aad5984bSPeter Klausler                 }
1578aad5984bSPeter Klausler               } else if (HasVectorSubscript(*targetExpr) ||
1579aad5984bSPeter Klausler                   ExtractCoarrayRef(*targetExpr)) {
15801c91d9bdSPeter Klausler                 messages.Say(targetArg->sourceLocation(),
1581aad5984bSPeter Klausler                     "TARGET= argument '%s' may not have a vector subscript or coindexing"_err_en_US,
1582aad5984bSPeter Klausler                     targetExpr->AsFortran());
1583aad5984bSPeter Klausler               }
1584458d9fbdSPeter Klausler             }
1585aad5984bSPeter Klausler             if (const auto pointerType{pointerArg->GetType()}) {
1586aad5984bSPeter Klausler               if (const auto targetType{targetArg->GetType()}) {
1587aad5984bSPeter Klausler                 ok = pointerType->IsTkCompatibleWith(*targetType);
1588aad5984bSPeter Klausler               }
1589aad5984bSPeter Klausler             }
1590458d9fbdSPeter Klausler           } else {
15911c91d9bdSPeter Klausler             messages.Say(
1592458d9fbdSPeter Klausler                 "POINTER= argument '%s' is an object pointer but the TARGET= argument '%s' is not a variable"_err_en_US,
1593f025e411SPeter Klausler                 pointerExpr->AsFortran(), targetExpr->AsFortran());
1594aad5984bSPeter Klausler           }
1595858a79ebSjeanPerier           if (!IsAssumedRank(*pointerExpr)) {
1596858a79ebSjeanPerier             if (IsAssumedRank(*targetExpr)) {
1597858a79ebSjeanPerier               messages.Say(
1598858a79ebSjeanPerier                   "TARGET= argument '%s' may not be assumed-rank when POINTER= argument is not"_err_en_US,
1599858a79ebSjeanPerier                   pointerExpr->AsFortran());
1600858a79ebSjeanPerier             } else if (pointerExpr->Rank() != targetExpr->Rank()) {
1601858a79ebSjeanPerier               messages.Say(
1602858a79ebSjeanPerier                   "POINTER= argument and TARGET= argument have incompatible ranks %d and %d"_err_en_US,
1603858a79ebSjeanPerier                   pointerExpr->Rank(), targetExpr->Rank());
1604858a79ebSjeanPerier             }
1605858a79ebSjeanPerier           }
1606aad5984bSPeter Klausler         }
1607aad5984bSPeter Klausler       }
1608aad5984bSPeter Klausler     }
1609aad5984bSPeter Klausler   } else {
1610aad5984bSPeter Klausler     // No arguments to ASSOCIATED()
1611aad5984bSPeter Klausler     ok = false;
1612aad5984bSPeter Klausler   }
1613aad5984bSPeter Klausler   if (!ok) {
16141c91d9bdSPeter Klausler     messages.Say(
1615f025e411SPeter Klausler         "Arguments of ASSOCIATED() must be a pointer and an optional valid target"_err_en_US);
1616aad5984bSPeter Klausler   }
1617aad5984bSPeter Klausler }
1618aad5984bSPeter Klausler 
16191e9b60cfSPeter Klausler // EVENT_QUERY (F'2023 16.9.82)
16201e9b60cfSPeter Klausler static void CheckEvent_Query(evaluate::ActualArguments &arguments,
16211e9b60cfSPeter Klausler     evaluate::FoldingContext &foldingContext) {
16221e9b60cfSPeter Klausler   if (arguments.size() > 0 && arguments[0] &&
16231e9b60cfSPeter Klausler       ExtractCoarrayRef(*arguments[0]).has_value()) {
16241e9b60cfSPeter Klausler     foldingContext.messages().Say(arguments[0]->sourceLocation(),
16251e9b60cfSPeter Klausler         "EVENT= argument to EVENT_QUERY must not be coindexed"_err_en_US);
16261e9b60cfSPeter Klausler   }
16271e9b60cfSPeter Klausler   if (arguments.size() > 1 && arguments[1]) {
16281e9b60cfSPeter Klausler     if (auto dyType{arguments[1]->GetType()}) {
16291e9b60cfSPeter Klausler       int defaultInt{
16301e9b60cfSPeter Klausler           foldingContext.defaults().GetDefaultKind(TypeCategory::Integer)};
16311e9b60cfSPeter Klausler       if (dyType->category() == TypeCategory::Integer &&
16321e9b60cfSPeter Klausler           dyType->kind() < defaultInt) {
16331e9b60cfSPeter Klausler         foldingContext.messages().Say(arguments[1]->sourceLocation(),
16341e9b60cfSPeter Klausler             "COUNT= argument to EVENT_QUERY must be an integer with kind >= %d"_err_en_US,
16351e9b60cfSPeter Klausler             defaultInt);
16361e9b60cfSPeter Klausler       }
16371e9b60cfSPeter Klausler     }
16381e9b60cfSPeter Klausler   }
16391e9b60cfSPeter Klausler   if (arguments.size() > 2 && arguments[2]) {
16401e9b60cfSPeter Klausler     if (auto dyType{arguments[2]->GetType()}) {
16411e9b60cfSPeter Klausler       if (dyType->category() == TypeCategory::Integer && dyType->kind() < 2) {
16421e9b60cfSPeter Klausler         foldingContext.messages().Say(arguments[2]->sourceLocation(),
16431e9b60cfSPeter Klausler             "STAT= argument to EVENT_QUERY must be an integer with kind >= 2 when present"_err_en_US);
16441e9b60cfSPeter Klausler       }
16451e9b60cfSPeter Klausler     }
16461e9b60cfSPeter Klausler   }
16471e9b60cfSPeter Klausler }
16481e9b60cfSPeter Klausler 
16498b389708SKatherine Rasmussen // IMAGE_INDEX (F'2023 16.9.107)
16508b389708SKatherine Rasmussen static void CheckImage_Index(evaluate::ActualArguments &arguments,
16518b389708SKatherine Rasmussen     parser::ContextualMessages &messages) {
16528b389708SKatherine Rasmussen   if (arguments[1] && arguments[0]) {
16538b389708SKatherine Rasmussen     if (const auto subArrShape{
16548b389708SKatherine Rasmussen             evaluate::GetShape(arguments[1]->UnwrapExpr())}) {
16558b389708SKatherine Rasmussen       if (const auto *coarrayArgSymbol{UnwrapWholeSymbolOrComponentDataRef(
16568b389708SKatherine Rasmussen               arguments[0]->UnwrapExpr())}) {
16573a8a52f4SPeter Klausler         auto coarrayArgCorank{coarrayArgSymbol->Corank()};
16583a8a52f4SPeter Klausler         if (auto subArrSize{evaluate::ToInt64(*subArrShape->front())}) {
16598b389708SKatherine Rasmussen           if (subArrSize != coarrayArgCorank) {
16608b389708SKatherine Rasmussen             messages.Say(arguments[1]->sourceLocation(),
16618b389708SKatherine Rasmussen                 "The size of 'SUB=' (%jd) for intrinsic 'image_index' must be equal to the corank of 'COARRAY=' (%d)"_err_en_US,
16628b389708SKatherine Rasmussen                 static_cast<std::int64_t>(*subArrSize), coarrayArgCorank);
16638b389708SKatherine Rasmussen           }
16648b389708SKatherine Rasmussen         }
16658b389708SKatherine Rasmussen       }
16668b389708SKatherine Rasmussen     }
16678b389708SKatherine Rasmussen   }
16688b389708SKatherine Rasmussen }
16698b389708SKatherine Rasmussen 
167097e3f605SPeter Klausler // Ensure that any optional argument that might be absent at run time
167197e3f605SPeter Klausler // does not require data conversion.
167297e3f605SPeter Klausler static void CheckMaxMin(const characteristics::Procedure &proc,
167397e3f605SPeter Klausler     evaluate::ActualArguments &arguments,
167497e3f605SPeter Klausler     parser::ContextualMessages &messages) {
167597e3f605SPeter Klausler   if (proc.functionResult) {
167697e3f605SPeter Klausler     if (const auto *typeAndShape{proc.functionResult->GetTypeAndShape()}) {
167797e3f605SPeter Klausler       for (std::size_t j{2}; j < arguments.size(); ++j) {
167897e3f605SPeter Klausler         if (arguments[j]) {
167997e3f605SPeter Klausler           if (const auto *expr{arguments[j]->UnwrapExpr()};
168097e3f605SPeter Klausler               expr && evaluate::MayBePassedAsAbsentOptional(*expr)) {
1681e8572d0fSPeter Klausler             if (auto thisType{expr->GetType()}) {
1682e8572d0fSPeter Klausler               if (thisType->category() == TypeCategory::Character &&
1683e8572d0fSPeter Klausler                   typeAndShape->type().category() == TypeCategory::Character &&
1684e8572d0fSPeter Klausler                   thisType->kind() == typeAndShape->type().kind()) {
1685e8572d0fSPeter Klausler                 // don't care about lengths
1686e8572d0fSPeter Klausler               } else if (*thisType != typeAndShape->type()) {
168797e3f605SPeter Klausler                 messages.Say(arguments[j]->sourceLocation(),
168897e3f605SPeter Klausler                     "An actual argument to MAX/MIN requiring data conversion may not be OPTIONAL, POINTER, or ALLOCATABLE"_err_en_US);
168997e3f605SPeter Klausler               }
169097e3f605SPeter Klausler             }
169197e3f605SPeter Klausler           }
169297e3f605SPeter Klausler         }
169397e3f605SPeter Klausler       }
169497e3f605SPeter Klausler     }
169597e3f605SPeter Klausler   }
1696e8572d0fSPeter Klausler }
169797e3f605SPeter Klausler 
169878ccffc0SDavid Truby static void CheckFree(evaluate::ActualArguments &arguments,
169978ccffc0SDavid Truby     parser::ContextualMessages &messages) {
170078ccffc0SDavid Truby   if (arguments.size() != 1) {
170178ccffc0SDavid Truby     messages.Say("FREE expects a single argument"_err_en_US);
170278ccffc0SDavid Truby   }
170378ccffc0SDavid Truby   auto arg = arguments[0];
170478ccffc0SDavid Truby   if (const Symbol * symbol{evaluate::UnwrapWholeSymbolDataRef(arg)};
170578ccffc0SDavid Truby       !symbol || !symbol->test(Symbol::Flag::CrayPointer)) {
170678ccffc0SDavid Truby     messages.Say("FREE should only be used with Cray pointers"_warn_en_US);
170778ccffc0SDavid Truby   }
170878ccffc0SDavid Truby }
170978ccffc0SDavid Truby 
1710f9b089a7SPeter Klausler // MOVE_ALLOC (F'2023 16.9.147)
1711f9b089a7SPeter Klausler static void CheckMove_Alloc(evaluate::ActualArguments &arguments,
1712f9b089a7SPeter Klausler     parser::ContextualMessages &messages) {
1713f9b089a7SPeter Klausler   if (arguments.size() >= 1) {
1714f9b089a7SPeter Klausler     evaluate::CheckForCoindexedObject(
1715f9b089a7SPeter Klausler         messages, arguments[0], "move_alloc", "from");
1716f9b089a7SPeter Klausler   }
1717f9b089a7SPeter Klausler   if (arguments.size() >= 2) {
1718f9b089a7SPeter Klausler     evaluate::CheckForCoindexedObject(
1719f9b089a7SPeter Klausler         messages, arguments[1], "move_alloc", "to");
1720f9b089a7SPeter Klausler   }
1721f9b089a7SPeter Klausler   if (arguments.size() >= 3) {
1722f9b089a7SPeter Klausler     evaluate::CheckForCoindexedObject(
1723f9b089a7SPeter Klausler         messages, arguments[2], "move_alloc", "stat");
1724f9b089a7SPeter Klausler   }
1725f9b089a7SPeter Klausler   if (arguments.size() >= 4) {
1726f9b089a7SPeter Klausler     evaluate::CheckForCoindexedObject(
1727f9b089a7SPeter Klausler         messages, arguments[3], "move_alloc", "errmsg");
1728f9b089a7SPeter Klausler   }
1729f9b089a7SPeter Klausler   if (arguments.size() >= 2 && arguments[0] && arguments[1]) {
1730f9b089a7SPeter Klausler     for (int j{0}; j < 2; ++j) {
1731f9b089a7SPeter Klausler       if (const Symbol *
1732f9b089a7SPeter Klausler               whole{UnwrapWholeSymbolOrComponentDataRef(arguments[j])};
1733f9b089a7SPeter Klausler           !whole || !IsAllocatable(whole->GetUltimate())) {
1734f9b089a7SPeter Klausler         messages.Say(*arguments[j]->sourceLocation(),
1735f9b089a7SPeter Klausler             "Argument #%d to MOVE_ALLOC must be allocatable"_err_en_US, j + 1);
1736f9b089a7SPeter Klausler       }
1737f9b089a7SPeter Klausler     }
1738f9b089a7SPeter Klausler     auto type0{arguments[0]->GetType()};
1739f9b089a7SPeter Klausler     auto type1{arguments[1]->GetType()};
1740f9b089a7SPeter Klausler     if (type0 && type1 && type0->IsPolymorphic() && !type1->IsPolymorphic()) {
1741f9b089a7SPeter Klausler       messages.Say(arguments[1]->sourceLocation(),
1742f9b089a7SPeter Klausler           "When MOVE_ALLOC(FROM=) is polymorphic, TO= must also be polymorphic"_err_en_US);
1743f9b089a7SPeter Klausler     }
1744f9b089a7SPeter Klausler   }
1745f9b089a7SPeter Klausler }
1746f9b089a7SPeter Klausler 
17473bca8506SPeter Klausler // PRESENT (F'2023 16.9.163)
17483bca8506SPeter Klausler static void CheckPresent(evaluate::ActualArguments &arguments,
17493bca8506SPeter Klausler     parser::ContextualMessages &messages) {
17503bca8506SPeter Klausler   if (arguments.size() == 1) {
17513bca8506SPeter Klausler     if (const auto &arg{arguments[0]}; arg) {
17523bca8506SPeter Klausler       const Symbol *symbol{nullptr};
17533bca8506SPeter Klausler       if (const auto *expr{arg->UnwrapExpr()}) {
17543bca8506SPeter Klausler         if (const auto *proc{
17553bca8506SPeter Klausler                 std::get_if<evaluate::ProcedureDesignator>(&expr->u)}) {
17563bca8506SPeter Klausler           symbol = proc->GetSymbol();
17573bca8506SPeter Klausler         } else {
17583bca8506SPeter Klausler           symbol = evaluate::UnwrapWholeSymbolDataRef(*expr);
17593bca8506SPeter Klausler         }
17603bca8506SPeter Klausler       } else {
17613bca8506SPeter Klausler         symbol = arg->GetAssumedTypeDummy();
17623bca8506SPeter Klausler       }
1763062e69a6SLeandro Lupori       if (!symbol ||
1764062e69a6SLeandro Lupori           !symbol->GetUltimate().attrs().test(semantics::Attr::OPTIONAL)) {
17653bca8506SPeter Klausler         messages.Say(arg ? arg->sourceLocation() : messages.at(),
17663bca8506SPeter Klausler             "Argument of PRESENT() must be the name of a whole OPTIONAL dummy argument"_err_en_US);
17673bca8506SPeter Klausler       }
17683bca8506SPeter Klausler     }
17693bca8506SPeter Klausler   }
17703bca8506SPeter Klausler }
17713bca8506SPeter Klausler 
1772bf4a8763SPeter Klausler // REDUCE (F'2023 16.9.173)
1773bf4a8763SPeter Klausler static void CheckReduce(
1774bf4a8763SPeter Klausler     evaluate::ActualArguments &arguments, evaluate::FoldingContext &context) {
1775bf4a8763SPeter Klausler   std::optional<evaluate::DynamicType> arrayType;
1776bf4a8763SPeter Klausler   parser::ContextualMessages &messages{context.messages()};
1777bf4a8763SPeter Klausler   if (const auto &array{arguments[0]}) {
1778bf4a8763SPeter Klausler     arrayType = array->GetType();
1779bf4a8763SPeter Klausler     if (!arguments[/*identity=*/4]) {
1780bf4a8763SPeter Klausler       if (const auto *expr{array->UnwrapExpr()}) {
1781bf4a8763SPeter Klausler         if (auto shape{
1782bf4a8763SPeter Klausler                 evaluate::GetShape(context, *expr, /*invariantOnly=*/false)}) {
1783bf4a8763SPeter Klausler           if (const auto &dim{arguments[2]}; dim && array->Rank() > 1) {
1784bf4a8763SPeter Klausler             // Partial reduction
1785bf4a8763SPeter Klausler             auto dimVal{evaluate::ToInt64(dim->UnwrapExpr())};
1786bf4a8763SPeter Klausler             std::int64_t j{0};
1787bf4a8763SPeter Klausler             int zeroDims{0};
1788bf4a8763SPeter Klausler             bool isSelectedDimEmpty{false};
1789bf4a8763SPeter Klausler             for (const auto &extent : *shape) {
1790bf4a8763SPeter Klausler               ++j;
1791bf4a8763SPeter Klausler               if (evaluate::ToInt64(extent) == 0) {
1792bf4a8763SPeter Klausler                 ++zeroDims;
1793bf4a8763SPeter Klausler                 isSelectedDimEmpty |= dimVal && j == *dimVal;
1794bf4a8763SPeter Klausler               }
1795bf4a8763SPeter Klausler             }
1796bf4a8763SPeter Klausler             if (isSelectedDimEmpty && zeroDims == 1) {
1797bf4a8763SPeter Klausler               messages.Say(
1798bf4a8763SPeter Klausler                   "IDENTITY= must be present when DIM=%d and the array has zero extent on that dimension"_err_en_US,
1799bf4a8763SPeter Klausler                   static_cast<int>(dimVal.value()));
1800bf4a8763SPeter Klausler             }
1801bf4a8763SPeter Klausler           } else { // no DIM= or DIM=1 on a vector: total reduction
1802bf4a8763SPeter Klausler             for (const auto &extent : *shape) {
1803bf4a8763SPeter Klausler               if (evaluate::ToInt64(extent) == 0) {
1804bf4a8763SPeter Klausler                 messages.Say(
1805bf4a8763SPeter Klausler                     "IDENTITY= must be present when the array is empty and the result is scalar"_err_en_US);
1806bf4a8763SPeter Klausler                 break;
1807bf4a8763SPeter Klausler               }
1808bf4a8763SPeter Klausler             }
1809bf4a8763SPeter Klausler           }
1810bf4a8763SPeter Klausler         }
1811bf4a8763SPeter Klausler       }
1812bf4a8763SPeter Klausler     }
1813bf4a8763SPeter Klausler   }
1814bf4a8763SPeter Klausler   std::optional<characteristics::Procedure> procChars;
1815bf4a8763SPeter Klausler   if (const auto &operation{arguments[1]}) {
1816bf4a8763SPeter Klausler     if (const auto *expr{operation->UnwrapExpr()}) {
1817bf4a8763SPeter Klausler       if (const auto *designator{
1818bf4a8763SPeter Klausler               std::get_if<evaluate::ProcedureDesignator>(&expr->u)}) {
1819cb263919SPeter Klausler         procChars = characteristics::Procedure::Characterize(
1820cb263919SPeter Klausler             *designator, context, /*emitError=*/true);
1821bf4a8763SPeter Klausler       } else if (const auto *ref{
1822bf4a8763SPeter Klausler                      std::get_if<evaluate::ProcedureRef>(&expr->u)}) {
1823bf4a8763SPeter Klausler         procChars = characteristics::Procedure::Characterize(*ref, context);
1824bf4a8763SPeter Klausler       }
1825bf4a8763SPeter Klausler     }
1826bf4a8763SPeter Klausler   }
1827bf4a8763SPeter Klausler   const auto *result{
1828bf4a8763SPeter Klausler       procChars ? procChars->functionResult->GetTypeAndShape() : nullptr};
1829bf4a8763SPeter Klausler   if (!procChars || !procChars->IsPure() ||
1830bf4a8763SPeter Klausler       procChars->dummyArguments.size() != 2 || !procChars->functionResult) {
1831bf4a8763SPeter Klausler     messages.Say(
1832bf4a8763SPeter Klausler         "OPERATION= argument of REDUCE() must be a pure function of two data arguments"_err_en_US);
18333ada883fSPeter Klausler   } else if (procChars->attrs.test(characteristics::Procedure::Attr::BindC)) {
18343ada883fSPeter Klausler     messages.Say(
18353ada883fSPeter Klausler         "A BIND(C) OPERATION= argument of REDUCE() is not supported"_err_en_US);
1836bf4a8763SPeter Klausler   } else if (!result || result->Rank() != 0) {
1837bf4a8763SPeter Klausler     messages.Say(
1838bf4a8763SPeter Klausler         "OPERATION= argument of REDUCE() must be a scalar function"_err_en_US);
1839bf4a8763SPeter Klausler   } else if (result->type().IsPolymorphic() ||
1840bf4a8763SPeter Klausler       (arrayType && !arrayType->IsTkLenCompatibleWith(result->type()))) {
1841bf4a8763SPeter Klausler     messages.Say(
1842bf4a8763SPeter Klausler         "OPERATION= argument of REDUCE() must have the same type as ARRAY="_err_en_US);
1843bf4a8763SPeter Klausler   } else {
1844bf4a8763SPeter Klausler     const characteristics::DummyDataObject *data[2]{};
1845bf4a8763SPeter Klausler     for (int j{0}; j < 2; ++j) {
1846bf4a8763SPeter Klausler       const auto &dummy{procChars->dummyArguments.at(j)};
1847bf4a8763SPeter Klausler       data[j] = std::get_if<characteristics::DummyDataObject>(&dummy.u);
1848bf4a8763SPeter Klausler     }
1849bf4a8763SPeter Klausler     if (!data[0] || !data[1]) {
1850bf4a8763SPeter Klausler       messages.Say(
1851bf4a8763SPeter Klausler           "OPERATION= argument of REDUCE() may not have dummy procedure arguments"_err_en_US);
1852bf4a8763SPeter Klausler     } else {
1853bf4a8763SPeter Klausler       for (int j{0}; j < 2; ++j) {
1854bf4a8763SPeter Klausler         if (data[j]->attrs.test(
1855bf4a8763SPeter Klausler                 characteristics::DummyDataObject::Attr::Optional) ||
1856bf4a8763SPeter Klausler             data[j]->attrs.test(
1857bf4a8763SPeter Klausler                 characteristics::DummyDataObject::Attr::Allocatable) ||
1858bf4a8763SPeter Klausler             data[j]->attrs.test(
1859bf4a8763SPeter Klausler                 characteristics::DummyDataObject::Attr::Pointer) ||
1860bf4a8763SPeter Klausler             data[j]->type.Rank() != 0 || data[j]->type.type().IsPolymorphic() ||
1861bf4a8763SPeter Klausler             (arrayType &&
1862bf4a8763SPeter Klausler                 !data[j]->type.type().IsTkCompatibleWith(*arrayType))) {
1863bf4a8763SPeter Klausler           messages.Say(
1864bf4a8763SPeter Klausler               "Arguments of OPERATION= procedure of REDUCE() must be both scalar of the same type as ARRAY=, and neither allocatable, pointer, polymorphic, nor optional"_err_en_US);
1865bf4a8763SPeter Klausler         }
1866bf4a8763SPeter Klausler       }
1867bf4a8763SPeter Klausler       static constexpr characteristics::DummyDataObject::Attr attrs[]{
1868bf4a8763SPeter Klausler           characteristics::DummyDataObject::Attr::Asynchronous,
1869bf4a8763SPeter Klausler           characteristics::DummyDataObject::Attr::Target,
1870bf4a8763SPeter Klausler           characteristics::DummyDataObject::Attr::Value,
1871bf4a8763SPeter Klausler       };
1872bf4a8763SPeter Klausler       for (std::size_t j{0}; j < sizeof attrs / sizeof *attrs; ++j) {
1873bf4a8763SPeter Klausler         if (data[0]->attrs.test(attrs[j]) != data[1]->attrs.test(attrs[j])) {
1874bf4a8763SPeter Klausler           messages.Say(
1875bf4a8763SPeter Klausler               "If either argument of the OPERATION= procedure of REDUCE() has the ASYNCHRONOUS, TARGET, or VALUE attribute, both must have that attribute"_err_en_US);
1876bf4a8763SPeter Klausler           break;
1877bf4a8763SPeter Klausler         }
1878bf4a8763SPeter Klausler       }
1879bf4a8763SPeter Klausler     }
1880bf4a8763SPeter Klausler   }
1881bf4a8763SPeter Klausler   // When the MASK= is present and has no .TRUE. element, and there is
1882bf4a8763SPeter Klausler   // no IDENTITY=, it's an error.
1883bf4a8763SPeter Klausler   if (const auto &mask{arguments[3]}; mask && !arguments[/*identity*/ 4]) {
1884bf4a8763SPeter Klausler     if (const auto *expr{mask->UnwrapExpr()}) {
1885bf4a8763SPeter Klausler       if (const auto *logical{
1886bf4a8763SPeter Klausler               std::get_if<evaluate::Expr<evaluate::SomeLogical>>(&expr->u)}) {
1887bf4a8763SPeter Klausler         if (common::visit(
1888bf4a8763SPeter Klausler                 [](const auto &kindExpr) {
1889bf4a8763SPeter Klausler                   using KindExprType = std::decay_t<decltype(kindExpr)>;
1890bf4a8763SPeter Klausler                   using KindLogical = typename KindExprType::Result;
1891bf4a8763SPeter Klausler                   if (const auto *c{evaluate::UnwrapConstantValue<KindLogical>(
1892bf4a8763SPeter Klausler                           kindExpr)}) {
1893bf4a8763SPeter Klausler                     for (const auto &element : c->values()) {
1894bf4a8763SPeter Klausler                       if (element.IsTrue()) {
1895bf4a8763SPeter Klausler                         return false;
1896bf4a8763SPeter Klausler                       }
1897bf4a8763SPeter Klausler                     }
1898bf4a8763SPeter Klausler                     return true;
1899bf4a8763SPeter Klausler                   }
1900bf4a8763SPeter Klausler                   return false;
1901bf4a8763SPeter Klausler                 },
1902bf4a8763SPeter Klausler                 logical->u)) {
1903bf4a8763SPeter Klausler           messages.Say(
1904bf4a8763SPeter Klausler               "MASK= has no .TRUE. element, so IDENTITY= must be present"_err_en_US);
1905bf4a8763SPeter Klausler         }
1906bf4a8763SPeter Klausler       }
1907bf4a8763SPeter Klausler     }
1908bf4a8763SPeter Klausler   }
1909bf4a8763SPeter Klausler }
1910bf4a8763SPeter Klausler 
1911860ed6c0SPeter Klausler // TRANSFER (16.9.193)
1912191d4872SPeter Klausler static void CheckTransferOperandType(SemanticsContext &context,
1913860ed6c0SPeter Klausler     const evaluate::DynamicType &type, const char *which) {
1914191d4872SPeter Klausler   if (type.IsPolymorphic() &&
1915191d4872SPeter Klausler       context.ShouldWarn(common::UsageWarning::PolymorphicTransferArg)) {
1916191d4872SPeter Klausler     context.foldingContext().messages().Say(
19170f973ac7SPeter Klausler         common::UsageWarning::PolymorphicTransferArg,
1918191d4872SPeter Klausler         "%s of TRANSFER is polymorphic"_warn_en_US, which);
1919860ed6c0SPeter Klausler   } else if (!type.IsUnlimitedPolymorphic() &&
1920191d4872SPeter Klausler       type.category() == TypeCategory::Derived &&
1921191d4872SPeter Klausler       context.ShouldWarn(common::UsageWarning::PointerComponentTransferArg)) {
1922860ed6c0SPeter Klausler     DirectComponentIterator directs{type.GetDerivedTypeSpec()};
1923860ed6c0SPeter Klausler     if (auto bad{std::find_if(directs.begin(), directs.end(), IsDescriptor)};
1924860ed6c0SPeter Klausler         bad != directs.end()) {
1925191d4872SPeter Klausler       evaluate::SayWithDeclaration(context.foldingContext().messages(), *bad,
19260f973ac7SPeter Klausler           common::UsageWarning::PointerComponentTransferArg,
1927860ed6c0SPeter Klausler           "%s of TRANSFER contains allocatable or pointer component %s"_warn_en_US,
1928860ed6c0SPeter Klausler           which, bad.BuildResultDesignatorName());
1929860ed6c0SPeter Klausler     }
1930860ed6c0SPeter Klausler   }
1931860ed6c0SPeter Klausler }
1932860ed6c0SPeter Klausler 
1933860ed6c0SPeter Klausler static void CheckTransfer(evaluate::ActualArguments &arguments,
1934191d4872SPeter Klausler     SemanticsContext &context, const Scope *scope) {
1935191d4872SPeter Klausler   evaluate::FoldingContext &foldingContext{context.foldingContext()};
1936191d4872SPeter Klausler   parser::ContextualMessages &messages{foldingContext.messages()};
1937860ed6c0SPeter Klausler   if (arguments.size() >= 2) {
1938860ed6c0SPeter Klausler     if (auto source{characteristics::TypeAndShape::Characterize(
1939191d4872SPeter Klausler             arguments[0], foldingContext)}) {
1940191d4872SPeter Klausler       CheckTransferOperandType(context, source->type(), "Source");
1941860ed6c0SPeter Klausler       if (auto mold{characteristics::TypeAndShape::Characterize(
1942191d4872SPeter Klausler               arguments[1], foldingContext)}) {
1943191d4872SPeter Klausler         CheckTransferOperandType(context, mold->type(), "Mold");
1944860ed6c0SPeter Klausler         if (mold->Rank() > 0 &&
1945860ed6c0SPeter Klausler             evaluate::ToInt64(
1946191d4872SPeter Klausler                 evaluate::Fold(foldingContext,
1947191d4872SPeter Klausler                     mold->MeasureElementSizeInBytes(foldingContext, false)))
1948860ed6c0SPeter Klausler                     .value_or(1) == 0) {
1949191d4872SPeter Klausler           if (auto sourceSize{evaluate::ToInt64(evaluate::Fold(foldingContext,
1950191d4872SPeter Klausler                   source->MeasureSizeInBytes(foldingContext)))}) {
1951860ed6c0SPeter Klausler             if (*sourceSize > 0) {
1952191d4872SPeter Klausler               messages.Say(
1953860ed6c0SPeter Klausler                   "Element size of MOLD= array may not be zero when SOURCE= is not empty"_err_en_US);
1954860ed6c0SPeter Klausler             }
1955505f6da1SPeter Klausler           } else if (context.ShouldWarn(common::UsageWarning::VoidMold)) {
19560f973ac7SPeter Klausler             messages.Say(common::UsageWarning::VoidMold,
1957860ed6c0SPeter Klausler                 "Element size of MOLD= array may not be zero unless SOURCE= is empty"_warn_en_US);
1958860ed6c0SPeter Klausler           }
1959860ed6c0SPeter Klausler         }
1960860ed6c0SPeter Klausler       }
1961860ed6c0SPeter Klausler     }
1962860ed6c0SPeter Klausler     if (arguments.size() > 2) { // SIZE=
1963860ed6c0SPeter Klausler       if (const Symbol *
1964860ed6c0SPeter Klausler           whole{UnwrapWholeSymbolOrComponentDataRef(arguments[2])}) {
1965860ed6c0SPeter Klausler         if (IsOptional(*whole)) {
1966191d4872SPeter Klausler           messages.Say(
1967860ed6c0SPeter Klausler               "SIZE= argument may not be the optional dummy argument '%s'"_err_en_US,
1968860ed6c0SPeter Klausler               whole->name());
1969191d4872SPeter Klausler         } else if (context.ShouldWarn(
1970191d4872SPeter Klausler                        common::UsageWarning::TransferSizePresence) &&
1971031b4e5eSPeter Klausler             IsAllocatableOrObjectPointer(whole)) {
19720f973ac7SPeter Klausler           messages.Say(common::UsageWarning::TransferSizePresence,
1973860ed6c0SPeter Klausler               "SIZE= argument that is allocatable or pointer must be present at execution; parenthesize to silence this warning"_warn_en_US);
1974860ed6c0SPeter Klausler         }
1975860ed6c0SPeter Klausler       }
1976860ed6c0SPeter Klausler     }
1977860ed6c0SPeter Klausler   }
1978860ed6c0SPeter Klausler }
1979860ed6c0SPeter Klausler 
198097e3f605SPeter Klausler static void CheckSpecificIntrinsic(const characteristics::Procedure &proc,
198197e3f605SPeter Klausler     evaluate::ActualArguments &arguments, SemanticsContext &context,
198297e3f605SPeter Klausler     const Scope *scope, const evaluate::SpecificIntrinsic &intrinsic) {
1983aad5984bSPeter Klausler   if (intrinsic.name == "associated") {
19841c91d9bdSPeter Klausler     CheckAssociated(arguments, context, scope);
19851e9b60cfSPeter Klausler   } else if (intrinsic.name == "event_query") {
19861e9b60cfSPeter Klausler     CheckEvent_Query(arguments, context.foldingContext());
19878b389708SKatherine Rasmussen   } else if (intrinsic.name == "image_index") {
19888b389708SKatherine Rasmussen     CheckImage_Index(arguments, context.foldingContext().messages());
198997e3f605SPeter Klausler   } else if (intrinsic.name == "max" || intrinsic.name == "min") {
199097e3f605SPeter Klausler     CheckMaxMin(proc, arguments, context.foldingContext().messages());
1991f9b089a7SPeter Klausler   } else if (intrinsic.name == "move_alloc") {
1992f9b089a7SPeter Klausler     CheckMove_Alloc(arguments, context.foldingContext().messages());
19933bca8506SPeter Klausler   } else if (intrinsic.name == "present") {
19943bca8506SPeter Klausler     CheckPresent(arguments, context.foldingContext().messages());
1995bf4a8763SPeter Klausler   } else if (intrinsic.name == "reduce") {
1996bf4a8763SPeter Klausler     CheckReduce(arguments, context.foldingContext());
1997860ed6c0SPeter Klausler   } else if (intrinsic.name == "transfer") {
1998860ed6c0SPeter Klausler     CheckTransfer(arguments, context, scope);
199978ccffc0SDavid Truby   } else if (intrinsic.name == "free") {
200078ccffc0SDavid Truby     CheckFree(arguments, context.foldingContext().messages());
2001aad5984bSPeter Klausler   }
2002aad5984bSPeter Klausler }
2003aad5984bSPeter Klausler 
200464ab3302SCarolineConcatto static parser::Messages CheckExplicitInterface(
200564ab3302SCarolineConcatto     const characteristics::Procedure &proc, evaluate::ActualArguments &actuals,
2006191d4872SPeter Klausler     SemanticsContext &context, const Scope *scope,
200714b90d1fSpeter klausler     const evaluate::SpecificIntrinsic *intrinsic,
20081c530b3dSPeter Klausler     bool allowActualArgumentConversions, bool extentErrors,
20091c530b3dSPeter Klausler     bool ignoreImplicitVsExplicit) {
2010191d4872SPeter Klausler   evaluate::FoldingContext &foldingContext{context.foldingContext()};
2011191d4872SPeter Klausler   parser::ContextualMessages &messages{foldingContext.messages()};
201264ab3302SCarolineConcatto   parser::Messages buffer;
2013191d4872SPeter Klausler   auto restorer{messages.SetMessages(buffer)};
201464ab3302SCarolineConcatto   RearrangeArguments(proc, actuals, messages);
2015aad5984bSPeter Klausler   if (!buffer.empty()) {
2016aad5984bSPeter Klausler     return buffer;
2017aad5984bSPeter Klausler   }
201864ab3302SCarolineConcatto   int index{0};
201964ab3302SCarolineConcatto   for (auto &actual : actuals) {
202064ab3302SCarolineConcatto     const auto &dummy{proc.dummyArguments.at(index++)};
202164ab3302SCarolineConcatto     if (actual) {
2022191d4872SPeter Klausler       CheckExplicitInterfaceArg(*actual, dummy, proc, context, scope, intrinsic,
20231c530b3dSPeter Klausler           allowActualArgumentConversions, extentErrors,
20241c530b3dSPeter Klausler           ignoreImplicitVsExplicit);
202564ab3302SCarolineConcatto     } else if (!dummy.IsOptional()) {
202664ab3302SCarolineConcatto       if (dummy.name.empty()) {
202764ab3302SCarolineConcatto         messages.Say(
202864ab3302SCarolineConcatto             "Dummy argument #%d is not OPTIONAL and is not associated with "
202964ab3302SCarolineConcatto             "an actual argument in this procedure reference"_err_en_US,
203064ab3302SCarolineConcatto             index);
203164ab3302SCarolineConcatto       } else {
203264ab3302SCarolineConcatto         messages.Say("Dummy argument '%s=' (#%d) is not OPTIONAL and is not "
203364ab3302SCarolineConcatto                      "associated with an actual argument in this procedure "
203464ab3302SCarolineConcatto                      "reference"_err_en_US,
203564ab3302SCarolineConcatto             dummy.name, index);
203664ab3302SCarolineConcatto       }
203764ab3302SCarolineConcatto     }
203864ab3302SCarolineConcatto   }
203919afc495Speter klausler   if (proc.IsElemental() && !buffer.AnyFatalError()) {
2040191d4872SPeter Klausler     CheckElementalConformance(messages, proc, actuals, foldingContext);
204119afc495Speter klausler   }
2042aad5984bSPeter Klausler   if (intrinsic) {
204397e3f605SPeter Klausler     CheckSpecificIntrinsic(proc, actuals, context, scope, *intrinsic);
204464ab3302SCarolineConcatto   }
204564ab3302SCarolineConcatto   return buffer;
204664ab3302SCarolineConcatto }
204764ab3302SCarolineConcatto 
204864ab3302SCarolineConcatto bool CheckInterfaceForGeneric(const characteristics::Procedure &proc,
2049191d4872SPeter Klausler     evaluate::ActualArguments &actuals, SemanticsContext &context,
2050574f9dfeSPeter Klausler     bool allowActualArgumentConversions) {
20513b7b7fa7SPeter Klausler   return proc.HasExplicitInterface() &&
20523b7b7fa7SPeter Klausler       !CheckExplicitInterface(proc, actuals, context, nullptr, nullptr,
20531c530b3dSPeter Klausler           allowActualArgumentConversions, /*extentErrors=*/false,
20541c530b3dSPeter Klausler           /*ignoreImplicitVsExplicit=*/false)
2055d9195d66Speter klausler            .AnyFatalError();
205664ab3302SCarolineConcatto }
205764ab3302SCarolineConcatto 
2058adc56f37SPaul Scoropan bool CheckArgumentIsConstantExprInRange(
2059adc56f37SPaul Scoropan     const evaluate::ActualArguments &actuals, int index, int lowerBound,
2060adc56f37SPaul Scoropan     int upperBound, parser::ContextualMessages &messages) {
2061a8f1185aSJie Fu   CHECK(index >= 0 && static_cast<unsigned>(index) < actuals.size());
2062adc56f37SPaul Scoropan 
2063adc56f37SPaul Scoropan   const std::optional<evaluate::ActualArgument> &argOptional{actuals[index]};
2064adc56f37SPaul Scoropan   if (!argOptional) {
2065adc56f37SPaul Scoropan     DIE("Actual argument should have value");
2066adc56f37SPaul Scoropan     return false;
2067adc56f37SPaul Scoropan   }
2068adc56f37SPaul Scoropan 
2069adc56f37SPaul Scoropan   const evaluate::ActualArgument &arg{argOptional.value()};
2070adc56f37SPaul Scoropan   const evaluate::Expr<evaluate::SomeType> *argExpr{arg.UnwrapExpr()};
2071adc56f37SPaul Scoropan   CHECK(argExpr != nullptr);
2072adc56f37SPaul Scoropan 
2073adc56f37SPaul Scoropan   if (!IsConstantExpr(*argExpr)) {
2074adc56f37SPaul Scoropan     messages.Say("Actual argument #%d must be a constant expression"_err_en_US,
2075adc56f37SPaul Scoropan         index + 1);
2076adc56f37SPaul Scoropan     return false;
2077adc56f37SPaul Scoropan   }
2078adc56f37SPaul Scoropan 
2079adc56f37SPaul Scoropan   // This does not imply that the kind of the argument is 8. The kind
2080adc56f37SPaul Scoropan   // for the intrinsic's argument should have been check prior. This is just
2081adc56f37SPaul Scoropan   // a conversion so that we can read the constant value.
2082adc56f37SPaul Scoropan   auto scalarValue{evaluate::ToInt64(argExpr)};
2083adc56f37SPaul Scoropan   CHECK(scalarValue.has_value());
2084adc56f37SPaul Scoropan 
2085adc56f37SPaul Scoropan   if (*scalarValue < lowerBound || *scalarValue > upperBound) {
2086adc56f37SPaul Scoropan     messages.Say(
2087f50eaea8SKelvin Li         "Argument #%d must be a constant expression in range %d to %d"_err_en_US,
2088adc56f37SPaul Scoropan         index + 1, lowerBound, upperBound);
2089adc56f37SPaul Scoropan     return false;
2090adc56f37SPaul Scoropan   }
2091adc56f37SPaul Scoropan   return true;
2092adc56f37SPaul Scoropan }
2093adc56f37SPaul Scoropan 
2094adc56f37SPaul Scoropan bool CheckPPCIntrinsic(const Symbol &generic, const Symbol &specific,
2095adc56f37SPaul Scoropan     const evaluate::ActualArguments &actuals,
2096adc56f37SPaul Scoropan     evaluate::FoldingContext &context) {
2097adc56f37SPaul Scoropan   parser::ContextualMessages &messages{context.messages()};
2098adc56f37SPaul Scoropan 
2099adc56f37SPaul Scoropan   if (specific.name() == "__ppc_mtfsf") {
2100adc56f37SPaul Scoropan     return CheckArgumentIsConstantExprInRange(actuals, 0, 0, 7, messages);
2101adc56f37SPaul Scoropan   }
2102adc56f37SPaul Scoropan   if (specific.name() == "__ppc_mtfsfi") {
2103adc56f37SPaul Scoropan     return CheckArgumentIsConstantExprInRange(actuals, 0, 0, 7, messages) &&
2104adc56f37SPaul Scoropan         CheckArgumentIsConstantExprInRange(actuals, 1, 0, 15, messages);
2105adc56f37SPaul Scoropan   }
210610124b3eSKelvin Li   if (specific.name().ToString().compare(0, 14, "__ppc_vec_sld_") == 0) {
210710124b3eSKelvin Li     return CheckArgumentIsConstantExprInRange(actuals, 2, 0, 15, messages);
210810124b3eSKelvin Li   }
210910124b3eSKelvin Li   if (specific.name().ToString().compare(0, 15, "__ppc_vec_sldw_") == 0) {
211010124b3eSKelvin Li     return CheckArgumentIsConstantExprInRange(actuals, 2, 0, 3, messages);
211110124b3eSKelvin Li   }
211299dc3935SKelvin Li   if (specific.name().ToString().compare(0, 14, "__ppc_vec_ctf_") == 0) {
211399dc3935SKelvin Li     return CheckArgumentIsConstantExprInRange(actuals, 1, 0, 31, messages);
211499dc3935SKelvin Li   }
2115d652884eSKelvin Li   if (specific.name().ToString().compare(0, 16, "__ppc_vec_permi_") == 0) {
2116d652884eSKelvin Li     return CheckArgumentIsConstantExprInRange(actuals, 2, 0, 3, messages);
2117d652884eSKelvin Li   }
2118f50eaea8SKelvin Li   if (specific.name().ToString().compare(0, 21, "__ppc_vec_splat_s32__") == 0) {
2119f50eaea8SKelvin Li     return CheckArgumentIsConstantExprInRange(actuals, 0, -16, 15, messages);
2120f50eaea8SKelvin Li   }
2121f50eaea8SKelvin Li   if (specific.name().ToString().compare(0, 16, "__ppc_vec_splat_") == 0) {
2122f50eaea8SKelvin Li     // The value of arg2 in vec_splat must be a constant expression that is
2123f50eaea8SKelvin Li     // greater than or equal to 0, and less than the number of elements in arg1.
2124f50eaea8SKelvin Li     auto *expr{actuals[0].value().UnwrapExpr()};
2125f50eaea8SKelvin Li     auto type{characteristics::TypeAndShape::Characterize(*expr, context)};
2126f50eaea8SKelvin Li     assert(type && "unknown type");
2127f50eaea8SKelvin Li     const auto *derived{evaluate::GetDerivedTypeSpec(type.value().type())};
2128f50eaea8SKelvin Li     if (derived && derived->IsVectorType()) {
2129f50eaea8SKelvin Li       for (const auto &pair : derived->parameters()) {
2130f50eaea8SKelvin Li         if (pair.first == "element_kind") {
2131f50eaea8SKelvin Li           auto vecElemKind{Fortran::evaluate::ToInt64(pair.second.GetExplicit())
2132f50eaea8SKelvin Li                                .value_or(0)};
2133f50eaea8SKelvin Li           auto numElem{vecElemKind == 0 ? 0 : (16 / vecElemKind)};
2134f50eaea8SKelvin Li           return CheckArgumentIsConstantExprInRange(
2135f50eaea8SKelvin Li               actuals, 1, 0, numElem - 1, messages);
2136f50eaea8SKelvin Li         }
2137f50eaea8SKelvin Li       }
2138f50eaea8SKelvin Li     } else
2139f50eaea8SKelvin Li       assert(false && "vector type is expected");
2140f50eaea8SKelvin Li   }
2141adc56f37SPaul Scoropan   return false;
2142adc56f37SPaul Scoropan }
2143adc56f37SPaul Scoropan 
2144856c38d5SDavid Truby bool CheckWindowsIntrinsic(
2145856c38d5SDavid Truby     const Symbol &intrinsic, evaluate::FoldingContext &foldingContext) {
2146856c38d5SDavid Truby   parser::ContextualMessages &messages{foldingContext.messages()};
2147856c38d5SDavid Truby   // TODO: there are other intrinsics that are unsupported on Windows that
2148856c38d5SDavid Truby   // should be added here.
2149856c38d5SDavid Truby   if (intrinsic.name() == "getuid") {
2150856c38d5SDavid Truby     messages.Say(
2151856c38d5SDavid Truby         "User IDs do not exist on Windows. This function will always return 1"_warn_en_US);
2152856c38d5SDavid Truby   }
2153856c38d5SDavid Truby   if (intrinsic.name() == "getgid") {
2154856c38d5SDavid Truby     messages.Say(
2155856c38d5SDavid Truby         "Group IDs do not exist on Windows. This function will always return 1"_warn_en_US);
2156856c38d5SDavid Truby   }
2157856c38d5SDavid Truby   return true;
2158856c38d5SDavid Truby }
2159856c38d5SDavid Truby 
21600d588347SPeter Klausler bool CheckArguments(const characteristics::Procedure &proc,
2161191d4872SPeter Klausler     evaluate::ActualArguments &actuals, SemanticsContext &context,
216229d1a494SJean Perier     const Scope &scope, bool treatingExternalAsImplicit,
21631c530b3dSPeter Klausler     bool ignoreImplicitVsExplicit,
216429d1a494SJean Perier     const evaluate::SpecificIntrinsic *intrinsic) {
216564ab3302SCarolineConcatto   bool explicitInterface{proc.HasExplicitInterface()};
2166191d4872SPeter Klausler   evaluate::FoldingContext foldingContext{context.foldingContext()};
2167191d4872SPeter Klausler   parser::ContextualMessages &messages{foldingContext.messages()};
21680e1bb1d8SPeter Klausler   bool allowArgumentConversions{true};
216902847775SPeter Klausler   if (!explicitInterface || treatingExternalAsImplicit) {
217002847775SPeter Klausler     parser::Messages buffer;
217102847775SPeter Klausler     {
217202847775SPeter Klausler       auto restorer{messages.SetMessages(buffer)};
217302847775SPeter Klausler       for (auto &actual : actuals) {
217402847775SPeter Klausler         if (actual) {
217533c27f28SPeter Klausler           CheckImplicitInterfaceArg(*actual, messages, context);
217602847775SPeter Klausler         }
217702847775SPeter Klausler       }
217802847775SPeter Klausler     }
217902847775SPeter Klausler     if (!buffer.empty()) {
218002847775SPeter Klausler       if (auto *msgs{messages.messages()}) {
218102847775SPeter Klausler         msgs->Annex(std::move(buffer));
218202847775SPeter Klausler       }
21830d588347SPeter Klausler       return false; // don't pile on
218402847775SPeter Klausler     }
21850e1bb1d8SPeter Klausler     allowArgumentConversions = false;
218602847775SPeter Klausler   }
218764ab3302SCarolineConcatto   if (explicitInterface) {
21881c530b3dSPeter Klausler     auto buffer{CheckExplicitInterface(proc, actuals, context, &scope,
21890e1bb1d8SPeter Klausler         intrinsic, allowArgumentConversions,
21900e1bb1d8SPeter Klausler         /*extentErrors=*/true, ignoreImplicitVsExplicit)};
21910d588347SPeter Klausler     if (!buffer.empty()) {
2192f28c1a9dSPeter Klausler       if (treatingExternalAsImplicit) {
2193505f6da1SPeter Klausler         if (context.ShouldWarn(
2194505f6da1SPeter Klausler                 common::UsageWarning::KnownBadImplicitInterface)) {
219502847775SPeter Klausler           if (auto *msg{messages.Say(
21960f973ac7SPeter Klausler                   common::UsageWarning::KnownBadImplicitInterface,
219766fdfff7SPeter Klausler                   "If the procedure's interface were explicit, this reference would be in error"_warn_en_US)}) {
2198ef141aecSPeter Klausler             buffer.AttachTo(*msg, parser::Severity::Because);
219964ab3302SCarolineConcatto           }
2200505f6da1SPeter Klausler         } else {
2201505f6da1SPeter Klausler           buffer.clear();
2202505f6da1SPeter Klausler         }
220364ab3302SCarolineConcatto       }
220402847775SPeter Klausler       if (auto *msgs{messages.messages()}) {
220502847775SPeter Klausler         msgs->Annex(std::move(buffer));
220664ab3302SCarolineConcatto       }
22070d588347SPeter Klausler       return false;
220864ab3302SCarolineConcatto     }
220964ab3302SCarolineConcatto   }
22100d588347SPeter Klausler   return true;
22110d588347SPeter Klausler }
22121f879005STim Keith } // namespace Fortran::semantics
2213