xref: /llvm-project/flang/lib/Semantics/check-declarations.cpp (revision c596aae47ad8cfaee0fe4af3c104cb89a1125ac5)
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