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