164ab3302SCarolineConcatto //===-- lib/Semantics/check-declarations.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 // Static declaration checking 1064ab3302SCarolineConcatto 1164ab3302SCarolineConcatto #include "check-declarations.h" 12e9a8ab00SPeter Klausler #include "definable.h" 13641ede93Speter klausler #include "pointer-assignment.h" 1464ab3302SCarolineConcatto #include "flang/Evaluate/check-expression.h" 1564ab3302SCarolineConcatto #include "flang/Evaluate/fold.h" 1664ab3302SCarolineConcatto #include "flang/Evaluate/tools.h" 1769373a5dSPeter Klausler #include "flang/Parser/characters.h" 1864ab3302SCarolineConcatto #include "flang/Semantics/scope.h" 1964ab3302SCarolineConcatto #include "flang/Semantics/semantics.h" 2064ab3302SCarolineConcatto #include "flang/Semantics/symbol.h" 2164ab3302SCarolineConcatto #include "flang/Semantics/tools.h" 2264ab3302SCarolineConcatto #include "flang/Semantics/type.h" 2364ab3302SCarolineConcatto #include <algorithm> 24b6f22fa5Speter klausler #include <map> 25b6f22fa5Speter klausler #include <string> 2664ab3302SCarolineConcatto 2764ab3302SCarolineConcatto namespace Fortran::semantics { 2864ab3302SCarolineConcatto 2982edd428STim Keith namespace characteristics = evaluate::characteristics; 3082edd428STim Keith using characteristics::DummyArgument; 3182edd428STim Keith using characteristics::DummyDataObject; 3282edd428STim Keith using characteristics::DummyProcedure; 3382edd428STim Keith using characteristics::FunctionResult; 3482edd428STim Keith using characteristics::Procedure; 3564ab3302SCarolineConcatto 3664ab3302SCarolineConcatto class CheckHelper { 3764ab3302SCarolineConcatto public: 3864ab3302SCarolineConcatto explicit CheckHelper(SemanticsContext &c) : context_{c} {} 3964ab3302SCarolineConcatto 4082edd428STim Keith SemanticsContext &context() { return context_; } 4164ab3302SCarolineConcatto void Check() { Check(context_.globalScope()); } 4264ab3302SCarolineConcatto void Check(const ParamValue &, bool canBeAssumed); 4367081badSPeter Klausler void Check(const Bound &bound) { 4467081badSPeter Klausler CheckSpecExpr(bound.GetExplicit(), /*forElementalFunctionResult=*/false); 4567081badSPeter Klausler } 4664ab3302SCarolineConcatto void Check(const ShapeSpec &spec) { 4764ab3302SCarolineConcatto Check(spec.lbound()); 4864ab3302SCarolineConcatto Check(spec.ubound()); 4964ab3302SCarolineConcatto } 5064ab3302SCarolineConcatto void Check(const ArraySpec &); 5164ab3302SCarolineConcatto void Check(const DeclTypeSpec &, bool canHaveAssumedTypeParameters); 5264ab3302SCarolineConcatto void Check(const Symbol &); 53dafd3cf8SPeixin-Qiao void CheckCommonBlock(const Symbol &); 5464ab3302SCarolineConcatto void Check(const Scope &); 5582edd428STim Keith const Procedure *Characterize(const Symbol &); 5664ab3302SCarolineConcatto 5764ab3302SCarolineConcatto private: 5867081badSPeter Klausler template <typename A> 5967081badSPeter Klausler void CheckSpecExpr(const A &x, bool forElementalFunctionResult) { 6067081badSPeter Klausler evaluate::CheckSpecificationExpr( 6167081badSPeter Klausler x, DEREF(scope_), foldingContext_, forElementalFunctionResult); 6264ab3302SCarolineConcatto } 6364ab3302SCarolineConcatto void CheckValue(const Symbol &, const DerivedTypeSpec *); 642de5ea3bSpeter klausler void CheckVolatile(const Symbol &, const DerivedTypeSpec *); 65a8654b44SPeter Klausler void CheckContiguous(const Symbol &); 6664ab3302SCarolineConcatto void CheckPointer(const Symbol &); 6764ab3302SCarolineConcatto void CheckPassArg( 6864ab3302SCarolineConcatto const Symbol &proc, const Symbol *interface, const WithPassArg &); 6964ab3302SCarolineConcatto void CheckProcBinding(const Symbol &, const ProcBindingDetails &); 7064ab3302SCarolineConcatto void CheckObjectEntity(const Symbol &, const ObjectEntityDetails &); 71641ede93Speter klausler void CheckPointerInitialization(const Symbol &); 7264ab3302SCarolineConcatto void CheckArraySpec(const Symbol &, const ArraySpec &); 7364ab3302SCarolineConcatto void CheckProcEntity(const Symbol &, const ProcEntityDetails &); 7461b1390eSTim Keith void CheckSubprogram(const Symbol &, const SubprogramDetails &); 75982614faSPeter Klausler void CheckExternal(const Symbol &); 7664ab3302SCarolineConcatto void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &); 7764ab3302SCarolineConcatto void CheckDerivedType(const Symbol &, const DerivedTypeDetails &); 7837b2e2b0Speter klausler bool CheckFinal( 7937b2e2b0Speter klausler const Symbol &subroutine, SourceName, const Symbol &derivedType); 8037b2e2b0Speter klausler bool CheckDistinguishableFinals(const Symbol &f1, SourceName f1name, 8137b2e2b0Speter klausler const Symbol &f2, SourceName f2name, const Symbol &derivedType); 8264ab3302SCarolineConcatto void CheckGeneric(const Symbol &, const GenericDetails &); 8382edd428STim Keith void CheckHostAssoc(const Symbol &, const HostAssocDetails &); 8482edd428STim Keith bool CheckDefinedOperator( 8582edd428STim Keith SourceName, GenericKind, const Symbol &, const Procedure &); 8664ab3302SCarolineConcatto std::optional<parser::MessageFixedText> CheckNumberOfArgs( 8764ab3302SCarolineConcatto const GenericKind &, std::size_t); 8864ab3302SCarolineConcatto bool CheckDefinedOperatorArg( 8964ab3302SCarolineConcatto const SourceName &, const Symbol &, const Procedure &, std::size_t); 9064ab3302SCarolineConcatto bool CheckDefinedAssignment(const Symbol &, const Procedure &); 9164ab3302SCarolineConcatto bool CheckDefinedAssignmentArg(const Symbol &, const DummyArgument &, int); 92e86bf468SPeter Klausler void CheckSpecifics(const Symbol &, const GenericDetails &); 9364ab3302SCarolineConcatto void CheckEquivalenceSet(const EquivalenceSet &); 94d742c2aaSPeter Klausler void CheckEquivalenceObject(const EquivalenceObject &); 9564ab3302SCarolineConcatto void CheckBlockData(const Scope &); 9682edd428STim Keith void CheckGenericOps(const Scope &); 9764ab3302SCarolineConcatto bool CheckConflicting(const Symbol &, Attr, Attr); 98c1168676Speter klausler void WarnMissingFinal(const Symbol &); 9978da80e2SPeter Klausler void CheckSymbolType(const Symbol &); // C702 10064ab3302SCarolineConcatto bool InPure() const { 10164ab3302SCarolineConcatto return innermostSymbol_ && IsPureProcedure(*innermostSymbol_); 10264ab3302SCarolineConcatto } 10343a263f5Speter klausler bool InElemental() const { 1046052025bSPeter Klausler return innermostSymbol_ && IsElementalProcedure(*innermostSymbol_); 10543a263f5Speter klausler } 10664ab3302SCarolineConcatto bool InFunction() const { 10764ab3302SCarolineConcatto return innermostSymbol_ && IsFunction(*innermostSymbol_); 10864ab3302SCarolineConcatto } 109e2eabb7eSPeter Klausler bool InInterface() const { 110e2eabb7eSPeter Klausler const SubprogramDetails *subp{innermostSymbol_ 111e2eabb7eSPeter Klausler ? innermostSymbol_->detailsIf<SubprogramDetails>() 112e2eabb7eSPeter Klausler : nullptr}; 113e2eabb7eSPeter Klausler return subp && subp->isInterface(); 114e2eabb7eSPeter Klausler } 11564ab3302SCarolineConcatto template <typename... A> 1160d588347SPeter Klausler parser::Message *SayWithDeclaration(const Symbol &symbol, A &&...x) { 1170d588347SPeter Klausler parser::Message *msg{messages_.Say(std::forward<A>(x)...)}; 1180d588347SPeter Klausler if (msg && messages_.at().begin() != symbol.name().begin()) { 11964ab3302SCarolineConcatto evaluate::AttachDeclaration(*msg, symbol); 12064ab3302SCarolineConcatto } 1210d588347SPeter Klausler return msg; 12264ab3302SCarolineConcatto } 12390501be3SPeter Klausler bool InModuleFile() const { 12490501be3SPeter Klausler return FindModuleFileContaining(context_.FindScope(messages_.at())) != 12590501be3SPeter Klausler nullptr; 1263332dc32SPeter Klausler } 1270f973ac7SPeter Klausler template <typename FeatureOrUsageWarning, typename... A> 1280f973ac7SPeter Klausler parser::Message *Warn(FeatureOrUsageWarning warning, A &&...x) { 1290f973ac7SPeter Klausler if (!context_.ShouldWarn(warning) || InModuleFile()) { 13090501be3SPeter Klausler return nullptr; 13190501be3SPeter Klausler } else { 1320f973ac7SPeter Klausler return messages_.Say(warning, std::forward<A>(x)...); 1333332dc32SPeter Klausler } 13490501be3SPeter Klausler } 1350f973ac7SPeter Klausler template <typename FeatureOrUsageWarning, typename... A> 1360f973ac7SPeter Klausler parser::Message *Warn( 1370f973ac7SPeter Klausler FeatureOrUsageWarning warning, parser::CharBlock source, A &&...x) { 1380f973ac7SPeter Klausler if (!context_.ShouldWarn(warning) || 1390f973ac7SPeter Klausler FindModuleFileContaining(context_.FindScope(source))) { 1403332dc32SPeter Klausler return nullptr; 1410f973ac7SPeter Klausler } else { 1420f973ac7SPeter Klausler return messages_.Say(warning, source, std::forward<A>(x)...); 1433332dc32SPeter Klausler } 1443332dc32SPeter Klausler } 145c42f6314Speter klausler bool IsResultOkToDiffer(const FunctionResult &); 1463077d614SPeter Klausler void CheckGlobalName(const Symbol &); 1471062c140SjeanPerier void CheckProcedureAssemblyName(const Symbol &symbol); 1483f6e0c24SPeter Klausler void CheckExplicitSave(const Symbol &); 14956cd8a50SPeter Klausler parser::Messages WhyNotInteroperableDerivedType(const Symbol &); 150b3026babSPeter Klausler parser::Messages WhyNotInteroperableObject( 151b3026babSPeter Klausler const Symbol &, bool allowNonInteroperableType = false); 152ab7930bdSPeter Klausler parser::Messages WhyNotInteroperableFunctionResult(const Symbol &); 153ab7930bdSPeter Klausler parser::Messages WhyNotInteroperableProcedure(const Symbol &, bool isError); 154f3d83353SPeixinQiao void CheckBindC(const Symbol &); 155bc56620bSPeter Steinfeld // Check functions for defined I/O procedures 156bc56620bSPeter Steinfeld void CheckDefinedIoProc( 1577cf1608bSPeter Klausler const Symbol &, const GenericDetails &, common::DefinedIo); 158bc56620bSPeter Steinfeld bool CheckDioDummyIsData(const Symbol &, const Symbol *, std::size_t); 1597cf1608bSPeter Klausler void CheckDioDummyIsDerived( 1607cf1608bSPeter Klausler const Symbol &, const Symbol &, common::DefinedIo ioKind, const Symbol &); 161bc56620bSPeter Steinfeld void CheckDioDummyIsDefaultInteger(const Symbol &, const Symbol &); 162bc56620bSPeter Steinfeld void CheckDioDummyIsScalar(const Symbol &, const Symbol &); 163bc56620bSPeter Steinfeld void CheckDioDummyAttrs(const Symbol &, const Symbol &, Attr); 164dcf9ba82SPeter Klausler void CheckDioDtvArg( 1657cf1608bSPeter Klausler const Symbol &, const Symbol *, common::DefinedIo, const Symbol &); 166eb14135eSPeter Klausler void CheckGenericVsIntrinsic(const Symbol &, const GenericDetails &); 167bc56620bSPeter Steinfeld void CheckDefaultIntegerArg(const Symbol &, const Symbol *, Attr); 168bc56620bSPeter Steinfeld void CheckDioAssumedLenCharacterArg( 169bc56620bSPeter Steinfeld const Symbol &, const Symbol *, std::size_t, Attr); 170bc56620bSPeter Steinfeld void CheckDioVlistArg(const Symbol &, const Symbol *, std::size_t); 1717cf1608bSPeter Klausler void CheckDioArgCount(const Symbol &, common::DefinedIo ioKind, std::size_t); 17222d7e298SPeter Steinfeld struct TypeWithDefinedIo { 173dcf9ba82SPeter Klausler const DerivedTypeSpec &type; 1747cf1608bSPeter Klausler common::DefinedIo ioKind; 17522d7e298SPeter Steinfeld const Symbol &proc; 176dcf9ba82SPeter Klausler const Symbol &generic; 17722d7e298SPeter Steinfeld }; 1787cf1608bSPeter Klausler void CheckAlreadySeenDefinedIo(const DerivedTypeSpec &, common::DefinedIo, 1797cf1608bSPeter Klausler const Symbol &, const Symbol &generic); 180f7be1aadSPeter Klausler void CheckModuleProcedureDef(const Symbol &); 18164ab3302SCarolineConcatto 18264ab3302SCarolineConcatto SemanticsContext &context_; 18364ab3302SCarolineConcatto evaluate::FoldingContext &foldingContext_{context_.foldingContext()}; 18464ab3302SCarolineConcatto parser::ContextualMessages &messages_{foldingContext_.messages()}; 18564ab3302SCarolineConcatto const Scope *scope_{nullptr}; 186641ede93Speter klausler bool scopeIsUninstantiatedPDT_{false}; 18764ab3302SCarolineConcatto // This symbol is the one attached to the innermost enclosing scope 18864ab3302SCarolineConcatto // that has a symbol. 18964ab3302SCarolineConcatto const Symbol *innermostSymbol_{nullptr}; 19082edd428STim Keith // Cache of calls to Procedure::Characterize(Symbol) 1910d8331c0Speter klausler std::map<SymbolRef, std::optional<Procedure>, SymbolAddressCompare> 1920d8331c0Speter klausler characterizeCache_; 193f7be1aadSPeter Klausler // Collection of module procedure symbols with non-BIND(C) 194f7be1aadSPeter Klausler // global names, qualified by their module. 195f7be1aadSPeter Klausler std::map<std::pair<SourceName, const Symbol *>, SymbolRef> moduleProcs_; 1963077d614SPeter Klausler // Collection of symbols with global names, BIND(C) or otherwise 1973077d614SPeter Klausler std::map<std::string, SymbolRef> globalNames_; 198982614faSPeter Klausler // Collection of external procedures without global definitions 199982614faSPeter Klausler std::map<std::string, SymbolRef> externalNames_; 2001062c140SjeanPerier // Collection of target dependent assembly names of external and BIND(C) 2011062c140SjeanPerier // procedures. 2021062c140SjeanPerier std::map<std::string, SymbolRef> procedureAssemblyNames_; 203ab7930bdSPeter Klausler // Derived types that have been examined by WhyNotInteroperable_XXX 204ab7930bdSPeter Klausler UnorderedSymbolSet examinedByWhyNotInteroperable_; 20582edd428STim Keith }; 20682edd428STim Keith 20782edd428STim Keith class DistinguishabilityHelper { 20882edd428STim Keith public: 20982edd428STim Keith DistinguishabilityHelper(SemanticsContext &context) : context_{context} {} 21082edd428STim Keith void Add(const Symbol &, GenericKind, const Symbol &, const Procedure &); 21186f59de1STim Keith void Check(const Scope &); 21282edd428STim Keith 21382edd428STim Keith private: 21486f59de1STim Keith void SayNotDistinguishable(const Scope &, const SourceName &, GenericKind, 2152236048fSPeter Klausler const Symbol &, const Symbol &, bool isHardConflict); 21686f59de1STim Keith void AttachDeclaration(parser::Message &, const Scope &, const Symbol &); 21782edd428STim Keith 21882edd428STim Keith SemanticsContext &context_; 21982edd428STim Keith struct ProcedureInfo { 22082edd428STim Keith GenericKind kind; 22182edd428STim Keith const Procedure &procedure; 22282edd428STim Keith }; 223e87cdda3SPeter Klausler std::map<SourceName, std::map<const Symbol *, ProcedureInfo>> 224e87cdda3SPeter Klausler nameToSpecifics_; 22564ab3302SCarolineConcatto }; 22664ab3302SCarolineConcatto 22764ab3302SCarolineConcatto void CheckHelper::Check(const ParamValue &value, bool canBeAssumed) { 22864ab3302SCarolineConcatto if (value.isAssumed()) { 229657aaf8bSPete Steinfeld if (!canBeAssumed) { // C795, C721, C726 23064ab3302SCarolineConcatto messages_.Say( 23141a964cfSPeter Klausler "An assumed (*) type parameter may be used only for a (non-statement function) dummy argument, associate name, character named constant, or external function result"_err_en_US); 23264ab3302SCarolineConcatto } 23364ab3302SCarolineConcatto } else { 23467081badSPeter Klausler CheckSpecExpr(value.GetExplicit(), /*forElementalFunctionResult=*/false); 23564ab3302SCarolineConcatto } 23664ab3302SCarolineConcatto } 23764ab3302SCarolineConcatto 23864ab3302SCarolineConcatto void CheckHelper::Check(const ArraySpec &shape) { 23964ab3302SCarolineConcatto for (const auto &spec : shape) { 24064ab3302SCarolineConcatto Check(spec); 24164ab3302SCarolineConcatto } 24264ab3302SCarolineConcatto } 24364ab3302SCarolineConcatto 24464ab3302SCarolineConcatto void CheckHelper::Check( 24564ab3302SCarolineConcatto const DeclTypeSpec &type, bool canHaveAssumedTypeParameters) { 24664ab3302SCarolineConcatto if (type.category() == DeclTypeSpec::Character) { 24764ab3302SCarolineConcatto Check(type.characterTypeSpec().length(), canHaveAssumedTypeParameters); 24864ab3302SCarolineConcatto } else if (const DerivedTypeSpec *derived{type.AsDerived()}) { 24964ab3302SCarolineConcatto for (auto &parm : derived->parameters()) { 25064ab3302SCarolineConcatto Check(parm.second, canHaveAssumedTypeParameters); 25164ab3302SCarolineConcatto } 25264ab3302SCarolineConcatto } 25364ab3302SCarolineConcatto } 25464ab3302SCarolineConcatto 255e83c5b25SPeter Klausler static bool IsBlockData(const Scope &scope) { 256e83c5b25SPeter Klausler return scope.kind() == Scope::Kind::BlockData; 257e83c5b25SPeter Klausler } 258e83c5b25SPeter Klausler 259e83c5b25SPeter Klausler static bool IsBlockData(const Symbol &symbol) { 260e83c5b25SPeter Klausler return symbol.scope() && IsBlockData(*symbol.scope()); 261e83c5b25SPeter Klausler } 262e83c5b25SPeter Klausler 26364ab3302SCarolineConcatto void CheckHelper::Check(const Symbol &symbol) { 2647cc789bcSPeter Klausler if (symbol.has<UseErrorDetails>()) { 2657cc789bcSPeter Klausler return; 2667cc789bcSPeter Klausler } 267c7285cd0SPeter Klausler if (symbol.name().size() > common::maxNameLen && 2683332dc32SPeter Klausler &symbol == &symbol.GetUltimate()) { 2690f973ac7SPeter Klausler Warn(common::LanguageFeature::LongNames, symbol.name(), 2709ab292d7SPeter Klausler "%s has length %d, which is greater than the maximum name length %d"_port_en_US, 271a2ac0bb2SPeixinQiao symbol.name(), symbol.name().size(), common::maxNameLen); 272a2ac0bb2SPeixinQiao } 27364ab3302SCarolineConcatto if (context_.HasError(symbol)) { 27464ab3302SCarolineConcatto return; 27564ab3302SCarolineConcatto } 27664ab3302SCarolineConcatto auto restorer{messages_.SetLocation(symbol.name())}; 27764ab3302SCarolineConcatto context_.set_location(symbol.name()); 278641ede93Speter klausler const DeclTypeSpec *type{symbol.GetType()}; 279641ede93Speter klausler const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr}; 2802de5ea3bSpeter klausler bool isDone{false}; 281cd03e96fSPeter Klausler common::visit( 28264ab3302SCarolineConcatto common::visitors{ 2832de5ea3bSpeter klausler [&](const UseDetails &x) { isDone = true; }, 2842de5ea3bSpeter klausler [&](const HostAssocDetails &x) { 2852de5ea3bSpeter klausler CheckHostAssoc(symbol, x); 2862de5ea3bSpeter klausler isDone = true; 2872de5ea3bSpeter klausler }, 2882de5ea3bSpeter klausler [&](const ProcBindingDetails &x) { 2892de5ea3bSpeter klausler CheckProcBinding(symbol, x); 2902de5ea3bSpeter klausler isDone = true; 2912de5ea3bSpeter klausler }, 29264ab3302SCarolineConcatto [&](const ObjectEntityDetails &x) { CheckObjectEntity(symbol, x); }, 29364ab3302SCarolineConcatto [&](const ProcEntityDetails &x) { CheckProcEntity(symbol, x); }, 29461b1390eSTim Keith [&](const SubprogramDetails &x) { CheckSubprogram(symbol, x); }, 29564ab3302SCarolineConcatto [&](const DerivedTypeDetails &x) { CheckDerivedType(symbol, x); }, 29664ab3302SCarolineConcatto [&](const GenericDetails &x) { CheckGeneric(symbol, x); }, 29764ab3302SCarolineConcatto [](const auto &) {}, 29864ab3302SCarolineConcatto }, 29964ab3302SCarolineConcatto symbol.details()); 3002de5ea3bSpeter klausler if (symbol.attrs().test(Attr::VOLATILE)) { 3012de5ea3bSpeter klausler CheckVolatile(symbol, derived); 3022de5ea3bSpeter klausler } 303199402c3SPeter Klausler if (symbol.attrs().test(Attr::BIND_C)) { 304f3d83353SPeixinQiao CheckBindC(symbol); 305199402c3SPeter Klausler } 3063f6e0c24SPeter Klausler if (symbol.attrs().test(Attr::SAVE) && 3073f6e0c24SPeter Klausler !symbol.implicitAttrs().test(Attr::SAVE)) { 3083f6e0c24SPeter Klausler CheckExplicitSave(symbol); 3093f6e0c24SPeter Klausler } 310a8654b44SPeter Klausler if (symbol.attrs().test(Attr::CONTIGUOUS)) { 311a8654b44SPeter Klausler CheckContiguous(symbol); 312a8654b44SPeter Klausler } 3133077d614SPeter Klausler CheckGlobalName(symbol); 3141062c140SjeanPerier CheckProcedureAssemblyName(symbol); 31524445fc1SPeter Klausler if (symbol.attrs().test(Attr::ASYNCHRONOUS) && 31624445fc1SPeter Klausler !evaluate::IsVariable(symbol)) { 31724445fc1SPeter Klausler messages_.Say( 31824445fc1SPeter Klausler "An entity may not have the ASYNCHRONOUS attribute unless it is a variable"_err_en_US); 31924445fc1SPeter Klausler } 320465807eeSPeter Klausler if (symbol.attrs().HasAny({Attr::INTENT_IN, Attr::INTENT_INOUT, 321465807eeSPeter Klausler Attr::INTENT_OUT, Attr::OPTIONAL, Attr::VALUE}) && 322465807eeSPeter Klausler !IsDummy(symbol)) { 323c1c99290SPeter Klausler if (context_.IsEnabled( 324c1c99290SPeter Klausler common::LanguageFeature::IgnoreIrrelevantAttributes)) { 325c1c99290SPeter Klausler context_.Warn(common::LanguageFeature::IgnoreIrrelevantAttributes, 326c1c99290SPeter Klausler "Only a dummy argument should have an INTENT, VALUE, or OPTIONAL attribute"_warn_en_US); 327c1c99290SPeter Klausler } else { 328465807eeSPeter Klausler messages_.Say( 329465807eeSPeter Klausler "Only a dummy argument may have an INTENT, VALUE, or OPTIONAL attribute"_err_en_US); 330c1c99290SPeter Klausler } 331465807eeSPeter Klausler } else if (symbol.attrs().test(Attr::VALUE)) { 332465807eeSPeter Klausler CheckValue(symbol, derived); 333465807eeSPeter Klausler } 33424445fc1SPeter Klausler 3352de5ea3bSpeter klausler if (isDone) { 3362de5ea3bSpeter klausler return; // following checks do not apply 3372de5ea3bSpeter klausler } 33886ce609dSPeter Klausler 3391457eb37SPeter Klausler if (symbol.attrs().test(Attr::PROTECTED)) { 3401457eb37SPeter Klausler if (symbol.owner().kind() != Scope::Kind::Module) { // C854 3411457eb37SPeter Klausler messages_.Say( 3421457eb37SPeter Klausler "A PROTECTED entity must be in the specification part of a module"_err_en_US); 3431457eb37SPeter Klausler } 3441457eb37SPeter Klausler if (!evaluate::IsVariable(symbol) && !IsProcedurePointer(symbol)) { // C855 3451457eb37SPeter Klausler messages_.Say( 3461457eb37SPeter Klausler "A PROTECTED entity must be a variable or pointer"_err_en_US); 3471457eb37SPeter Klausler } 34805e62db2SPeter Klausler if (FindCommonBlockContaining(symbol)) { // C856 3491457eb37SPeter Klausler messages_.Say( 3501457eb37SPeter Klausler "A PROTECTED entity may not be in a common block"_err_en_US); 3511457eb37SPeter Klausler } 3521457eb37SPeter Klausler } 3532de5ea3bSpeter klausler if (IsPointer(symbol)) { 3542de5ea3bSpeter klausler CheckPointer(symbol); 3552de5ea3bSpeter klausler } 35664ab3302SCarolineConcatto if (InPure()) { 357e2eabb7eSPeter Klausler if (InInterface()) { 358e2eabb7eSPeter Klausler // Declarations in interface definitions "have no effect" if they 359e2eabb7eSPeter Klausler // are not pertinent to the characteristics of the procedure. 360e2eabb7eSPeter Klausler // Restrictions on entities in pure procedure interfaces don't need 361e2eabb7eSPeter Klausler // enforcement. 3626d4c8878SPeter Klausler } else if (!FindCommonBlockContaining(symbol) && IsSaved(symbol)) { 3632985d562SPeter Klausler if (IsInitialized(symbol)) { 3642985d562SPeter Klausler messages_.Say( 3652985d562SPeter Klausler "A pure subprogram may not initialize a variable"_err_en_US); 3662985d562SPeter Klausler } else { 36764ab3302SCarolineConcatto messages_.Say( 36864ab3302SCarolineConcatto "A pure subprogram may not have a variable with the SAVE attribute"_err_en_US); 36964ab3302SCarolineConcatto } 3702985d562SPeter Klausler } 371e2eabb7eSPeter Klausler if (symbol.attrs().test(Attr::VOLATILE) && 372e2eabb7eSPeter Klausler (IsDummy(symbol) || !InInterface())) { 373e2eabb7eSPeter Klausler messages_.Say( 374e2eabb7eSPeter Klausler "A pure subprogram may not have a variable with the VOLATILE attribute"_err_en_US); 375e2eabb7eSPeter Klausler } 3763602efa7SPeter Klausler if (innermostSymbol_ && innermostSymbol_->name() == "__builtin_c_funloc") { 3773602efa7SPeter Klausler // The intrinsic procedure C_FUNLOC() gets a pass on this check. 3783602efa7SPeter Klausler } else if (IsProcedure(symbol) && !IsPureProcedure(symbol) && 3793602efa7SPeter Klausler IsDummy(symbol)) { 380e2eabb7eSPeter Klausler messages_.Say( 381e2eabb7eSPeter Klausler "A dummy procedure of a pure subprogram must be pure"_err_en_US); 382e2eabb7eSPeter Klausler } 383e2eabb7eSPeter Klausler } 384a8654b44SPeter Klausler const auto *object{symbol.detailsIf<ObjectEntityDetails>()}; 38541a964cfSPeter Klausler if (type) { // Section 7.2, paragraph 7; C795 38641a964cfSPeter Klausler bool isChar{type->category() == DeclTypeSpec::Character}; 38741a964cfSPeter Klausler bool canHaveAssumedParameter{(isChar && IsNamedConstant(symbol)) || 388c42f6314Speter klausler (IsAssumedLengthCharacter(symbol) && // C722 3893077d614SPeter Klausler (IsExternal(symbol) || 3903077d614SPeter Klausler ClassifyProcedure(symbol) == 3913077d614SPeter Klausler ProcedureDefinitionClass::Dummy)) || 39264ab3302SCarolineConcatto symbol.test(Symbol::Flag::ParentComp)}; 393657aaf8bSPete Steinfeld if (!IsStmtFunctionDummy(symbol)) { // C726 39486ce609dSPeter Klausler if (object) { 39564ab3302SCarolineConcatto canHaveAssumedParameter |= object->isDummy() || 39641a964cfSPeter Klausler (isChar && object->isFuncResult()) || 397657aaf8bSPete Steinfeld IsStmtFunctionResult(symbol); // Avoids multiple messages 39864ab3302SCarolineConcatto } else { 39964ab3302SCarolineConcatto canHaveAssumedParameter |= symbol.has<AssocEntityDetails>(); 40064ab3302SCarolineConcatto } 401657aaf8bSPete Steinfeld } 402525ff9bdSPeter Klausler if (IsProcedurePointer(symbol) && symbol.HasExplicitInterface()) { 403525ff9bdSPeter Klausler // Don't check function result types here 404525ff9bdSPeter Klausler } else { 40564ab3302SCarolineConcatto Check(*type, canHaveAssumedParameter); 406525ff9bdSPeter Klausler } 40767081badSPeter Klausler if (InFunction() && IsFunctionResult(symbol)) { 40867081badSPeter Klausler if (InPure()) { 40964ab3302SCarolineConcatto if (type->IsPolymorphic() && IsAllocatable(symbol)) { // C1585 41064ab3302SCarolineConcatto messages_.Say( 41164ab3302SCarolineConcatto "Result of pure function may not be both polymorphic and ALLOCATABLE"_err_en_US); 41264ab3302SCarolineConcatto } 41364ab3302SCarolineConcatto if (derived) { 414e9a8ab00SPeter Klausler // These cases would be caught be the general validation of local 415e9a8ab00SPeter Klausler // variables in a pure context, but these messages are more specific. 416e9a8ab00SPeter Klausler if (HasImpureFinal(symbol)) { // C1584 417e9a8ab00SPeter Klausler messages_.Say( 418e9a8ab00SPeter Klausler "Result of pure function may not have an impure FINAL subroutine"_err_en_US); 419e9a8ab00SPeter Klausler } 42025822dc3SPeter Klausler if (auto bad{ 42125822dc3SPeter Klausler FindPolymorphicAllocatablePotentialComponent(*derived)}) { 42264ab3302SCarolineConcatto SayWithDeclaration(*bad, 42325822dc3SPeter Klausler "Result of pure function may not have polymorphic ALLOCATABLE potential component '%s'"_err_en_US, 42464ab3302SCarolineConcatto bad.BuildResultDesignatorName()); 42564ab3302SCarolineConcatto } 42664ab3302SCarolineConcatto } 42764ab3302SCarolineConcatto } 42867081badSPeter Klausler if (InElemental() && isChar) { // F'2023 C15121 42967081badSPeter Klausler CheckSpecExpr(type->characterTypeSpec().length().GetExplicit(), 43067081badSPeter Klausler /*forElementalFunctionResult=*/true); 43167081badSPeter Klausler // TODO: check PDT LEN parameters 43267081badSPeter Klausler } 43367081badSPeter Klausler } 43464ab3302SCarolineConcatto } 4353077d614SPeter Klausler if (IsAssumedLengthCharacter(symbol) && IsFunction(symbol)) { // C723 43664ab3302SCarolineConcatto if (symbol.attrs().test(Attr::RECURSIVE)) { 43764ab3302SCarolineConcatto messages_.Say( 43864ab3302SCarolineConcatto "An assumed-length CHARACTER(*) function cannot be RECURSIVE"_err_en_US); 43964ab3302SCarolineConcatto } 44064ab3302SCarolineConcatto if (symbol.Rank() > 0) { 44164ab3302SCarolineConcatto messages_.Say( 44264ab3302SCarolineConcatto "An assumed-length CHARACTER(*) function cannot return an array"_err_en_US); 44364ab3302SCarolineConcatto } 4443077d614SPeter Klausler if (!IsStmtFunction(symbol)) { 4456052025bSPeter Klausler if (IsElementalProcedure(symbol)) { 44664ab3302SCarolineConcatto messages_.Say( 44764ab3302SCarolineConcatto "An assumed-length CHARACTER(*) function cannot be ELEMENTAL"_err_en_US); 4486052025bSPeter Klausler } else if (IsPureProcedure(symbol)) { 4496052025bSPeter Klausler messages_.Say( 4506052025bSPeter Klausler "An assumed-length CHARACTER(*) function cannot be PURE"_err_en_US); 45164ab3302SCarolineConcatto } 4523077d614SPeter Klausler } 45364ab3302SCarolineConcatto if (const Symbol *result{FindFunctionResult(symbol)}) { 45464ab3302SCarolineConcatto if (IsPointer(*result)) { 45564ab3302SCarolineConcatto messages_.Say( 45664ab3302SCarolineConcatto "An assumed-length CHARACTER(*) function cannot return a POINTER"_err_en_US); 45764ab3302SCarolineConcatto } 45824d29391SPeter Klausler } 45924d29391SPeter Klausler if (IsProcedurePointer(symbol) && IsDummy(symbol)) { 4600f973ac7SPeter Klausler Warn(common::UsageWarning::Portability, 4613077d614SPeter Klausler "A dummy procedure pointer should not have assumed-length CHARACTER(*) result type"_port_en_US); 4623077d614SPeter Klausler // The non-dummy case is a hard error that's caught elsewhere. 46364ab3302SCarolineConcatto } 46464ab3302SCarolineConcatto } 465c42f6314Speter klausler if (IsDummy(symbol)) { 466c42f6314Speter klausler if (IsNamedConstant(symbol)) { 467c42f6314Speter klausler messages_.Say( 468c42f6314Speter klausler "A dummy argument may not also be a named constant"_err_en_US); 469c42f6314Speter klausler } 4704171f80dSpeter klausler } else if (IsFunctionResult(symbol)) { 4715491fdf5SPeixin-Qiao if (IsNamedConstant(symbol)) { 4725491fdf5SPeixin-Qiao messages_.Say( 4735491fdf5SPeixin-Qiao "A function result may not also be a named constant"_err_en_US); 4745491fdf5SPeixin-Qiao } 475c42f6314Speter klausler } 476c0f5015aSPeter Klausler if (IsAutomatic(symbol)) { 477c0f5015aSPeter Klausler if (const Symbol * common{FindCommonBlockContaining(symbol)}) { 478b297563aSTim Keith messages_.Say( 479c0f5015aSPeter Klausler "Automatic data object '%s' may not appear in COMMON block /%s/"_err_en_US, 480c0f5015aSPeter Klausler symbol.name(), common->name()); 481c0f5015aSPeter Klausler } else if (symbol.owner().IsModule()) { 482c0f5015aSPeter Klausler messages_.Say( 483c0f5015aSPeter Klausler "Automatic data object '%s' may not appear in a module"_err_en_US, 484b297563aSTim Keith symbol.name()); 485e83c5b25SPeter Klausler } else if (IsBlockData(symbol.owner())) { 486e83c5b25SPeter Klausler messages_.Say( 487e83c5b25SPeter Klausler "Automatic data object '%s' may not appear in a BLOCK DATA subprogram"_err_en_US, 488e83c5b25SPeter Klausler symbol.name()); 489e83c5b25SPeter Klausler } else if (symbol.owner().kind() == Scope::Kind::MainProgram) { 490e83c5b25SPeter Klausler if (context_.IsEnabled(common::LanguageFeature::AutomaticInMainProgram)) { 4910f973ac7SPeter Klausler Warn(common::LanguageFeature::AutomaticInMainProgram, 492e83c5b25SPeter Klausler "Automatic data object '%s' should not appear in the specification part of a main program"_port_en_US, 493e83c5b25SPeter Klausler symbol.name()); 494e83c5b25SPeter Klausler } else { 495e83c5b25SPeter Klausler messages_.Say( 496e83c5b25SPeter Klausler "Automatic data object '%s' may not appear in the specification part of a main program"_err_en_US, 497e83c5b25SPeter Klausler symbol.name()); 498e83c5b25SPeter Klausler } 499b297563aSTim Keith } 500c0f5015aSPeter Klausler } 5019ab292d7SPeter Klausler if (IsProcedure(symbol)) { 5029ab292d7SPeter Klausler if (IsAllocatable(symbol)) { 5039ab292d7SPeter Klausler messages_.Say( 5049ab292d7SPeter Klausler "Procedure '%s' may not be ALLOCATABLE"_err_en_US, symbol.name()); 5059ab292d7SPeter Klausler } 5069ab292d7SPeter Klausler if (!symbol.HasExplicitInterface() && symbol.Rank() > 0) { 50788a097d1SPeter Klausler messages_.Say( 50888a097d1SPeter Klausler "Procedure '%s' may not be an array without an explicit interface"_err_en_US, 50988a097d1SPeter Klausler symbol.name()); 51088a097d1SPeter Klausler } 51188a097d1SPeter Klausler } 5129ab292d7SPeter Klausler } 51364ab3302SCarolineConcatto 5143077d614SPeter Klausler void CheckHelper::CheckCommonBlock(const Symbol &symbol) { 5153077d614SPeter Klausler CheckGlobalName(symbol); 516199402c3SPeter Klausler if (symbol.attrs().test(Attr::BIND_C)) { 5173077d614SPeter Klausler CheckBindC(symbol); 5183077d614SPeter Klausler } 519b788d628SPeter Klausler for (MutableSymbolRef ref : symbol.get<CommonBlockDetails>().objects()) { 520b788d628SPeter Klausler if (ref->test(Symbol::Flag::CrayPointee)) { 521b788d628SPeter Klausler messages_.Say(ref->name(), 522b788d628SPeter Klausler "Cray pointee '%s' may not be a member of a COMMON block"_err_en_US, 523b788d628SPeter Klausler ref->name()); 524b788d628SPeter Klausler } 525b788d628SPeter Klausler } 526199402c3SPeter Klausler } 527dafd3cf8SPeixin-Qiao 5283f6e0c24SPeter Klausler // C859, C860 5293f6e0c24SPeter Klausler void CheckHelper::CheckExplicitSave(const Symbol &symbol) { 5303f6e0c24SPeter Klausler const Symbol &ultimate{symbol.GetUltimate()}; 5313f6e0c24SPeter Klausler if (ultimate.test(Symbol::Flag::InDataStmt)) { 5323f6e0c24SPeter Klausler // checked elsewhere 5333f6e0c24SPeter Klausler } else if (symbol.has<UseDetails>()) { 5343f6e0c24SPeter Klausler messages_.Say( 5353f6e0c24SPeter Klausler "The USE-associated name '%s' may not have an explicit SAVE attribute"_err_en_US, 5363f6e0c24SPeter Klausler symbol.name()); 5373f6e0c24SPeter Klausler } else if (IsDummy(ultimate)) { 5383f6e0c24SPeter Klausler messages_.Say( 5393f6e0c24SPeter Klausler "The dummy argument '%s' may not have an explicit SAVE attribute"_err_en_US, 5403f6e0c24SPeter Klausler symbol.name()); 5413f6e0c24SPeter Klausler } else if (IsFunctionResult(ultimate)) { 5423f6e0c24SPeter Klausler messages_.Say( 5433f6e0c24SPeter Klausler "The function result variable '%s' may not have an explicit SAVE attribute"_err_en_US, 5443f6e0c24SPeter Klausler symbol.name()); 5453f6e0c24SPeter Klausler } else if (const Symbol * common{FindCommonBlockContaining(ultimate)}) { 5463f6e0c24SPeter Klausler messages_.Say( 5473f6e0c24SPeter Klausler "The entity '%s' in COMMON block /%s/ may not have an explicit SAVE attribute"_err_en_US, 5483f6e0c24SPeter Klausler symbol.name(), common->name()); 5493f6e0c24SPeter Klausler } else if (IsAutomatic(ultimate)) { 5503f6e0c24SPeter Klausler messages_.Say( 5513f6e0c24SPeter Klausler "The automatic object '%s' may not have an explicit SAVE attribute"_err_en_US, 5523f6e0c24SPeter Klausler symbol.name()); 5533f6e0c24SPeter Klausler } else if (!evaluate::IsVariable(ultimate) && !IsProcedurePointer(ultimate)) { 5543f6e0c24SPeter Klausler messages_.Say( 5553f6e0c24SPeter Klausler "The entity '%s' with an explicit SAVE attribute must be a variable, procedure pointer, or COMMON block"_err_en_US, 5563f6e0c24SPeter Klausler symbol.name()); 5573f6e0c24SPeter Klausler } 5583f6e0c24SPeter Klausler } 5593f6e0c24SPeter Klausler 56064ab3302SCarolineConcatto void CheckHelper::CheckValue( 56164ab3302SCarolineConcatto const Symbol &symbol, const DerivedTypeSpec *derived) { // C863 - C865 56264ab3302SCarolineConcatto if (IsProcedure(symbol)) { 56364ab3302SCarolineConcatto messages_.Say( 56464ab3302SCarolineConcatto "VALUE attribute may apply only to a dummy data object"_err_en_US); 565465807eeSPeter Klausler return; // don't pile on 56664ab3302SCarolineConcatto } 56764ab3302SCarolineConcatto if (IsAssumedSizeArray(symbol)) { 56864ab3302SCarolineConcatto messages_.Say( 56964ab3302SCarolineConcatto "VALUE attribute may not apply to an assumed-size array"_err_en_US); 57064ab3302SCarolineConcatto } 5711ee6f7adSPeter Klausler if (evaluate::IsCoarray(symbol)) { 57264ab3302SCarolineConcatto messages_.Say("VALUE attribute may not apply to a coarray"_err_en_US); 57364ab3302SCarolineConcatto } 57464ab3302SCarolineConcatto if (IsAllocatable(symbol)) { 57564ab3302SCarolineConcatto messages_.Say("VALUE attribute may not apply to an ALLOCATABLE"_err_en_US); 57664ab3302SCarolineConcatto } else if (IsPointer(symbol)) { 57764ab3302SCarolineConcatto messages_.Say("VALUE attribute may not apply to a POINTER"_err_en_US); 57864ab3302SCarolineConcatto } 57964ab3302SCarolineConcatto if (IsIntentInOut(symbol)) { 58064ab3302SCarolineConcatto messages_.Say( 58164ab3302SCarolineConcatto "VALUE attribute may not apply to an INTENT(IN OUT) argument"_err_en_US); 58264ab3302SCarolineConcatto } else if (IsIntentOut(symbol)) { 58364ab3302SCarolineConcatto messages_.Say( 58464ab3302SCarolineConcatto "VALUE attribute may not apply to an INTENT(OUT) argument"_err_en_US); 58564ab3302SCarolineConcatto } 58664ab3302SCarolineConcatto if (symbol.attrs().test(Attr::VOLATILE)) { 58764ab3302SCarolineConcatto messages_.Say("VALUE attribute may not apply to a VOLATILE"_err_en_US); 58864ab3302SCarolineConcatto } 589ac44cb76SPeixin-Qiao if (innermostSymbol_ && IsBindCProcedure(*innermostSymbol_)) { 590ac44cb76SPeixin-Qiao if (IsOptional(symbol)) { 59164ab3302SCarolineConcatto messages_.Say( 59264ab3302SCarolineConcatto "VALUE attribute may not apply to an OPTIONAL in a BIND(C) procedure"_err_en_US); 59364ab3302SCarolineConcatto } 594ac44cb76SPeixin-Qiao if (symbol.Rank() > 0) { 595ac44cb76SPeixin-Qiao messages_.Say( 596ac44cb76SPeixin-Qiao "VALUE attribute may not apply to an array in a BIND(C) procedure"_err_en_US); 597ac44cb76SPeixin-Qiao } 598ac44cb76SPeixin-Qiao } 59964ab3302SCarolineConcatto if (derived) { 60064ab3302SCarolineConcatto if (FindCoarrayUltimateComponent(*derived)) { 60164ab3302SCarolineConcatto messages_.Say( 60264ab3302SCarolineConcatto "VALUE attribute may not apply to a type with a coarray ultimate component"_err_en_US); 60364ab3302SCarolineConcatto } 60464ab3302SCarolineConcatto } 605b288b412SPeter Klausler if (evaluate::IsAssumedRank(symbol)) { 606b288b412SPeter Klausler messages_.Say( 607b288b412SPeter Klausler "VALUE attribute may not apply to an assumed-rank array"_err_en_US); 608b288b412SPeter Klausler } 6090f973ac7SPeter Klausler if (IsAssumedLengthCharacter(symbol)) { 610691770caSPeter Klausler // F'2008 feature not widely implemented 6110f973ac7SPeter Klausler Warn(common::UsageWarning::Portability, 612691770caSPeter Klausler "VALUE attribute on assumed-length CHARACTER may not be portable"_port_en_US); 613691770caSPeter Klausler } 61464ab3302SCarolineConcatto } 61564ab3302SCarolineConcatto 61664ab3302SCarolineConcatto void CheckHelper::CheckAssumedTypeEntity( // C709 61764ab3302SCarolineConcatto const Symbol &symbol, const ObjectEntityDetails &details) { 61864ab3302SCarolineConcatto if (const DeclTypeSpec *type{symbol.GetType()}; 61964ab3302SCarolineConcatto type && type->category() == DeclTypeSpec::TypeStar) { 62014f49599STim Keith if (!IsDummy(symbol)) { 62164ab3302SCarolineConcatto messages_.Say( 62264ab3302SCarolineConcatto "Assumed-type entity '%s' must be a dummy argument"_err_en_US, 62364ab3302SCarolineConcatto symbol.name()); 62464ab3302SCarolineConcatto } else { 62564ab3302SCarolineConcatto if (symbol.attrs().test(Attr::ALLOCATABLE)) { 62664ab3302SCarolineConcatto messages_.Say("Assumed-type argument '%s' cannot have the ALLOCATABLE" 62764ab3302SCarolineConcatto " attribute"_err_en_US, 62864ab3302SCarolineConcatto symbol.name()); 62964ab3302SCarolineConcatto } 63064ab3302SCarolineConcatto if (symbol.attrs().test(Attr::POINTER)) { 63164ab3302SCarolineConcatto messages_.Say("Assumed-type argument '%s' cannot have the POINTER" 63264ab3302SCarolineConcatto " attribute"_err_en_US, 63364ab3302SCarolineConcatto symbol.name()); 63464ab3302SCarolineConcatto } 63564ab3302SCarolineConcatto if (symbol.attrs().test(Attr::VALUE)) { 63664ab3302SCarolineConcatto messages_.Say("Assumed-type argument '%s' cannot have the VALUE" 63764ab3302SCarolineConcatto " attribute"_err_en_US, 63864ab3302SCarolineConcatto symbol.name()); 63964ab3302SCarolineConcatto } 64064ab3302SCarolineConcatto if (symbol.attrs().test(Attr::INTENT_OUT)) { 64164ab3302SCarolineConcatto messages_.Say( 64264ab3302SCarolineConcatto "Assumed-type argument '%s' cannot be INTENT(OUT)"_err_en_US, 64364ab3302SCarolineConcatto symbol.name()); 64464ab3302SCarolineConcatto } 6451ee6f7adSPeter Klausler if (evaluate::IsCoarray(symbol)) { 64664ab3302SCarolineConcatto messages_.Say( 64764ab3302SCarolineConcatto "Assumed-type argument '%s' cannot be a coarray"_err_en_US, 64864ab3302SCarolineConcatto symbol.name()); 64964ab3302SCarolineConcatto } 650a0a1f519STim Keith if (details.IsArray() && details.shape().IsExplicitShape()) { 6516f5df419SJean Perier messages_.Say("Assumed-type array argument '%s' must be assumed shape," 652a0a1f519STim Keith " assumed size, or assumed rank"_err_en_US, 65364ab3302SCarolineConcatto symbol.name()); 65464ab3302SCarolineConcatto } 65564ab3302SCarolineConcatto } 65664ab3302SCarolineConcatto } 65764ab3302SCarolineConcatto } 65864ab3302SCarolineConcatto 65964ab3302SCarolineConcatto void CheckHelper::CheckObjectEntity( 66064ab3302SCarolineConcatto const Symbol &symbol, const ObjectEntityDetails &details) { 66178da80e2SPeter Klausler CheckSymbolType(symbol); 66264ab3302SCarolineConcatto CheckArraySpec(symbol, details.shape()); 66353e8d501SPeter Klausler CheckConflicting(symbol, Attr::ALLOCATABLE, Attr::PARAMETER); 66453e8d501SPeter Klausler CheckConflicting(symbol, Attr::ASYNCHRONOUS, Attr::PARAMETER); 66553e8d501SPeter Klausler CheckConflicting(symbol, Attr::SAVE, Attr::PARAMETER); 66653e8d501SPeter Klausler CheckConflicting(symbol, Attr::TARGET, Attr::PARAMETER); 66753e8d501SPeter Klausler CheckConflicting(symbol, Attr::VOLATILE, Attr::PARAMETER); 66864ab3302SCarolineConcatto Check(details.shape()); 66964ab3302SCarolineConcatto Check(details.coshape()); 670940871ddSPeter Klausler if (details.shape().Rank() > common::maxRank) { 671940871ddSPeter Klausler messages_.Say( 672940871ddSPeter Klausler "'%s' has rank %d, which is greater than the maximum supported rank %d"_err_en_US, 673940871ddSPeter Klausler symbol.name(), details.shape().Rank(), common::maxRank); 674940871ddSPeter Klausler } else if (details.shape().Rank() + details.coshape().Rank() > 675940871ddSPeter Klausler common::maxRank) { 676940871ddSPeter Klausler messages_.Say( 677940871ddSPeter Klausler "'%s' has rank %d and corank %d, whose sum is greater than the maximum supported rank %d"_err_en_US, 678940871ddSPeter Klausler symbol.name(), details.shape().Rank(), details.coshape().Rank(), 679940871ddSPeter Klausler common::maxRank); 680940871ddSPeter Klausler } 68164ab3302SCarolineConcatto CheckAssumedTypeEntity(symbol, details); 682c1168676Speter klausler WarnMissingFinal(symbol); 683faa1338cSPeter Klausler const DeclTypeSpec *type{details.type()}; 684faa1338cSPeter Klausler const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr}; 6853332dc32SPeter Klausler bool isComponent{symbol.owner().IsDerivedType()}; 6862625510eSPeter Klausler if (details.coshape().empty()) { // not a coarray 6872625510eSPeter Klausler if (!isComponent && !IsPointer(symbol) && derived) { 6882625510eSPeter Klausler if (IsEventTypeOrLockType(derived)) { 6892625510eSPeter Klausler messages_.Say( 6902625510eSPeter Klausler "Variable '%s' with EVENT_TYPE or LOCK_TYPE must be a coarray"_err_en_US, 6912625510eSPeter Klausler symbol.name()); 6922625510eSPeter Klausler } else if (auto component{FindEventOrLockPotentialComponent( 6932625510eSPeter Klausler *derived, /*ignoreCoarrays=*/true)}) { 6942625510eSPeter Klausler messages_.Say( 6952625510eSPeter Klausler "Variable '%s' with EVENT_TYPE or LOCK_TYPE potential component '%s' must be a coarray"_err_en_US, 6962625510eSPeter Klausler symbol.name(), component.BuildResultDesignatorName()); 6972625510eSPeter Klausler } 6982625510eSPeter Klausler } 6992625510eSPeter Klausler } else { // it's a coarray 70044bc97c8SPeter Klausler bool isDeferredCoshape{details.coshape().CanBeDeferredShape()}; 70164ab3302SCarolineConcatto if (IsAllocatable(symbol)) { 70252711fb8Speter klausler if (!isDeferredCoshape) { // C827 7038d0c3c05SPete Steinfeld messages_.Say("'%s' is an ALLOCATABLE coarray and must have a deferred" 7048d0c3c05SPete Steinfeld " coshape"_err_en_US, 7058d0c3c05SPete Steinfeld symbol.name()); 70664ab3302SCarolineConcatto } 7073332dc32SPeter Klausler } else if (isComponent) { // C746 7088d0c3c05SPete Steinfeld std::string deferredMsg{ 70952711fb8Speter klausler isDeferredCoshape ? "" : " and have a deferred coshape"}; 7108d0c3c05SPete Steinfeld messages_.Say("Component '%s' is a coarray and must have the ALLOCATABLE" 7118d0c3c05SPete Steinfeld " attribute%s"_err_en_US, 7128d0c3c05SPete Steinfeld symbol.name(), deferredMsg); 71364ab3302SCarolineConcatto } else { 71444bc97c8SPeter Klausler if (!details.coshape().CanBeAssumedSize()) { // C828 71564ab3302SCarolineConcatto messages_.Say( 71652711fb8Speter klausler "'%s' is a non-ALLOCATABLE coarray and must have an explicit coshape"_err_en_US, 71752711fb8Speter klausler symbol.name()); 71852711fb8Speter klausler } 71952711fb8Speter klausler } 720faa1338cSPeter Klausler if (IsBadCoarrayType(derived)) { // C747 & C824 72152711fb8Speter klausler messages_.Say( 72252711fb8Speter klausler "Coarray '%s' may not have type TEAM_TYPE, C_PTR, or C_FUNPTR"_err_en_US, 7238d0c3c05SPete Steinfeld symbol.name()); 72464ab3302SCarolineConcatto } 725b288b412SPeter Klausler if (evaluate::IsAssumedRank(symbol)) { 726b288b412SPeter Klausler messages_.Say("Coarray '%s' may not be an assumed-rank array"_err_en_US, 727b288b412SPeter Klausler symbol.name()); 728b288b412SPeter Klausler } 72964ab3302SCarolineConcatto } 73064ab3302SCarolineConcatto if (details.isDummy()) { 731faa1338cSPeter Klausler if (IsIntentOut(symbol)) { 732e9a8ab00SPeter Klausler // Some of these errors would also be caught by the general check 733e9a8ab00SPeter Klausler // for definability of automatically deallocated local variables, 734e9a8ab00SPeter Klausler // but these messages are more specific. 73564ab3302SCarolineConcatto if (FindUltimateComponent(symbol, [](const Symbol &x) { 7361ee6f7adSPeter Klausler return evaluate::IsCoarray(x) && IsAllocatable(x); 73764ab3302SCarolineConcatto })) { // C846 73864ab3302SCarolineConcatto messages_.Say( 73964ab3302SCarolineConcatto "An INTENT(OUT) dummy argument may not be, or contain, an ALLOCATABLE coarray"_err_en_US); 74064ab3302SCarolineConcatto } 74164ab3302SCarolineConcatto if (IsOrContainsEventOrLockComponent(symbol)) { // C847 74264ab3302SCarolineConcatto messages_.Say( 74364ab3302SCarolineConcatto "An INTENT(OUT) dummy argument may not be, or contain, EVENT_TYPE or LOCK_TYPE"_err_en_US); 74464ab3302SCarolineConcatto } 7457b801233SPeter Klausler if (IsAssumedSizeArray(symbol)) { // C834 746faa1338cSPeter Klausler if (type && type->IsPolymorphic()) { 747faa1338cSPeter Klausler messages_.Say( 748faa1338cSPeter Klausler "An INTENT(OUT) assumed-size dummy argument array may not be polymorphic"_err_en_US); 749faa1338cSPeter Klausler } 750faa1338cSPeter Klausler if (derived) { 751faa1338cSPeter Klausler if (derived->HasDefaultInitialization()) { 752faa1338cSPeter Klausler messages_.Say( 753faa1338cSPeter Klausler "An INTENT(OUT) assumed-size dummy argument array may not have a derived type with any default component initialization"_err_en_US); 754faa1338cSPeter Klausler } 755faa1338cSPeter Klausler if (IsFinalizable(*derived)) { 756faa1338cSPeter Klausler messages_.Say( 757faa1338cSPeter Klausler "An INTENT(OUT) assumed-size dummy argument array may not be finalizable"_err_en_US); 758faa1338cSPeter Klausler } 759faa1338cSPeter Klausler } 760faa1338cSPeter Klausler } 76164ab3302SCarolineConcatto } 7628d0c3c05SPete Steinfeld if (InPure() && !IsStmtFunction(DEREF(innermostSymbol_)) && 7638d0c3c05SPete Steinfeld !IsPointer(symbol) && !IsIntentIn(symbol) && 76464ab3302SCarolineConcatto !symbol.attrs().test(Attr::VALUE)) { 765b61d7ec1SPeter Klausler const char *what{InFunction() ? "function" : "subroutine"}; 766b61d7ec1SPeter Klausler bool ok{true}; 767b61d7ec1SPeter Klausler if (IsIntentOut(symbol)) { 76864ab3302SCarolineConcatto if (type && type->IsPolymorphic()) { // C1588 76964ab3302SCarolineConcatto messages_.Say( 770b61d7ec1SPeter Klausler "An INTENT(OUT) dummy argument of a pure %s may not be polymorphic"_err_en_US, 771b61d7ec1SPeter Klausler what); 772b61d7ec1SPeter Klausler ok = false; 773faa1338cSPeter Klausler } else if (derived) { 77464ab3302SCarolineConcatto if (FindUltimateComponent(*derived, [](const Symbol &x) { 77564ab3302SCarolineConcatto const DeclTypeSpec *type{x.GetType()}; 77664ab3302SCarolineConcatto return type && type->IsPolymorphic(); 77764ab3302SCarolineConcatto })) { // C1588 77864ab3302SCarolineConcatto messages_.Say( 779b61d7ec1SPeter Klausler "An INTENT(OUT) dummy argument of a pure %s may not have a polymorphic ultimate component"_err_en_US, 780b61d7ec1SPeter Klausler what); 781b61d7ec1SPeter Klausler ok = false; 78264ab3302SCarolineConcatto } 783e9a8ab00SPeter Klausler if (HasImpureFinal(symbol)) { // C1587 78464ab3302SCarolineConcatto messages_.Say( 785b61d7ec1SPeter Klausler "An INTENT(OUT) dummy argument of a pure %s may not have an impure FINAL subroutine"_err_en_US, 786b61d7ec1SPeter Klausler what); 787b61d7ec1SPeter Klausler ok = false; 78864ab3302SCarolineConcatto } 78964ab3302SCarolineConcatto } 79064ab3302SCarolineConcatto } else if (!IsIntentInOut(symbol)) { // C1586 79164ab3302SCarolineConcatto messages_.Say( 7928bcf40baSPeter Klausler "non-POINTER dummy argument of pure %s must have INTENT() or VALUE attribute"_err_en_US, 793b61d7ec1SPeter Klausler what); 794b61d7ec1SPeter Klausler ok = false; 795b61d7ec1SPeter Klausler } 7968bcf40baSPeter Klausler if (ok && InFunction() && !InModuleFile() && !InElemental()) { 797b61d7ec1SPeter Klausler if (context_.IsEnabled(common::LanguageFeature::RelaxedPureDummy)) { 7980f973ac7SPeter Klausler Warn(common::LanguageFeature::RelaxedPureDummy, 799b61d7ec1SPeter Klausler "non-POINTER dummy argument of pure function should be INTENT(IN) or VALUE"_warn_en_US); 800b61d7ec1SPeter Klausler } else { 801b61d7ec1SPeter Klausler messages_.Say( 802b61d7ec1SPeter Klausler "non-POINTER dummy argument of pure function must be INTENT(IN) or VALUE"_err_en_US); 803b61d7ec1SPeter Klausler } 80464ab3302SCarolineConcatto } 80564ab3302SCarolineConcatto } 806864cb2aaSPeter Klausler if (auto ignoreTKR{GetIgnoreTKR(symbol)}; !ignoreTKR.empty()) { 80778f19d9bSPeter Klausler const Symbol *ownerSymbol{symbol.owner().symbol()}; 808d6f314ceSPeter Klausler bool inModuleProc{ownerSymbol && IsModuleProcedure(*ownerSymbol)}; 809d6f314ceSPeter Klausler bool inExplicitExternalInterface{ 810d6f314ceSPeter Klausler InInterface() && !IsSeparateModuleProcedureInterface(ownerSymbol)}; 811d6f314ceSPeter Klausler if (!InInterface() && !inModuleProc) { 812864cb2aaSPeter Klausler messages_.Say( 81378f19d9bSPeter Klausler "!DIR$ IGNORE_TKR may apply only in an interface or a module procedure"_err_en_US); 81478f19d9bSPeter Klausler } 81578f19d9bSPeter Klausler if (ownerSymbol && ownerSymbol->attrs().test(Attr::ELEMENTAL) && 816864cb2aaSPeter Klausler details.ignoreTKR().test(common::IgnoreTKR::Rank)) { 817864cb2aaSPeter Klausler messages_.Say( 818864cb2aaSPeter Klausler "!DIR$ IGNORE_TKR(R) may not apply in an ELEMENTAL procedure"_err_en_US); 819864cb2aaSPeter Klausler } 82078f19d9bSPeter Klausler if (IsPassedViaDescriptor(symbol)) { 821031b4e5eSPeter Klausler if (IsAllocatableOrObjectPointer(&symbol)) { 822d6f314ceSPeter Klausler if (inExplicitExternalInterface) { 8230f973ac7SPeter Klausler Warn(common::UsageWarning::IgnoreTKRUsage, 82478f19d9bSPeter Klausler "!DIR$ IGNORE_TKR should not apply to an allocatable or pointer"_warn_en_US); 82578f19d9bSPeter Klausler } else { 82678f19d9bSPeter Klausler messages_.Say( 82778f19d9bSPeter Klausler "!DIR$ IGNORE_TKR may not apply to an allocatable or pointer"_err_en_US); 82878f19d9bSPeter Klausler } 82978f19d9bSPeter Klausler } else if (ignoreTKR.test(common::IgnoreTKR::Rank)) { 83078f19d9bSPeter Klausler if (ignoreTKR.count() == 1 && evaluate::IsAssumedRank(symbol)) { 8310f973ac7SPeter Klausler Warn(common::UsageWarning::IgnoreTKRUsage, 83278f19d9bSPeter Klausler "!DIR$ IGNORE_TKR(R) is not meaningful for an assumed-rank array"_warn_en_US); 833d6f314ceSPeter Klausler } else if (inExplicitExternalInterface) { 8340f973ac7SPeter Klausler Warn(common::UsageWarning::IgnoreTKRUsage, 83578f19d9bSPeter Klausler "!DIR$ IGNORE_TKR(R) should not apply to a dummy argument passed via descriptor"_warn_en_US); 83678f19d9bSPeter Klausler } else { 83778f19d9bSPeter Klausler messages_.Say( 83878f19d9bSPeter Klausler "!DIR$ IGNORE_TKR(R) may not apply to a dummy argument passed via descriptor"_err_en_US); 83978f19d9bSPeter Klausler } 84078f19d9bSPeter Klausler } 841864cb2aaSPeter Klausler } 842864cb2aaSPeter Klausler } 843864cb2aaSPeter Klausler } else if (!details.ignoreTKR().empty()) { 844864cb2aaSPeter Klausler messages_.Say( 845864cb2aaSPeter Klausler "!DIR$ IGNORE_TKR directive may apply only to a dummy data argument"_err_en_US); 84664ab3302SCarolineConcatto } 84743a263f5Speter klausler if (InElemental()) { 84843a263f5Speter klausler if (details.isDummy()) { // C15100 84943a263f5Speter klausler if (details.shape().Rank() > 0) { 85043a263f5Speter klausler messages_.Say( 85143a263f5Speter klausler "A dummy argument of an ELEMENTAL procedure must be scalar"_err_en_US); 85243a263f5Speter klausler } 85343a263f5Speter klausler if (IsAllocatable(symbol)) { 85443a263f5Speter klausler messages_.Say( 85543a263f5Speter klausler "A dummy argument of an ELEMENTAL procedure may not be ALLOCATABLE"_err_en_US); 85643a263f5Speter klausler } 8571ee6f7adSPeter Klausler if (evaluate::IsCoarray(symbol)) { 85843a263f5Speter klausler messages_.Say( 85943a263f5Speter klausler "A dummy argument of an ELEMENTAL procedure may not be a coarray"_err_en_US); 86043a263f5Speter klausler } 86143a263f5Speter klausler if (IsPointer(symbol)) { 86243a263f5Speter klausler messages_.Say( 86343a263f5Speter klausler "A dummy argument of an ELEMENTAL procedure may not be a POINTER"_err_en_US); 86443a263f5Speter klausler } 86543a263f5Speter klausler if (!symbol.attrs().HasAny(Attrs{Attr::VALUE, Attr::INTENT_IN, 866b61d7ec1SPeter Klausler Attr::INTENT_INOUT, Attr::INTENT_OUT})) { // F'2023 C15120 86743a263f5Speter klausler messages_.Say( 86843a263f5Speter klausler "A dummy argument of an ELEMENTAL procedure must have an INTENT() or VALUE attribute"_err_en_US); 86943a263f5Speter klausler } 87043a263f5Speter klausler } else if (IsFunctionResult(symbol)) { // C15101 87143a263f5Speter klausler if (details.shape().Rank() > 0) { 87243a263f5Speter klausler messages_.Say( 87343a263f5Speter klausler "The result of an ELEMENTAL function must be scalar"_err_en_US); 87443a263f5Speter klausler } 87543a263f5Speter klausler if (IsAllocatable(symbol)) { 87643a263f5Speter klausler messages_.Say( 87743a263f5Speter klausler "The result of an ELEMENTAL function may not be ALLOCATABLE"_err_en_US); 87843a263f5Speter klausler } 87943a263f5Speter klausler if (IsPointer(symbol)) { 88043a263f5Speter klausler messages_.Say( 88143a263f5Speter klausler "The result of an ELEMENTAL function may not be a POINTER"_err_en_US); 88243a263f5Speter klausler } 88343a263f5Speter klausler } 88443a263f5Speter klausler } 885d60a0220Speter klausler if (HasDeclarationInitializer(symbol)) { // C808; ignore DATA initialization 886641ede93Speter klausler CheckPointerInitialization(symbol); 8874171f80dSpeter klausler if (IsAutomatic(symbol)) { 888641ede93Speter klausler messages_.Say( 889641ede93Speter klausler "An automatic variable or component must not be initialized"_err_en_US); 8904171f80dSpeter klausler } else if (IsDummy(symbol)) { 8914171f80dSpeter klausler messages_.Say("A dummy argument must not be initialized"_err_en_US); 8924171f80dSpeter klausler } else if (IsFunctionResult(symbol)) { 8934171f80dSpeter klausler messages_.Say("A function result must not be initialized"_err_en_US); 8943332dc32SPeter Klausler } else if (IsInBlankCommon(symbol)) { 8950f973ac7SPeter Klausler Warn(common::LanguageFeature::InitBlankCommon, 896a53967cdSPeter Klausler "A variable in blank COMMON should not be initialized"_port_en_US); 89764ab3302SCarolineConcatto } 8984171f80dSpeter klausler } 899641ede93Speter klausler if (symbol.owner().kind() == Scope::Kind::BlockData) { 90064ab3302SCarolineConcatto if (IsAllocatable(symbol)) { 90164ab3302SCarolineConcatto messages_.Say( 90264ab3302SCarolineConcatto "An ALLOCATABLE variable may not appear in a BLOCK DATA subprogram"_err_en_US); 903641ede93Speter klausler } else if (IsInitialized(symbol) && !FindCommonBlockContaining(symbol)) { 90464ab3302SCarolineConcatto messages_.Say( 90564ab3302SCarolineConcatto "An initialized variable in BLOCK DATA must be in a COMMON block"_err_en_US); 90664ab3302SCarolineConcatto } 90764ab3302SCarolineConcatto } 908e9a8ab00SPeter Klausler if (derived && InPure() && !InInterface() && 909e9a8ab00SPeter Klausler IsAutomaticallyDestroyed(symbol) && 910e9a8ab00SPeter Klausler !IsIntentOut(symbol) /*has better messages*/ && 911e9a8ab00SPeter Klausler !IsFunctionResult(symbol) /*ditto*/) { 912e9a8ab00SPeter Klausler // Check automatically deallocated local variables for possible 913e9a8ab00SPeter Klausler // problems with finalization in PURE. 914e9a8ab00SPeter Klausler if (auto whyNot{ 915e9a8ab00SPeter Klausler WhyNotDefinable(symbol.name(), symbol.owner(), {}, symbol)}) { 916e9a8ab00SPeter Klausler if (auto *msg{messages_.Say( 917e9a8ab00SPeter Klausler "'%s' may not be a local variable in a pure subprogram"_err_en_US, 918e9a8ab00SPeter Klausler symbol.name())}) { 919d5285fefSPeter Klausler msg->Attach(std::move(whyNot->set_severity(parser::Severity::Because))); 920e9a8ab00SPeter Klausler } 921e9a8ab00SPeter Klausler } 922e9a8ab00SPeter Klausler } 923864cb2aaSPeter Klausler if (symbol.attrs().test(Attr::EXTERNAL)) { 924864cb2aaSPeter Klausler SayWithDeclaration(symbol, 925864cb2aaSPeter Klausler "'%s' is a data object and may not be EXTERNAL"_err_en_US, 926864cb2aaSPeter Klausler symbol.name()); 927864cb2aaSPeter Klausler } 9283332dc32SPeter Klausler 9293332dc32SPeter Klausler // Check CUDA attributes and special circumstances of being in device 9303332dc32SPeter Klausler // subprograms 9313332dc32SPeter Klausler const Scope &progUnit{GetProgramUnitContaining(symbol)}; 9323332dc32SPeter Klausler const auto *subpDetails{!isComponent && progUnit.symbol() 9333332dc32SPeter Klausler ? progUnit.symbol()->detailsIf<SubprogramDetails>() 9343332dc32SPeter Klausler : nullptr}; 9353332dc32SPeter Klausler bool inDeviceSubprogram{IsCUDADeviceContext(&symbol.owner())}; 9363332dc32SPeter Klausler if (inDeviceSubprogram) { 9373332dc32SPeter Klausler if (IsSaved(symbol)) { 9380f973ac7SPeter Klausler Warn(common::UsageWarning::CUDAUsage, 9393332dc32SPeter Klausler "'%s' should not have the SAVE attribute or initialization in a device subprogram"_warn_en_US, 9403332dc32SPeter Klausler symbol.name()); 9413332dc32SPeter Klausler } 9423332dc32SPeter Klausler if (IsPointer(symbol)) { 9430f973ac7SPeter Klausler Warn(common::UsageWarning::CUDAUsage, 9443332dc32SPeter Klausler "Pointer '%s' may not be associated in a device subprogram"_warn_en_US, 9453332dc32SPeter Klausler symbol.name()); 9463332dc32SPeter Klausler } 9473332dc32SPeter Klausler if (details.isDummy() && 9483332dc32SPeter Klausler details.cudaDataAttr().value_or(common::CUDADataAttr::Device) != 9493332dc32SPeter Klausler common::CUDADataAttr::Device && 9503332dc32SPeter Klausler details.cudaDataAttr().value_or(common::CUDADataAttr::Device) != 951a878dc8fSValentin Clement (バレンタイン クレメン) common::CUDADataAttr::Managed && 952a878dc8fSValentin Clement (バレンタイン クレメン) details.cudaDataAttr().value_or(common::CUDADataAttr::Device) != 953a878dc8fSValentin Clement (バレンタイン クレメン) common::CUDADataAttr::Shared) { 9540f973ac7SPeter Klausler Warn(common::UsageWarning::CUDAUsage, 9553332dc32SPeter Klausler "Dummy argument '%s' may not have ATTRIBUTES(%s) in a device subprogram"_warn_en_US, 9563332dc32SPeter Klausler symbol.name(), 9573332dc32SPeter Klausler parser::ToUpperCaseLetters( 9583332dc32SPeter Klausler common::EnumToString(*details.cudaDataAttr()))); 9593332dc32SPeter Klausler } 9603332dc32SPeter Klausler } 9613332dc32SPeter Klausler if (details.cudaDataAttr()) { 9623332dc32SPeter Klausler if (auto dyType{evaluate::DynamicType::From(symbol)}) { 9633332dc32SPeter Klausler if (dyType->category() != TypeCategory::Derived) { 9643332dc32SPeter Klausler if (!IsCUDAIntrinsicType(*dyType)) { 9653332dc32SPeter Klausler messages_.Say( 9663332dc32SPeter Klausler "'%s' has intrinsic type '%s' that is not available on the device"_err_en_US, 9673332dc32SPeter Klausler symbol.name(), dyType->AsFortran()); 9683332dc32SPeter Klausler } 9693332dc32SPeter Klausler } 9703332dc32SPeter Klausler } 9713332dc32SPeter Klausler auto attr{*details.cudaDataAttr()}; 9723332dc32SPeter Klausler switch (attr) { 9733332dc32SPeter Klausler case common::CUDADataAttr::Constant: 97499f31babSValentin Clement (バレンタイン クレメン) if (subpDetails && !inDeviceSubprogram) { 97599f31babSValentin Clement (バレンタイン クレメン) messages_.Say( 97699f31babSValentin Clement (バレンタイン クレメン) "Object '%s' with ATTRIBUTES(CONSTANT) may not be declared in a host subprogram"_err_en_US, 97799f31babSValentin Clement (バレンタイン クレメン) symbol.name()); 97899f31babSValentin Clement (バレンタイン クレメン) } else if (IsAllocatableOrPointer(symbol) || 97999f31babSValentin Clement (バレンタイン クレメン) symbol.attrs().test(Attr::TARGET)) { 9803332dc32SPeter Klausler messages_.Say( 9813332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(CONSTANT) may not be allocatable, pointer, or target"_err_en_US, 9823332dc32SPeter Klausler symbol.name()); 9833332dc32SPeter Klausler } else if (auto shape{evaluate::GetShape(foldingContext_, symbol)}; 9843332dc32SPeter Klausler !shape || 9853332dc32SPeter Klausler !evaluate::AsConstantExtents(foldingContext_, *shape)) { 9863332dc32SPeter Klausler messages_.Say( 9873332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(CONSTANT) must have constant array bounds"_err_en_US, 9883332dc32SPeter Klausler symbol.name()); 9893332dc32SPeter Klausler } 9903332dc32SPeter Klausler break; 9913332dc32SPeter Klausler case common::CUDADataAttr::Device: 9923332dc32SPeter Klausler if (isComponent && !IsAllocatable(symbol)) { 9933332dc32SPeter Klausler messages_.Say( 9943332dc32SPeter Klausler "Component '%s' with ATTRIBUTES(DEVICE) must also be allocatable"_err_en_US, 9953332dc32SPeter Klausler symbol.name()); 9963332dc32SPeter Klausler } 9973332dc32SPeter Klausler break; 9983332dc32SPeter Klausler case common::CUDADataAttr::Managed: 9993332dc32SPeter Klausler if (!IsAutomatic(symbol) && !IsAllocatable(symbol) && 1000aa7c1041SValentin Clement (バレンタイン クレメン) !details.isDummy() && !evaluate::IsExplicitShape(symbol)) { 10013332dc32SPeter Klausler messages_.Say( 1002aa7c1041SValentin Clement (バレンタイン クレメン) "Object '%s' with ATTRIBUTES(MANAGED) must also be allocatable, automatic, explicit shape, or a dummy argument"_err_en_US, 10033332dc32SPeter Klausler symbol.name()); 10043332dc32SPeter Klausler } 10053332dc32SPeter Klausler break; 10063332dc32SPeter Klausler case common::CUDADataAttr::Pinned: 10073332dc32SPeter Klausler if (inDeviceSubprogram) { 10080f973ac7SPeter Klausler Warn(common::UsageWarning::CUDAUsage, 10093332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(PINNED) may not be declared in a device subprogram"_warn_en_US, 10103332dc32SPeter Klausler symbol.name()); 10113332dc32SPeter Klausler } else if (IsPointer(symbol)) { 10120f973ac7SPeter Klausler Warn(common::UsageWarning::CUDAUsage, 10133332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(PINNED) may not be a pointer"_warn_en_US, 10143332dc32SPeter Klausler symbol.name()); 10153332dc32SPeter Klausler } else if (!IsAllocatable(symbol)) { 10160f973ac7SPeter Klausler Warn(common::UsageWarning::CUDAUsage, 10173332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(PINNED) should also be allocatable"_warn_en_US, 10183332dc32SPeter Klausler symbol.name()); 10193332dc32SPeter Klausler } 10203332dc32SPeter Klausler break; 10213332dc32SPeter Klausler case common::CUDADataAttr::Shared: 10223332dc32SPeter Klausler if (IsAllocatableOrPointer(symbol) || symbol.attrs().test(Attr::TARGET)) { 10233332dc32SPeter Klausler messages_.Say( 10243332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(SHARED) may not be allocatable, pointer, or target"_err_en_US, 10253332dc32SPeter Klausler symbol.name()); 10263332dc32SPeter Klausler } else if (!inDeviceSubprogram) { 10273332dc32SPeter Klausler messages_.Say( 10283332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(SHARED) must be declared in a device subprogram"_err_en_US, 10293332dc32SPeter Klausler symbol.name()); 10303332dc32SPeter Klausler } 10313332dc32SPeter Klausler break; 1032e953c862SValentin Clement (バレンタイン クレメン) case common::CUDADataAttr::Unified: 10331fb5083aSValentin Clement (バレンタイン クレメン) if (((!subpDetails && 10341fb5083aSValentin Clement (バレンタイン クレメン) symbol.owner().kind() != Scope::Kind::MainProgram) || 10351fb5083aSValentin Clement (バレンタイン クレメン) inDeviceSubprogram) && 10361fb5083aSValentin Clement (バレンタイン クレメン) !isComponent) { 1037e953c862SValentin Clement (バレンタイン クレメン) messages_.Say( 1038e953c862SValentin Clement (バレンタイン クレメン) "Object '%s' with ATTRIBUTES(UNIFIED) must be declared in a host subprogram"_err_en_US, 1039e953c862SValentin Clement (バレンタイン クレメン) symbol.name()); 1040e953c862SValentin Clement (バレンタイン クレメン) } 1041e953c862SValentin Clement (バレンタイン クレメン) break; 10423332dc32SPeter Klausler case common::CUDADataAttr::Texture: 10433332dc32SPeter Klausler messages_.Say( 10443332dc32SPeter Klausler "ATTRIBUTES(TEXTURE) is obsolete and no longer supported"_err_en_US); 10453332dc32SPeter Klausler break; 10463332dc32SPeter Klausler } 10473332dc32SPeter Klausler if (attr != common::CUDADataAttr::Pinned) { 10483332dc32SPeter Klausler if (details.commonBlock()) { 10493332dc32SPeter Klausler messages_.Say( 10503332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(%s) may not be in COMMON"_err_en_US, 10513332dc32SPeter Klausler symbol.name(), 10523332dc32SPeter Klausler parser::ToUpperCaseLetters(common::EnumToString(attr))); 10533332dc32SPeter Klausler } else if (FindEquivalenceSet(symbol)) { 10543332dc32SPeter Klausler messages_.Say( 10553332dc32SPeter Klausler "Object '%s' with ATTRIBUTES(%s) may not be in an equivalence group"_err_en_US, 10563332dc32SPeter Klausler symbol.name(), 10573332dc32SPeter Klausler parser::ToUpperCaseLetters(common::EnumToString(attr))); 10583332dc32SPeter Klausler } 10593332dc32SPeter Klausler } 10603332dc32SPeter Klausler if (subpDetails /* not a module variable */ && IsSaved(symbol) && 10613332dc32SPeter Klausler !inDeviceSubprogram && !IsAllocatable(symbol) && 10623332dc32SPeter Klausler attr == common::CUDADataAttr::Device) { 10633332dc32SPeter Klausler messages_.Say( 10643332dc32SPeter Klausler "Saved object '%s' in host code may not have ATTRIBUTES(DEVICE) unless allocatable"_err_en_US, 10653332dc32SPeter Klausler symbol.name(), 10663332dc32SPeter Klausler parser::ToUpperCaseLetters(common::EnumToString(attr))); 10673332dc32SPeter Klausler } 10683332dc32SPeter Klausler if (isComponent) { 10693332dc32SPeter Klausler if (attr == common::CUDADataAttr::Device) { 10703332dc32SPeter Klausler const DeclTypeSpec *type{symbol.GetType()}; 10713332dc32SPeter Klausler if (const DerivedTypeSpec * 10723332dc32SPeter Klausler derived{type ? type->AsDerived() : nullptr}) { 10733332dc32SPeter Klausler DirectComponentIterator directs{*derived}; 10743332dc32SPeter Klausler if (auto iter{std::find_if(directs.begin(), directs.end(), 10753332dc32SPeter Klausler [](const Symbol &) { return false; })}) { 10763332dc32SPeter Klausler messages_.Say( 10773332dc32SPeter Klausler "Derived type component '%s' may not have ATTRIBUTES(DEVICE) as it has a direct device component '%s'"_err_en_US, 10783332dc32SPeter Klausler symbol.name(), iter.BuildResultDesignatorName()); 10793332dc32SPeter Klausler } 10803332dc32SPeter Klausler } 10813332dc32SPeter Klausler } else if (attr == common::CUDADataAttr::Constant || 10823332dc32SPeter Klausler attr == common::CUDADataAttr::Shared) { 10833332dc32SPeter Klausler messages_.Say( 10843332dc32SPeter Klausler "Derived type component '%s' may not have ATTRIBUTES(%s)"_err_en_US, 10853332dc32SPeter Klausler symbol.name(), 10863332dc32SPeter Klausler parser::ToUpperCaseLetters(common::EnumToString(attr))); 10873332dc32SPeter Klausler } 10883332dc32SPeter Klausler } else if (!subpDetails && symbol.owner().kind() != Scope::Kind::Module && 10893090efc6SValentin Clement (バレンタイン クレメン) symbol.owner().kind() != Scope::Kind::MainProgram && 10903090efc6SValentin Clement (バレンタイン クレメン) symbol.owner().kind() != Scope::Kind::BlockConstruct) { 10913332dc32SPeter Klausler messages_.Say( 10923090efc6SValentin Clement (バレンタイン クレメン) "ATTRIBUTES(%s) may apply only to module, host subprogram, block, or device subprogram data"_err_en_US, 10933332dc32SPeter Klausler parser::ToUpperCaseLetters(common::EnumToString(attr))); 10943332dc32SPeter Klausler } 10953332dc32SPeter Klausler } 1096a734de6dSKelvin Li 1097a734de6dSKelvin Li if (derived && derived->IsVectorType()) { 1098a734de6dSKelvin Li CHECK(type); 1099a734de6dSKelvin Li std::string typeName{type->AsFortran()}; 1100a734de6dSKelvin Li if (IsAssumedShape(symbol)) { 1101a734de6dSKelvin Li SayWithDeclaration(symbol, 1102a734de6dSKelvin Li "Assumed-shape entity of %s type is not supported"_err_en_US, 1103a734de6dSKelvin Li typeName); 1104a734de6dSKelvin Li } else if (IsDeferredShape(symbol)) { 1105a734de6dSKelvin Li SayWithDeclaration(symbol, 1106a734de6dSKelvin Li "Deferred-shape entity of %s type is not supported"_err_en_US, 1107a734de6dSKelvin Li typeName); 1108a734de6dSKelvin Li } else if (evaluate::IsAssumedRank(symbol)) { 1109a734de6dSKelvin Li SayWithDeclaration(symbol, 1110a734de6dSKelvin Li "Assumed Rank entity of %s type is not supported"_err_en_US, 1111a734de6dSKelvin Li typeName); 1112a734de6dSKelvin Li } 1113a734de6dSKelvin Li } 111464ab3302SCarolineConcatto } 1115f862d858Speter klausler 1116641ede93Speter klausler void CheckHelper::CheckPointerInitialization(const Symbol &symbol) { 1117641ede93Speter klausler if (IsPointer(symbol) && !context_.HasError(symbol) && 1118641ede93Speter klausler !scopeIsUninstantiatedPDT_) { 1119641ede93Speter klausler if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) { 1120641ede93Speter klausler if (object->init()) { // C764, C765; C808 11216829bd3eSpeter klausler if (auto designator{evaluate::AsGenericExpr(symbol)}) { 1122641ede93Speter klausler auto restorer{messages_.SetLocation(symbol.name())}; 1123641ede93Speter klausler context_.set_location(symbol.name()); 11240c0b2ea9SPeter Klausler CheckInitialDataPointerTarget( 1125191d4872SPeter Klausler context_, *designator, *object->init(), DEREF(scope_)); 1126f862d858Speter klausler } 1127641ede93Speter klausler } 1128641ede93Speter klausler } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { 1129641ede93Speter klausler if (proc->init() && *proc->init()) { 1130641ede93Speter klausler // C1519 - must be nonelemental external or module procedure, 1131641ede93Speter klausler // or an unrestricted specific intrinsic function. 1132d20f55fbSPeter Klausler const Symbol &local{DEREF(*proc->init())}; 1133d20f55fbSPeter Klausler const Symbol &ultimate{local.GetUltimate()}; 11340c0b2ea9SPeter Klausler bool checkTarget{true}; 1135641ede93Speter klausler if (ultimate.attrs().test(Attr::INTRINSIC)) { 11360c0b2ea9SPeter Klausler if (auto intrinsic{context_.intrinsics().IsSpecificIntrinsicFunction( 1137848cca6cSEmil Kieri ultimate.name().ToString())}; 1138848cca6cSEmil Kieri !intrinsic || intrinsic->isRestrictedSpecific) { // C1030 1139f8f70028Speter klausler context_.Say( 1140848cca6cSEmil Kieri "Intrinsic procedure '%s' is not an unrestricted specific " 1141848cca6cSEmil Kieri "intrinsic permitted for use as the initializer for procedure " 1142848cca6cSEmil Kieri "pointer '%s'"_err_en_US, 1143f8f70028Speter klausler ultimate.name(), symbol.name()); 11440c0b2ea9SPeter Klausler checkTarget = false; 1145f8f70028Speter klausler } 1146d20f55fbSPeter Klausler } else if (!(ultimate.attrs().test(Attr::EXTERNAL) || 1147d20f55fbSPeter Klausler ultimate.owner().kind() == Scope::Kind::Module || 1148d20f55fbSPeter Klausler ultimate.owner().IsTopLevel()) || 11490c0b2ea9SPeter Klausler IsDummy(ultimate) || IsPointer(ultimate)) { 1150d20f55fbSPeter Klausler context_.Say( 1151d20f55fbSPeter Klausler "Procedure pointer '%s' initializer '%s' is neither an external nor a module procedure"_err_en_US, 1152641ede93Speter klausler symbol.name(), ultimate.name()); 11530c0b2ea9SPeter Klausler checkTarget = false; 11546052025bSPeter Klausler } else if (IsElementalProcedure(ultimate)) { 1155641ede93Speter klausler context_.Say("Procedure pointer '%s' cannot be initialized with the " 11560c0b2ea9SPeter Klausler "elemental procedure '%s'"_err_en_US, 1157641ede93Speter klausler symbol.name(), ultimate.name()); 11580c0b2ea9SPeter Klausler checkTarget = false; 11590c0b2ea9SPeter Klausler } 11600c0b2ea9SPeter Klausler if (checkTarget) { 11610c0b2ea9SPeter Klausler SomeExpr lhs{evaluate::ProcedureDesignator{symbol}}; 11620c0b2ea9SPeter Klausler SomeExpr rhs{evaluate::ProcedureDesignator{**proc->init()}}; 11630c0b2ea9SPeter Klausler CheckPointerAssignment(context_, lhs, rhs, 1164f82ee155SPeter Klausler GetProgramUnitOrBlockConstructContaining(symbol), 1165f82ee155SPeter Klausler /*isBoundsRemapping=*/false, /*isAssumedRank=*/false); 1166f862d858Speter klausler } 1167f862d858Speter klausler } 1168f862d858Speter klausler } 1169f862d858Speter klausler } 117064ab3302SCarolineConcatto } 117164ab3302SCarolineConcatto 117264ab3302SCarolineConcatto // The six different kinds of array-specs: 117364ab3302SCarolineConcatto // array-spec -> explicit-shape-list | deferred-shape-list 117464ab3302SCarolineConcatto // | assumed-shape-list | implied-shape-list 117564ab3302SCarolineConcatto // | assumed-size | assumed-rank 117664ab3302SCarolineConcatto // explicit-shape -> [ lb : ] ub 117764ab3302SCarolineConcatto // deferred-shape -> : 117864ab3302SCarolineConcatto // assumed-shape -> [ lb ] : 117964ab3302SCarolineConcatto // implied-shape -> [ lb : ] * 118064ab3302SCarolineConcatto // assumed-size -> [ explicit-shape-list , ] [ lb : ] * 118164ab3302SCarolineConcatto // assumed-rank -> .. 118264ab3302SCarolineConcatto // Note: 118364ab3302SCarolineConcatto // - deferred-shape is also an assumed-shape 118464ab3302SCarolineConcatto // - A single "*" or "lb:*" might be assumed-size or implied-shape-list 118564ab3302SCarolineConcatto void CheckHelper::CheckArraySpec( 118664ab3302SCarolineConcatto const Symbol &symbol, const ArraySpec &arraySpec) { 118764ab3302SCarolineConcatto if (arraySpec.Rank() == 0) { 118864ab3302SCarolineConcatto return; 118964ab3302SCarolineConcatto } 119064ab3302SCarolineConcatto bool isExplicit{arraySpec.IsExplicitShape()}; 119144bc97c8SPeter Klausler bool canBeDeferred{arraySpec.CanBeDeferredShape()}; 119244bc97c8SPeter Klausler bool canBeImplied{arraySpec.CanBeImpliedShape()}; 119344bc97c8SPeter Klausler bool canBeAssumedShape{arraySpec.CanBeAssumedShape()}; 119444bc97c8SPeter Klausler bool canBeAssumedSize{arraySpec.CanBeAssumedSize()}; 119564ab3302SCarolineConcatto bool isAssumedRank{arraySpec.IsAssumedRank()}; 11963332dc32SPeter Klausler bool isCUDAShared{ 11973332dc32SPeter Klausler GetCUDADataAttr(&symbol).value_or(common::CUDADataAttr::Device) == 11983332dc32SPeter Klausler common::CUDADataAttr::Shared}; 11997b801233SPeter Klausler bool isCrayPointee{symbol.test(Symbol::Flag::CrayPointee)}; 120064ab3302SCarolineConcatto std::optional<parser::MessageFixedText> msg; 12017b801233SPeter Klausler if (isCrayPointee && !isExplicit && !canBeAssumedSize) { 12027b801233SPeter Klausler msg = 12037b801233SPeter Klausler "Cray pointee '%s' must have explicit shape or assumed size"_err_en_US; 120444bc97c8SPeter Klausler } else if (IsAllocatableOrPointer(symbol) && !canBeDeferred && 120544bc97c8SPeter Klausler !isAssumedRank) { 120664ab3302SCarolineConcatto if (symbol.owner().IsDerivedType()) { // C745 120764ab3302SCarolineConcatto if (IsAllocatable(symbol)) { 120864ab3302SCarolineConcatto msg = "Allocatable array component '%s' must have" 120964ab3302SCarolineConcatto " deferred shape"_err_en_US; 121064ab3302SCarolineConcatto } else { 121164ab3302SCarolineConcatto msg = "Array pointer component '%s' must have deferred shape"_err_en_US; 121264ab3302SCarolineConcatto } 121364ab3302SCarolineConcatto } else { 121464ab3302SCarolineConcatto if (IsAllocatable(symbol)) { // C832 121564ab3302SCarolineConcatto msg = "Allocatable array '%s' must have deferred shape or" 121664ab3302SCarolineConcatto " assumed rank"_err_en_US; 121764ab3302SCarolineConcatto } else { 121864ab3302SCarolineConcatto msg = "Array pointer '%s' must have deferred shape or" 121964ab3302SCarolineConcatto " assumed rank"_err_en_US; 122064ab3302SCarolineConcatto } 122164ab3302SCarolineConcatto } 122214f49599STim Keith } else if (IsDummy(symbol)) { 122344bc97c8SPeter Klausler if (canBeImplied && !canBeAssumedSize) { // C836 122464ab3302SCarolineConcatto msg = "Dummy array argument '%s' may not have implied shape"_err_en_US; 122564ab3302SCarolineConcatto } 122644bc97c8SPeter Klausler } else if (canBeAssumedShape && !canBeDeferred) { 122764ab3302SCarolineConcatto msg = "Assumed-shape array '%s' must be a dummy argument"_err_en_US; 122864ab3302SCarolineConcatto } else if (isAssumedRank) { // C837 122964ab3302SCarolineConcatto msg = "Assumed-rank array '%s' must be a dummy argument"_err_en_US; 12307b801233SPeter Klausler } else if (canBeAssumedSize && !canBeImplied && !isCUDAShared && 12317b801233SPeter Klausler !isCrayPointee) { // C833 12327b801233SPeter Klausler msg = "Assumed-size array '%s' must be a dummy argument"_err_en_US; 123344bc97c8SPeter Klausler } else if (canBeImplied) { 12347b801233SPeter Klausler if (!IsNamedConstant(symbol) && !isCUDAShared && 12357b801233SPeter Klausler !isCrayPointee) { // C835, C836 1236f9c0859eSPeter Steinfeld msg = "Implied-shape array '%s' must be a named constant or a " 1237f9c0859eSPeter Steinfeld "dummy argument"_err_en_US; 123864ab3302SCarolineConcatto } 123964ab3302SCarolineConcatto } else if (IsNamedConstant(symbol)) { 124044bc97c8SPeter Klausler if (!isExplicit && !canBeImplied) { 1241641ede93Speter klausler msg = "Named constant '%s' array must have constant or" 124264ab3302SCarolineConcatto " implied shape"_err_en_US; 124364ab3302SCarolineConcatto } 12447b801233SPeter Klausler } else if (!isExplicit && 12457b801233SPeter Klausler !(IsAllocatableOrPointer(symbol) || isCrayPointee)) { 124664ab3302SCarolineConcatto if (symbol.owner().IsDerivedType()) { // C749 124764ab3302SCarolineConcatto msg = "Component array '%s' without ALLOCATABLE or POINTER attribute must" 124864ab3302SCarolineConcatto " have explicit shape"_err_en_US; 124964ab3302SCarolineConcatto } else { // C816 125064ab3302SCarolineConcatto msg = "Array '%s' without ALLOCATABLE or POINTER attribute must have" 125164ab3302SCarolineConcatto " explicit shape"_err_en_US; 125264ab3302SCarolineConcatto } 125364ab3302SCarolineConcatto } 125464ab3302SCarolineConcatto if (msg) { 125564ab3302SCarolineConcatto context_.Say(std::move(*msg), symbol.name()); 125664ab3302SCarolineConcatto } 125764ab3302SCarolineConcatto } 125864ab3302SCarolineConcatto 125964ab3302SCarolineConcatto void CheckHelper::CheckProcEntity( 126064ab3302SCarolineConcatto const Symbol &symbol, const ProcEntityDetails &details) { 126178da80e2SPeter Klausler CheckSymbolType(symbol); 126283ca78deSPeter Klausler const Symbol *interface{details.procInterface()}; 126364ab3302SCarolineConcatto if (details.isDummy()) { 12641e1a011bSPeter Steinfeld if (!symbol.attrs().test(Attr::POINTER) && // C843 1265465807eeSPeter Klausler symbol.attrs().HasAny( 1266465807eeSPeter Klausler {Attr::INTENT_IN, Attr::INTENT_OUT, Attr::INTENT_INOUT})) { 12671e1a011bSPeter Steinfeld messages_.Say("A dummy procedure without the POINTER attribute" 12681e1a011bSPeter Steinfeld " may not have an INTENT attribute"_err_en_US); 12691e1a011bSPeter Steinfeld } 127043a263f5Speter klausler if (InElemental()) { // C15100 127143a263f5Speter klausler messages_.Say( 127243a263f5Speter klausler "An ELEMENTAL subprogram may not have a dummy procedure"_err_en_US); 127343a263f5Speter klausler } 12740c0b2ea9SPeter Klausler if (interface && IsElementalProcedure(*interface)) { 127564ab3302SCarolineConcatto // There's no explicit constraint or "shall" that we can find in the 127664ab3302SCarolineConcatto // standard for this check, but it seems to be implied in multiple 127764ab3302SCarolineConcatto // sites, and ELEMENTAL non-intrinsic actual arguments *are* 127864ab3302SCarolineConcatto // explicitly forbidden. But we allow "PROCEDURE(SIN)::dummy" 127964ab3302SCarolineConcatto // because it is explicitly legal to *pass* the specific intrinsic 128064ab3302SCarolineConcatto // function SIN as an actual argument. 12810c0b2ea9SPeter Klausler if (interface->attrs().test(Attr::INTRINSIC)) { 12820f973ac7SPeter Klausler Warn(common::UsageWarning::Portability, 12830c0b2ea9SPeter Klausler "A dummy procedure should not have an ELEMENTAL intrinsic as its interface"_port_en_US); 12840c0b2ea9SPeter Klausler } else { 128564ab3302SCarolineConcatto messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US); 128664ab3302SCarolineConcatto } 12870c0b2ea9SPeter Klausler } 12880c0b2ea9SPeter Klausler } else if (IsPointer(symbol)) { 1289641ede93Speter klausler CheckPointerInitialization(symbol); 12900c0b2ea9SPeter Klausler if (interface) { 12910c0b2ea9SPeter Klausler if (interface->attrs().test(Attr::INTRINSIC)) { 12920c0b2ea9SPeter Klausler auto intrinsic{context_.intrinsics().IsSpecificIntrinsicFunction( 12930c0b2ea9SPeter Klausler interface->name().ToString())}; 12940c0b2ea9SPeter Klausler if (!intrinsic || intrinsic->isRestrictedSpecific) { // C1515 1295f8f70028Speter klausler messages_.Say( 1296848cca6cSEmil Kieri "Intrinsic procedure '%s' is not an unrestricted specific " 1297848cca6cSEmil Kieri "intrinsic permitted for use as the definition of the interface " 1298848cca6cSEmil Kieri "to procedure pointer '%s'"_err_en_US, 12990c0b2ea9SPeter Klausler interface->name(), symbol.name()); 13000c0b2ea9SPeter Klausler } else if (IsElementalProcedure(*interface)) { 13010f973ac7SPeter Klausler Warn(common::UsageWarning::Portability, 13020c0b2ea9SPeter Klausler "Procedure pointer '%s' should not have an ELEMENTAL intrinsic as its interface"_port_en_US, 13030c0b2ea9SPeter Klausler symbol.name()); // C1517 1304f8f70028Speter klausler } 13056052025bSPeter Klausler } else if (IsElementalProcedure(*interface)) { 130664ab3302SCarolineConcatto messages_.Say("Procedure pointer '%s' may not be ELEMENTAL"_err_en_US, 130764ab3302SCarolineConcatto symbol.name()); // C1517 130864ab3302SCarolineConcatto } 130964ab3302SCarolineConcatto } 13100c0b2ea9SPeter Klausler if (symbol.owner().IsDerivedType()) { 13110c0b2ea9SPeter Klausler CheckPassArg(symbol, interface, details); 13120c0b2ea9SPeter Klausler } 13130c0b2ea9SPeter Klausler } else if (symbol.owner().IsDerivedType()) { 13140c0b2ea9SPeter Klausler const auto &name{symbol.name()}; 13150c0b2ea9SPeter Klausler messages_.Say(name, 13160c0b2ea9SPeter Klausler "Procedure component '%s' must have POINTER attribute"_err_en_US, name); 131764ab3302SCarolineConcatto } 1318982614faSPeter Klausler CheckExternal(symbol); 131964ab3302SCarolineConcatto } 132064ab3302SCarolineConcatto 132161b1390eSTim Keith // When a module subprogram has the MODULE prefix the following must match 132261b1390eSTim Keith // with the corresponding separate module procedure interface body: 132361b1390eSTim Keith // - C1549: characteristics and dummy argument names 132461b1390eSTim Keith // - C1550: binding label 132561b1390eSTim Keith // - C1551: NON_RECURSIVE prefix 132661b1390eSTim Keith class SubprogramMatchHelper { 132761b1390eSTim Keith public: 132882edd428STim Keith explicit SubprogramMatchHelper(CheckHelper &checkHelper) 132982edd428STim Keith : checkHelper{checkHelper} {} 133061b1390eSTim Keith 133161b1390eSTim Keith void Check(const Symbol &, const Symbol &); 133261b1390eSTim Keith 133361b1390eSTim Keith private: 133482edd428STim Keith SemanticsContext &context() { return checkHelper.context(); } 133561b1390eSTim Keith void CheckDummyArg(const Symbol &, const Symbol &, const DummyArgument &, 133661b1390eSTim Keith const DummyArgument &); 133761b1390eSTim Keith void CheckDummyDataObject(const Symbol &, const Symbol &, 133861b1390eSTim Keith const DummyDataObject &, const DummyDataObject &); 133961b1390eSTim Keith void CheckDummyProcedure(const Symbol &, const Symbol &, 134061b1390eSTim Keith const DummyProcedure &, const DummyProcedure &); 134161b1390eSTim Keith bool CheckSameIntent( 134261b1390eSTim Keith const Symbol &, const Symbol &, common::Intent, common::Intent); 134361b1390eSTim Keith template <typename... A> 134461b1390eSTim Keith void Say( 134561b1390eSTim Keith const Symbol &, const Symbol &, parser::MessageFixedText &&, A &&...); 134661b1390eSTim Keith template <typename ATTRS> 134761b1390eSTim Keith bool CheckSameAttrs(const Symbol &, const Symbol &, ATTRS, ATTRS); 134861b1390eSTim Keith bool ShapesAreCompatible(const DummyDataObject &, const DummyDataObject &); 134961b1390eSTim Keith evaluate::Shape FoldShape(const evaluate::Shape &); 135073cf0142SjeanPerier std::optional<evaluate::Shape> FoldShape( 135173cf0142SjeanPerier const std::optional<evaluate::Shape> &shape) { 135273cf0142SjeanPerier if (shape) { 135373cf0142SjeanPerier return FoldShape(*shape); 135473cf0142SjeanPerier } 135573cf0142SjeanPerier return std::nullopt; 135673cf0142SjeanPerier } 135761b1390eSTim Keith std::string AsFortran(DummyDataObject::Attr attr) { 135861b1390eSTim Keith return parser::ToUpperCaseLetters(DummyDataObject::EnumToString(attr)); 135961b1390eSTim Keith } 136061b1390eSTim Keith std::string AsFortran(DummyProcedure::Attr attr) { 136161b1390eSTim Keith return parser::ToUpperCaseLetters(DummyProcedure::EnumToString(attr)); 136261b1390eSTim Keith } 136361b1390eSTim Keith 136482edd428STim Keith CheckHelper &checkHelper; 136561b1390eSTim Keith }; 136661b1390eSTim Keith 1367c42f6314Speter klausler // 15.6.2.6 para 3 - can the result of an ENTRY differ from its function? 1368c42f6314Speter klausler bool CheckHelper::IsResultOkToDiffer(const FunctionResult &result) { 1369c42f6314Speter klausler if (result.attrs.test(FunctionResult::Attr::Allocatable) || 1370c42f6314Speter klausler result.attrs.test(FunctionResult::Attr::Pointer)) { 1371c42f6314Speter klausler return false; 1372c42f6314Speter klausler } 1373c42f6314Speter klausler const auto *typeAndShape{result.GetTypeAndShape()}; 1374c42f6314Speter klausler if (!typeAndShape || typeAndShape->Rank() != 0) { 1375c42f6314Speter klausler return false; 1376c42f6314Speter klausler } 1377c42f6314Speter klausler auto category{typeAndShape->type().category()}; 1378c42f6314Speter klausler if (category == TypeCategory::Character || 1379c42f6314Speter klausler category == TypeCategory::Derived) { 1380c42f6314Speter klausler return false; 1381c42f6314Speter klausler } 1382c42f6314Speter klausler int kind{typeAndShape->type().kind()}; 1383c42f6314Speter klausler return kind == context_.GetDefaultKind(category) || 1384c42f6314Speter klausler (category == TypeCategory::Real && 1385c42f6314Speter klausler kind == context_.doublePrecisionKind()); 1386c42f6314Speter klausler } 1387c42f6314Speter klausler 138861b1390eSTim Keith void CheckHelper::CheckSubprogram( 1389c42f6314Speter klausler const Symbol &symbol, const SubprogramDetails &details) { 13907605ad8aSPeter Klausler // Evaluate a procedure definition's characteristics to flush out 13917605ad8aSPeter Klausler // any errors that analysis might expose, in case this subprogram hasn't 13927605ad8aSPeter Klausler // had any calls in this compilation unit that would have validated them. 13937605ad8aSPeter Klausler if (!context_.HasError(symbol) && !details.isDummy() && 13947605ad8aSPeter Klausler !details.isInterface() && !details.stmtFunction()) { 13957605ad8aSPeter Klausler if (!Procedure::Characterize(symbol, foldingContext_)) { 13967605ad8aSPeter Klausler context_.SetError(symbol); 13977605ad8aSPeter Klausler } 13987605ad8aSPeter Klausler } 1399c42f6314Speter klausler if (const Symbol *iface{FindSeparateModuleSubprogramInterface(&symbol)}) { 140082edd428STim Keith SubprogramMatchHelper{*this}.Check(symbol, *iface); 140161b1390eSTim Keith } 1402c42f6314Speter klausler if (const Scope *entryScope{details.entryScope()}) { 140319b41f40SPeter Klausler // ENTRY F'2023 15.6.2.6 1404c42f6314Speter klausler std::optional<parser::MessageFixedText> error; 1405c42f6314Speter klausler const Symbol *subprogram{entryScope->symbol()}; 1406c42f6314Speter klausler const SubprogramDetails *subprogramDetails{nullptr}; 1407c42f6314Speter klausler if (subprogram) { 1408c42f6314Speter klausler subprogramDetails = subprogram->detailsIf<SubprogramDetails>(); 1409c42f6314Speter klausler } 14107f680b26SPeter Klausler if (!(entryScope->parent().IsGlobal() || entryScope->parent().IsModule() || 1411c42f6314Speter klausler entryScope->parent().IsSubmodule())) { 1412c42f6314Speter klausler error = "ENTRY may not appear in an internal subprogram"_err_en_US; 1413c42f6314Speter klausler } else if (subprogramDetails && details.isFunction() && 1414562bfe12Speter klausler subprogramDetails->isFunction() && 1415562bfe12Speter klausler !context_.HasError(details.result()) && 1416562bfe12Speter klausler !context_.HasError(subprogramDetails->result())) { 1417c42f6314Speter klausler auto result{FunctionResult::Characterize( 1418641ede93Speter klausler details.result(), context_.foldingContext())}; 1419c42f6314Speter klausler auto subpResult{FunctionResult::Characterize( 1420641ede93Speter klausler subprogramDetails->result(), context_.foldingContext())}; 1421c42f6314Speter klausler if (result && subpResult && *result != *subpResult && 1422c42f6314Speter klausler (!IsResultOkToDiffer(*result) || !IsResultOkToDiffer(*subpResult))) { 1423c42f6314Speter klausler error = 1424c42f6314Speter klausler "Result of ENTRY is not compatible with result of containing function"_err_en_US; 1425c42f6314Speter klausler } 1426c42f6314Speter klausler } 1427c42f6314Speter klausler if (error) { 1428c42f6314Speter klausler if (auto *msg{messages_.Say(symbol.name(), *error)}) { 1429c42f6314Speter klausler if (subprogram) { 1430c42f6314Speter klausler msg->Attach(subprogram->name(), "Containing subprogram"_en_US); 1431c42f6314Speter klausler } 1432c42f6314Speter klausler } 1433c42f6314Speter klausler } 143461b1390eSTim Keith } 143519b41f40SPeter Klausler if (details.isFunction() && 143619b41f40SPeter Klausler details.result().name() != symbol.name()) { // F'2023 C1569 & C1583 143719b41f40SPeter Klausler if (auto iter{symbol.owner().find(details.result().name())}; 143819b41f40SPeter Klausler iter != symbol.owner().end()) { 143919b41f40SPeter Klausler const Symbol &resNameSym{*iter->second}; 144019b41f40SPeter Klausler if (const auto *resNameSubp{resNameSym.detailsIf<SubprogramDetails>()}) { 144119b41f40SPeter Klausler if (const Scope * resNameEntryScope{resNameSubp->entryScope()}) { 144219b41f40SPeter Klausler const Scope *myScope{ 144319b41f40SPeter Klausler details.entryScope() ? details.entryScope() : symbol.scope()}; 144419b41f40SPeter Klausler if (resNameEntryScope == myScope) { 144519b41f40SPeter Klausler if (auto *msg{messages_.Say(symbol.name(), 144619b41f40SPeter Klausler "Explicit RESULT('%s') of function '%s' cannot have the same name as a distinct ENTRY into the same scope"_err_en_US, 144719b41f40SPeter Klausler details.result().name(), symbol.name())}) { 144819b41f40SPeter Klausler msg->Attach( 144919b41f40SPeter Klausler resNameSym.name(), "ENTRY with conflicting name"_en_US); 145019b41f40SPeter Klausler } 145119b41f40SPeter Klausler } 145219b41f40SPeter Klausler } 145319b41f40SPeter Klausler } 145419b41f40SPeter Klausler } 145519b41f40SPeter Klausler } 14561623aee4SPeter Klausler if (const MaybeExpr & stmtFunction{details.stmtFunction()}) { 14571623aee4SPeter Klausler if (auto msg{evaluate::CheckStatementFunction( 14581623aee4SPeter Klausler symbol, *stmtFunction, context_.foldingContext())}) { 14591623aee4SPeter Klausler SayWithDeclaration(symbol, std::move(*msg)); 1460a4745ff9SPeter Klausler } else if (IsPointer(symbol)) { 1461a4745ff9SPeter Klausler SayWithDeclaration(symbol, 1462a4745ff9SPeter Klausler "A statement function must not have the POINTER attribute"_err_en_US); 146341b5f371SPeter Klausler } else if (details.result().flags().test(Symbol::Flag::Implicit)) { 146441b5f371SPeter Klausler // 15.6.4 p2 weird requirement 146541b5f371SPeter Klausler if (const Symbol * 146641b5f371SPeter Klausler host{symbol.owner().parent().FindSymbol(symbol.name())}) { 146741b5f371SPeter Klausler evaluate::AttachDeclaration( 14680f973ac7SPeter Klausler Warn(common::LanguageFeature::StatementFunctionExtensions, 14690f973ac7SPeter Klausler symbol.name(), 147041b5f371SPeter Klausler "An implicitly typed statement function should not appear when the same symbol is available in its host scope"_port_en_US), 147141b5f371SPeter Klausler *host); 147241b5f371SPeter Klausler } 14731623aee4SPeter Klausler } 1474a183668aSPeter Klausler if (GetProgramUnitOrBlockConstructContaining(symbol).kind() == 1475a183668aSPeter Klausler Scope::Kind::BlockConstruct) { // C1107 1476a183668aSPeter Klausler messages_.Say(symbol.name(), 1477a183668aSPeter Klausler "A statement function definition may not appear in a BLOCK construct"_err_en_US); 1478a183668aSPeter Klausler } 14791623aee4SPeter Klausler } 14806052025bSPeter Klausler if (IsElementalProcedure(symbol)) { 1481c4a65434Speter klausler // See comment on the similar check in CheckProcEntity() 148243a263f5Speter klausler if (details.isDummy()) { 1483c4a65434Speter klausler messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US); 148443a263f5Speter klausler } else { 148543a263f5Speter klausler for (const Symbol *dummy : details.dummyArgs()) { 148643a263f5Speter klausler if (!dummy) { // C15100 148743a263f5Speter klausler messages_.Say( 148843a263f5Speter klausler "An ELEMENTAL subroutine may not have an alternate return dummy argument"_err_en_US); 148943a263f5Speter klausler } 149043a263f5Speter klausler } 149143a263f5Speter klausler } 1492c4a65434Speter klausler } 14930d588347SPeter Klausler if (details.isInterface()) { 14940d588347SPeter Klausler if (!details.isDummy() && details.isFunction() && 14959b86a722SPeter Klausler IsAssumedLengthCharacter(details.result())) { // C721 14969b86a722SPeter Klausler messages_.Say(details.result().name(), 14979b86a722SPeter Klausler "A function interface may not declare an assumed-length CHARACTER(*) result"_err_en_US); 14989b86a722SPeter Klausler } 149961b1390eSTim Keith } 1500982614faSPeter Klausler CheckExternal(symbol); 1501f7be1aadSPeter Klausler CheckModuleProcedureDef(symbol); 15023332dc32SPeter Klausler auto cudaAttrs{details.cudaSubprogramAttrs()}; 15033332dc32SPeter Klausler if (cudaAttrs && 15043332dc32SPeter Klausler (*cudaAttrs == common::CUDASubprogramAttrs::Global || 15053332dc32SPeter Klausler *cudaAttrs == common::CUDASubprogramAttrs::Grid_Global) && 15063332dc32SPeter Klausler details.isFunction()) { 15073332dc32SPeter Klausler messages_.Say(symbol.name(), 15083332dc32SPeter Klausler "A function may not have ATTRIBUTES(GLOBAL) or ATTRIBUTES(GRID_GLOBAL)"_err_en_US); 15093332dc32SPeter Klausler } 15107c555cb2SValentin Clement (バレンタイン クレメン) if (cudaAttrs && 15117c555cb2SValentin Clement (バレンタイン クレメン) (*cudaAttrs == common::CUDASubprogramAttrs::Global || 15127c555cb2SValentin Clement (バレンタイン クレメン) *cudaAttrs == common::CUDASubprogramAttrs::Grid_Global) && 15137c555cb2SValentin Clement (バレンタイン クレメン) symbol.attrs().HasAny({Attr::RECURSIVE, Attr::PURE, Attr::ELEMENTAL})) { 15147c555cb2SValentin Clement (バレンタイン クレメン) messages_.Say(symbol.name(), 15157c555cb2SValentin Clement (バレンタイン クレメン) "A kernel subprogram may not be RECURSIVE, PURE, or ELEMENTAL"_err_en_US); 15167c555cb2SValentin Clement (バレンタイン クレメン) } 15173332dc32SPeter Klausler if (cudaAttrs && *cudaAttrs != common::CUDASubprogramAttrs::Host) { 15183332dc32SPeter Klausler // CUDA device subprogram checks 15193332dc32SPeter Klausler if (ClassifyProcedure(symbol) == ProcedureDefinitionClass::Internal) { 15203332dc32SPeter Klausler messages_.Say(symbol.name(), 15213332dc32SPeter Klausler "A device subprogram may not be an internal subprogram"_err_en_US); 15223332dc32SPeter Klausler } 15233332dc32SPeter Klausler } 15243332dc32SPeter Klausler if ((!details.cudaLaunchBounds().empty() || 15253332dc32SPeter Klausler !details.cudaClusterDims().empty()) && 15263332dc32SPeter Klausler !(cudaAttrs && 15273332dc32SPeter Klausler (*cudaAttrs == common::CUDASubprogramAttrs::Global || 15283332dc32SPeter Klausler *cudaAttrs == common::CUDASubprogramAttrs::Grid_Global))) { 15293332dc32SPeter Klausler messages_.Say(symbol.name(), 15303332dc32SPeter Klausler "A subroutine may not have LAUNCH_BOUNDS() or CLUSTER_DIMS() unless it has ATTRIBUTES(GLOBAL) or ATTRIBUTES(GRID_GLOBAL)"_err_en_US); 15313332dc32SPeter Klausler } 15323332dc32SPeter Klausler if (!IsStmtFunction(symbol)) { 15333332dc32SPeter Klausler if (const Scope * outerDevice{FindCUDADeviceContext(&symbol.owner())}; 15343332dc32SPeter Klausler outerDevice && outerDevice->symbol()) { 15353332dc32SPeter Klausler if (auto *msg{messages_.Say(symbol.name(), 15363332dc32SPeter Klausler "'%s' may not be an internal procedure of CUDA device subprogram '%s'"_err_en_US, 15373332dc32SPeter Klausler symbol.name(), outerDevice->symbol()->name())}) { 15383332dc32SPeter Klausler msg->Attach(outerDevice->symbol()->name(), 15393332dc32SPeter Klausler "Containing CUDA device subprogram"_en_US); 15403332dc32SPeter Klausler } 15413332dc32SPeter Klausler } 15423332dc32SPeter Klausler } 15430d588347SPeter Klausler } 15440d588347SPeter Klausler 1545982614faSPeter Klausler void CheckHelper::CheckExternal(const Symbol &symbol) { 15463077d614SPeter Klausler if (IsExternal(symbol)) { 15470d588347SPeter Klausler std::string interfaceName{symbol.name().ToString()}; 15480d588347SPeter Klausler if (const auto *bind{symbol.GetBindName()}) { 15490d588347SPeter Klausler interfaceName = *bind; 15500d588347SPeter Klausler } 1551982614faSPeter Klausler if (const Symbol * global{FindGlobal(symbol)}; 1552982614faSPeter Klausler global && global != &symbol) { 15530d588347SPeter Klausler std::string definitionName{global->name().ToString()}; 15540d588347SPeter Klausler if (const auto *bind{global->GetBindName()}) { 15550d588347SPeter Klausler definitionName = *bind; 15560d588347SPeter Klausler } 15570d588347SPeter Klausler if (interfaceName == definitionName) { 15580d588347SPeter Klausler parser::Message *msg{nullptr}; 15590d588347SPeter Klausler if (!IsProcedure(*global)) { 15602ba94bfbSPeter Klausler if ((symbol.flags().test(Symbol::Flag::Function) || 15610f973ac7SPeter Klausler symbol.flags().test(Symbol::Flag::Subroutine))) { 15620f973ac7SPeter Klausler msg = Warn(common::UsageWarning::ExternalNameConflict, 15632ba94bfbSPeter Klausler "The global entity '%s' corresponding to the local procedure '%s' is not a callable subprogram"_warn_en_US, 15640d588347SPeter Klausler global->name(), symbol.name()); 15650d588347SPeter Klausler } 15660d588347SPeter Klausler } else if (auto chars{Characterize(symbol)}) { 15670d588347SPeter Klausler if (auto globalChars{Characterize(*global)}) { 15680d588347SPeter Klausler if (chars->HasExplicitInterface()) { 15690d588347SPeter Klausler std::string whyNot; 15701c530b3dSPeter Klausler if (!chars->IsCompatibleWith(*globalChars, 15710f973ac7SPeter Klausler /*ignoreImplicitVsExplicit=*/false, &whyNot)) { 15720f973ac7SPeter Klausler msg = Warn(common::UsageWarning::ExternalInterfaceMismatch, 15730d588347SPeter Klausler "The global subprogram '%s' is not compatible with its local procedure declaration (%s)"_warn_en_US, 15740d588347SPeter Klausler global->name(), whyNot); 15750d588347SPeter Klausler } 15760f973ac7SPeter Klausler } else if (!globalChars->CanBeCalledViaImplicitInterface()) { 15778ed82106SPeter Klausler // TODO: This should be a hard error if the procedure has 15788ed82106SPeter Klausler // actually been called (as opposed to just being used as a 15798ed82106SPeter Klausler // procedure pointer target or passed as an actual argument). 15800f973ac7SPeter Klausler msg = Warn(common::UsageWarning::ExternalInterfaceMismatch, 15818ed82106SPeter Klausler "The global subprogram '%s' should not be referenced via the implicit interface '%s'"_warn_en_US, 15820d588347SPeter Klausler global->name(), symbol.name()); 15830d588347SPeter Klausler } 15840d588347SPeter Klausler } 15850d588347SPeter Klausler } 15863077d614SPeter Klausler if (msg) { 15873077d614SPeter Klausler if (msg->IsFatal()) { 15883077d614SPeter Klausler context_.SetError(symbol); 15893077d614SPeter Klausler } 15900d588347SPeter Klausler evaluate::AttachDeclaration(msg, *global); 15910d588347SPeter Klausler evaluate::AttachDeclaration(msg, symbol); 15920d588347SPeter Klausler } 15930d588347SPeter Klausler } 1594982614faSPeter Klausler } else if (auto iter{externalNames_.find(interfaceName)}; 1595982614faSPeter Klausler iter != externalNames_.end()) { 1596982614faSPeter Klausler const Symbol &previous{*iter->second}; 1597982614faSPeter Klausler if (auto chars{Characterize(symbol)}) { 1598982614faSPeter Klausler if (auto previousChars{Characterize(previous)}) { 1599982614faSPeter Klausler std::string whyNot; 16001c530b3dSPeter Klausler if (!chars->IsCompatibleWith(*previousChars, 16010f973ac7SPeter Klausler /*ignoreImplicitVsExplicit=*/false, &whyNot)) { 16020f973ac7SPeter Klausler if (auto *msg{Warn(common::UsageWarning::ExternalInterfaceMismatch, 1603982614faSPeter Klausler "The external interface '%s' is not compatible with an earlier definition (%s)"_warn_en_US, 1604982614faSPeter Klausler symbol.name(), whyNot)}) { 1605982614faSPeter Klausler evaluate::AttachDeclaration(msg, previous); 1606982614faSPeter Klausler evaluate::AttachDeclaration(msg, symbol); 1607982614faSPeter Klausler } 1608982614faSPeter Klausler } 1609982614faSPeter Klausler } 1610982614faSPeter Klausler } 1611982614faSPeter Klausler } else { 1612982614faSPeter Klausler externalNames_.emplace(interfaceName, symbol); 16130d588347SPeter Klausler } 16140d588347SPeter Klausler } 16153077d614SPeter Klausler } 161661b1390eSTim Keith 161764ab3302SCarolineConcatto void CheckHelper::CheckDerivedType( 161837b2e2b0Speter klausler const Symbol &derivedType, const DerivedTypeDetails &details) { 16192b795ec6SPeter Steinfeld if (details.isForwardReferenced() && !context_.HasError(derivedType)) { 16202b795ec6SPeter Steinfeld messages_.Say("The derived type '%s' has not been defined"_err_en_US, 16212b795ec6SPeter Steinfeld derivedType.name()); 16222b795ec6SPeter Steinfeld } 162337b2e2b0Speter klausler const Scope *scope{derivedType.scope()}; 16242b790490SPete Steinfeld if (!scope) { 162564ab3302SCarolineConcatto CHECK(details.isForwardReferenced()); 162664ab3302SCarolineConcatto return; 162764ab3302SCarolineConcatto } 162837b2e2b0Speter klausler CHECK(scope->symbol() == &derivedType); 16292b790490SPete Steinfeld CHECK(scope->IsDerivedType()); 163037b2e2b0Speter klausler if (derivedType.attrs().test(Attr::ABSTRACT) && // C734 163137b2e2b0Speter klausler (derivedType.attrs().test(Attr::BIND_C) || details.sequence())) { 163264ab3302SCarolineConcatto messages_.Say("An ABSTRACT derived type must be extensible"_err_en_US); 163364ab3302SCarolineConcatto } 163437b2e2b0Speter klausler if (const DeclTypeSpec *parent{FindParentTypeSpec(derivedType)}) { 163564ab3302SCarolineConcatto const DerivedTypeSpec *parentDerived{parent->AsDerived()}; 163664ab3302SCarolineConcatto if (!IsExtensibleType(parentDerived)) { // C705 163764ab3302SCarolineConcatto messages_.Say("The parent type is not extensible"_err_en_US); 163864ab3302SCarolineConcatto } 163937b2e2b0Speter klausler if (!derivedType.attrs().test(Attr::ABSTRACT) && parentDerived && 164064ab3302SCarolineConcatto parentDerived->typeSymbol().attrs().test(Attr::ABSTRACT)) { 164164ab3302SCarolineConcatto ScopeComponentIterator components{*parentDerived}; 164264ab3302SCarolineConcatto for (const Symbol &component : components) { 164364ab3302SCarolineConcatto if (component.attrs().test(Attr::DEFERRED)) { 16442b790490SPete Steinfeld if (scope->FindComponent(component.name()) == &component) { 164564ab3302SCarolineConcatto SayWithDeclaration(component, 164664ab3302SCarolineConcatto "Non-ABSTRACT extension of ABSTRACT derived type '%s' lacks a binding for DEFERRED procedure '%s'"_err_en_US, 164764ab3302SCarolineConcatto parentDerived->typeSymbol().name(), component.name()); 164864ab3302SCarolineConcatto } 164964ab3302SCarolineConcatto } 165064ab3302SCarolineConcatto } 165164ab3302SCarolineConcatto } 165237b2e2b0Speter klausler DerivedTypeSpec derived{derivedType.name(), derivedType}; 16532b790490SPete Steinfeld derived.set_scope(*scope); 16542b790490SPete Steinfeld if (FindCoarrayUltimateComponent(derived) && // C736 16552b790490SPete Steinfeld !(parentDerived && FindCoarrayUltimateComponent(*parentDerived))) { 16562b790490SPete Steinfeld messages_.Say( 16572b790490SPete Steinfeld "Type '%s' has a coarray ultimate component so the type at the base " 16582b790490SPete Steinfeld "of its type extension chain ('%s') must be a type that has a " 16592b790490SPete Steinfeld "coarray ultimate component"_err_en_US, 166037b2e2b0Speter klausler derivedType.name(), scope->GetDerivedTypeBase().GetSymbol()->name()); 16612b790490SPete Steinfeld } 16622b790490SPete Steinfeld if (FindEventOrLockPotentialComponent(derived) && // C737 16632b790490SPete Steinfeld !(FindEventOrLockPotentialComponent(*parentDerived) || 16642b790490SPete Steinfeld IsEventTypeOrLockType(parentDerived))) { 16652b790490SPete Steinfeld messages_.Say( 16662b790490SPete Steinfeld "Type '%s' has an EVENT_TYPE or LOCK_TYPE component, so the type " 16672b790490SPete Steinfeld "at the base of its type extension chain ('%s') must either have an " 16682b790490SPete Steinfeld "EVENT_TYPE or LOCK_TYPE component, or be EVENT_TYPE or " 16692b790490SPete Steinfeld "LOCK_TYPE"_err_en_US, 167037b2e2b0Speter klausler derivedType.name(), scope->GetDerivedTypeBase().GetSymbol()->name()); 16712b790490SPete Steinfeld } 167264ab3302SCarolineConcatto } 167337b2e2b0Speter klausler if (HasIntrinsicTypeName(derivedType)) { // C729 1674e17e7173SPete Steinfeld messages_.Say("A derived type name cannot be the name of an intrinsic" 1675e17e7173SPete Steinfeld " type"_err_en_US); 1676e17e7173SPete Steinfeld } 167737b2e2b0Speter klausler std::map<SourceName, SymbolRef> previous; 167837b2e2b0Speter klausler for (const auto &pair : details.finals()) { 167937b2e2b0Speter klausler SourceName source{pair.first}; 168037b2e2b0Speter klausler const Symbol &ref{*pair.second}; 168137b2e2b0Speter klausler if (CheckFinal(ref, source, derivedType) && 168237b2e2b0Speter klausler std::all_of(previous.begin(), previous.end(), 168337b2e2b0Speter klausler [&](std::pair<SourceName, SymbolRef> prev) { 168437b2e2b0Speter klausler return CheckDistinguishableFinals( 168537b2e2b0Speter klausler ref, source, *prev.second, prev.first, derivedType); 168637b2e2b0Speter klausler })) { 168737b2e2b0Speter klausler previous.emplace(source, ref); 168837b2e2b0Speter klausler } 168937b2e2b0Speter klausler } 169037b2e2b0Speter klausler } 169137b2e2b0Speter klausler 169237b2e2b0Speter klausler // C786 169337b2e2b0Speter klausler bool CheckHelper::CheckFinal( 169437b2e2b0Speter klausler const Symbol &subroutine, SourceName finalName, const Symbol &derivedType) { 169537b2e2b0Speter klausler if (!IsModuleProcedure(subroutine)) { 169637b2e2b0Speter klausler SayWithDeclaration(subroutine, finalName, 169737b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must be a module procedure"_err_en_US, 169837b2e2b0Speter klausler subroutine.name(), derivedType.name()); 169937b2e2b0Speter klausler return false; 170037b2e2b0Speter klausler } 170137b2e2b0Speter klausler const Procedure *proc{Characterize(subroutine)}; 170237b2e2b0Speter klausler if (!proc) { 170337b2e2b0Speter klausler return false; // error recovery 170437b2e2b0Speter klausler } 170537b2e2b0Speter klausler if (!proc->IsSubroutine()) { 170637b2e2b0Speter klausler SayWithDeclaration(subroutine, finalName, 170737b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must be a subroutine"_err_en_US, 170837b2e2b0Speter klausler subroutine.name(), derivedType.name()); 170937b2e2b0Speter klausler return false; 171037b2e2b0Speter klausler } 171137b2e2b0Speter klausler if (proc->dummyArguments.size() != 1) { 171237b2e2b0Speter klausler SayWithDeclaration(subroutine, finalName, 171337b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must have a single dummy argument"_err_en_US, 171437b2e2b0Speter klausler subroutine.name(), derivedType.name()); 171537b2e2b0Speter klausler return false; 171637b2e2b0Speter klausler } 171737b2e2b0Speter klausler const auto &arg{proc->dummyArguments[0]}; 171837b2e2b0Speter klausler const Symbol *errSym{&subroutine}; 171937b2e2b0Speter klausler if (const auto *details{subroutine.detailsIf<SubprogramDetails>()}) { 172037b2e2b0Speter klausler if (!details->dummyArgs().empty()) { 172137b2e2b0Speter klausler if (const Symbol *argSym{details->dummyArgs()[0]}) { 172237b2e2b0Speter klausler errSym = argSym; 172337b2e2b0Speter klausler } 172437b2e2b0Speter klausler } 172537b2e2b0Speter klausler } 172637b2e2b0Speter klausler const auto *ddo{std::get_if<DummyDataObject>(&arg.u)}; 172737b2e2b0Speter klausler if (!ddo) { 172837b2e2b0Speter klausler SayWithDeclaration(subroutine, finalName, 172937b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must have a single dummy argument that is a data object"_err_en_US, 173037b2e2b0Speter klausler subroutine.name(), derivedType.name()); 173137b2e2b0Speter klausler return false; 173237b2e2b0Speter klausler } 173337b2e2b0Speter klausler bool ok{true}; 173437b2e2b0Speter klausler if (arg.IsOptional()) { 173537b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 173637b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must not have an OPTIONAL dummy argument"_err_en_US, 173737b2e2b0Speter klausler subroutine.name(), derivedType.name()); 173837b2e2b0Speter klausler ok = false; 173937b2e2b0Speter klausler } 174037b2e2b0Speter klausler if (ddo->attrs.test(DummyDataObject::Attr::Allocatable)) { 174137b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 174237b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must not have an ALLOCATABLE dummy argument"_err_en_US, 174337b2e2b0Speter klausler subroutine.name(), derivedType.name()); 174437b2e2b0Speter klausler ok = false; 174537b2e2b0Speter klausler } 174637b2e2b0Speter klausler if (ddo->attrs.test(DummyDataObject::Attr::Pointer)) { 174737b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 174837b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must not have a POINTER dummy argument"_err_en_US, 174937b2e2b0Speter klausler subroutine.name(), derivedType.name()); 175037b2e2b0Speter klausler ok = false; 175137b2e2b0Speter klausler } 175237b2e2b0Speter klausler if (ddo->intent == common::Intent::Out) { 175337b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 175437b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must not have a dummy argument with INTENT(OUT)"_err_en_US, 175537b2e2b0Speter klausler subroutine.name(), derivedType.name()); 175637b2e2b0Speter klausler ok = false; 175737b2e2b0Speter klausler } 175837b2e2b0Speter klausler if (ddo->attrs.test(DummyDataObject::Attr::Value)) { 175937b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 176037b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must not have a dummy argument with the VALUE attribute"_err_en_US, 176137b2e2b0Speter klausler subroutine.name(), derivedType.name()); 176237b2e2b0Speter klausler ok = false; 176337b2e2b0Speter klausler } 176437b2e2b0Speter klausler if (ddo->type.corank() > 0) { 176537b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 176637b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must not have a coarray dummy argument"_err_en_US, 176737b2e2b0Speter klausler subroutine.name(), derivedType.name()); 176837b2e2b0Speter klausler ok = false; 176937b2e2b0Speter klausler } 177037b2e2b0Speter klausler if (ddo->type.type().IsPolymorphic()) { 177137b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 177237b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must not have a polymorphic dummy argument"_err_en_US, 177337b2e2b0Speter klausler subroutine.name(), derivedType.name()); 177437b2e2b0Speter klausler ok = false; 177537b2e2b0Speter klausler } else if (ddo->type.type().category() != TypeCategory::Derived || 177637b2e2b0Speter klausler &ddo->type.type().GetDerivedTypeSpec().typeSymbol() != &derivedType) { 177737b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 177837b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must have a TYPE(%s) dummy argument"_err_en_US, 177937b2e2b0Speter klausler subroutine.name(), derivedType.name(), derivedType.name()); 178037b2e2b0Speter klausler ok = false; 178137b2e2b0Speter klausler } else { // check that all LEN type parameters are assumed 178237b2e2b0Speter klausler for (auto ref : OrderParameterDeclarations(derivedType)) { 1783641ede93Speter klausler if (IsLenTypeParameter(*ref)) { 178437b2e2b0Speter klausler const auto *value{ 178537b2e2b0Speter klausler ddo->type.type().GetDerivedTypeSpec().FindParameter(ref->name())}; 178637b2e2b0Speter klausler if (!value || !value->isAssumed()) { 178737b2e2b0Speter klausler SayWithDeclaration(*errSym, finalName, 178837b2e2b0Speter klausler "FINAL subroutine '%s' of derived type '%s' must have a dummy argument with an assumed LEN type parameter '%s=*'"_err_en_US, 178937b2e2b0Speter klausler subroutine.name(), derivedType.name(), ref->name()); 179037b2e2b0Speter klausler ok = false; 179137b2e2b0Speter klausler } 179237b2e2b0Speter klausler } 179337b2e2b0Speter klausler } 179437b2e2b0Speter klausler } 179537b2e2b0Speter klausler return ok; 179637b2e2b0Speter klausler } 179737b2e2b0Speter klausler 179837b2e2b0Speter klausler bool CheckHelper::CheckDistinguishableFinals(const Symbol &f1, 179937b2e2b0Speter klausler SourceName f1Name, const Symbol &f2, SourceName f2Name, 180037b2e2b0Speter klausler const Symbol &derivedType) { 180137b2e2b0Speter klausler const Procedure *p1{Characterize(f1)}; 180237b2e2b0Speter klausler const Procedure *p2{Characterize(f2)}; 180337b2e2b0Speter klausler if (p1 && p2) { 18043d115700SPeter Klausler std::optional<bool> areDistinct{characteristics::Distinguishable( 18053d115700SPeter Klausler context_.languageFeatures(), *p1, *p2)}; 18063d115700SPeter Klausler if (areDistinct.value_or(false)) { 180737b2e2b0Speter klausler return true; 180837b2e2b0Speter klausler } 180937b2e2b0Speter klausler if (auto *msg{messages_.Say(f1Name, 181037b2e2b0Speter klausler "FINAL subroutines '%s' and '%s' of derived type '%s' cannot be distinguished by rank or KIND type parameter value"_err_en_US, 181137b2e2b0Speter klausler f1Name, f2Name, derivedType.name())}) { 181237b2e2b0Speter klausler msg->Attach(f2Name, "FINAL declaration of '%s'"_en_US, f2.name()) 181337b2e2b0Speter klausler .Attach(f1.name(), "Definition of '%s'"_en_US, f1Name) 181437b2e2b0Speter klausler .Attach(f2.name(), "Definition of '%s'"_en_US, f2Name); 181537b2e2b0Speter klausler } 181637b2e2b0Speter klausler } 181737b2e2b0Speter klausler return false; 181864ab3302SCarolineConcatto } 181964ab3302SCarolineConcatto 1820b8bfe358STim Keith void CheckHelper::CheckHostAssoc( 1821b8bfe358STim Keith const Symbol &symbol, const HostAssocDetails &details) { 1822b8bfe358STim Keith const Symbol &hostSymbol{details.symbol()}; 1823b8bfe358STim Keith if (hostSymbol.test(Symbol::Flag::ImplicitOrError)) { 1824b8bfe358STim Keith if (details.implicitOrSpecExprError) { 1825b8bfe358STim Keith messages_.Say("Implicitly typed local entity '%s' not allowed in" 1826b8bfe358STim Keith " specification expression"_err_en_US, 1827b8bfe358STim Keith symbol.name()); 1828b8bfe358STim Keith } else if (details.implicitOrExplicitTypeError) { 1829b8bfe358STim Keith messages_.Say( 1830b8bfe358STim Keith "No explicit type declared for '%s'"_err_en_US, symbol.name()); 1831b8bfe358STim Keith } 1832b8bfe358STim Keith } 1833b8bfe358STim Keith } 1834b8bfe358STim Keith 183564ab3302SCarolineConcatto void CheckHelper::CheckGeneric( 183664ab3302SCarolineConcatto const Symbol &symbol, const GenericDetails &details) { 1837e86bf468SPeter Klausler CheckSpecifics(symbol, details); 1838cd03e96fSPeter Klausler common::visit(common::visitors{ 18397cf1608bSPeter Klausler [&](const common::DefinedIo &io) { 1840bc56620bSPeter Steinfeld CheckDefinedIoProc(symbol, details, io); 1841bc56620bSPeter Steinfeld }, 1842eb14135eSPeter Klausler [&](const GenericKind::OtherKind &other) { 1843eb14135eSPeter Klausler if (other == GenericKind::OtherKind::Name) { 1844eb14135eSPeter Klausler CheckGenericVsIntrinsic(symbol, details); 1845eb14135eSPeter Klausler } 1846eb14135eSPeter Klausler }, 1847bc56620bSPeter Steinfeld [](const auto &) {}, 1848bc56620bSPeter Steinfeld }, 1849bc56620bSPeter Steinfeld details.kind().u); 1850f7be1aadSPeter Klausler // Ensure that shadowed symbols are checked 1851f7be1aadSPeter Klausler if (details.specific()) { 1852f7be1aadSPeter Klausler Check(*details.specific()); 1853f7be1aadSPeter Klausler } 1854f7be1aadSPeter Klausler if (details.derivedType()) { 1855f7be1aadSPeter Klausler Check(*details.derivedType()); 1856f7be1aadSPeter Klausler } 185764ab3302SCarolineConcatto } 185864ab3302SCarolineConcatto 185964ab3302SCarolineConcatto // Check that the specifics of this generic are distinguishable from each other 1860e86bf468SPeter Klausler void CheckHelper::CheckSpecifics( 186182edd428STim Keith const Symbol &generic, const GenericDetails &details) { 186282edd428STim Keith GenericKind kind{details.kind()}; 186382edd428STim Keith DistinguishabilityHelper helper{context_}; 18643b7b7fa7SPeter Klausler for (const Symbol &specific : details.specificProcs()) { 18655be7f8a6SPeter Klausler if (specific.attrs().test(Attr::ABSTRACT)) { 18665be7f8a6SPeter Klausler if (auto *msg{messages_.Say(generic.name(), 18675be7f8a6SPeter Klausler "Generic interface '%s' must not use abstract interface '%s' as a specific procedure"_err_en_US, 18685be7f8a6SPeter Klausler generic.name(), specific.name())}) { 18695be7f8a6SPeter Klausler msg->Attach( 18705be7f8a6SPeter Klausler specific.name(), "Definition of '%s'"_en_US, specific.name()); 18715be7f8a6SPeter Klausler } 1872e86bf468SPeter Klausler continue; 1873e86bf468SPeter Klausler } 1874e86bf468SPeter Klausler if (specific.attrs().test(Attr::INTRINSIC)) { 1875d325c5d0SPeter Klausler // GNU Fortran allows INTRINSIC procedures in generics. 1876d325c5d0SPeter Klausler auto intrinsic{context_.intrinsics().IsSpecificIntrinsicFunction( 1877d325c5d0SPeter Klausler specific.name().ToString())}; 1878d325c5d0SPeter Klausler if (intrinsic && !intrinsic->isRestrictedSpecific) { 18790f973ac7SPeter Klausler if (auto *msg{Warn(common::LanguageFeature::IntrinsicAsSpecific, 18800f973ac7SPeter Klausler specific.name(), 1881d325c5d0SPeter Klausler "Specific procedure '%s' of generic interface '%s' should not be INTRINSIC"_port_en_US, 1882e86bf468SPeter Klausler specific.name(), generic.name())}) { 1883d325c5d0SPeter Klausler msg->Attach( 1884d325c5d0SPeter Klausler generic.name(), "Definition of '%s'"_en_US, generic.name()); 1885d325c5d0SPeter Klausler } 1886d325c5d0SPeter Klausler } else { 18870f973ac7SPeter Klausler if (auto *msg{Warn(common::LanguageFeature::IntrinsicAsSpecific, 18880f973ac7SPeter Klausler specific.name(), 1889d325c5d0SPeter Klausler "Procedure '%s' of generic interface '%s' is INTRINSIC but not an unrestricted specific intrinsic function"_port_en_US, 1890d325c5d0SPeter Klausler specific.name(), generic.name())}) { 1891d325c5d0SPeter Klausler msg->Attach( 1892d325c5d0SPeter Klausler generic.name(), "Definition of '%s'"_en_US, generic.name()); 1893e86bf468SPeter Klausler } 1894e86bf468SPeter Klausler continue; 1895e86bf468SPeter Klausler } 1896d325c5d0SPeter Klausler } 1897e86bf468SPeter Klausler if (IsStmtFunction(specific)) { 1898e86bf468SPeter Klausler if (auto *msg{messages_.Say(specific.name(), 1899e86bf468SPeter Klausler "Specific procedure '%s' of generic interface '%s' may not be a statement function"_err_en_US, 1900e86bf468SPeter Klausler specific.name(), generic.name())}) { 1901e86bf468SPeter Klausler msg->Attach(generic.name(), "Definition of '%s'"_en_US, generic.name()); 1902e86bf468SPeter Klausler } 1903e86bf468SPeter Klausler continue; 19045be7f8a6SPeter Klausler } 190582edd428STim Keith if (const Procedure *procedure{Characterize(specific)}) { 19063b7b7fa7SPeter Klausler if (procedure->HasExplicitInterface()) { 190782edd428STim Keith helper.Add(generic, kind, specific, *procedure); 19083b7b7fa7SPeter Klausler } else { 19093b7b7fa7SPeter Klausler if (auto *msg{messages_.Say(specific.name(), 19103b7b7fa7SPeter Klausler "Specific procedure '%s' of generic interface '%s' must have an explicit interface"_err_en_US, 19113b7b7fa7SPeter Klausler specific.name(), generic.name())}) { 19123b7b7fa7SPeter Klausler msg->Attach( 19133b7b7fa7SPeter Klausler generic.name(), "Definition of '%s'"_en_US, generic.name()); 19143b7b7fa7SPeter Klausler } 19153b7b7fa7SPeter Klausler } 191664ab3302SCarolineConcatto } 191764ab3302SCarolineConcatto } 191886f59de1STim Keith helper.Check(generic.owner()); 191964ab3302SCarolineConcatto } 192064ab3302SCarolineConcatto 19210ee71124SPeter Klausler static bool CUDAHostDeviceDiffer( 19220ee71124SPeter Klausler const Procedure &proc, const DummyDataObject &arg) { 19230ee71124SPeter Klausler auto procCUDA{ 19240ee71124SPeter Klausler proc.cudaSubprogramAttrs.value_or(common::CUDASubprogramAttrs::Host)}; 19250ee71124SPeter Klausler bool procIsHostOnly{procCUDA == common::CUDASubprogramAttrs::Host}; 19260ee71124SPeter Klausler bool procIsDeviceOnly{ 19270ee71124SPeter Klausler !procIsHostOnly && procCUDA != common::CUDASubprogramAttrs::HostDevice}; 19280ee71124SPeter Klausler const auto &argCUDA{arg.cudaDataAttr}; 19290ee71124SPeter Klausler bool argIsHostOnly{!argCUDA || *argCUDA == common::CUDADataAttr::Pinned}; 19300ee71124SPeter Klausler bool argIsDeviceOnly{(!argCUDA && procIsDeviceOnly) || 19310ee71124SPeter Klausler (argCUDA && 19320ee71124SPeter Klausler (*argCUDA != common::CUDADataAttr::Managed && 19330ee71124SPeter Klausler *argCUDA != common::CUDADataAttr::Pinned && 19340ee71124SPeter Klausler *argCUDA != common::CUDADataAttr::Unified))}; 19350ee71124SPeter Klausler return (procIsHostOnly && argIsDeviceOnly) || 19360ee71124SPeter Klausler (procIsDeviceOnly && argIsHostOnly); 19370ee71124SPeter Klausler } 19380ee71124SPeter Klausler 193964ab3302SCarolineConcatto static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) { 19400ee71124SPeter Klausler const auto &lhsData{std::get<DummyDataObject>(proc.dummyArguments[0].u)}; 19410ee71124SPeter Klausler const auto &lhsTnS{lhsData.type}; 19420ee71124SPeter Klausler const auto &rhsData{std::get<DummyDataObject>(proc.dummyArguments[1].u)}; 19430ee71124SPeter Klausler const auto &rhsTnS{rhsData.type}; 19440ee71124SPeter Klausler return !CUDAHostDeviceDiffer(proc, lhsData) && 19450ee71124SPeter Klausler !CUDAHostDeviceDiffer(proc, rhsData) && 19460ee71124SPeter Klausler Tristate::No == 19470ee71124SPeter Klausler IsDefinedAssignment( 19480ee71124SPeter Klausler lhsTnS.type(), lhsTnS.Rank(), rhsTnS.type(), rhsTnS.Rank()); 194964ab3302SCarolineConcatto } 195064ab3302SCarolineConcatto 195164ab3302SCarolineConcatto static bool ConflictsWithIntrinsicOperator( 195264ab3302SCarolineConcatto const GenericKind &kind, const Procedure &proc) { 195382edd428STim Keith if (!kind.IsIntrinsicOperator()) { 195482edd428STim Keith return false; 195582edd428STim Keith } 19560ee71124SPeter Klausler const auto &arg0Data{std::get<DummyDataObject>(proc.dummyArguments[0].u)}; 19570ee71124SPeter Klausler if (CUDAHostDeviceDiffer(proc, arg0Data)) { 19580ee71124SPeter Klausler return false; 19590ee71124SPeter Klausler } 19600ee71124SPeter Klausler const auto &arg0TnS{arg0Data.type}; 19610ee71124SPeter Klausler auto type0{arg0TnS.type()}; 196264ab3302SCarolineConcatto if (proc.dummyArguments.size() == 1) { // unary 1963cd03e96fSPeter Klausler return common::visit( 196464ab3302SCarolineConcatto common::visitors{ 196564ab3302SCarolineConcatto [&](common::NumericOperator) { return IsIntrinsicNumeric(type0); }, 196664ab3302SCarolineConcatto [&](common::LogicalOperator) { return IsIntrinsicLogical(type0); }, 196764ab3302SCarolineConcatto [](const auto &) -> bool { DIE("bad generic kind"); }, 196864ab3302SCarolineConcatto }, 196964ab3302SCarolineConcatto kind.u); 197064ab3302SCarolineConcatto } else { // binary 19710ee71124SPeter Klausler int rank0{arg0TnS.Rank()}; 19720ee71124SPeter Klausler const auto &arg1Data{std::get<DummyDataObject>(proc.dummyArguments[1].u)}; 19730ee71124SPeter Klausler if (CUDAHostDeviceDiffer(proc, arg1Data)) { 19740ee71124SPeter Klausler return false; 19750ee71124SPeter Klausler } 19760ee71124SPeter Klausler const auto &arg1TnS{arg1Data.type}; 19770ee71124SPeter Klausler auto type1{arg1TnS.type()}; 19780ee71124SPeter Klausler int rank1{arg1TnS.Rank()}; 1979cd03e96fSPeter Klausler return common::visit( 198064ab3302SCarolineConcatto common::visitors{ 198164ab3302SCarolineConcatto [&](common::NumericOperator) { 198264ab3302SCarolineConcatto return IsIntrinsicNumeric(type0, rank0, type1, rank1); 198364ab3302SCarolineConcatto }, 198464ab3302SCarolineConcatto [&](common::LogicalOperator) { 198564ab3302SCarolineConcatto return IsIntrinsicLogical(type0, rank0, type1, rank1); 198664ab3302SCarolineConcatto }, 198764ab3302SCarolineConcatto [&](common::RelationalOperator opr) { 198864ab3302SCarolineConcatto return IsIntrinsicRelational(opr, type0, rank0, type1, rank1); 198964ab3302SCarolineConcatto }, 199064ab3302SCarolineConcatto [&](GenericKind::OtherKind x) { 199164ab3302SCarolineConcatto CHECK(x == GenericKind::OtherKind::Concat); 199264ab3302SCarolineConcatto return IsIntrinsicConcat(type0, rank0, type1, rank1); 199364ab3302SCarolineConcatto }, 199464ab3302SCarolineConcatto [](const auto &) -> bool { DIE("bad generic kind"); }, 199564ab3302SCarolineConcatto }, 199664ab3302SCarolineConcatto kind.u); 199764ab3302SCarolineConcatto } 199864ab3302SCarolineConcatto } 199964ab3302SCarolineConcatto 200064ab3302SCarolineConcatto // Check if this procedure can be used for defined operators (see 15.4.3.4.2). 200182edd428STim Keith bool CheckHelper::CheckDefinedOperator(SourceName opName, GenericKind kind, 200282edd428STim Keith const Symbol &specific, const Procedure &proc) { 200382edd428STim Keith if (context_.HasError(specific)) { 200482edd428STim Keith return false; 200582edd428STim Keith } 200664ab3302SCarolineConcatto std::optional<parser::MessageFixedText> msg; 2007e962718dSIvan Zhechev auto checkDefinedOperatorArgs{ 2008e962718dSIvan Zhechev [&](SourceName opName, const Symbol &specific, const Procedure &proc) { 2009e962718dSIvan Zhechev bool arg0Defined{CheckDefinedOperatorArg(opName, specific, proc, 0)}; 2010e962718dSIvan Zhechev bool arg1Defined{CheckDefinedOperatorArg(opName, specific, proc, 1)}; 2011e962718dSIvan Zhechev return arg0Defined && arg1Defined; 2012e962718dSIvan Zhechev }}; 201364ab3302SCarolineConcatto if (specific.attrs().test(Attr::NOPASS)) { // C774 201464ab3302SCarolineConcatto msg = "%s procedure '%s' may not have NOPASS attribute"_err_en_US; 201564ab3302SCarolineConcatto } else if (!proc.functionResult.has_value()) { 201664ab3302SCarolineConcatto msg = "%s procedure '%s' must be a function"_err_en_US; 201764ab3302SCarolineConcatto } else if (proc.functionResult->IsAssumedLengthCharacter()) { 20189b86a722SPeter Klausler const auto *subpDetails{specific.detailsIf<SubprogramDetails>()}; 20199b86a722SPeter Klausler if (subpDetails && !subpDetails->isDummy() && subpDetails->isInterface()) { 20209b86a722SPeter Klausler // Error is caught by more general test for interfaces with 20219b86a722SPeter Klausler // assumed-length character function results 20229b86a722SPeter Klausler return true; 20239b86a722SPeter Klausler } 202464ab3302SCarolineConcatto msg = "%s function '%s' may not have assumed-length CHARACTER(*)" 202564ab3302SCarolineConcatto " result"_err_en_US; 202664ab3302SCarolineConcatto } else if (auto m{CheckNumberOfArgs(kind, proc.dummyArguments.size())}) { 20270f973ac7SPeter Klausler if (m->IsFatal()) { 20280f973ac7SPeter Klausler msg = *m; 20290f973ac7SPeter Klausler } else { 20300f973ac7SPeter Klausler evaluate::AttachDeclaration( 20310f973ac7SPeter Klausler Warn(common::UsageWarning::DefinedOperatorArgs, specific.name(), 20320f973ac7SPeter Klausler std::move(*m), MakeOpName(opName), specific.name()), 20330f973ac7SPeter Klausler specific); 20340f973ac7SPeter Klausler return true; 20350f973ac7SPeter Klausler } 2036e962718dSIvan Zhechev } else if (!checkDefinedOperatorArgs(opName, specific, proc)) { 203764ab3302SCarolineConcatto return false; // error was reported 203864ab3302SCarolineConcatto } else if (ConflictsWithIntrinsicOperator(kind, proc)) { 203964ab3302SCarolineConcatto msg = "%s function '%s' conflicts with intrinsic operator"_err_en_US; 204064ab3302SCarolineConcatto } 20410f973ac7SPeter Klausler if (msg) { 204282edd428STim Keith SayWithDeclaration( 204382edd428STim Keith specific, std::move(*msg), MakeOpName(opName), specific.name()); 204482edd428STim Keith context_.SetError(specific); 20450f973ac7SPeter Klausler return false; 20462d528fd7SPeter Klausler } 20470f973ac7SPeter Klausler return true; 204864ab3302SCarolineConcatto } 204964ab3302SCarolineConcatto 205064ab3302SCarolineConcatto // If the number of arguments is wrong for this intrinsic operator, return 205164ab3302SCarolineConcatto // false and return the error message in msg. 205264ab3302SCarolineConcatto std::optional<parser::MessageFixedText> CheckHelper::CheckNumberOfArgs( 205364ab3302SCarolineConcatto const GenericKind &kind, std::size_t nargs) { 205482edd428STim Keith if (!kind.IsIntrinsicOperator()) { 20552d528fd7SPeter Klausler if (nargs < 1 || nargs > 2) { 2056505f6da1SPeter Klausler if (context_.ShouldWarn(common::UsageWarning::DefinedOperatorArgs)) { 20572d528fd7SPeter Klausler return "%s function '%s' should have 1 or 2 dummy arguments"_warn_en_US; 20582d528fd7SPeter Klausler } 2059505f6da1SPeter Klausler } 206082edd428STim Keith return std::nullopt; 206182edd428STim Keith } 206264ab3302SCarolineConcatto std::size_t min{2}, max{2}; // allowed number of args; default is binary 2063cd03e96fSPeter Klausler common::visit(common::visitors{ 206464ab3302SCarolineConcatto [&](const common::NumericOperator &x) { 206564ab3302SCarolineConcatto if (x == common::NumericOperator::Add || 206664ab3302SCarolineConcatto x == common::NumericOperator::Subtract) { 206764ab3302SCarolineConcatto min = 1; // + and - are unary or binary 206864ab3302SCarolineConcatto } 206964ab3302SCarolineConcatto }, 207064ab3302SCarolineConcatto [&](const common::LogicalOperator &x) { 207164ab3302SCarolineConcatto if (x == common::LogicalOperator::Not) { 207264ab3302SCarolineConcatto min = 1; // .NOT. is unary 207364ab3302SCarolineConcatto max = 1; 207464ab3302SCarolineConcatto } 207564ab3302SCarolineConcatto }, 207664ab3302SCarolineConcatto [](const common::RelationalOperator &) { 207764ab3302SCarolineConcatto // all are binary 207864ab3302SCarolineConcatto }, 207964ab3302SCarolineConcatto [](const GenericKind::OtherKind &x) { 208064ab3302SCarolineConcatto CHECK(x == GenericKind::OtherKind::Concat); 208164ab3302SCarolineConcatto }, 208264ab3302SCarolineConcatto [](const auto &) { DIE("expected intrinsic operator"); }, 208364ab3302SCarolineConcatto }, 208464ab3302SCarolineConcatto kind.u); 208564ab3302SCarolineConcatto if (nargs >= min && nargs <= max) { 208664ab3302SCarolineConcatto return std::nullopt; 208764ab3302SCarolineConcatto } else if (max == 1) { 208864ab3302SCarolineConcatto return "%s function '%s' must have one dummy argument"_err_en_US; 208964ab3302SCarolineConcatto } else if (min == 2) { 209064ab3302SCarolineConcatto return "%s function '%s' must have two dummy arguments"_err_en_US; 209164ab3302SCarolineConcatto } else { 209264ab3302SCarolineConcatto return "%s function '%s' must have one or two dummy arguments"_err_en_US; 209364ab3302SCarolineConcatto } 209464ab3302SCarolineConcatto } 209564ab3302SCarolineConcatto 209664ab3302SCarolineConcatto bool CheckHelper::CheckDefinedOperatorArg(const SourceName &opName, 209764ab3302SCarolineConcatto const Symbol &symbol, const Procedure &proc, std::size_t pos) { 209864ab3302SCarolineConcatto if (pos >= proc.dummyArguments.size()) { 209964ab3302SCarolineConcatto return true; 210064ab3302SCarolineConcatto } 210164ab3302SCarolineConcatto auto &arg{proc.dummyArguments.at(pos)}; 210264ab3302SCarolineConcatto std::optional<parser::MessageFixedText> msg; 210364ab3302SCarolineConcatto if (arg.IsOptional()) { 21040f973ac7SPeter Klausler msg = 21050f973ac7SPeter Klausler "In %s function '%s', dummy argument '%s' may not be OPTIONAL"_err_en_US; 210664ab3302SCarolineConcatto } else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)}; 210764ab3302SCarolineConcatto dataObject == nullptr) { 21080f973ac7SPeter Klausler msg = 21090f973ac7SPeter Klausler "In %s function '%s', dummy argument '%s' must be a data object"_err_en_US; 21103332dc32SPeter Klausler } else if (dataObject->intent == common::Intent::Out) { 21113332dc32SPeter Klausler msg = 21123332dc32SPeter Klausler "In %s function '%s', dummy argument '%s' may not be INTENT(OUT)"_err_en_US; 211364ab3302SCarolineConcatto } else if (dataObject->intent != common::Intent::In && 211464ab3302SCarolineConcatto !dataObject->attrs.test(DummyDataObject::Attr::Value)) { 21150f973ac7SPeter Klausler evaluate::AttachDeclaration( 21160f973ac7SPeter Klausler Warn(common::UsageWarning::DefinedOperatorArgs, 21170f973ac7SPeter Klausler "In %s function '%s', dummy argument '%s' should have INTENT(IN) or VALUE attribute"_warn_en_US, 21180f973ac7SPeter Klausler parser::ToUpperCaseLetters(opName.ToString()), symbol.name(), 21190f973ac7SPeter Klausler arg.name), 21200f973ac7SPeter Klausler symbol); 21210f973ac7SPeter Klausler return true; 2122505f6da1SPeter Klausler } 212364ab3302SCarolineConcatto if (msg) { 212464ab3302SCarolineConcatto SayWithDeclaration(symbol, std::move(*msg), 21250f973ac7SPeter Klausler parser::ToUpperCaseLetters(opName.ToString()), symbol.name(), arg.name); 212664ab3302SCarolineConcatto return false; 212764ab3302SCarolineConcatto } 212864ab3302SCarolineConcatto return true; 212964ab3302SCarolineConcatto } 213064ab3302SCarolineConcatto 213164ab3302SCarolineConcatto // Check if this procedure can be used for defined assignment (see 15.4.3.4.3). 213264ab3302SCarolineConcatto bool CheckHelper::CheckDefinedAssignment( 213364ab3302SCarolineConcatto const Symbol &specific, const Procedure &proc) { 213482edd428STim Keith if (context_.HasError(specific)) { 213582edd428STim Keith return false; 213682edd428STim Keith } 213764ab3302SCarolineConcatto std::optional<parser::MessageFixedText> msg; 213864ab3302SCarolineConcatto if (specific.attrs().test(Attr::NOPASS)) { // C774 213964ab3302SCarolineConcatto msg = "Defined assignment procedure '%s' may not have" 214064ab3302SCarolineConcatto " NOPASS attribute"_err_en_US; 214164ab3302SCarolineConcatto } else if (!proc.IsSubroutine()) { 214264ab3302SCarolineConcatto msg = "Defined assignment procedure '%s' must be a subroutine"_err_en_US; 214364ab3302SCarolineConcatto } else if (proc.dummyArguments.size() != 2) { 214464ab3302SCarolineConcatto msg = "Defined assignment subroutine '%s' must have" 214564ab3302SCarolineConcatto " two dummy arguments"_err_en_US; 2146fc3f92a8Speter klausler } else { 2147fc3f92a8Speter klausler // Check both arguments even if the first has an error. 2148fc3f92a8Speter klausler bool ok0{CheckDefinedAssignmentArg(specific, proc.dummyArguments[0], 0)}; 2149fc3f92a8Speter klausler bool ok1{CheckDefinedAssignmentArg(specific, proc.dummyArguments[1], 1)}; 2150fc3f92a8Speter klausler if (!(ok0 && ok1)) { 215164ab3302SCarolineConcatto return false; // error was reported 215264ab3302SCarolineConcatto } else if (ConflictsWithIntrinsicAssignment(proc)) { 21530ee71124SPeter Klausler msg = 21540ee71124SPeter Klausler "Defined assignment subroutine '%s' conflicts with intrinsic assignment"_err_en_US; 215564ab3302SCarolineConcatto } else { 215664ab3302SCarolineConcatto return true; // OK 215764ab3302SCarolineConcatto } 2158fc3f92a8Speter klausler } 215964ab3302SCarolineConcatto SayWithDeclaration(specific, std::move(msg.value()), specific.name()); 216082edd428STim Keith context_.SetError(specific); 216164ab3302SCarolineConcatto return false; 216264ab3302SCarolineConcatto } 216364ab3302SCarolineConcatto 216464ab3302SCarolineConcatto bool CheckHelper::CheckDefinedAssignmentArg( 216564ab3302SCarolineConcatto const Symbol &symbol, const DummyArgument &arg, int pos) { 216664ab3302SCarolineConcatto std::optional<parser::MessageFixedText> msg; 216764ab3302SCarolineConcatto if (arg.IsOptional()) { 216864ab3302SCarolineConcatto msg = "In defined assignment subroutine '%s', dummy argument '%s'" 216964ab3302SCarolineConcatto " may not be OPTIONAL"_err_en_US; 217064ab3302SCarolineConcatto } else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)}) { 217164ab3302SCarolineConcatto if (pos == 0) { 21723332dc32SPeter Klausler if (dataObject->intent == common::Intent::In) { 21733332dc32SPeter Klausler msg = "In defined assignment subroutine '%s', first dummy argument '%s'" 21743332dc32SPeter Klausler " may not have INTENT(IN)"_err_en_US; 21753332dc32SPeter Klausler } else if (dataObject->intent != common::Intent::Out && 217664ab3302SCarolineConcatto dataObject->intent != common::Intent::InOut) { 2177505f6da1SPeter Klausler msg = 2178505f6da1SPeter Klausler "In defined assignment subroutine '%s', first dummy argument '%s' should have INTENT(OUT) or INTENT(INOUT)"_warn_en_US; 2179505f6da1SPeter Klausler } 218064ab3302SCarolineConcatto } else if (pos == 1) { 21813332dc32SPeter Klausler if (dataObject->intent == common::Intent::Out) { 21823332dc32SPeter Klausler msg = "In defined assignment subroutine '%s', second dummy" 21833332dc32SPeter Klausler " argument '%s' may not have INTENT(OUT)"_err_en_US; 21843332dc32SPeter Klausler } else if (dataObject->intent != common::Intent::In && 218564ab3302SCarolineConcatto !dataObject->attrs.test(DummyDataObject::Attr::Value)) { 218664ab3302SCarolineConcatto msg = 2187505f6da1SPeter Klausler "In defined assignment subroutine '%s', second dummy argument '%s' should have INTENT(IN) or VALUE attribute"_warn_en_US; 218854912dd2SPeter Klausler } else if (dataObject->attrs.test(DummyDataObject::Attr::Pointer)) { 218954912dd2SPeter Klausler msg = 219054912dd2SPeter Klausler "In defined assignment subroutine '%s', second dummy argument '%s' must not be a pointer"_err_en_US; 219154912dd2SPeter Klausler } else if (dataObject->attrs.test(DummyDataObject::Attr::Allocatable)) { 219254912dd2SPeter Klausler msg = 219354912dd2SPeter Klausler "In defined assignment subroutine '%s', second dummy argument '%s' must not be an allocatable"_err_en_US; 219464ab3302SCarolineConcatto } 219564ab3302SCarolineConcatto } else { 219664ab3302SCarolineConcatto DIE("pos must be 0 or 1"); 219764ab3302SCarolineConcatto } 219864ab3302SCarolineConcatto } else { 219964ab3302SCarolineConcatto msg = "In defined assignment subroutine '%s', dummy argument '%s'" 220064ab3302SCarolineConcatto " must be a data object"_err_en_US; 220164ab3302SCarolineConcatto } 220264ab3302SCarolineConcatto if (msg) { 22030f973ac7SPeter Klausler if (msg->IsFatal()) { 220464ab3302SCarolineConcatto SayWithDeclaration(symbol, std::move(*msg), symbol.name(), arg.name); 220582edd428STim Keith context_.SetError(symbol); 220664ab3302SCarolineConcatto return false; 22070f973ac7SPeter Klausler } else { 22080f973ac7SPeter Klausler evaluate::AttachDeclaration( 22090f973ac7SPeter Klausler Warn(common::UsageWarning::DefinedOperatorArgs, std::move(*msg), 22100f973ac7SPeter Klausler symbol.name(), arg.name), 22110f973ac7SPeter Klausler symbol); 221264ab3302SCarolineConcatto } 22133332dc32SPeter Klausler } 221464ab3302SCarolineConcatto return true; 221564ab3302SCarolineConcatto } 221664ab3302SCarolineConcatto 221764ab3302SCarolineConcatto // Report a conflicting attribute error if symbol has both of these attributes 221864ab3302SCarolineConcatto bool CheckHelper::CheckConflicting(const Symbol &symbol, Attr a1, Attr a2) { 221964ab3302SCarolineConcatto if (symbol.attrs().test(a1) && symbol.attrs().test(a2)) { 222064ab3302SCarolineConcatto messages_.Say("'%s' may not have both the %s and %s attributes"_err_en_US, 2221bc56620bSPeter Steinfeld symbol.name(), AttrToString(a1), AttrToString(a2)); 222264ab3302SCarolineConcatto return true; 222364ab3302SCarolineConcatto } else { 222464ab3302SCarolineConcatto return false; 222564ab3302SCarolineConcatto } 222664ab3302SCarolineConcatto } 222764ab3302SCarolineConcatto 2228c1168676Speter klausler void CheckHelper::WarnMissingFinal(const Symbol &symbol) { 2229c1168676Speter klausler const auto *object{symbol.detailsIf<ObjectEntityDetails>()}; 2230338e3125SPeter Klausler if (!object || object->IsAssumedRank() || 2231e9a8ab00SPeter Klausler (!IsAutomaticallyDestroyed(symbol) && 2232e9a8ab00SPeter Klausler symbol.owner().kind() != Scope::Kind::DerivedType)) { 2233c1168676Speter klausler return; 2234c1168676Speter klausler } 2235c1168676Speter klausler const DeclTypeSpec *type{object->type()}; 2236c1168676Speter klausler const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr}; 2237c1168676Speter klausler const Symbol *derivedSym{derived ? &derived->typeSymbol() : nullptr}; 2238c1168676Speter klausler int rank{object->shape().Rank()}; 2239c1168676Speter klausler const Symbol *initialDerivedSym{derivedSym}; 2240c1168676Speter klausler while (const auto *derivedDetails{ 2241c1168676Speter klausler derivedSym ? derivedSym->detailsIf<DerivedTypeDetails>() : nullptr}) { 2242c1168676Speter klausler if (!derivedDetails->finals().empty() && 22430f973ac7SPeter Klausler !derivedDetails->GetFinalForRank(rank)) { 2244c1168676Speter klausler if (auto *msg{derivedSym == initialDerivedSym 22450f973ac7SPeter Klausler ? Warn(common::UsageWarning::Final, symbol.name(), 2246a53967cdSPeter Klausler "'%s' of derived type '%s' does not have a FINAL subroutine for its rank (%d)"_warn_en_US, 2247c1168676Speter klausler symbol.name(), derivedSym->name(), rank) 22480f973ac7SPeter Klausler : Warn(common::UsageWarning::Final, symbol.name(), 2249a53967cdSPeter Klausler "'%s' of derived type '%s' extended from '%s' does not have a FINAL subroutine for its rank (%d)"_warn_en_US, 2250c1168676Speter klausler symbol.name(), initialDerivedSym->name(), 2251c1168676Speter klausler derivedSym->name(), rank)}) { 2252c1168676Speter klausler msg->Attach(derivedSym->name(), 2253c1168676Speter klausler "Declaration of derived type '%s'"_en_US, derivedSym->name()); 2254c1168676Speter klausler } 2255c1168676Speter klausler return; 2256c1168676Speter klausler } 2257c1168676Speter klausler derived = derivedSym->GetParentTypeSpec(); 2258c1168676Speter klausler derivedSym = derived ? &derived->typeSymbol() : nullptr; 2259c1168676Speter klausler } 2260c1168676Speter klausler } 2261c1168676Speter klausler 226282edd428STim Keith const Procedure *CheckHelper::Characterize(const Symbol &symbol) { 226382edd428STim Keith auto it{characterizeCache_.find(symbol)}; 226482edd428STim Keith if (it == characterizeCache_.end()) { 226582edd428STim Keith auto pair{characterizeCache_.emplace(SymbolRef{symbol}, 2266641ede93Speter klausler Procedure::Characterize(symbol, context_.foldingContext()))}; 226782edd428STim Keith it = pair.first; 226864ab3302SCarolineConcatto } 226982edd428STim Keith return common::GetPtrFromOptional(it->second); 227064ab3302SCarolineConcatto } 227164ab3302SCarolineConcatto 22722de5ea3bSpeter klausler void CheckHelper::CheckVolatile(const Symbol &symbol, 227364ab3302SCarolineConcatto const DerivedTypeSpec *derived) { // C866 - C868 227464ab3302SCarolineConcatto if (IsIntentIn(symbol)) { 227564ab3302SCarolineConcatto messages_.Say( 227664ab3302SCarolineConcatto "VOLATILE attribute may not apply to an INTENT(IN) argument"_err_en_US); 227764ab3302SCarolineConcatto } 227864ab3302SCarolineConcatto if (IsProcedure(symbol)) { 227964ab3302SCarolineConcatto messages_.Say("VOLATILE attribute may apply only to a variable"_err_en_US); 228064ab3302SCarolineConcatto } 22812de5ea3bSpeter klausler if (symbol.has<UseDetails>() || symbol.has<HostAssocDetails>()) { 228264ab3302SCarolineConcatto const Symbol &ultimate{symbol.GetUltimate()}; 22831ee6f7adSPeter Klausler if (evaluate::IsCoarray(ultimate)) { 228464ab3302SCarolineConcatto messages_.Say( 228564ab3302SCarolineConcatto "VOLATILE attribute may not apply to a coarray accessed by USE or host association"_err_en_US); 228664ab3302SCarolineConcatto } 228764ab3302SCarolineConcatto if (derived) { 228864ab3302SCarolineConcatto if (FindCoarrayUltimateComponent(*derived)) { 228964ab3302SCarolineConcatto messages_.Say( 229064ab3302SCarolineConcatto "VOLATILE attribute may not apply to a type with a coarray ultimate component accessed by USE or host association"_err_en_US); 229164ab3302SCarolineConcatto } 229264ab3302SCarolineConcatto } 229364ab3302SCarolineConcatto } 229464ab3302SCarolineConcatto } 229564ab3302SCarolineConcatto 2296a8654b44SPeter Klausler void CheckHelper::CheckContiguous(const Symbol &symbol) { 2297a8654b44SPeter Klausler if (evaluate::IsVariable(symbol) && 2298a8654b44SPeter Klausler ((IsPointer(symbol) && symbol.Rank() > 0) || IsAssumedShape(symbol) || 2299a8654b44SPeter Klausler evaluate::IsAssumedRank(symbol))) { 23000f973ac7SPeter Klausler } else { 23011c91d9bdSPeter Klausler parser::MessageFixedText msg{symbol.owner().IsDerivedType() 23021c91d9bdSPeter Klausler ? "CONTIGUOUS component '%s' should be an array with the POINTER attribute"_port_en_US 23031c91d9bdSPeter Klausler : "CONTIGUOUS entity '%s' should be an array pointer, assumed-shape, or assumed-rank"_port_en_US}; 23041c91d9bdSPeter Klausler if (!context_.IsEnabled(common::LanguageFeature::RedundantContiguous)) { 23051c91d9bdSPeter Klausler msg.set_severity(parser::Severity::Error); 23061c91d9bdSPeter Klausler messages_.Say(std::move(msg), symbol.name()); 23070f973ac7SPeter Klausler } else { 23080f973ac7SPeter Klausler Warn(common::LanguageFeature::RedundantContiguous, std::move(msg), 23090f973ac7SPeter Klausler symbol.name()); 23100f973ac7SPeter Klausler } 2311a8654b44SPeter Klausler } 2312a8654b44SPeter Klausler } 2313a8654b44SPeter Klausler 231464ab3302SCarolineConcatto void CheckHelper::CheckPointer(const Symbol &symbol) { // C852 231564ab3302SCarolineConcatto CheckConflicting(symbol, Attr::POINTER, Attr::TARGET); 231638095549SPete Steinfeld CheckConflicting(symbol, Attr::POINTER, Attr::ALLOCATABLE); // C751 231764ab3302SCarolineConcatto CheckConflicting(symbol, Attr::POINTER, Attr::INTRINSIC); 2318f2897b8fSPeter Steinfeld // Prohibit constant pointers. The standard does not explicitly prohibit 2319f2897b8fSPeter Steinfeld // them, but the PARAMETER attribute requires a entity-decl to have an 2320f2897b8fSPeter Steinfeld // initialization that is a constant-expr, and the only form of 2321f2897b8fSPeter Steinfeld // initialization that allows a constant-expr is the one that's not a "=>" 2322f2897b8fSPeter Steinfeld // pointer initialization. See C811, C807, and section 8.5.13. 2323f2897b8fSPeter Steinfeld CheckConflicting(symbol, Attr::POINTER, Attr::PARAMETER); 232464ab3302SCarolineConcatto if (symbol.Corank() > 0) { 232564ab3302SCarolineConcatto messages_.Say( 232664ab3302SCarolineConcatto "'%s' may not have the POINTER attribute because it is a coarray"_err_en_US, 232764ab3302SCarolineConcatto symbol.name()); 232864ab3302SCarolineConcatto } 232964ab3302SCarolineConcatto } 233064ab3302SCarolineConcatto 233164ab3302SCarolineConcatto // C760 constraints on the passed-object dummy argument 233238095549SPete Steinfeld // C757 constraints on procedure pointer components 233364ab3302SCarolineConcatto void CheckHelper::CheckPassArg( 233446c49e66SPeter Klausler const Symbol &proc, const Symbol *interface0, const WithPassArg &details) { 233564ab3302SCarolineConcatto if (proc.attrs().test(Attr::NOPASS)) { 233664ab3302SCarolineConcatto return; 233764ab3302SCarolineConcatto } 233864ab3302SCarolineConcatto const auto &name{proc.name()}; 23390d588347SPeter Klausler const Symbol *interface { 23400d588347SPeter Klausler interface0 ? FindInterface(*interface0) : nullptr 23410d588347SPeter Klausler }; 234264ab3302SCarolineConcatto if (!interface) { 234364ab3302SCarolineConcatto messages_.Say(name, 234464ab3302SCarolineConcatto "Procedure component '%s' must have NOPASS attribute or explicit interface"_err_en_US, 234564ab3302SCarolineConcatto name); 234664ab3302SCarolineConcatto return; 234764ab3302SCarolineConcatto } 234864ab3302SCarolineConcatto const auto *subprogram{interface->detailsIf<SubprogramDetails>()}; 234964ab3302SCarolineConcatto if (!subprogram) { 235064ab3302SCarolineConcatto messages_.Say(name, 235164ab3302SCarolineConcatto "Procedure component '%s' has invalid interface '%s'"_err_en_US, name, 235264ab3302SCarolineConcatto interface->name()); 235364ab3302SCarolineConcatto return; 235464ab3302SCarolineConcatto } 235564ab3302SCarolineConcatto std::optional<SourceName> passName{details.passName()}; 235664ab3302SCarolineConcatto const auto &dummyArgs{subprogram->dummyArgs()}; 235764ab3302SCarolineConcatto if (!passName) { 235864ab3302SCarolineConcatto if (dummyArgs.empty()) { 235964ab3302SCarolineConcatto messages_.Say(name, 236064ab3302SCarolineConcatto proc.has<ProcEntityDetails>() 236164ab3302SCarolineConcatto ? "Procedure component '%s' with no dummy arguments" 236264ab3302SCarolineConcatto " must have NOPASS attribute"_err_en_US 236364ab3302SCarolineConcatto : "Procedure binding '%s' with no dummy arguments" 236464ab3302SCarolineConcatto " must have NOPASS attribute"_err_en_US, 236564ab3302SCarolineConcatto name); 236640e26180SPeter Steinfeld context_.SetError(*interface); 236764ab3302SCarolineConcatto return; 236864ab3302SCarolineConcatto } 2369868187dfSPeter Steinfeld Symbol *argSym{dummyArgs[0]}; 2370868187dfSPeter Steinfeld if (!argSym) { 2371868187dfSPeter Steinfeld messages_.Say(interface->name(), 2372868187dfSPeter Steinfeld "Cannot use an alternate return as the passed-object dummy " 2373868187dfSPeter Steinfeld "argument"_err_en_US); 2374868187dfSPeter Steinfeld return; 2375868187dfSPeter Steinfeld } 237664ab3302SCarolineConcatto passName = dummyArgs[0]->name(); 237764ab3302SCarolineConcatto } 237864ab3302SCarolineConcatto std::optional<int> passArgIndex{}; 237964ab3302SCarolineConcatto for (std::size_t i{0}; i < dummyArgs.size(); ++i) { 238064ab3302SCarolineConcatto if (dummyArgs[i] && dummyArgs[i]->name() == *passName) { 238164ab3302SCarolineConcatto passArgIndex = i; 238264ab3302SCarolineConcatto break; 238364ab3302SCarolineConcatto } 238464ab3302SCarolineConcatto } 238538095549SPete Steinfeld if (!passArgIndex) { // C758 238664ab3302SCarolineConcatto messages_.Say(*passName, 238764ab3302SCarolineConcatto "'%s' is not a dummy argument of procedure interface '%s'"_err_en_US, 238864ab3302SCarolineConcatto *passName, interface->name()); 238964ab3302SCarolineConcatto return; 239064ab3302SCarolineConcatto } 239164ab3302SCarolineConcatto const Symbol &passArg{*dummyArgs[*passArgIndex]}; 239264ab3302SCarolineConcatto std::optional<parser::MessageFixedText> msg; 239364ab3302SCarolineConcatto if (!passArg.has<ObjectEntityDetails>()) { 239464ab3302SCarolineConcatto msg = "Passed-object dummy argument '%s' of procedure '%s'" 239564ab3302SCarolineConcatto " must be a data object"_err_en_US; 239664ab3302SCarolineConcatto } else if (passArg.attrs().test(Attr::POINTER)) { 239764ab3302SCarolineConcatto msg = "Passed-object dummy argument '%s' of procedure '%s'" 239864ab3302SCarolineConcatto " may not have the POINTER attribute"_err_en_US; 239964ab3302SCarolineConcatto } else if (passArg.attrs().test(Attr::ALLOCATABLE)) { 240064ab3302SCarolineConcatto msg = "Passed-object dummy argument '%s' of procedure '%s'" 240164ab3302SCarolineConcatto " may not have the ALLOCATABLE attribute"_err_en_US; 240264ab3302SCarolineConcatto } else if (passArg.attrs().test(Attr::VALUE)) { 240364ab3302SCarolineConcatto msg = "Passed-object dummy argument '%s' of procedure '%s'" 240464ab3302SCarolineConcatto " may not have the VALUE attribute"_err_en_US; 240564ab3302SCarolineConcatto } else if (passArg.Rank() > 0) { 240664ab3302SCarolineConcatto msg = "Passed-object dummy argument '%s' of procedure '%s'" 240764ab3302SCarolineConcatto " must be scalar"_err_en_US; 240864ab3302SCarolineConcatto } 240964ab3302SCarolineConcatto if (msg) { 241064ab3302SCarolineConcatto messages_.Say(name, std::move(*msg), passName.value(), name); 241164ab3302SCarolineConcatto return; 241264ab3302SCarolineConcatto } 241364ab3302SCarolineConcatto const DeclTypeSpec *type{passArg.GetType()}; 241464ab3302SCarolineConcatto if (!type) { 241564ab3302SCarolineConcatto return; // an error already occurred 241664ab3302SCarolineConcatto } 241764ab3302SCarolineConcatto const Symbol &typeSymbol{*proc.owner().GetSymbol()}; 241864ab3302SCarolineConcatto const DerivedTypeSpec *derived{type->AsDerived()}; 241964ab3302SCarolineConcatto if (!derived || derived->typeSymbol() != typeSymbol) { 242064ab3302SCarolineConcatto messages_.Say(name, 242164ab3302SCarolineConcatto "Passed-object dummy argument '%s' of procedure '%s'" 242264ab3302SCarolineConcatto " must be of type '%s' but is '%s'"_err_en_US, 242364ab3302SCarolineConcatto passName.value(), name, typeSymbol.name(), type->AsFortran()); 242464ab3302SCarolineConcatto return; 242564ab3302SCarolineConcatto } 242664ab3302SCarolineConcatto if (IsExtensibleType(derived) != type->IsPolymorphic()) { 242764ab3302SCarolineConcatto messages_.Say(name, 242864ab3302SCarolineConcatto type->IsPolymorphic() 242964ab3302SCarolineConcatto ? "Passed-object dummy argument '%s' of procedure '%s'" 243064ab3302SCarolineConcatto " may not be polymorphic because '%s' is not extensible"_err_en_US 243164ab3302SCarolineConcatto : "Passed-object dummy argument '%s' of procedure '%s'" 243264ab3302SCarolineConcatto " must be polymorphic because '%s' is extensible"_err_en_US, 243364ab3302SCarolineConcatto passName.value(), name, typeSymbol.name()); 243464ab3302SCarolineConcatto return; 243564ab3302SCarolineConcatto } 243664ab3302SCarolineConcatto for (const auto &[paramName, paramValue] : derived->parameters()) { 243764ab3302SCarolineConcatto if (paramValue.isLen() && !paramValue.isAssumed()) { 243864ab3302SCarolineConcatto messages_.Say(name, 243964ab3302SCarolineConcatto "Passed-object dummy argument '%s' of procedure '%s'" 244064ab3302SCarolineConcatto " has non-assumed length parameter '%s'"_err_en_US, 244164ab3302SCarolineConcatto passName.value(), name, paramName); 244264ab3302SCarolineConcatto } 244364ab3302SCarolineConcatto } 244464ab3302SCarolineConcatto } 244564ab3302SCarolineConcatto 244664ab3302SCarolineConcatto void CheckHelper::CheckProcBinding( 244764ab3302SCarolineConcatto const Symbol &symbol, const ProcBindingDetails &binding) { 244864ab3302SCarolineConcatto const Scope &dtScope{symbol.owner()}; 244964ab3302SCarolineConcatto CHECK(dtScope.kind() == Scope::Kind::DerivedType); 245064ab3302SCarolineConcatto if (symbol.attrs().test(Attr::DEFERRED)) { 245173c3530fSpeter klausler if (const Symbol *dtSymbol{dtScope.symbol()}) { 24522b790490SPete Steinfeld if (!dtSymbol->attrs().test(Attr::ABSTRACT)) { // C733 245364ab3302SCarolineConcatto SayWithDeclaration(*dtSymbol, 245464ab3302SCarolineConcatto "Procedure bound to non-ABSTRACT derived type '%s' may not be DEFERRED"_err_en_US, 245564ab3302SCarolineConcatto dtSymbol->name()); 245664ab3302SCarolineConcatto } 245773c3530fSpeter klausler } 245864ab3302SCarolineConcatto if (symbol.attrs().test(Attr::NON_OVERRIDABLE)) { 245964ab3302SCarolineConcatto messages_.Say( 246064ab3302SCarolineConcatto "Type-bound procedure '%s' may not be both DEFERRED and NON_OVERRIDABLE"_err_en_US, 246164ab3302SCarolineConcatto symbol.name()); 246264ab3302SCarolineConcatto } 246364ab3302SCarolineConcatto } 246473c3530fSpeter klausler if (binding.symbol().attrs().test(Attr::INTRINSIC) && 246573c3530fSpeter klausler !context_.intrinsics().IsSpecificIntrinsicFunction( 246673c3530fSpeter klausler binding.symbol().name().ToString())) { 246773c3530fSpeter klausler messages_.Say( 246873c3530fSpeter klausler "Intrinsic procedure '%s' is not a specific intrinsic permitted for use in the definition of binding '%s'"_err_en_US, 246973c3530fSpeter klausler binding.symbol().name(), symbol.name()); 247064ab3302SCarolineConcatto } 2471f4fc959cSPeter Klausler bool isInaccessibleDeferred{false}; 2472f4fc959cSPeter Klausler if (const Symbol * 2473f4fc959cSPeter Klausler overridden{FindOverriddenBinding(symbol, isInaccessibleDeferred)}) { 2474f4fc959cSPeter Klausler if (isInaccessibleDeferred) { 2475f4fc959cSPeter Klausler SayWithDeclaration(*overridden, 2476f4fc959cSPeter Klausler "Override of PRIVATE DEFERRED '%s' must appear in its module"_err_en_US, 2477f4fc959cSPeter Klausler symbol.name()); 2478f4fc959cSPeter Klausler } 247964ab3302SCarolineConcatto if (overridden->attrs().test(Attr::NON_OVERRIDABLE)) { 248064ab3302SCarolineConcatto SayWithDeclaration(*overridden, 248164ab3302SCarolineConcatto "Override of NON_OVERRIDABLE '%s' is not permitted"_err_en_US, 248264ab3302SCarolineConcatto symbol.name()); 248364ab3302SCarolineConcatto } 248464ab3302SCarolineConcatto if (const auto *overriddenBinding{ 248564ab3302SCarolineConcatto overridden->detailsIf<ProcBindingDetails>()}) { 248664ab3302SCarolineConcatto if (!IsPureProcedure(symbol) && IsPureProcedure(*overridden)) { 248764ab3302SCarolineConcatto SayWithDeclaration(*overridden, 248864ab3302SCarolineConcatto "An overridden pure type-bound procedure binding must also be pure"_err_en_US); 248964ab3302SCarolineConcatto return; 249064ab3302SCarolineConcatto } 24916052025bSPeter Klausler if (!IsElementalProcedure(binding.symbol()) && 24925e8094baSPeter Klausler IsElementalProcedure(*overridden)) { 249364ab3302SCarolineConcatto SayWithDeclaration(*overridden, 249464ab3302SCarolineConcatto "A type-bound procedure and its override must both, or neither, be ELEMENTAL"_err_en_US); 249564ab3302SCarolineConcatto return; 249664ab3302SCarolineConcatto } 249764ab3302SCarolineConcatto bool isNopass{symbol.attrs().test(Attr::NOPASS)}; 249864ab3302SCarolineConcatto if (isNopass != overridden->attrs().test(Attr::NOPASS)) { 249964ab3302SCarolineConcatto SayWithDeclaration(*overridden, 250064ab3302SCarolineConcatto isNopass 250164ab3302SCarolineConcatto ? "A NOPASS type-bound procedure may not override a passed-argument procedure"_err_en_US 250264ab3302SCarolineConcatto : "A passed-argument type-bound procedure may not override a NOPASS procedure"_err_en_US); 250364ab3302SCarolineConcatto } else { 2504b0bdc7fcSPeter Klausler const auto *bindingChars{Characterize(symbol)}; 25055e8094baSPeter Klausler const auto *overriddenChars{Characterize(*overridden)}; 250664ab3302SCarolineConcatto if (bindingChars && overriddenChars) { 250764ab3302SCarolineConcatto if (isNopass) { 250864ab3302SCarolineConcatto if (!bindingChars->CanOverride(*overriddenChars, std::nullopt)) { 250964ab3302SCarolineConcatto SayWithDeclaration(*overridden, 2510a3c6a7d5SPeter Klausler "A NOPASS type-bound procedure and its override must have identical interfaces"_err_en_US); 251164ab3302SCarolineConcatto } 251240e26180SPeter Steinfeld } else if (!context_.HasError(binding.symbol())) { 25136d2b23c4SPeter Klausler auto passIndex{bindingChars->FindPassIndex(binding.passName())}; 25146d2b23c4SPeter Klausler auto overriddenPassIndex{ 251564ab3302SCarolineConcatto overriddenChars->FindPassIndex(overriddenBinding->passName())}; 25166d2b23c4SPeter Klausler if (passIndex && overriddenPassIndex) { 25176d2b23c4SPeter Klausler if (*passIndex != *overriddenPassIndex) { 251864ab3302SCarolineConcatto SayWithDeclaration(*overridden, 251964ab3302SCarolineConcatto "A type-bound procedure and its override must use the same PASS argument"_err_en_US); 252064ab3302SCarolineConcatto } else if (!bindingChars->CanOverride( 252164ab3302SCarolineConcatto *overriddenChars, passIndex)) { 252264ab3302SCarolineConcatto SayWithDeclaration(*overridden, 2523a3c6a7d5SPeter Klausler "A type-bound procedure and its override must have compatible interfaces"_err_en_US); 252464ab3302SCarolineConcatto } 252564ab3302SCarolineConcatto } 252664ab3302SCarolineConcatto } 252764ab3302SCarolineConcatto } 25286d2b23c4SPeter Klausler } 252970201802SPeter Klausler if (symbol.attrs().test(Attr::PRIVATE)) { 253070201802SPeter Klausler if (FindModuleContaining(dtScope) == 253170201802SPeter Klausler FindModuleContaining(overridden->owner())) { 253270201802SPeter Klausler // types declared in same madule 25332e0873c7SPeter Klausler if (!overridden->attrs().test(Attr::PRIVATE)) { 253464ab3302SCarolineConcatto SayWithDeclaration(*overridden, 253564ab3302SCarolineConcatto "A PRIVATE procedure may not override a PUBLIC procedure"_err_en_US); 253664ab3302SCarolineConcatto } 253770201802SPeter Klausler } else { // types declared in distinct madules 253870201802SPeter Klausler if (!CheckAccessibleSymbol(dtScope.parent(), *overridden)) { 253970201802SPeter Klausler SayWithDeclaration(*overridden, 254070201802SPeter Klausler "A PRIVATE procedure may not override an accessible procedure"_err_en_US); 254170201802SPeter Klausler } 254270201802SPeter Klausler } 254370201802SPeter Klausler } 254464ab3302SCarolineConcatto } else { 254564ab3302SCarolineConcatto SayWithDeclaration(*overridden, 254664ab3302SCarolineConcatto "A type-bound procedure binding may not have the same name as a parent component"_err_en_US); 254764ab3302SCarolineConcatto } 254864ab3302SCarolineConcatto } 254964ab3302SCarolineConcatto CheckPassArg(symbol, &binding.symbol(), binding); 255064ab3302SCarolineConcatto } 255164ab3302SCarolineConcatto 255264ab3302SCarolineConcatto void CheckHelper::Check(const Scope &scope) { 255364ab3302SCarolineConcatto scope_ = &scope; 25542aa43358SMichael Kruse common::Restorer<const Symbol *> restorer{innermostSymbol_, innermostSymbol_}; 255564ab3302SCarolineConcatto if (const Symbol *symbol{scope.symbol()}) { 255664ab3302SCarolineConcatto innermostSymbol_ = symbol; 255764ab3302SCarolineConcatto } 2558641ede93Speter klausler if (scope.IsParameterizedDerivedTypeInstantiation()) { 2559641ede93Speter klausler auto restorer{common::ScopedSet(scopeIsUninstantiatedPDT_, false)}; 2560641ede93Speter klausler auto restorer2{context_.foldingContext().messages().SetContext( 2561641ede93Speter klausler scope.instantiationContext().get())}; 2562641ede93Speter klausler for (const auto &pair : scope) { 2563641ede93Speter klausler CheckPointerInitialization(*pair.second); 2564641ede93Speter klausler } 2565641ede93Speter klausler } else { 2566641ede93Speter klausler auto restorer{common::ScopedSet( 2567641ede93Speter klausler scopeIsUninstantiatedPDT_, scope.IsParameterizedDerivedType())}; 256864ab3302SCarolineConcatto for (const auto &set : scope.equivalenceSets()) { 256964ab3302SCarolineConcatto CheckEquivalenceSet(set); 257064ab3302SCarolineConcatto } 257164ab3302SCarolineConcatto for (const auto &pair : scope) { 257264ab3302SCarolineConcatto Check(*pair.second); 257364ab3302SCarolineConcatto } 2574a9b3f200SPeter Klausler if (scope.IsSubmodule() && scope.symbol()) { 2575a9b3f200SPeter Klausler // Submodule names are not in their parent's scopes 2576a9b3f200SPeter Klausler Check(*scope.symbol()); 2577a9b3f200SPeter Klausler } 2578dafd3cf8SPeixin-Qiao for (const auto &pair : scope.commonBlocks()) { 2579dafd3cf8SPeixin-Qiao CheckCommonBlock(*pair.second); 2580dafd3cf8SPeixin-Qiao } 2581c207e360SPeixin-Qiao int mainProgCnt{0}; 258264ab3302SCarolineConcatto for (const Scope &child : scope.children()) { 258364ab3302SCarolineConcatto Check(child); 2584c207e360SPeixin-Qiao // A program shall consist of exactly one main program (5.2.2). 2585c207e360SPeixin-Qiao if (child.kind() == Scope::Kind::MainProgram) { 2586c207e360SPeixin-Qiao ++mainProgCnt; 2587c207e360SPeixin-Qiao if (mainProgCnt > 1) { 2588c207e360SPeixin-Qiao messages_.Say(child.sourceRange(), 2589c207e360SPeixin-Qiao "A source file cannot contain more than one main program"_err_en_US); 2590c207e360SPeixin-Qiao } 2591c207e360SPeixin-Qiao } 259264ab3302SCarolineConcatto } 259364ab3302SCarolineConcatto if (scope.kind() == Scope::Kind::BlockData) { 259464ab3302SCarolineConcatto CheckBlockData(scope); 259564ab3302SCarolineConcatto } 2596dc0d56feSPeter Klausler if (auto name{scope.GetName()}) { 2597dc0d56feSPeter Klausler auto iter{scope.find(*name)}; 2598dc0d56feSPeter Klausler if (iter != scope.end()) { 2599dc0d56feSPeter Klausler const char *kind{nullptr}; 2600dc0d56feSPeter Klausler switch (scope.kind()) { 2601dc0d56feSPeter Klausler case Scope::Kind::Module: 2602dc0d56feSPeter Klausler kind = scope.symbol()->get<ModuleDetails>().isSubmodule() 2603dc0d56feSPeter Klausler ? "submodule" 2604dc0d56feSPeter Klausler : "module"; 2605dc0d56feSPeter Klausler break; 2606dc0d56feSPeter Klausler case Scope::Kind::MainProgram: 2607dc0d56feSPeter Klausler kind = "main program"; 2608dc0d56feSPeter Klausler break; 2609dc0d56feSPeter Klausler case Scope::Kind::BlockData: 2610dc0d56feSPeter Klausler kind = "BLOCK DATA subprogram"; 2611dc0d56feSPeter Klausler break; 2612dc0d56feSPeter Klausler default:; 2613dc0d56feSPeter Klausler } 2614dc0d56feSPeter Klausler if (kind) { 26150f973ac7SPeter Klausler Warn(common::LanguageFeature::BenignNameClash, iter->second->name(), 2616dc0d56feSPeter Klausler "Name '%s' declared in a %s should not have the same name as the %s"_port_en_US, 2617dc0d56feSPeter Klausler *name, kind, kind); 2618dc0d56feSPeter Klausler } 2619dc0d56feSPeter Klausler } 2620dc0d56feSPeter Klausler } 262182edd428STim Keith CheckGenericOps(scope); 262264ab3302SCarolineConcatto } 2623641ede93Speter klausler } 262464ab3302SCarolineConcatto 262564ab3302SCarolineConcatto void CheckHelper::CheckEquivalenceSet(const EquivalenceSet &set) { 262664ab3302SCarolineConcatto auto iter{ 262764ab3302SCarolineConcatto std::find_if(set.begin(), set.end(), [](const EquivalenceObject &object) { 262864ab3302SCarolineConcatto return FindCommonBlockContaining(object.symbol) != nullptr; 262964ab3302SCarolineConcatto })}; 263064ab3302SCarolineConcatto if (iter != set.end()) { 263164ab3302SCarolineConcatto const Symbol &commonBlock{DEREF(FindCommonBlockContaining(iter->symbol))}; 263264ab3302SCarolineConcatto for (auto &object : set) { 263364ab3302SCarolineConcatto if (&object != &*iter) { 263464ab3302SCarolineConcatto if (auto *details{object.symbol.detailsIf<ObjectEntityDetails>()}) { 263564ab3302SCarolineConcatto if (details->commonBlock()) { 263664ab3302SCarolineConcatto if (details->commonBlock() != &commonBlock) { // 8.10.3 paragraph 1 263764ab3302SCarolineConcatto if (auto *msg{messages_.Say(object.symbol.name(), 263864ab3302SCarolineConcatto "Two objects in the same EQUIVALENCE set may not be members of distinct COMMON blocks"_err_en_US)}) { 263964ab3302SCarolineConcatto msg->Attach(iter->symbol.name(), 264064ab3302SCarolineConcatto "Other object in EQUIVALENCE set"_en_US) 264164ab3302SCarolineConcatto .Attach(details->commonBlock()->name(), 264264ab3302SCarolineConcatto "COMMON block containing '%s'"_en_US, 264364ab3302SCarolineConcatto object.symbol.name()) 264464ab3302SCarolineConcatto .Attach(commonBlock.name(), 264564ab3302SCarolineConcatto "COMMON block containing '%s'"_en_US, 264664ab3302SCarolineConcatto iter->symbol.name()); 264764ab3302SCarolineConcatto } 264864ab3302SCarolineConcatto } 264964ab3302SCarolineConcatto } else { 265064ab3302SCarolineConcatto // Mark all symbols in the equivalence set with the same COMMON 265164ab3302SCarolineConcatto // block to prevent spurious error messages about initialization 265264ab3302SCarolineConcatto // in BLOCK DATA outside COMMON 265364ab3302SCarolineConcatto details->set_commonBlock(commonBlock); 265464ab3302SCarolineConcatto } 265564ab3302SCarolineConcatto } 265664ab3302SCarolineConcatto } 265764ab3302SCarolineConcatto } 265864ab3302SCarolineConcatto } 2659b788d628SPeter Klausler for (const EquivalenceObject &object : set) { 2660d742c2aaSPeter Klausler CheckEquivalenceObject(object); 2661d742c2aaSPeter Klausler } 2662d742c2aaSPeter Klausler } 2663d742c2aaSPeter Klausler 2664d742c2aaSPeter Klausler static bool InCommonWithBind(const Symbol &symbol) { 2665d742c2aaSPeter Klausler if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) { 2666d742c2aaSPeter Klausler const Symbol *commonBlock{details->commonBlock()}; 2667d742c2aaSPeter Klausler return commonBlock && commonBlock->attrs().test(Attr::BIND_C); 2668d742c2aaSPeter Klausler } else { 2669d742c2aaSPeter Klausler return false; 2670d742c2aaSPeter Klausler } 2671d742c2aaSPeter Klausler } 2672d742c2aaSPeter Klausler 2673d742c2aaSPeter Klausler void CheckHelper::CheckEquivalenceObject(const EquivalenceObject &object) { 2674d742c2aaSPeter Klausler parser::MessageFixedText msg; 2675d742c2aaSPeter Klausler const Symbol &symbol{object.symbol}; 2676d742c2aaSPeter Klausler if (symbol.owner().IsDerivedType()) { 2677d742c2aaSPeter Klausler msg = 2678d742c2aaSPeter Klausler "Derived type component '%s' is not allowed in an equivalence set"_err_en_US; 2679d742c2aaSPeter Klausler } else if (IsDummy(symbol)) { 2680d742c2aaSPeter Klausler msg = "Dummy argument '%s' is not allowed in an equivalence set"_err_en_US; 2681d742c2aaSPeter Klausler } else if (symbol.IsFuncResult()) { 2682d742c2aaSPeter Klausler msg = "Function result '%s' is not allow in an equivalence set"_err_en_US; 2683d742c2aaSPeter Klausler } else if (IsPointer(symbol)) { 2684d742c2aaSPeter Klausler msg = "Pointer '%s' is not allowed in an equivalence set"_err_en_US; 2685d742c2aaSPeter Klausler } else if (IsAllocatable(symbol)) { 2686d742c2aaSPeter Klausler msg = 2687d742c2aaSPeter Klausler "Allocatable variable '%s' is not allowed in an equivalence set"_err_en_US; 2688d742c2aaSPeter Klausler } else if (symbol.Corank() > 0) { 2689d742c2aaSPeter Klausler msg = "Coarray '%s' is not allowed in an equivalence set"_err_en_US; 2690d742c2aaSPeter Klausler } else if (symbol.has<UseDetails>()) { 2691d742c2aaSPeter Klausler msg = 2692d742c2aaSPeter Klausler "Use-associated variable '%s' is not allowed in an equivalence set"_err_en_US; 2693d742c2aaSPeter Klausler } else if (symbol.attrs().test(Attr::BIND_C)) { 2694d742c2aaSPeter Klausler msg = 2695d742c2aaSPeter Klausler "Variable '%s' with BIND attribute is not allowed in an equivalence set"_err_en_US; 2696d742c2aaSPeter Klausler } else if (symbol.attrs().test(Attr::TARGET)) { 2697d742c2aaSPeter Klausler msg = 2698d742c2aaSPeter Klausler "Variable '%s' with TARGET attribute is not allowed in an equivalence set"_err_en_US; 2699d742c2aaSPeter Klausler } else if (IsNamedConstant(symbol)) { 2700d742c2aaSPeter Klausler msg = "Named constant '%s' is not allowed in an equivalence set"_err_en_US; 2701d742c2aaSPeter Klausler } else if (InCommonWithBind(symbol)) { 2702d742c2aaSPeter Klausler msg = 2703d742c2aaSPeter Klausler "Variable '%s' in common block with BIND attribute is not allowed in an equivalence set"_err_en_US; 2704d742c2aaSPeter Klausler } else if (!symbol.has<ObjectEntityDetails>()) { 2705d742c2aaSPeter Klausler msg = "'%s' in equivalence set is not a data object"_err_en_US; 2706d742c2aaSPeter Klausler } else if (const auto *type{symbol.GetType()}) { 2707d742c2aaSPeter Klausler const auto *derived{type->AsDerived()}; 2708d742c2aaSPeter Klausler if (derived && !derived->IsVectorType()) { 2709d742c2aaSPeter Klausler if (const auto *comp{ 2710d742c2aaSPeter Klausler FindUltimateComponent(*derived, IsAllocatableOrPointer)}) { 2711d742c2aaSPeter Klausler msg = IsPointer(*comp) 2712d742c2aaSPeter Klausler ? "Derived type object '%s' with pointer ultimate component is not allowed in an equivalence set"_err_en_US 2713d742c2aaSPeter Klausler : "Derived type object '%s' with allocatable ultimate component is not allowed in an equivalence set"_err_en_US; 2714d742c2aaSPeter Klausler } else if (!derived->typeSymbol().get<DerivedTypeDetails>().sequence()) { 2715d742c2aaSPeter Klausler msg = 2716d742c2aaSPeter Klausler "Nonsequence derived type object '%s' is not allowed in an equivalence set"_err_en_US; 2717d742c2aaSPeter Klausler } 2718d742c2aaSPeter Klausler } else if (IsAutomatic(symbol)) { 2719d742c2aaSPeter Klausler msg = 2720d742c2aaSPeter Klausler "Automatic object '%s' is not allowed in an equivalence set"_err_en_US; 2721d742c2aaSPeter Klausler } else if (symbol.test(Symbol::Flag::CrayPointee)) { 2722b788d628SPeter Klausler messages_.Say(object.symbol.name(), 2723b788d628SPeter Klausler "Cray pointee '%s' may not be a member of an EQUIVALENCE group"_err_en_US, 2724b788d628SPeter Klausler object.symbol.name()); 2725b788d628SPeter Klausler } 2726b788d628SPeter Klausler } 2727d742c2aaSPeter Klausler if (!msg.text().empty()) { 2728d742c2aaSPeter Klausler context_.Say(object.source, std::move(msg), symbol.name()); 2729d742c2aaSPeter Klausler } 273064ab3302SCarolineConcatto } 273164ab3302SCarolineConcatto 273264ab3302SCarolineConcatto void CheckHelper::CheckBlockData(const Scope &scope) { 273364ab3302SCarolineConcatto // BLOCK DATA subprograms should contain only named common blocks. 273464ab3302SCarolineConcatto // C1415 presents a list of statements that shouldn't appear in 273564ab3302SCarolineConcatto // BLOCK DATA, but so long as the subprogram contains no executable 273664ab3302SCarolineConcatto // code and allocates no storage outside named COMMON, we're happy 273764ab3302SCarolineConcatto // (e.g., an ENUM is strictly not allowed). 273864ab3302SCarolineConcatto for (const auto &pair : scope) { 273964ab3302SCarolineConcatto const Symbol &symbol{*pair.second}; 274064ab3302SCarolineConcatto if (!(symbol.has<CommonBlockDetails>() || symbol.has<UseDetails>() || 274164ab3302SCarolineConcatto symbol.has<UseErrorDetails>() || symbol.has<DerivedTypeDetails>() || 274264ab3302SCarolineConcatto symbol.has<SubprogramDetails>() || 274364ab3302SCarolineConcatto symbol.has<ObjectEntityDetails>() || 274464ab3302SCarolineConcatto (symbol.has<ProcEntityDetails>() && 274564ab3302SCarolineConcatto !symbol.attrs().test(Attr::POINTER)))) { 274664ab3302SCarolineConcatto messages_.Say(symbol.name(), 274764ab3302SCarolineConcatto "'%s' may not appear in a BLOCK DATA subprogram"_err_en_US, 274864ab3302SCarolineConcatto symbol.name()); 274964ab3302SCarolineConcatto } 275064ab3302SCarolineConcatto } 275164ab3302SCarolineConcatto } 275264ab3302SCarolineConcatto 275382edd428STim Keith // Check distinguishability of generic assignment and operators. 275482edd428STim Keith // For these, generics and generic bindings must be considered together. 275582edd428STim Keith void CheckHelper::CheckGenericOps(const Scope &scope) { 275682edd428STim Keith DistinguishabilityHelper helper{context_}; 275782edd428STim Keith auto addSpecifics{[&](const Symbol &generic) { 2758ecf264d3SPeter Klausler if (!IsAccessible(generic, scope)) { 2759ecf264d3SPeter Klausler return; 2760ecf264d3SPeter Klausler } 276182edd428STim Keith const auto *details{generic.GetUltimate().detailsIf<GenericDetails>()}; 276282edd428STim Keith if (!details) { 27637f680b26SPeter Klausler // Not a generic; ensure characteristics are defined if a function. 27647f680b26SPeter Klausler auto restorer{messages_.SetLocation(generic.name())}; 27657f680b26SPeter Klausler if (IsFunction(generic) && !context_.HasError(generic)) { 27667f680b26SPeter Klausler if (const Symbol *result{FindFunctionResult(generic)}; 27677f680b26SPeter Klausler result && !context_.HasError(*result)) { 2768488b9fd1SDaniil Dudkin Characterize(generic); 2769488b9fd1SDaniil Dudkin } 27707f680b26SPeter Klausler } 277182edd428STim Keith return; 277282edd428STim Keith } 277382edd428STim Keith GenericKind kind{details->kind()}; 277482edd428STim Keith if (!kind.IsAssignment() && !kind.IsOperator()) { 277582edd428STim Keith return; 277682edd428STim Keith } 277782edd428STim Keith const SymbolVector &specifics{details->specificProcs()}; 277882edd428STim Keith const std::vector<SourceName> &bindingNames{details->bindingNames()}; 277982edd428STim Keith for (std::size_t i{0}; i < specifics.size(); ++i) { 278082edd428STim Keith const Symbol &specific{*specifics[i]}; 278182edd428STim Keith auto restorer{messages_.SetLocation(bindingNames[i])}; 27827f680b26SPeter Klausler if (const Procedure *proc{Characterize(specific)}) { 278382edd428STim Keith if (kind.IsAssignment()) { 278482edd428STim Keith if (!CheckDefinedAssignment(specific, *proc)) { 278582edd428STim Keith continue; 278682edd428STim Keith } 278782edd428STim Keith } else { 278882edd428STim Keith if (!CheckDefinedOperator(generic.name(), kind, specific, *proc)) { 278982edd428STim Keith continue; 279082edd428STim Keith } 279182edd428STim Keith } 279282edd428STim Keith helper.Add(generic, kind, specific, *proc); 279382edd428STim Keith } 279482edd428STim Keith } 279582edd428STim Keith }}; 279682edd428STim Keith for (const auto &pair : scope) { 279782edd428STim Keith const Symbol &symbol{*pair.second}; 279882edd428STim Keith addSpecifics(symbol); 279982edd428STim Keith const Symbol &ultimate{symbol.GetUltimate()}; 280082edd428STim Keith if (ultimate.has<DerivedTypeDetails>()) { 280182edd428STim Keith if (const Scope *typeScope{ultimate.scope()}) { 280282edd428STim Keith for (const auto &pair2 : *typeScope) { 280382edd428STim Keith addSpecifics(*pair2.second); 280482edd428STim Keith } 280582edd428STim Keith } 280682edd428STim Keith } 280782edd428STim Keith } 280886f59de1STim Keith helper.Check(scope); 280982edd428STim Keith } 281082edd428STim Keith 28113077d614SPeter Klausler static bool IsSubprogramDefinition(const Symbol &symbol) { 2812b6f22fa5Speter klausler const auto *subp{symbol.detailsIf<SubprogramDetails>()}; 28133077d614SPeter Klausler return subp && !subp->isInterface() && symbol.scope() && 28143077d614SPeter Klausler symbol.scope()->kind() == Scope::Kind::Subprogram; 28153077d614SPeter Klausler } 28163077d614SPeter Klausler 28173077d614SPeter Klausler static bool IsExternalProcedureDefinition(const Symbol &symbol) { 28183077d614SPeter Klausler return IsBlockData(symbol) || 28193077d614SPeter Klausler (IsSubprogramDefinition(symbol) && 28203077d614SPeter Klausler (IsExternal(symbol) || symbol.GetBindName())); 28213077d614SPeter Klausler } 28223077d614SPeter Klausler 28233077d614SPeter Klausler static std::optional<std::string> DefinesGlobalName(const Symbol &symbol) { 28243077d614SPeter Klausler if (const auto *module{symbol.detailsIf<ModuleDetails>()}) { 28253077d614SPeter Klausler if (!module->isSubmodule() && !symbol.owner().IsIntrinsicModules()) { 28263077d614SPeter Klausler return symbol.name().ToString(); 28273077d614SPeter Klausler } 28283077d614SPeter Klausler } else if (IsBlockData(symbol)) { 28293077d614SPeter Klausler return symbol.name().ToString(); 2830b6f22fa5Speter klausler } else { 28313077d614SPeter Klausler const std::string *bindC{symbol.GetBindName()}; 28323077d614SPeter Klausler if (symbol.has<CommonBlockDetails>() || 28331c900ed3SPeter Klausler IsExternalProcedureDefinition(symbol) || 28341c900ed3SPeter Klausler (symbol.owner().IsGlobal() && IsExternal(symbol))) { 28353077d614SPeter Klausler return bindC ? *bindC : symbol.name().ToString(); 28363077d614SPeter Klausler } else if (bindC && 28373077d614SPeter Klausler (symbol.has<ObjectEntityDetails>() || IsModuleProcedure(symbol))) { 28383077d614SPeter Klausler return *bindC; 28393077d614SPeter Klausler } 28403077d614SPeter Klausler } 28413077d614SPeter Klausler return std::nullopt; 28423077d614SPeter Klausler } 28433077d614SPeter Klausler 28443077d614SPeter Klausler // 19.2 p2 28453077d614SPeter Klausler void CheckHelper::CheckGlobalName(const Symbol &symbol) { 28463077d614SPeter Klausler if (auto global{DefinesGlobalName(symbol)}) { 28473077d614SPeter Klausler auto pair{globalNames_.emplace(std::move(*global), symbol)}; 28483077d614SPeter Klausler if (!pair.second) { 28493077d614SPeter Klausler const Symbol &other{*pair.first->second}; 28503077d614SPeter Klausler if (context_.HasError(symbol) || context_.HasError(other)) { 28513077d614SPeter Klausler // don't pile on 28523077d614SPeter Klausler } else if (symbol.has<CommonBlockDetails>() && 28533077d614SPeter Klausler other.has<CommonBlockDetails>() && symbol.name() == other.name()) { 28543077d614SPeter Klausler // Two common blocks can have the same global name so long as 28553077d614SPeter Klausler // they're not in the same scope. 28563077d614SPeter Klausler } else if ((IsProcedure(symbol) || IsBlockData(symbol)) && 28573077d614SPeter Klausler (IsProcedure(other) || IsBlockData(other)) && 28583077d614SPeter Klausler (!IsExternalProcedureDefinition(symbol) || 28593077d614SPeter Klausler !IsExternalProcedureDefinition(other))) { 28603077d614SPeter Klausler // both are procedures/BLOCK DATA, not both definitions 28613077d614SPeter Klausler } else if (symbol.has<ModuleDetails>()) { 28620f973ac7SPeter Klausler Warn(common::LanguageFeature::BenignNameClash, symbol.name(), 28633077d614SPeter Klausler "Module '%s' conflicts with a global name"_port_en_US, 28643077d614SPeter Klausler pair.first->first); 28653077d614SPeter Klausler } else if (other.has<ModuleDetails>()) { 28660f973ac7SPeter Klausler Warn(common::LanguageFeature::BenignNameClash, symbol.name(), 28673077d614SPeter Klausler "Global name '%s' conflicts with a module"_port_en_US, 28683077d614SPeter Klausler pair.first->first); 28693077d614SPeter Klausler } else if (auto *msg{messages_.Say(symbol.name(), 28703077d614SPeter Klausler "Two entities have the same global name '%s'"_err_en_US, 28713077d614SPeter Klausler pair.first->first)}) { 28723077d614SPeter Klausler msg->Attach(other.name(), "Conflicting declaration"_en_US); 28733077d614SPeter Klausler context_.SetError(symbol); 28743077d614SPeter Klausler context_.SetError(other); 28753077d614SPeter Klausler } 28763077d614SPeter Klausler } 2877b6f22fa5Speter klausler } 2878b6f22fa5Speter klausler } 2879b6f22fa5Speter klausler 28801062c140SjeanPerier void CheckHelper::CheckProcedureAssemblyName(const Symbol &symbol) { 28811062c140SjeanPerier if (!IsProcedure(symbol) || symbol != symbol.GetUltimate()) 28821062c140SjeanPerier return; 28831062c140SjeanPerier const std::string *bindName{symbol.GetBindName()}; 28841062c140SjeanPerier const bool hasExplicitBindingLabel{ 28851062c140SjeanPerier symbol.GetIsExplicitBindName() && bindName}; 28861062c140SjeanPerier if (hasExplicitBindingLabel || IsExternal(symbol)) { 28871062c140SjeanPerier const std::string assemblyName{hasExplicitBindingLabel 28881062c140SjeanPerier ? *bindName 28891062c140SjeanPerier : common::GetExternalAssemblyName( 28901062c140SjeanPerier symbol.name().ToString(), context_.underscoring())}; 28911062c140SjeanPerier auto pair{procedureAssemblyNames_.emplace(std::move(assemblyName), symbol)}; 28921062c140SjeanPerier if (!pair.second) { 28931062c140SjeanPerier const Symbol &other{*pair.first->second}; 28941062c140SjeanPerier const bool otherHasExplicitBindingLabel{ 28951062c140SjeanPerier other.GetIsExplicitBindName() && other.GetBindName()}; 28961062c140SjeanPerier if (otherHasExplicitBindingLabel != hasExplicitBindingLabel) { 28971062c140SjeanPerier // The BIND(C,NAME="...") binding label is the same as the name that 28981062c140SjeanPerier // will be used in LLVM IR for an external procedure declared without 28991062c140SjeanPerier // BIND(C) in the same file. While this is not forbidden by the 29001062c140SjeanPerier // standard, this name collision would lead to a crash when producing 29011062c140SjeanPerier // the IR. 29021062c140SjeanPerier if (auto *msg{messages_.Say(symbol.name(), 29031062c140SjeanPerier "%s procedure assembly name conflicts with %s procedure assembly name"_err_en_US, 29041062c140SjeanPerier hasExplicitBindingLabel ? "BIND(C)" : "Non BIND(C)", 29051062c140SjeanPerier hasExplicitBindingLabel ? "non BIND(C)" : "BIND(C)")}) { 29061062c140SjeanPerier msg->Attach(other.name(), "Conflicting declaration"_en_US); 29071062c140SjeanPerier } 29081062c140SjeanPerier context_.SetError(symbol); 29091062c140SjeanPerier context_.SetError(other); 29101062c140SjeanPerier } 29111062c140SjeanPerier // Otherwise, the global names also match and the conflict is analyzed 29121062c140SjeanPerier // by CheckGlobalName. 29131062c140SjeanPerier } 29141062c140SjeanPerier } 29151062c140SjeanPerier } 29161062c140SjeanPerier 291790501be3SPeter Klausler parser::Messages CheckHelper::WhyNotInteroperableDerivedType( 291856cd8a50SPeter Klausler const Symbol &symbol) { 291990501be3SPeter Klausler parser::Messages msgs; 2920ab7930bdSPeter Klausler if (examinedByWhyNotInteroperable_.find(symbol) != 2921ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.end()) { 292290501be3SPeter Klausler return msgs; 292390501be3SPeter Klausler } 2924ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.insert(symbol); 292590501be3SPeter Klausler if (const auto *derived{symbol.detailsIf<DerivedTypeDetails>()}) { 292690501be3SPeter Klausler if (derived->sequence()) { // C1801 292790501be3SPeter Klausler msgs.Say(symbol.name(), 292890501be3SPeter Klausler "An interoperable derived type cannot have the SEQUENCE attribute"_err_en_US); 2929539a6b50SPeter Klausler } else if (!derived->paramNameOrder().empty()) { // C1802 293090501be3SPeter Klausler msgs.Say(symbol.name(), 293190501be3SPeter Klausler "An interoperable derived type cannot have a type parameter"_err_en_US); 293290501be3SPeter Klausler } else if (const auto *parent{ 293390501be3SPeter Klausler symbol.scope()->GetDerivedTypeParent()}) { // C1803 293456cd8a50SPeter Klausler if (symbol.attrs().test(Attr::BIND_C)) { 293590501be3SPeter Klausler msgs.Say(symbol.name(), 293690501be3SPeter Klausler "A derived type with the BIND attribute cannot be an extended derived type"_err_en_US); 293790501be3SPeter Klausler } else { 293890501be3SPeter Klausler bool interoperableParent{true}; 293990501be3SPeter Klausler if (parent->symbol()) { 294056cd8a50SPeter Klausler auto bad{WhyNotInteroperableDerivedType(*parent->symbol())}; 294190501be3SPeter Klausler if (bad.AnyFatalError()) { 294290501be3SPeter Klausler auto &msg{msgs.Say(symbol.name(), 294390501be3SPeter Klausler "The parent of an interoperable type is not interoperable"_err_en_US)}; 294490501be3SPeter Klausler bad.AttachTo(msg, parser::Severity::None); 294590501be3SPeter Klausler interoperableParent = false; 294690501be3SPeter Klausler } 294790501be3SPeter Klausler } 294890501be3SPeter Klausler if (interoperableParent) { 294990501be3SPeter Klausler msgs.Say(symbol.name(), 295090501be3SPeter Klausler "An interoperable type should not be an extended derived type"_warn_en_US); 295190501be3SPeter Klausler } 295290501be3SPeter Klausler } 295390501be3SPeter Klausler } 295490501be3SPeter Klausler const Symbol *parentComponent{symbol.scope() 295590501be3SPeter Klausler ? derived->GetParentComponent(*symbol.scope()) 295690501be3SPeter Klausler : nullptr}; 295790501be3SPeter Klausler for (const auto &pair : *symbol.scope()) { 295890501be3SPeter Klausler const Symbol &component{*pair.second}; 295990501be3SPeter Klausler if (&component == parentComponent) { 296090501be3SPeter Klausler continue; // was checked above 296190501be3SPeter Klausler } 296290501be3SPeter Klausler if (IsProcedure(component)) { // C1804 296390501be3SPeter Klausler msgs.Say(component.name(), 296490501be3SPeter Klausler "An interoperable derived type cannot have a type bound procedure"_err_en_US); 296590501be3SPeter Klausler } else if (IsAllocatableOrPointer(component)) { // C1806 296690501be3SPeter Klausler msgs.Say(component.name(), 296790501be3SPeter Klausler "An interoperable derived type cannot have a pointer or allocatable component"_err_en_US); 296890501be3SPeter Klausler } else if (const auto *type{component.GetType()}) { 296990501be3SPeter Klausler if (const auto *derived{type->AsDerived()}) { 297056cd8a50SPeter Klausler auto bad{WhyNotInteroperableDerivedType(derived->typeSymbol())}; 297190501be3SPeter Klausler if (bad.AnyFatalError()) { 297290501be3SPeter Klausler auto &msg{msgs.Say(component.name(), 297390501be3SPeter Klausler "Component '%s' of an interoperable derived type must have an interoperable type but does not"_err_en_US, 297490501be3SPeter Klausler component.name())}; 297590501be3SPeter Klausler bad.AttachTo(msg, parser::Severity::None); 297690501be3SPeter Klausler } else if (!derived->typeSymbol().GetUltimate().attrs().test( 297790501be3SPeter Klausler Attr::BIND_C)) { 297890501be3SPeter Klausler auto &msg{ 297990501be3SPeter Klausler msgs.Say(component.name(), 298090501be3SPeter Klausler "Derived type of component '%s' of an interoperable derived type should have the BIND attribute"_warn_en_US, 298190501be3SPeter Klausler component.name()) 298290501be3SPeter Klausler .Attach(derived->typeSymbol().name(), 298390501be3SPeter Klausler "Non-BIND(C) component type"_en_US)}; 298490501be3SPeter Klausler bad.AttachTo(msg, parser::Severity::None); 298590501be3SPeter Klausler } else { 298690501be3SPeter Klausler msgs.Annex(std::move(bad)); 298790501be3SPeter Klausler } 2988ce392471SPeter Klausler } else if (auto dyType{evaluate::DynamicType::From(*type)}; dyType && 2989ce392471SPeter Klausler !evaluate::IsInteroperableIntrinsicType( 2990ce392471SPeter Klausler *dyType, &context_.languageFeatures()) 299160c90336SPeter Klausler .value_or(false)) { 299290501be3SPeter Klausler if (type->category() == DeclTypeSpec::Logical) { 299390501be3SPeter Klausler if (context_.ShouldWarn(common::UsageWarning::LogicalVsCBool)) { 29940f973ac7SPeter Klausler msgs.Say(common::UsageWarning::LogicalVsCBool, component.name(), 299590501be3SPeter Klausler "A LOGICAL component of an interoperable type should have the interoperable KIND=C_BOOL"_port_en_US); 299690501be3SPeter Klausler } 2997ce392471SPeter Klausler } else if (type->category() == DeclTypeSpec::Character && dyType && 2998ce392471SPeter Klausler dyType->kind() == 1) { 299990501be3SPeter Klausler if (context_.ShouldWarn(common::UsageWarning::BindCCharLength)) { 30000f973ac7SPeter Klausler msgs.Say(common::UsageWarning::BindCCharLength, component.name(), 300190501be3SPeter Klausler "A CHARACTER component of an interoperable type should have length 1"_port_en_US); 300290501be3SPeter Klausler } 300390501be3SPeter Klausler } else { 300490501be3SPeter Klausler msgs.Say(component.name(), 300590501be3SPeter Klausler "Each component of an interoperable derived type must have an interoperable type"_err_en_US); 300690501be3SPeter Klausler } 300790501be3SPeter Klausler } 300890501be3SPeter Klausler } 300990501be3SPeter Klausler if (auto extents{ 301090501be3SPeter Klausler evaluate::GetConstantExtents(foldingContext_, &component)}; 301190501be3SPeter Klausler extents && evaluate::GetSize(*extents) == 0) { 301290501be3SPeter Klausler msgs.Say(component.name(), 301390501be3SPeter Klausler "An array component of an interoperable type must have at least one element"_err_en_US); 301490501be3SPeter Klausler } 301590501be3SPeter Klausler } 301690501be3SPeter Klausler if (derived->componentNames().empty()) { // F'2023 C1805 301790501be3SPeter Klausler if (context_.ShouldWarn(common::LanguageFeature::EmptyBindCDerivedType)) { 30180f973ac7SPeter Klausler msgs.Say(common::LanguageFeature::EmptyBindCDerivedType, symbol.name(), 3019ab7930bdSPeter Klausler "A derived type with the BIND attribute should not be empty"_warn_en_US); 302090501be3SPeter Klausler } 302190501be3SPeter Klausler } 302290501be3SPeter Klausler } 3023463f58a5SPeter Klausler if (msgs.AnyFatalError()) { 3024ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.erase(symbol); 3025ab7930bdSPeter Klausler } 3026ab7930bdSPeter Klausler return msgs; 3027ab7930bdSPeter Klausler } 3028ab7930bdSPeter Klausler 3029b3026babSPeter Klausler parser::Messages CheckHelper::WhyNotInteroperableObject( 3030b3026babSPeter Klausler const Symbol &symbol, bool allowNonInteroperableType) { 3031ab7930bdSPeter Klausler parser::Messages msgs; 3032ab7930bdSPeter Klausler if (examinedByWhyNotInteroperable_.find(symbol) != 3033ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.end()) { 3034ab7930bdSPeter Klausler return msgs; 3035ab7930bdSPeter Klausler } 3036ab7930bdSPeter Klausler bool isExplicitBindC{symbol.attrs().test(Attr::BIND_C)}; 3037ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.insert(symbol); 3038ab7930bdSPeter Klausler CHECK(symbol.has<ObjectEntityDetails>()); 3039ab7930bdSPeter Klausler if (isExplicitBindC && !symbol.owner().IsModule()) { 3040c24f8813SPeter Klausler msgs.Say(symbol.name(), 3041ab7930bdSPeter Klausler "A variable with BIND(C) attribute may only appear in the specification part of a module"_err_en_US); 3042ab7930bdSPeter Klausler } 3043ab7930bdSPeter Klausler auto shape{evaluate::GetShape(foldingContext_, symbol)}; 3044ab7930bdSPeter Klausler if (shape) { 3045ab7930bdSPeter Klausler if (evaluate::GetRank(*shape) == 0) { // 18.3.4 3046ab7930bdSPeter Klausler if (IsAllocatableOrPointer(symbol) && !IsDummy(symbol)) { 3047c24f8813SPeter Klausler msgs.Say(symbol.name(), 3048ab7930bdSPeter Klausler "A scalar interoperable variable may not be ALLOCATABLE or POINTER"_err_en_US); 3049ab7930bdSPeter Klausler } 3050ab7930bdSPeter Klausler } else if (auto extents{ 3051ab7930bdSPeter Klausler evaluate::AsConstantExtents(foldingContext_, *shape)}) { 3052ab7930bdSPeter Klausler if (evaluate::GetSize(*extents) == 0) { 3053ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3054ab7930bdSPeter Klausler "Interoperable array must have at least one element"_err_en_US); 3055ab7930bdSPeter Klausler } 3056ab7930bdSPeter Klausler } else if (!evaluate::IsExplicitShape(symbol) && 3057ab7930bdSPeter Klausler !IsAssumedSizeArray(symbol) && 3058ab7930bdSPeter Klausler !(IsDummy(symbol) && !symbol.attrs().test(Attr::VALUE))) { 3059ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3060ab7930bdSPeter Klausler "BIND(C) array must have explicit shape or be assumed-size unless a dummy argument without the VALUE attribute"_err_en_US); 3061ab7930bdSPeter Klausler } 3062ab7930bdSPeter Klausler } 3063ab7930bdSPeter Klausler if (const auto *type{symbol.GetType()}) { 3064ab7930bdSPeter Klausler const auto *derived{type->AsDerived()}; 3065055df491SPeter Klausler if (derived && !derived->typeSymbol().attrs().test(Attr::BIND_C)) { 3066b3026babSPeter Klausler if (allowNonInteroperableType) { // portability warning only 3067b3026babSPeter Klausler evaluate::AttachDeclaration( 3068b3026babSPeter Klausler context_.Warn(common::UsageWarning::Portability, symbol.name(), 3069b3026babSPeter Klausler "The derived type of this interoperable object should be BIND(C)"_port_en_US), 3070b3026babSPeter Klausler derived->typeSymbol()); 3071b3026babSPeter Klausler } else if (!context_.IsEnabled( 3072055df491SPeter Klausler common::LanguageFeature::NonBindCInteroperability)) { 3073c24f8813SPeter Klausler msgs.Say(symbol.name(), 3074055df491SPeter Klausler "The derived type of an interoperable object must be BIND(C)"_err_en_US) 3075c24f8813SPeter Klausler .Attach(derived->typeSymbol().name(), "Non-BIND(C) type"_en_US); 307656cd8a50SPeter Klausler } else if (auto bad{ 307756cd8a50SPeter Klausler WhyNotInteroperableDerivedType(derived->typeSymbol())}; 3078ab7930bdSPeter Klausler bad.AnyFatalError()) { 3079c24f8813SPeter Klausler bad.AttachTo( 3080c24f8813SPeter Klausler msgs.Say(symbol.name(), 3081c24f8813SPeter Klausler "The derived type of an interoperable object must be interoperable, but is not"_err_en_US) 3082c24f8813SPeter Klausler .Attach(derived->typeSymbol().name(), 3083c24f8813SPeter Klausler "Non-interoperable type"_en_US), 3084c24f8813SPeter Klausler parser::Severity::None); 3085ab7930bdSPeter Klausler } else { 3086c24f8813SPeter Klausler msgs.Say(symbol.name(), 3087c24f8813SPeter Klausler "The derived type of an interoperable object should be BIND(C)"_warn_en_US) 3088c24f8813SPeter Klausler .Attach(derived->typeSymbol().name(), "Non-BIND(C) type"_en_US); 3089ab7930bdSPeter Klausler } 3090ab7930bdSPeter Klausler } 3091ab7930bdSPeter Klausler if (type->IsAssumedType()) { // ok 3092*c596aae4SPeter Klausler } else if (IsAssumedLengthCharacter(symbol) && 3093*c596aae4SPeter Klausler !IsAllocatableOrPointer(symbol)) { 3094ab7930bdSPeter Klausler } else if (IsAllocatableOrPointer(symbol) && 3095ab7930bdSPeter Klausler type->category() == DeclTypeSpec::Character && 3096ab7930bdSPeter Klausler type->characterTypeSpec().length().isDeferred()) { 3097ab7930bdSPeter Klausler // ok; F'2023 18.3.7 p2(6) 3098ce392471SPeter Klausler } else if (derived) { // type has been checked 3099ce392471SPeter Klausler } else if (auto dyType{evaluate::DynamicType::From(*type)}; dyType && 3100*c596aae4SPeter Klausler evaluate::IsInteroperableIntrinsicType( 3101*c596aae4SPeter Klausler *dyType, InModuleFile() ? nullptr : &context_.languageFeatures()) 310260c90336SPeter Klausler .value_or(false)) { 3103ab7930bdSPeter Klausler // F'2023 18.3.7 p2(4,5) 3104ce392471SPeter Klausler // N.B. Language features are not passed to IsInteroperableIntrinsicType 3105ce392471SPeter Klausler // when processing a module file, since the module file might have been 3106ce392471SPeter Klausler // compiled with CUDA while the client is not. 3107ab7930bdSPeter Klausler } else if (type->category() == DeclTypeSpec::Logical) { 31080f973ac7SPeter Klausler if (context_.ShouldWarn(common::UsageWarning::LogicalVsCBool)) { 3109ab7930bdSPeter Klausler if (IsDummy(symbol)) { 31100f973ac7SPeter Klausler msgs.Say(common::UsageWarning::LogicalVsCBool, symbol.name(), 3111ab7930bdSPeter Klausler "A BIND(C) LOGICAL dummy argument should have the interoperable KIND=C_BOOL"_port_en_US); 3112ab7930bdSPeter Klausler } else { 31130f973ac7SPeter Klausler msgs.Say(common::UsageWarning::LogicalVsCBool, symbol.name(), 3114ab7930bdSPeter Klausler "A BIND(C) LOGICAL object should have the interoperable KIND=C_BOOL"_port_en_US); 3115ab7930bdSPeter Klausler } 3116ab7930bdSPeter Klausler } 3117ab7930bdSPeter Klausler } else if (symbol.attrs().test(Attr::VALUE)) { 3118ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3119ab7930bdSPeter Klausler "A BIND(C) VALUE dummy argument must have an interoperable type"_err_en_US); 3120ab7930bdSPeter Klausler } else { 3121ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3122ab7930bdSPeter Klausler "A BIND(C) object must have an interoperable type"_err_en_US); 3123ab7930bdSPeter Klausler } 3124ab7930bdSPeter Klausler } 3125ab7930bdSPeter Klausler if (IsOptional(symbol) && !symbol.attrs().test(Attr::VALUE)) { 3126ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3127ab7930bdSPeter Klausler "An interoperable procedure with an OPTIONAL dummy argument might not be portable"_port_en_US); 3128ab7930bdSPeter Klausler } 3129ab7930bdSPeter Klausler if (IsDescriptor(symbol) && IsPointer(symbol) && 3130ab7930bdSPeter Klausler symbol.attrs().test(Attr::CONTIGUOUS)) { 3131ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3132ab7930bdSPeter Klausler "An interoperable pointer must not be CONTIGUOUS"_err_en_US); 3133ab7930bdSPeter Klausler } 3134ab7930bdSPeter Klausler if (msgs.AnyFatalError()) { 3135ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.erase(symbol); 3136ab7930bdSPeter Klausler } 3137ab7930bdSPeter Klausler return msgs; 3138ab7930bdSPeter Klausler } 3139ab7930bdSPeter Klausler 3140ab7930bdSPeter Klausler parser::Messages CheckHelper::WhyNotInteroperableFunctionResult( 3141ab7930bdSPeter Klausler const Symbol &symbol) { 3142ab7930bdSPeter Klausler parser::Messages msgs; 3143ab7930bdSPeter Klausler if (IsPointer(symbol) || IsAllocatable(symbol)) { 3144ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3145ab7930bdSPeter Klausler "Interoperable function result may not have ALLOCATABLE or POINTER attribute"_err_en_US); 3146ab7930bdSPeter Klausler } 3147ab7930bdSPeter Klausler if (const DeclTypeSpec * type{symbol.GetType()}; 3148ab7930bdSPeter Klausler type && type->category() == DeclTypeSpec::Character) { 3149ab7930bdSPeter Klausler bool isConstOne{false}; // 18.3.1(1) 3150ab7930bdSPeter Klausler if (const auto &len{type->characterTypeSpec().length().GetExplicit()}) { 3151ab7930bdSPeter Klausler if (auto constLen{evaluate::ToInt64(*len)}) { 3152ab7930bdSPeter Klausler isConstOne = constLen == 1; 3153ab7930bdSPeter Klausler } 3154ab7930bdSPeter Klausler } 3155ab7930bdSPeter Klausler if (!isConstOne) { 3156ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3157ab7930bdSPeter Klausler "Interoperable character function result must have length one"_err_en_US); 3158ab7930bdSPeter Klausler } 3159ab7930bdSPeter Klausler } 3160ab7930bdSPeter Klausler if (symbol.Rank() > 0) { 3161ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3162ab7930bdSPeter Klausler "Interoperable function result must be scalar"_err_en_US); 3163ab7930bdSPeter Klausler } 3164ab7930bdSPeter Klausler if (symbol.Corank()) { 3165ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3166ab7930bdSPeter Klausler "Interoperable function result may not be a coarray"_err_en_US); 3167ab7930bdSPeter Klausler } 3168ab7930bdSPeter Klausler return msgs; 3169ab7930bdSPeter Klausler } 3170ab7930bdSPeter Klausler 3171ab7930bdSPeter Klausler parser::Messages CheckHelper::WhyNotInteroperableProcedure( 3172ab7930bdSPeter Klausler const Symbol &symbol, bool isError) { 3173ab7930bdSPeter Klausler parser::Messages msgs; 3174ab7930bdSPeter Klausler if (examinedByWhyNotInteroperable_.find(symbol) != 3175ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.end()) { 3176ab7930bdSPeter Klausler return msgs; 3177ab7930bdSPeter Klausler } 3178ab7930bdSPeter Klausler isError |= symbol.attrs().test(Attr::BIND_C); 3179ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.insert(symbol); 3180ab7930bdSPeter Klausler if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) { 3181ab7930bdSPeter Klausler if (isError) { 3182ab7930bdSPeter Klausler if (!proc->procInterface() || 3183ab7930bdSPeter Klausler !proc->procInterface()->attrs().test(Attr::BIND_C)) { 3184ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3185ab7930bdSPeter Klausler "An interface name with the BIND attribute must appear if the BIND attribute appears in a procedure declaration"_err_en_US); 3186ab7930bdSPeter Klausler } 3187ab7930bdSPeter Klausler } else if (!proc->procInterface()) { 3188ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3189ab7930bdSPeter Klausler "An interoperable procedure should have an interface"_port_en_US); 3190ab7930bdSPeter Klausler } else if (!proc->procInterface()->attrs().test(Attr::BIND_C)) { 3191ab7930bdSPeter Klausler auto bad{WhyNotInteroperableProcedure( 3192ab7930bdSPeter Klausler *proc->procInterface(), /*isError=*/false)}; 3193ab7930bdSPeter Klausler if (bad.AnyFatalError()) { 3194ab7930bdSPeter Klausler bad.AttachTo(msgs.Say(symbol.name(), 3195ab7930bdSPeter Klausler "An interoperable procedure must have an interoperable interface"_err_en_US)); 3196ab7930bdSPeter Klausler } else { 3197ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3198ab7930bdSPeter Klausler "An interoperable procedure should have an interface with the BIND attribute"_warn_en_US); 3199ab7930bdSPeter Klausler } 3200ab7930bdSPeter Klausler } 3201ab7930bdSPeter Klausler } else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) { 3202ab7930bdSPeter Klausler for (const Symbol *dummy : subp->dummyArgs()) { 3203ab7930bdSPeter Klausler if (dummy) { 3204ab7930bdSPeter Klausler parser::Messages dummyMsgs; 3205ab7930bdSPeter Klausler if (dummy->has<ProcEntityDetails>() || 3206ab7930bdSPeter Klausler dummy->has<SubprogramDetails>()) { 3207ab7930bdSPeter Klausler dummyMsgs = WhyNotInteroperableProcedure(*dummy, /*isError=*/false); 3208ab7930bdSPeter Klausler if (dummyMsgs.empty() && !dummy->attrs().test(Attr::BIND_C)) { 3209ab7930bdSPeter Klausler dummyMsgs.Say(dummy->name(), 3210ab7930bdSPeter Klausler "A dummy procedure of an interoperable procedure should be BIND(C)"_warn_en_US); 3211ab7930bdSPeter Klausler } 3212ab7930bdSPeter Klausler } else if (dummy->has<ObjectEntityDetails>()) { 3213b3026babSPeter Klausler // Emit only optional portability warnings for non-interoperable 3214b3026babSPeter Klausler // types when the dummy argument is not VALUE and will be implemented 3215b3026babSPeter Klausler // on the C side by either a cdesc_t * or a void *. F'2023 18.3.7 (5) 3216b3026babSPeter Klausler bool allowNonInteroperableType{!dummy->attrs().test(Attr::VALUE) && 3217b3026babSPeter Klausler (IsDescriptor(*dummy) || IsAssumedType(*dummy))}; 3218b3026babSPeter Klausler dummyMsgs = 3219b3026babSPeter Klausler WhyNotInteroperableObject(*dummy, allowNonInteroperableType); 3220ab7930bdSPeter Klausler } else { 3221ab7930bdSPeter Klausler CheckBindC(*dummy); 3222ab7930bdSPeter Klausler } 3223ab7930bdSPeter Klausler msgs.Annex(std::move(dummyMsgs)); 3224ab7930bdSPeter Klausler } else { 3225ab7930bdSPeter Klausler msgs.Say(symbol.name(), 3226ab7930bdSPeter Klausler "A subprogram interface with the BIND attribute may not have an alternate return argument"_err_en_US); 3227ab7930bdSPeter Klausler } 3228ab7930bdSPeter Klausler } 3229ab7930bdSPeter Klausler if (subp->isFunction()) { 3230ab7930bdSPeter Klausler if (subp->result().has<ObjectEntityDetails>()) { 3231ab7930bdSPeter Klausler msgs.Annex(WhyNotInteroperableFunctionResult(subp->result())); 3232ab7930bdSPeter Klausler } else { 3233ab7930bdSPeter Klausler msgs.Say(subp->result().name(), 3234ab7930bdSPeter Klausler "The result of an interoperable function must be a data object"_err_en_US); 3235ab7930bdSPeter Klausler } 3236ab7930bdSPeter Klausler } 3237ab7930bdSPeter Klausler } 3238ab7930bdSPeter Klausler if (msgs.AnyFatalError()) { 3239ab7930bdSPeter Klausler examinedByWhyNotInteroperable_.erase(symbol); 3240463f58a5SPeter Klausler } 324190501be3SPeter Klausler return msgs; 324290501be3SPeter Klausler } 324390501be3SPeter Klausler 3244f3d83353SPeixinQiao void CheckHelper::CheckBindC(const Symbol &symbol) { 3245199402c3SPeter Klausler bool isExplicitBindC{symbol.attrs().test(Attr::BIND_C)}; 3246199402c3SPeter Klausler if (isExplicitBindC) { 3247ac44cb76SPeixin-Qiao CheckConflicting(symbol, Attr::BIND_C, Attr::ELEMENTAL); 324890501be3SPeter Klausler CheckConflicting(symbol, Attr::BIND_C, Attr::INTRINSIC); 324990501be3SPeter Klausler CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER); 3250199402c3SPeter Klausler } else { 3251199402c3SPeter Klausler // symbol must be interoperable (e.g., dummy argument of interoperable 3252199402c3SPeter Klausler // procedure interface) but is not itself BIND(C). 3253199402c3SPeter Klausler } 3254ab7930bdSPeter Klausler parser::Messages whyNot; 325569373a5dSPeter Klausler if (const std::string * bindName{symbol.GetBindName()}; 3256199402c3SPeter Klausler bindName) { // has a binding name 325769e2665cSPeter Klausler if (!bindName->empty()) { 325869373a5dSPeter Klausler bool ok{bindName->front() == '_' || parser::IsLetter(bindName->front())}; 325969373a5dSPeter Klausler for (char ch : *bindName) { 326069373a5dSPeter Klausler ok &= ch == '_' || parser::IsLetter(ch) || parser::IsDecimalDigit(ch); 326169373a5dSPeter Klausler } 326269373a5dSPeter Klausler if (!ok) { 326369373a5dSPeter Klausler messages_.Say(symbol.name(), 326469373a5dSPeter Klausler "Symbol has a BIND(C) name that is not a valid C language identifier"_err_en_US); 326569373a5dSPeter Klausler context_.SetError(symbol); 326669373a5dSPeter Klausler } 326769373a5dSPeter Klausler } 326869e2665cSPeter Klausler } 3269199402c3SPeter Klausler if (symbol.GetIsExplicitBindName()) { // BIND(C,NAME=...); C1552, C1529 327069e2665cSPeter Klausler auto defClass{ClassifyProcedure(symbol)}; 327169e2665cSPeter Klausler if (IsProcedurePointer(symbol)) { 327269e2665cSPeter Klausler messages_.Say(symbol.name(), 327369e2665cSPeter Klausler "A procedure pointer may not have a BIND attribute with a name"_err_en_US); 327469e2665cSPeter Klausler context_.SetError(symbol); 327569e2665cSPeter Klausler } else if (defClass == ProcedureDefinitionClass::None || 327669e2665cSPeter Klausler IsExternal(symbol)) { 327769e2665cSPeter Klausler } else if (symbol.attrs().test(Attr::ABSTRACT)) { 327869e2665cSPeter Klausler messages_.Say(symbol.name(), 327969e2665cSPeter Klausler "An ABSTRACT interface may not have a BIND attribute with a name"_err_en_US); 328069e2665cSPeter Klausler context_.SetError(symbol); 328169e2665cSPeter Klausler } else if (defClass == ProcedureDefinitionClass::Internal || 328269e2665cSPeter Klausler defClass == ProcedureDefinitionClass::Dummy) { 328369e2665cSPeter Klausler messages_.Say(symbol.name(), 328469e2665cSPeter Klausler "An internal or dummy procedure may not have a BIND(C,NAME=) binding label"_err_en_US); 328569e2665cSPeter Klausler context_.SetError(symbol); 328669e2665cSPeter Klausler } 328769e2665cSPeter Klausler } 32887b801233SPeter Klausler if (symbol.has<ObjectEntityDetails>()) { 3289055df491SPeter Klausler whyNot = WhyNotInteroperableObject(symbol); 3290ab7930bdSPeter Klausler } else if (symbol.has<ProcEntityDetails>() || 3291ab7930bdSPeter Klausler symbol.has<SubprogramDetails>()) { 3292ab7930bdSPeter Klausler whyNot = WhyNotInteroperableProcedure(symbol, /*isError=*/isExplicitBindC); 329390501be3SPeter Klausler } else if (symbol.has<DerivedTypeDetails>()) { 329456cd8a50SPeter Klausler whyNot = WhyNotInteroperableDerivedType(symbol); 3295ab7930bdSPeter Klausler } 3296ab7930bdSPeter Klausler if (!whyNot.empty()) { 3297ab7930bdSPeter Klausler bool anyFatal{whyNot.AnyFatalError()}; 3298ab7930bdSPeter Klausler if (anyFatal || 329990501be3SPeter Klausler (!InModuleFile() && 330090501be3SPeter Klausler context_.ShouldWarn( 330190501be3SPeter Klausler common::LanguageFeature::NonBindCInteroperability))) { 3302ab7930bdSPeter Klausler context_.messages().Annex(std::move(whyNot)); 330390501be3SPeter Klausler } 330490501be3SPeter Klausler if (anyFatal) { 330548b6f5c7SPeixin Qiao context_.SetError(symbol); 330648b6f5c7SPeixin Qiao } 330748b6f5c7SPeixin Qiao } 3308b6f22fa5Speter klausler } 3309b6f22fa5Speter klausler 3310bc56620bSPeter Steinfeld bool CheckHelper::CheckDioDummyIsData( 3311bc56620bSPeter Steinfeld const Symbol &subp, const Symbol *arg, std::size_t position) { 3312bc56620bSPeter Steinfeld if (arg && arg->detailsIf<ObjectEntityDetails>()) { 3313bc56620bSPeter Steinfeld return true; 3314bc56620bSPeter Steinfeld } else { 3315bc56620bSPeter Steinfeld if (arg) { 3316bc56620bSPeter Steinfeld messages_.Say(arg->name(), 3317bc56620bSPeter Steinfeld "Dummy argument '%s' must be a data object"_err_en_US, arg->name()); 3318bc56620bSPeter Steinfeld } else { 3319bc56620bSPeter Steinfeld messages_.Say(subp.name(), 3320bc56620bSPeter Steinfeld "Dummy argument %d of '%s' must be a data object"_err_en_US, position, 3321bc56620bSPeter Steinfeld subp.name()); 3322bc56620bSPeter Steinfeld } 3323bc56620bSPeter Steinfeld return false; 3324bc56620bSPeter Steinfeld } 3325bc56620bSPeter Steinfeld } 3326bc56620bSPeter Steinfeld 3327dcf9ba82SPeter Klausler void CheckHelper::CheckAlreadySeenDefinedIo(const DerivedTypeSpec &derivedType, 33287cf1608bSPeter Klausler common::DefinedIo ioKind, const Symbol &proc, const Symbol &generic) { 33297cf1608bSPeter Klausler // Check for conflict between non-type-bound defined I/O and type-bound 33307cf1608bSPeter Klausler // generics. It's okay to have two or more distinct defined I/O procedures for 33317cf1608bSPeter Klausler // the same type if they're coming from distinct non-type-bound interfaces. 33327cf1608bSPeter Klausler // (The non-type-bound interfaces would have been merged into a single generic 33337cf1608bSPeter Klausler // -- with errors where indistinguishable -- when both were visible from the 33347cf1608bSPeter Klausler // same scope.) 333509b00ab4SPeter Klausler if (generic.owner().IsDerivedType()) { 333609b00ab4SPeter Klausler return; 333709b00ab4SPeter Klausler } 333809b00ab4SPeter Klausler if (const Scope * dtScope{derivedType.scope()}) { 333909b00ab4SPeter Klausler if (auto iter{dtScope->find(generic.name())}; iter != dtScope->end()) { 334009b00ab4SPeter Klausler for (auto specRef : iter->second->get<GenericDetails>().specificProcs()) { 334109b00ab4SPeter Klausler const Symbol &specific{specRef->get<ProcBindingDetails>().symbol()}; 334209b00ab4SPeter Klausler if (specific == proc) { // unambiguous, accept 334309b00ab4SPeter Klausler continue; 334409b00ab4SPeter Klausler } 334509b00ab4SPeter Klausler if (const auto *specDT{GetDtvArgDerivedType(specific)}; 334609b00ab4SPeter Klausler specDT && evaluate::AreSameDerivedType(derivedType, *specDT)) { 334709b00ab4SPeter Klausler SayWithDeclaration(*specRef, proc.name(), 334809b00ab4SPeter Klausler "Derived type '%s' has conflicting type-bound input/output procedure '%s'"_err_en_US, 3349042c964dSPeter Klausler derivedType.name(), GenericKind::AsFortran(ioKind)); 3350bc56620bSPeter Steinfeld return; 3351bc56620bSPeter Steinfeld } 335222d7e298SPeter Steinfeld } 335309b00ab4SPeter Klausler } 335409b00ab4SPeter Klausler } 335522d7e298SPeter Steinfeld } 335622d7e298SPeter Steinfeld 3357dcf9ba82SPeter Klausler void CheckHelper::CheckDioDummyIsDerived(const Symbol &subp, const Symbol &arg, 33587cf1608bSPeter Klausler common::DefinedIo ioKind, const Symbol &generic) { 335922d7e298SPeter Steinfeld if (const DeclTypeSpec *type{arg.GetType()}) { 336043fadefbSpeter klausler if (const DerivedTypeSpec *derivedType{type->AsDerived()}) { 3361dcf9ba82SPeter Klausler CheckAlreadySeenDefinedIo(*derivedType, ioKind, subp, generic); 336243fadefbSpeter klausler bool isPolymorphic{type->IsPolymorphic()}; 336343fadefbSpeter klausler if (isPolymorphic != IsExtensibleType(derivedType)) { 336443fadefbSpeter klausler messages_.Say(arg.name(), 336543fadefbSpeter klausler "Dummy argument '%s' of a defined input/output procedure must be %s when the derived type is %s"_err_en_US, 336643fadefbSpeter klausler arg.name(), isPolymorphic ? "TYPE()" : "CLASS()", 336743fadefbSpeter klausler isPolymorphic ? "not extensible" : "extensible"); 336843fadefbSpeter klausler } 336922d7e298SPeter Steinfeld } else { 3370bc56620bSPeter Steinfeld messages_.Say(arg.name(), 3371bc56620bSPeter Steinfeld "Dummy argument '%s' of a defined input/output procedure must have a" 3372bc56620bSPeter Steinfeld " derived type"_err_en_US, 3373bc56620bSPeter Steinfeld arg.name()); 3374bc56620bSPeter Steinfeld } 337522d7e298SPeter Steinfeld } 337622d7e298SPeter Steinfeld } 3377bc56620bSPeter Steinfeld 3378bc56620bSPeter Steinfeld void CheckHelper::CheckDioDummyIsDefaultInteger( 3379bc56620bSPeter Steinfeld const Symbol &subp, const Symbol &arg) { 3380bc56620bSPeter Steinfeld if (const DeclTypeSpec *type{arg.GetType()}; 3381bc56620bSPeter Steinfeld type && type->IsNumeric(TypeCategory::Integer)) { 3382bc56620bSPeter Steinfeld if (const auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())}; 3383bc56620bSPeter Steinfeld kind && *kind == context_.GetDefaultKind(TypeCategory::Integer)) { 3384bc56620bSPeter Steinfeld return; 3385bc56620bSPeter Steinfeld } 3386bc56620bSPeter Steinfeld } 3387bc56620bSPeter Steinfeld messages_.Say(arg.name(), 3388bc56620bSPeter Steinfeld "Dummy argument '%s' of a defined input/output procedure" 3389bc56620bSPeter Steinfeld " must be an INTEGER of default KIND"_err_en_US, 3390bc56620bSPeter Steinfeld arg.name()); 3391bc56620bSPeter Steinfeld } 3392bc56620bSPeter Steinfeld 3393bc56620bSPeter Steinfeld void CheckHelper::CheckDioDummyIsScalar(const Symbol &subp, const Symbol &arg) { 3394bc56620bSPeter Steinfeld if (arg.Rank() > 0 || arg.Corank() > 0) { 3395bc56620bSPeter Steinfeld messages_.Say(arg.name(), 3396bc56620bSPeter Steinfeld "Dummy argument '%s' of a defined input/output procedure" 3397bc56620bSPeter Steinfeld " must be a scalar"_err_en_US, 3398bc56620bSPeter Steinfeld arg.name()); 3399bc56620bSPeter Steinfeld } 3400bc56620bSPeter Steinfeld } 3401bc56620bSPeter Steinfeld 3402dcf9ba82SPeter Klausler void CheckHelper::CheckDioDtvArg(const Symbol &subp, const Symbol *arg, 34037cf1608bSPeter Klausler common::DefinedIo ioKind, const Symbol &generic) { 3404bc56620bSPeter Steinfeld // Dtv argument looks like: dtv-type-spec, INTENT(INOUT) :: dtv 3405bc56620bSPeter Steinfeld if (CheckDioDummyIsData(subp, arg, 0)) { 3406dcf9ba82SPeter Klausler CheckDioDummyIsDerived(subp, *arg, ioKind, generic); 3407bc56620bSPeter Steinfeld CheckDioDummyAttrs(subp, *arg, 34087cf1608bSPeter Klausler ioKind == common::DefinedIo::ReadFormatted || 34097cf1608bSPeter Klausler ioKind == common::DefinedIo::ReadUnformatted 3410bc56620bSPeter Steinfeld ? Attr::INTENT_INOUT 3411bc56620bSPeter Steinfeld : Attr::INTENT_IN); 3412bc56620bSPeter Steinfeld } 3413bc56620bSPeter Steinfeld } 3414bc56620bSPeter Steinfeld 3415eb14135eSPeter Klausler // If an explicit INTRINSIC name is a function, so must all the specifics be, 3416eb14135eSPeter Klausler // and similarly for subroutines 3417eb14135eSPeter Klausler void CheckHelper::CheckGenericVsIntrinsic( 3418eb14135eSPeter Klausler const Symbol &symbol, const GenericDetails &generic) { 3419eb14135eSPeter Klausler if (symbol.attrs().test(Attr::INTRINSIC)) { 3420eb14135eSPeter Klausler const evaluate::IntrinsicProcTable &table{ 3421eb14135eSPeter Klausler context_.foldingContext().intrinsics()}; 3422eb14135eSPeter Klausler bool isSubroutine{table.IsIntrinsicSubroutine(symbol.name().ToString())}; 3423eb14135eSPeter Klausler if (isSubroutine || table.IsIntrinsicFunction(symbol.name().ToString())) { 3424eb14135eSPeter Klausler for (const SymbolRef &ref : generic.specificProcs()) { 3425eb14135eSPeter Klausler const Symbol &ultimate{ref->GetUltimate()}; 3426eb14135eSPeter Klausler bool specificFunc{ultimate.test(Symbol::Flag::Function)}; 3427eb14135eSPeter Klausler bool specificSubr{ultimate.test(Symbol::Flag::Subroutine)}; 3428eb14135eSPeter Klausler if (!specificFunc && !specificSubr) { 3429eb14135eSPeter Klausler if (const auto *proc{ultimate.detailsIf<SubprogramDetails>()}) { 3430eb14135eSPeter Klausler if (proc->isFunction()) { 3431eb14135eSPeter Klausler specificFunc = true; 3432eb14135eSPeter Klausler } else { 3433eb14135eSPeter Klausler specificSubr = true; 3434eb14135eSPeter Klausler } 3435eb14135eSPeter Klausler } 3436eb14135eSPeter Klausler } 3437eb14135eSPeter Klausler if ((specificFunc || specificSubr) && 3438eb14135eSPeter Klausler isSubroutine != specificSubr) { // C848 3439eb14135eSPeter Klausler messages_.Say(symbol.name(), 3440eb14135eSPeter Klausler "Generic interface '%s' with explicit intrinsic %s of the same name may not have specific procedure '%s' that is a %s"_err_en_US, 3441eb14135eSPeter Klausler symbol.name(), isSubroutine ? "subroutine" : "function", 3442eb14135eSPeter Klausler ref->name(), isSubroutine ? "function" : "subroutine"); 3443eb14135eSPeter Klausler } 3444eb14135eSPeter Klausler } 3445eb14135eSPeter Klausler } 3446eb14135eSPeter Klausler } 3447eb14135eSPeter Klausler } 3448eb14135eSPeter Klausler 3449bc56620bSPeter Steinfeld void CheckHelper::CheckDefaultIntegerArg( 3450bc56620bSPeter Steinfeld const Symbol &subp, const Symbol *arg, Attr intent) { 3451bc56620bSPeter Steinfeld // Argument looks like: INTEGER, INTENT(intent) :: arg 3452bc56620bSPeter Steinfeld if (CheckDioDummyIsData(subp, arg, 1)) { 3453bc56620bSPeter Steinfeld CheckDioDummyIsDefaultInteger(subp, *arg); 3454bc56620bSPeter Steinfeld CheckDioDummyIsScalar(subp, *arg); 3455bc56620bSPeter Steinfeld CheckDioDummyAttrs(subp, *arg, intent); 3456bc56620bSPeter Steinfeld } 3457bc56620bSPeter Steinfeld } 3458bc56620bSPeter Steinfeld 3459bc56620bSPeter Steinfeld void CheckHelper::CheckDioAssumedLenCharacterArg(const Symbol &subp, 3460bc56620bSPeter Steinfeld const Symbol *arg, std::size_t argPosition, Attr intent) { 3461bc56620bSPeter Steinfeld // Argument looks like: CHARACTER (LEN=*), INTENT(intent) :: (iotype OR iomsg) 3462bc56620bSPeter Steinfeld if (CheckDioDummyIsData(subp, arg, argPosition)) { 3463bc56620bSPeter Steinfeld CheckDioDummyAttrs(subp, *arg, intent); 346452962927SPeter Klausler const DeclTypeSpec *type{arg ? arg->GetType() : nullptr}; 346552962927SPeter Klausler const IntrinsicTypeSpec *intrinsic{type ? type->AsIntrinsic() : nullptr}; 346652962927SPeter Klausler const auto kind{ 346752962927SPeter Klausler intrinsic ? evaluate::ToInt64(intrinsic->kind()) : std::nullopt}; 346852962927SPeter Klausler if (!IsAssumedLengthCharacter(*arg) || 346952962927SPeter Klausler (!kind || 347052962927SPeter Klausler *kind != 347152962927SPeter Klausler context_.defaultKinds().GetDefaultKind( 347252962927SPeter Klausler TypeCategory::Character))) { 3473bc56620bSPeter Steinfeld messages_.Say(arg->name(), 3474bc56620bSPeter Steinfeld "Dummy argument '%s' of a defined input/output procedure" 347552962927SPeter Klausler " must be assumed-length CHARACTER of default kind"_err_en_US, 3476bc56620bSPeter Steinfeld arg->name()); 3477bc56620bSPeter Steinfeld } 3478bc56620bSPeter Steinfeld } 3479bc56620bSPeter Steinfeld } 3480bc56620bSPeter Steinfeld 3481bc56620bSPeter Steinfeld void CheckHelper::CheckDioVlistArg( 3482bc56620bSPeter Steinfeld const Symbol &subp, const Symbol *arg, std::size_t argPosition) { 3483bc56620bSPeter Steinfeld // Vlist argument looks like: INTEGER, INTENT(IN) :: v_list(:) 3484bc56620bSPeter Steinfeld if (CheckDioDummyIsData(subp, arg, argPosition)) { 3485bc56620bSPeter Steinfeld CheckDioDummyIsDefaultInteger(subp, *arg); 3486bc56620bSPeter Steinfeld CheckDioDummyAttrs(subp, *arg, Attr::INTENT_IN); 348744bc97c8SPeter Klausler const auto *objectDetails{arg->detailsIf<ObjectEntityDetails>()}; 348844bc97c8SPeter Klausler if (!objectDetails || !objectDetails->shape().CanBeDeferredShape()) { 3489bc56620bSPeter Steinfeld messages_.Say(arg->name(), 3490bc56620bSPeter Steinfeld "Dummy argument '%s' of a defined input/output procedure must be" 3491bc56620bSPeter Steinfeld " deferred shape"_err_en_US, 3492bc56620bSPeter Steinfeld arg->name()); 3493bc56620bSPeter Steinfeld } 3494bc56620bSPeter Steinfeld } 349544bc97c8SPeter Klausler } 3496bc56620bSPeter Steinfeld 3497bc56620bSPeter Steinfeld void CheckHelper::CheckDioArgCount( 34987cf1608bSPeter Klausler const Symbol &subp, common::DefinedIo ioKind, std::size_t argCount) { 3499bc56620bSPeter Steinfeld const std::size_t requiredArgCount{ 35007cf1608bSPeter Klausler (std::size_t)(ioKind == common::DefinedIo::ReadFormatted || 35017cf1608bSPeter Klausler ioKind == common::DefinedIo::WriteFormatted 3502bc56620bSPeter Steinfeld ? 6 3503bc56620bSPeter Steinfeld : 4)}; 3504bc56620bSPeter Steinfeld if (argCount != requiredArgCount) { 3505bc56620bSPeter Steinfeld SayWithDeclaration(subp, 3506bc56620bSPeter Steinfeld "Defined input/output procedure '%s' must have" 3507bc56620bSPeter Steinfeld " %d dummy arguments rather than %d"_err_en_US, 3508bc56620bSPeter Steinfeld subp.name(), requiredArgCount, argCount); 3509bc56620bSPeter Steinfeld context_.SetError(subp); 3510bc56620bSPeter Steinfeld } 3511bc56620bSPeter Steinfeld } 3512bc56620bSPeter Steinfeld 3513bc56620bSPeter Steinfeld void CheckHelper::CheckDioDummyAttrs( 3514bc56620bSPeter Steinfeld const Symbol &subp, const Symbol &arg, Attr goodIntent) { 3515bc56620bSPeter Steinfeld // Defined I/O procedures can't have attributes other than INTENT 3516bc56620bSPeter Steinfeld Attrs attrs{arg.attrs()}; 3517bc56620bSPeter Steinfeld if (!attrs.test(goodIntent)) { 3518bc56620bSPeter Steinfeld messages_.Say(arg.name(), 3519bc56620bSPeter Steinfeld "Dummy argument '%s' of a defined input/output procedure" 3520bc56620bSPeter Steinfeld " must have intent '%s'"_err_en_US, 3521bc56620bSPeter Steinfeld arg.name(), AttrToString(goodIntent)); 3522bc56620bSPeter Steinfeld } 3523bc56620bSPeter Steinfeld attrs = attrs - Attr::INTENT_IN - Attr::INTENT_OUT - Attr::INTENT_INOUT; 3524bc56620bSPeter Steinfeld if (!attrs.empty()) { 3525bc56620bSPeter Steinfeld messages_.Say(arg.name(), 3526bc56620bSPeter Steinfeld "Dummy argument '%s' of a defined input/output procedure may not have" 3527bc56620bSPeter Steinfeld " any attributes"_err_en_US, 3528bc56620bSPeter Steinfeld arg.name()); 3529bc56620bSPeter Steinfeld } 3530bc56620bSPeter Steinfeld } 3531bc56620bSPeter Steinfeld 3532bc56620bSPeter Steinfeld // Enforce semantics for defined input/output procedures (12.6.4.8.2) and C777 3533bc56620bSPeter Steinfeld void CheckHelper::CheckDefinedIoProc(const Symbol &symbol, 35347cf1608bSPeter Klausler const GenericDetails &details, common::DefinedIo ioKind) { 3535bc56620bSPeter Steinfeld for (auto ref : details.specificProcs()) { 353652962927SPeter Klausler const Symbol &ultimate{ref->GetUltimate()}; 353752962927SPeter Klausler const auto *binding{ultimate.detailsIf<ProcBindingDetails>()}; 353852962927SPeter Klausler const Symbol &specific{*(binding ? &binding->symbol() : &ultimate)}; 353952962927SPeter Klausler if (ultimate.attrs().test(Attr::NOPASS)) { // C774 3540bc56620bSPeter Steinfeld messages_.Say("Defined input/output procedure '%s' may not have NOPASS " 3541bc56620bSPeter Steinfeld "attribute"_err_en_US, 354252962927SPeter Klausler ultimate.name()); 354352962927SPeter Klausler context_.SetError(ultimate); 3544bc56620bSPeter Steinfeld } 3545bc56620bSPeter Steinfeld if (const auto *subpDetails{specific.detailsIf<SubprogramDetails>()}) { 3546bc56620bSPeter Steinfeld const std::vector<Symbol *> &dummyArgs{subpDetails->dummyArgs()}; 3547bc56620bSPeter Steinfeld CheckDioArgCount(specific, ioKind, dummyArgs.size()); 3548bc56620bSPeter Steinfeld int argCount{0}; 3549bc56620bSPeter Steinfeld for (auto *arg : dummyArgs) { 3550bc56620bSPeter Steinfeld switch (argCount++) { 3551bc56620bSPeter Steinfeld case 0: 3552bc56620bSPeter Steinfeld // dtv-type-spec, INTENT(INOUT) :: dtv 3553dcf9ba82SPeter Klausler CheckDioDtvArg(specific, arg, ioKind, symbol); 3554bc56620bSPeter Steinfeld break; 3555bc56620bSPeter Steinfeld case 1: 3556bc56620bSPeter Steinfeld // INTEGER, INTENT(IN) :: unit 3557bc56620bSPeter Steinfeld CheckDefaultIntegerArg(specific, arg, Attr::INTENT_IN); 3558bc56620bSPeter Steinfeld break; 3559bc56620bSPeter Steinfeld case 2: 35607cf1608bSPeter Klausler if (ioKind == common::DefinedIo::ReadFormatted || 35617cf1608bSPeter Klausler ioKind == common::DefinedIo::WriteFormatted) { 3562bc56620bSPeter Steinfeld // CHARACTER (LEN=*), INTENT(IN) :: iotype 3563bc56620bSPeter Steinfeld CheckDioAssumedLenCharacterArg( 3564bc56620bSPeter Steinfeld specific, arg, argCount, Attr::INTENT_IN); 3565bc56620bSPeter Steinfeld } else { 3566bc56620bSPeter Steinfeld // INTEGER, INTENT(OUT) :: iostat 3567bc56620bSPeter Steinfeld CheckDefaultIntegerArg(specific, arg, Attr::INTENT_OUT); 3568bc56620bSPeter Steinfeld } 3569bc56620bSPeter Steinfeld break; 3570bc56620bSPeter Steinfeld case 3: 35717cf1608bSPeter Klausler if (ioKind == common::DefinedIo::ReadFormatted || 35727cf1608bSPeter Klausler ioKind == common::DefinedIo::WriteFormatted) { 3573bc56620bSPeter Steinfeld // INTEGER, INTENT(IN) :: v_list(:) 3574bc56620bSPeter Steinfeld CheckDioVlistArg(specific, arg, argCount); 3575bc56620bSPeter Steinfeld } else { 3576bc56620bSPeter Steinfeld // CHARACTER (LEN=*), INTENT(INOUT) :: iomsg 3577bc56620bSPeter Steinfeld CheckDioAssumedLenCharacterArg( 3578bc56620bSPeter Steinfeld specific, arg, argCount, Attr::INTENT_INOUT); 3579bc56620bSPeter Steinfeld } 3580bc56620bSPeter Steinfeld break; 3581bc56620bSPeter Steinfeld case 4: 3582bc56620bSPeter Steinfeld // INTEGER, INTENT(OUT) :: iostat 3583bc56620bSPeter Steinfeld CheckDefaultIntegerArg(specific, arg, Attr::INTENT_OUT); 3584bc56620bSPeter Steinfeld break; 3585bc56620bSPeter Steinfeld case 5: 3586bc56620bSPeter Steinfeld // CHARACTER (LEN=*), INTENT(INOUT) :: iomsg 3587bc56620bSPeter Steinfeld CheckDioAssumedLenCharacterArg( 3588bc56620bSPeter Steinfeld specific, arg, argCount, Attr::INTENT_INOUT); 3589bc56620bSPeter Steinfeld break; 3590bc56620bSPeter Steinfeld default:; 3591bc56620bSPeter Steinfeld } 3592bc56620bSPeter Steinfeld } 3593bc56620bSPeter Steinfeld } 3594bc56620bSPeter Steinfeld } 3595bc56620bSPeter Steinfeld } 3596bc56620bSPeter Steinfeld 359778da80e2SPeter Klausler void CheckHelper::CheckSymbolType(const Symbol &symbol) { 35980bb3260bSPeter Klausler const Symbol *result{FindFunctionResult(symbol)}; 35990bb3260bSPeter Klausler const Symbol &relevant{result ? *result : symbol}; 36000bb3260bSPeter Klausler if (IsAllocatable(relevant)) { // always ok 36013cef82d6SPeter Klausler } else if (IsProcedurePointer(symbol) && result && IsPointer(*result)) { 36023cef82d6SPeter Klausler // procedure pointer returning allocatable or pointer: ok 36030bb3260bSPeter Klausler } else if (IsPointer(relevant) && !IsProcedure(relevant)) { 36040bb3260bSPeter Klausler // object pointers are always ok 36050bb3260bSPeter Klausler } else if (auto dyType{evaluate::DynamicType::From(relevant)}) { 36060bb3260bSPeter Klausler if (dyType->IsPolymorphic() && !dyType->IsAssumedType() && 36070bb3260bSPeter Klausler !(IsDummy(symbol) && !IsProcedure(relevant))) { // C708 36080bb3260bSPeter Klausler messages_.Say( 36090bb3260bSPeter Klausler "CLASS entity '%s' must be a dummy argument, allocatable, or object pointer"_err_en_US, 36100bb3260bSPeter Klausler symbol.name()); 36110bb3260bSPeter Klausler } 36120bb3260bSPeter Klausler if (dyType->HasDeferredTypeParameter()) { // C702 361378da80e2SPeter Klausler messages_.Say( 3614bcd0bf92SPeter Klausler "'%s' has a type %s with a deferred type parameter but is neither an allocatable nor an object pointer"_err_en_US, 361578da80e2SPeter Klausler symbol.name(), dyType->AsFortran()); 361678da80e2SPeter Klausler } 361778da80e2SPeter Klausler } 361878da80e2SPeter Klausler } 361978da80e2SPeter Klausler 3620f7be1aadSPeter Klausler void CheckHelper::CheckModuleProcedureDef(const Symbol &symbol) { 3621f7be1aadSPeter Klausler auto procClass{ClassifyProcedure(symbol)}; 3622f7be1aadSPeter Klausler if (const auto *subprogram{symbol.detailsIf<SubprogramDetails>()}; 3623f7be1aadSPeter Klausler subprogram && 3624f7be1aadSPeter Klausler (procClass == ProcedureDefinitionClass::Module && 3625f7be1aadSPeter Klausler symbol.attrs().test(Attr::MODULE)) && 3626f7be1aadSPeter Klausler !subprogram->bindName() && !subprogram->isInterface()) { 362777e965efSPeter Klausler const Symbol &interface { 362877e965efSPeter Klausler subprogram->moduleInterface() ? *subprogram->moduleInterface() : symbol 362977e965efSPeter Klausler }; 363077e965efSPeter Klausler if (const Symbol * 363177e965efSPeter Klausler module{interface.owner().kind() == Scope::Kind::Module 363277e965efSPeter Klausler ? interface.owner().symbol() 363377e965efSPeter Klausler : nullptr}; 363477e965efSPeter Klausler module && module->has<ModuleDetails>()) { 3635f7be1aadSPeter Klausler std::pair<SourceName, const Symbol *> key{symbol.name(), module}; 3636f7be1aadSPeter Klausler auto iter{moduleProcs_.find(key)}; 3637f7be1aadSPeter Klausler if (iter == moduleProcs_.end()) { 3638f7be1aadSPeter Klausler moduleProcs_.emplace(std::move(key), symbol); 3639f7be1aadSPeter Klausler } else if ( 3640f7be1aadSPeter Klausler auto *msg{messages_.Say(symbol.name(), 364177e965efSPeter Klausler "Module procedure '%s' in '%s' has multiple definitions"_err_en_US, 364277e965efSPeter Klausler symbol.name(), GetModuleOrSubmoduleName(*module))}) { 3643f7be1aadSPeter Klausler msg->Attach(iter->second->name(), "Previous definition of '%s'"_en_US, 3644f7be1aadSPeter Klausler symbol.name()); 3645f7be1aadSPeter Klausler } 3646f7be1aadSPeter Klausler } 3647f7be1aadSPeter Klausler } 3648f7be1aadSPeter Klausler } 3649f7be1aadSPeter Klausler 365061b1390eSTim Keith void SubprogramMatchHelper::Check( 365161b1390eSTim Keith const Symbol &symbol1, const Symbol &symbol2) { 365261b1390eSTim Keith const auto details1{symbol1.get<SubprogramDetails>()}; 365361b1390eSTim Keith const auto details2{symbol2.get<SubprogramDetails>()}; 365461b1390eSTim Keith if (details1.isFunction() != details2.isFunction()) { 365561b1390eSTim Keith Say(symbol1, symbol2, 365661b1390eSTim Keith details1.isFunction() 365761b1390eSTim Keith ? "Module function '%s' was declared as a subroutine in the" 365861b1390eSTim Keith " corresponding interface body"_err_en_US 365961b1390eSTim Keith : "Module subroutine '%s' was declared as a function in the" 366061b1390eSTim Keith " corresponding interface body"_err_en_US); 366161b1390eSTim Keith return; 366261b1390eSTim Keith } 366361b1390eSTim Keith const auto &args1{details1.dummyArgs()}; 366461b1390eSTim Keith const auto &args2{details2.dummyArgs()}; 366561b1390eSTim Keith int nargs1{static_cast<int>(args1.size())}; 366661b1390eSTim Keith int nargs2{static_cast<int>(args2.size())}; 366761b1390eSTim Keith if (nargs1 != nargs2) { 366861b1390eSTim Keith Say(symbol1, symbol2, 366961b1390eSTim Keith "Module subprogram '%s' has %d args but the corresponding interface" 367061b1390eSTim Keith " body has %d"_err_en_US, 367161b1390eSTim Keith nargs1, nargs2); 367261b1390eSTim Keith return; 367361b1390eSTim Keith } 367461b1390eSTim Keith bool nonRecursive1{symbol1.attrs().test(Attr::NON_RECURSIVE)}; 367561b1390eSTim Keith if (nonRecursive1 != symbol2.attrs().test(Attr::NON_RECURSIVE)) { // C1551 367661b1390eSTim Keith Say(symbol1, symbol2, 367761b1390eSTim Keith nonRecursive1 367861b1390eSTim Keith ? "Module subprogram '%s' has NON_RECURSIVE prefix but" 367961b1390eSTim Keith " the corresponding interface body does not"_err_en_US 368061b1390eSTim Keith : "Module subprogram '%s' does not have NON_RECURSIVE prefix but " 368161b1390eSTim Keith "the corresponding interface body does"_err_en_US); 368261b1390eSTim Keith } 36835d3249e9STim Keith const std::string *bindName1{details1.bindName()}; 36845d3249e9STim Keith const std::string *bindName2{details2.bindName()}; 36855d3249e9STim Keith if (!bindName1 && !bindName2) { 36865d3249e9STim Keith // OK - neither has a binding label 36875d3249e9STim Keith } else if (!bindName1) { 368861b1390eSTim Keith Say(symbol1, symbol2, 36895d3249e9STim Keith "Module subprogram '%s' does not have a binding label but the" 369061b1390eSTim Keith " corresponding interface body does"_err_en_US); 36915d3249e9STim Keith } else if (!bindName2) { 369261b1390eSTim Keith Say(symbol1, symbol2, 36935d3249e9STim Keith "Module subprogram '%s' has a binding label but the" 36945d3249e9STim Keith " corresponding interface body does not"_err_en_US); 36955d3249e9STim Keith } else if (*bindName1 != *bindName2) { 36965d3249e9STim Keith Say(symbol1, symbol2, 36975d3249e9STim Keith "Module subprogram '%s' has binding label '%s' but the corresponding" 36985d3249e9STim Keith " interface body has '%s'"_err_en_US, 36995d3249e9STim Keith *details1.bindName(), *details2.bindName()); 370061b1390eSTim Keith } 370182edd428STim Keith const Procedure *proc1{checkHelper.Characterize(symbol1)}; 370282edd428STim Keith const Procedure *proc2{checkHelper.Characterize(symbol2)}; 370361b1390eSTim Keith if (!proc1 || !proc2) { 370461b1390eSTim Keith return; 370561b1390eSTim Keith } 370639686557SPeter Klausler if (proc1->attrs.test(Procedure::Attr::Pure) != 370739686557SPeter Klausler proc2->attrs.test(Procedure::Attr::Pure)) { 370839686557SPeter Klausler Say(symbol1, symbol2, 370939686557SPeter Klausler "Module subprogram '%s' and its corresponding interface body are not both PURE"_err_en_US); 371039686557SPeter Klausler } 371139686557SPeter Klausler if (proc1->attrs.test(Procedure::Attr::Elemental) != 371239686557SPeter Klausler proc2->attrs.test(Procedure::Attr::Elemental)) { 371339686557SPeter Klausler Say(symbol1, symbol2, 371439686557SPeter Klausler "Module subprogram '%s' and its corresponding interface body are not both ELEMENTAL"_err_en_US); 371539686557SPeter Klausler } 371639686557SPeter Klausler if (proc1->attrs.test(Procedure::Attr::BindC) != 371739686557SPeter Klausler proc2->attrs.test(Procedure::Attr::BindC)) { 371839686557SPeter Klausler Say(symbol1, symbol2, 371939686557SPeter Klausler "Module subprogram '%s' and its corresponding interface body are not both BIND(C)"_err_en_US); 372039686557SPeter Klausler } 372111529d5bSPeter Klausler if (proc1->functionResult && proc2->functionResult) { 372211529d5bSPeter Klausler std::string whyNot; 372311529d5bSPeter Klausler if (!proc1->functionResult->IsCompatibleWith( 372411529d5bSPeter Klausler *proc2->functionResult, &whyNot)) { 372561b1390eSTim Keith Say(symbol1, symbol2, 372611529d5bSPeter Klausler "Result of function '%s' is not compatible with the result of the corresponding interface body: %s"_err_en_US, 372711529d5bSPeter Klausler whyNot); 372811529d5bSPeter Klausler } 372961b1390eSTim Keith } 373061b1390eSTim Keith for (int i{0}; i < nargs1; ++i) { 373161b1390eSTim Keith const Symbol *arg1{args1[i]}; 373261b1390eSTim Keith const Symbol *arg2{args2[i]}; 373361b1390eSTim Keith if (arg1 && !arg2) { 373461b1390eSTim Keith Say(symbol1, symbol2, 373561b1390eSTim Keith "Dummy argument %2$d of '%1$s' is not an alternate return indicator" 373661b1390eSTim Keith " but the corresponding argument in the interface body is"_err_en_US, 373761b1390eSTim Keith i + 1); 373861b1390eSTim Keith } else if (!arg1 && arg2) { 373961b1390eSTim Keith Say(symbol1, symbol2, 374061b1390eSTim Keith "Dummy argument %2$d of '%1$s' is an alternate return indicator but" 374161b1390eSTim Keith " the corresponding argument in the interface body is not"_err_en_US, 374261b1390eSTim Keith i + 1); 374361b1390eSTim Keith } else if (arg1 && arg2) { 374461b1390eSTim Keith SourceName name1{arg1->name()}; 374561b1390eSTim Keith SourceName name2{arg2->name()}; 374661b1390eSTim Keith if (name1 != name2) { 374761b1390eSTim Keith Say(*arg1, *arg2, 374861b1390eSTim Keith "Dummy argument name '%s' does not match corresponding name '%s'" 374961b1390eSTim Keith " in interface body"_err_en_US, 375061b1390eSTim Keith name2); 375161b1390eSTim Keith } else { 375261b1390eSTim Keith CheckDummyArg( 375361b1390eSTim Keith *arg1, *arg2, proc1->dummyArguments[i], proc2->dummyArguments[i]); 375461b1390eSTim Keith } 375561b1390eSTim Keith } 375661b1390eSTim Keith } 375761b1390eSTim Keith } 375861b1390eSTim Keith 375961b1390eSTim Keith void SubprogramMatchHelper::CheckDummyArg(const Symbol &symbol1, 376061b1390eSTim Keith const Symbol &symbol2, const DummyArgument &arg1, 376161b1390eSTim Keith const DummyArgument &arg2) { 3762cd03e96fSPeter Klausler common::visit( 3763cd03e96fSPeter Klausler common::visitors{ 376461b1390eSTim Keith [&](const DummyDataObject &obj1, const DummyDataObject &obj2) { 376561b1390eSTim Keith CheckDummyDataObject(symbol1, symbol2, obj1, obj2); 376661b1390eSTim Keith }, 376761b1390eSTim Keith [&](const DummyProcedure &proc1, const DummyProcedure &proc2) { 376861b1390eSTim Keith CheckDummyProcedure(symbol1, symbol2, proc1, proc2); 376961b1390eSTim Keith }, 377061b1390eSTim Keith [&](const DummyDataObject &, const auto &) { 377161b1390eSTim Keith Say(symbol1, symbol2, 377261b1390eSTim Keith "Dummy argument '%s' is a data object; the corresponding" 377361b1390eSTim Keith " argument in the interface body is not"_err_en_US); 377461b1390eSTim Keith }, 377561b1390eSTim Keith [&](const DummyProcedure &, const auto &) { 377661b1390eSTim Keith Say(symbol1, symbol2, 377761b1390eSTim Keith "Dummy argument '%s' is a procedure; the corresponding" 377861b1390eSTim Keith " argument in the interface body is not"_err_en_US); 377961b1390eSTim Keith }, 378093626984SDavid Truby [&](const auto &, const auto &) { 378193626984SDavid Truby llvm_unreachable("Dummy arguments are not data objects or" 378293626984SDavid Truby "procedures"); 378393626984SDavid Truby }, 378461b1390eSTim Keith }, 378561b1390eSTim Keith arg1.u, arg2.u); 378661b1390eSTim Keith } 378761b1390eSTim Keith 378861b1390eSTim Keith void SubprogramMatchHelper::CheckDummyDataObject(const Symbol &symbol1, 378961b1390eSTim Keith const Symbol &symbol2, const DummyDataObject &obj1, 379061b1390eSTim Keith const DummyDataObject &obj2) { 379161b1390eSTim Keith if (!CheckSameIntent(symbol1, symbol2, obj1.intent, obj2.intent)) { 379261b1390eSTim Keith } else if (!CheckSameAttrs(symbol1, symbol2, obj1.attrs, obj2.attrs)) { 3793c2f642d9SPeter Klausler } else if (!obj1.type.type().IsEquivalentTo(obj2.type.type())) { 379461b1390eSTim Keith Say(symbol1, symbol2, 3795c2f642d9SPeter Klausler "Dummy argument '%s' has type %s; the corresponding argument in the interface body has distinct type %s"_err_en_US, 379661b1390eSTim Keith obj1.type.type().AsFortran(), obj2.type.type().AsFortran()); 379761b1390eSTim Keith } else if (!ShapesAreCompatible(obj1, obj2)) { 379861b1390eSTim Keith Say(symbol1, symbol2, 379961b1390eSTim Keith "The shape of dummy argument '%s' does not match the shape of the" 380061b1390eSTim Keith " corresponding argument in the interface body"_err_en_US); 380161b1390eSTim Keith } 380261b1390eSTim Keith // TODO: coshape 380361b1390eSTim Keith } 380461b1390eSTim Keith 380561b1390eSTim Keith void SubprogramMatchHelper::CheckDummyProcedure(const Symbol &symbol1, 380661b1390eSTim Keith const Symbol &symbol2, const DummyProcedure &proc1, 380761b1390eSTim Keith const DummyProcedure &proc2) { 3808ce5edfd2SPeter Klausler std::string whyNot; 380961b1390eSTim Keith if (!CheckSameIntent(symbol1, symbol2, proc1.intent, proc2.intent)) { 381061b1390eSTim Keith } else if (!CheckSameAttrs(symbol1, symbol2, proc1.attrs, proc2.attrs)) { 3811ce5edfd2SPeter Klausler } else if (!proc2.IsCompatibleWith(proc1, &whyNot)) { 381261b1390eSTim Keith Say(symbol1, symbol2, 3813ce5edfd2SPeter Klausler "Dummy procedure '%s' is not compatible with the corresponding argument in the interface body: %s"_err_en_US, 3814ce5edfd2SPeter Klausler whyNot); 3815ce5edfd2SPeter Klausler } else if (proc1 != proc2) { 3816ce5edfd2SPeter Klausler evaluate::AttachDeclaration( 3817ce5edfd2SPeter Klausler symbol1.owner().context().Warn( 3818ce5edfd2SPeter Klausler common::UsageWarning::MismatchingDummyProcedure, 3819ce5edfd2SPeter Klausler "Dummy procedure '%s' does not exactly match the corresponding argument in the interface body"_warn_en_US, 3820ce5edfd2SPeter Klausler symbol1.name()), 3821ce5edfd2SPeter Klausler symbol2); 382261b1390eSTim Keith } 382361b1390eSTim Keith } 382461b1390eSTim Keith 382561b1390eSTim Keith bool SubprogramMatchHelper::CheckSameIntent(const Symbol &symbol1, 382661b1390eSTim Keith const Symbol &symbol2, common::Intent intent1, common::Intent intent2) { 382761b1390eSTim Keith if (intent1 == intent2) { 382861b1390eSTim Keith return true; 382961b1390eSTim Keith } else { 383061b1390eSTim Keith Say(symbol1, symbol2, 383161b1390eSTim Keith "The intent of dummy argument '%s' does not match the intent" 383261b1390eSTim Keith " of the corresponding argument in the interface body"_err_en_US); 383361b1390eSTim Keith return false; 383461b1390eSTim Keith } 383561b1390eSTim Keith } 383661b1390eSTim Keith 383761b1390eSTim Keith // Report an error referring to first symbol with declaration of second symbol 383861b1390eSTim Keith template <typename... A> 383961b1390eSTim Keith void SubprogramMatchHelper::Say(const Symbol &symbol1, const Symbol &symbol2, 384061b1390eSTim Keith parser::MessageFixedText &&text, A &&...args) { 384182edd428STim Keith auto &message{context().Say(symbol1.name(), std::move(text), symbol1.name(), 384261b1390eSTim Keith std::forward<A>(args)...)}; 384361b1390eSTim Keith evaluate::AttachDeclaration(message, symbol2); 384461b1390eSTim Keith } 384561b1390eSTim Keith 384661b1390eSTim Keith template <typename ATTRS> 384761b1390eSTim Keith bool SubprogramMatchHelper::CheckSameAttrs( 384861b1390eSTim Keith const Symbol &symbol1, const Symbol &symbol2, ATTRS attrs1, ATTRS attrs2) { 384961b1390eSTim Keith if (attrs1 == attrs2) { 385061b1390eSTim Keith return true; 385161b1390eSTim Keith } 385261b1390eSTim Keith attrs1.IterateOverMembers([&](auto attr) { 385361b1390eSTim Keith if (!attrs2.test(attr)) { 385461b1390eSTim Keith Say(symbol1, symbol2, 385561b1390eSTim Keith "Dummy argument '%s' has the %s attribute; the corresponding" 385661b1390eSTim Keith " argument in the interface body does not"_err_en_US, 385761b1390eSTim Keith AsFortran(attr)); 385861b1390eSTim Keith } 385961b1390eSTim Keith }); 386061b1390eSTim Keith attrs2.IterateOverMembers([&](auto attr) { 386161b1390eSTim Keith if (!attrs1.test(attr)) { 386261b1390eSTim Keith Say(symbol1, symbol2, 386361b1390eSTim Keith "Dummy argument '%s' does not have the %s attribute; the" 386461b1390eSTim Keith " corresponding argument in the interface body does"_err_en_US, 386561b1390eSTim Keith AsFortran(attr)); 386661b1390eSTim Keith } 386761b1390eSTim Keith }); 386861b1390eSTim Keith return false; 386961b1390eSTim Keith } 387061b1390eSTim Keith 387161b1390eSTim Keith bool SubprogramMatchHelper::ShapesAreCompatible( 387261b1390eSTim Keith const DummyDataObject &obj1, const DummyDataObject &obj2) { 387382edd428STim Keith return characteristics::ShapesAreCompatible( 387461b1390eSTim Keith FoldShape(obj1.type.shape()), FoldShape(obj2.type.shape())); 387561b1390eSTim Keith } 387661b1390eSTim Keith 387761b1390eSTim Keith evaluate::Shape SubprogramMatchHelper::FoldShape(const evaluate::Shape &shape) { 387861b1390eSTim Keith evaluate::Shape result; 387961b1390eSTim Keith for (const auto &extent : shape) { 388061b1390eSTim Keith result.emplace_back( 388182edd428STim Keith evaluate::Fold(context().foldingContext(), common::Clone(extent))); 388261b1390eSTim Keith } 388361b1390eSTim Keith return result; 388461b1390eSTim Keith } 388561b1390eSTim Keith 388682edd428STim Keith void DistinguishabilityHelper::Add(const Symbol &generic, GenericKind kind, 3887e87cdda3SPeter Klausler const Symbol &ultimateSpecific, const Procedure &procedure) { 3888e87cdda3SPeter Klausler if (!context_.HasError(ultimateSpecific)) { 3889e87cdda3SPeter Klausler nameToSpecifics_[generic.name()].emplace( 3890e87cdda3SPeter Klausler &ultimateSpecific, ProcedureInfo{kind, procedure}); 389182edd428STim Keith } 389282edd428STim Keith } 389382edd428STim Keith 389486f59de1STim Keith void DistinguishabilityHelper::Check(const Scope &scope) { 38952236048fSPeter Klausler if (FindModuleFileContaining(scope)) { 38962236048fSPeter Klausler // Distinguishability was checked when the module was created; 38972236048fSPeter Klausler // don't let optional warnings then become errors now. 38982236048fSPeter Klausler return; 38992236048fSPeter Klausler } 3900e87cdda3SPeter Klausler for (const auto &[name, info] : nameToSpecifics_) { 3901e87cdda3SPeter Klausler for (auto iter1{info.begin()}; iter1 != info.end(); ++iter1) { 3902e87cdda3SPeter Klausler const auto &[ultimate, procInfo]{*iter1}; 3903e87cdda3SPeter Klausler const auto &[kind, proc]{procInfo}; 3904e87cdda3SPeter Klausler for (auto iter2{iter1}; ++iter2 != info.end();) { 3905e3b2f1b6Speter klausler auto distinguishable{kind.IsName() 390682edd428STim Keith ? evaluate::characteristics::Distinguishable 390782edd428STim Keith : evaluate::characteristics::DistinguishableOpOrAssign}; 39083d115700SPeter Klausler std::optional<bool> distinct{distinguishable( 39093d115700SPeter Klausler context_.languageFeatures(), proc, iter2->second.procedure)}; 39103d115700SPeter Klausler if (!distinct.value_or(false)) { 3911e3b2f1b6Speter klausler SayNotDistinguishable(GetTopLevelUnitContaining(scope), name, kind, 39123d115700SPeter Klausler *ultimate, *iter2->first, distinct.has_value()); 391382edd428STim Keith } 391482edd428STim Keith } 391582edd428STim Keith } 391682edd428STim Keith } 391782edd428STim Keith } 391882edd428STim Keith 391986f59de1STim Keith void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope, 392086f59de1STim Keith const SourceName &name, GenericKind kind, const Symbol &proc1, 39212236048fSPeter Klausler const Symbol &proc2, bool isHardConflict) { 39222236048fSPeter Klausler bool isUseAssociated{!scope.sourceRange().Contains(name)}; 392337180ed7SPeter Klausler // The rules for distinguishing specific procedures (F'2023 15.4.3.4.5) 39243d115700SPeter Klausler // are inadequate for some real-world cases like pFUnit. 39253d115700SPeter Klausler // When there are optional dummy arguments or unlimited polymorphic 39263d115700SPeter Klausler // dummy data object arguments, the best that we can do is emit an optional 39272236048fSPeter Klausler // portability warning. Also, named generics created by USE association 39282236048fSPeter Klausler // merging shouldn't receive hard errors for ambiguity. 39292236048fSPeter Klausler // (Non-named generics might be defined I/O procedures or defined 39302236048fSPeter Klausler // assignments that need to be used by the runtime.) 39312236048fSPeter Klausler bool isWarning{!isHardConflict || (isUseAssociated && kind.IsName())}; 39322236048fSPeter Klausler if (isWarning && 39332236048fSPeter Klausler (!context_.ShouldWarn( 39342236048fSPeter Klausler common::LanguageFeature::IndistinguishableSpecifics) || 39352236048fSPeter Klausler FindModuleFileContaining(scope))) { 393637180ed7SPeter Klausler return; 393737180ed7SPeter Klausler } 393882edd428STim Keith std::string name1{proc1.name().ToString()}; 393982edd428STim Keith std::string name2{proc2.name().ToString()}; 394082edd428STim Keith if (kind.IsOperator() || kind.IsAssignment()) { 394182edd428STim Keith // proc1 and proc2 may come from different scopes so qualify their names 394282edd428STim Keith if (proc1.owner().IsDerivedType()) { 394382edd428STim Keith name1 = proc1.owner().GetName()->ToString() + '%' + name1; 394482edd428STim Keith } 394582edd428STim Keith if (proc2.owner().IsDerivedType()) { 394682edd428STim Keith name2 = proc2.owner().GetName()->ToString() + '%' + name2; 394782edd428STim Keith } 394882edd428STim Keith } 394986f59de1STim Keith parser::Message *msg; 39502236048fSPeter Klausler if (!isUseAssociated) { 39512236048fSPeter Klausler CHECK(isWarning == !isHardConflict); 395286f59de1STim Keith msg = &context_.Say(name, 39532236048fSPeter Klausler isHardConflict 39543d115700SPeter Klausler ? "Generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US 39553d115700SPeter Klausler : "Generic '%s' should not have specific procedures '%s' and '%s' as their interfaces are not distinguishable by the rules in the standard"_port_en_US, 395686f59de1STim Keith MakeOpName(name), name1, name2); 395786f59de1STim Keith } else { 395886f59de1STim Keith msg = &context_.Say(*GetTopLevelUnitContaining(proc1).GetName(), 39592236048fSPeter Klausler isHardConflict 39602236048fSPeter Klausler ? (isWarning 39612236048fSPeter Klausler ? "USE-associated generic '%s' should not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_warn_en_US 39622236048fSPeter Klausler : "USE-associated generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US) 39632236048fSPeter Klausler : "USE-associated generic '%s' should not have specific procedures '%s' and '%s' as their interfaces are not distinguishable by the rules in the standard"_port_en_US, 396486f59de1STim Keith MakeOpName(name), name1, name2); 396586f59de1STim Keith } 396686f59de1STim Keith AttachDeclaration(*msg, scope, proc1); 396786f59de1STim Keith AttachDeclaration(*msg, scope, proc2); 396886f59de1STim Keith } 396986f59de1STim Keith 397086f59de1STim Keith // `evaluate::AttachDeclaration` doesn't handle the generic case where `proc` 397186f59de1STim Keith // comes from a different module but is not necessarily use-associated. 397286f59de1STim Keith void DistinguishabilityHelper::AttachDeclaration( 397386f59de1STim Keith parser::Message &msg, const Scope &scope, const Symbol &proc) { 397486f59de1STim Keith const Scope &unit{GetTopLevelUnitContaining(proc)}; 397586f59de1STim Keith if (unit == scope) { 397686f59de1STim Keith evaluate::AttachDeclaration(msg, proc); 397786f59de1STim Keith } else { 397886f59de1STim Keith msg.Attach(unit.GetName().value(), 397986f59de1STim Keith "'%s' is USE-associated from module '%s'"_en_US, proc.name(), 398086f59de1STim Keith unit.GetName().value()); 398186f59de1STim Keith } 398282edd428STim Keith } 398382edd428STim Keith 398464ab3302SCarolineConcatto void CheckDeclarations(SemanticsContext &context) { 398564ab3302SCarolineConcatto CheckHelper{context}.Check(); 398664ab3302SCarolineConcatto } 39871f879005STim Keith } // namespace Fortran::semantics 3988