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"
12641ede93Speter klausler #include "pointer-assignment.h"
1364ab3302SCarolineConcatto #include "flang/Evaluate/check-expression.h"
1464ab3302SCarolineConcatto #include "flang/Evaluate/fold.h"
1564ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
1664ab3302SCarolineConcatto #include "flang/Semantics/scope.h"
1764ab3302SCarolineConcatto #include "flang/Semantics/semantics.h"
1864ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
1964ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
2064ab3302SCarolineConcatto #include "flang/Semantics/type.h"
2164ab3302SCarolineConcatto #include <algorithm>
22b6f22fa5Speter klausler #include <map>
23b6f22fa5Speter klausler #include <string>
2464ab3302SCarolineConcatto 
2564ab3302SCarolineConcatto namespace Fortran::semantics {
2664ab3302SCarolineConcatto 
2782edd428STim Keith namespace characteristics = evaluate::characteristics;
2882edd428STim Keith using characteristics::DummyArgument;
2982edd428STim Keith using characteristics::DummyDataObject;
3082edd428STim Keith using characteristics::DummyProcedure;
3182edd428STim Keith using characteristics::FunctionResult;
3282edd428STim Keith using characteristics::Procedure;
3364ab3302SCarolineConcatto 
3464ab3302SCarolineConcatto class CheckHelper {
3564ab3302SCarolineConcatto public:
CheckHelper(SemanticsContext & c)3664ab3302SCarolineConcatto   explicit CheckHelper(SemanticsContext &c) : context_{c} {}
3764ab3302SCarolineConcatto 
context()3882edd428STim Keith   SemanticsContext &context() { return context_; }
Check()3964ab3302SCarolineConcatto   void Check() { Check(context_.globalScope()); }
4064ab3302SCarolineConcatto   void Check(const ParamValue &, bool canBeAssumed);
Check(const Bound & bound)4138095549SPete Steinfeld   void Check(const Bound &bound) { CheckSpecExpr(bound.GetExplicit()); }
Check(const ShapeSpec & spec)4264ab3302SCarolineConcatto   void Check(const ShapeSpec &spec) {
4364ab3302SCarolineConcatto     Check(spec.lbound());
4464ab3302SCarolineConcatto     Check(spec.ubound());
4564ab3302SCarolineConcatto   }
4664ab3302SCarolineConcatto   void Check(const ArraySpec &);
4764ab3302SCarolineConcatto   void Check(const DeclTypeSpec &, bool canHaveAssumedTypeParameters);
4864ab3302SCarolineConcatto   void Check(const Symbol &);
49dafd3cf8SPeixin-Qiao   void CheckCommonBlock(const Symbol &);
5064ab3302SCarolineConcatto   void Check(const Scope &);
5182edd428STim Keith   const Procedure *Characterize(const Symbol &);
5264ab3302SCarolineConcatto 
5364ab3302SCarolineConcatto private:
CheckSpecExpr(const A & x)5438095549SPete Steinfeld   template <typename A> void CheckSpecExpr(const A &x) {
55641ede93Speter klausler     evaluate::CheckSpecificationExpr(x, DEREF(scope_), foldingContext_);
5664ab3302SCarolineConcatto   }
5764ab3302SCarolineConcatto   void CheckValue(const Symbol &, const DerivedTypeSpec *);
582de5ea3bSpeter klausler   void CheckVolatile(const Symbol &, const DerivedTypeSpec *);
5964ab3302SCarolineConcatto   void CheckPointer(const Symbol &);
6064ab3302SCarolineConcatto   void CheckPassArg(
6164ab3302SCarolineConcatto       const Symbol &proc, const Symbol *interface, const WithPassArg &);
6264ab3302SCarolineConcatto   void CheckProcBinding(const Symbol &, const ProcBindingDetails &);
6364ab3302SCarolineConcatto   void CheckObjectEntity(const Symbol &, const ObjectEntityDetails &);
64641ede93Speter klausler   void CheckPointerInitialization(const Symbol &);
6564ab3302SCarolineConcatto   void CheckArraySpec(const Symbol &, const ArraySpec &);
6664ab3302SCarolineConcatto   void CheckProcEntity(const Symbol &, const ProcEntityDetails &);
6761b1390eSTim Keith   void CheckSubprogram(const Symbol &, const SubprogramDetails &);
6864ab3302SCarolineConcatto   void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &);
6964ab3302SCarolineConcatto   void CheckDerivedType(const Symbol &, const DerivedTypeDetails &);
7037b2e2b0Speter klausler   bool CheckFinal(
7137b2e2b0Speter klausler       const Symbol &subroutine, SourceName, const Symbol &derivedType);
7237b2e2b0Speter klausler   bool CheckDistinguishableFinals(const Symbol &f1, SourceName f1name,
7337b2e2b0Speter klausler       const Symbol &f2, SourceName f2name, const Symbol &derivedType);
7464ab3302SCarolineConcatto   void CheckGeneric(const Symbol &, const GenericDetails &);
7582edd428STim Keith   void CheckHostAssoc(const Symbol &, const HostAssocDetails &);
7682edd428STim Keith   bool CheckDefinedOperator(
7782edd428STim Keith       SourceName, GenericKind, const Symbol &, const Procedure &);
7864ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> CheckNumberOfArgs(
7964ab3302SCarolineConcatto       const GenericKind &, std::size_t);
8064ab3302SCarolineConcatto   bool CheckDefinedOperatorArg(
8164ab3302SCarolineConcatto       const SourceName &, const Symbol &, const Procedure &, std::size_t);
8264ab3302SCarolineConcatto   bool CheckDefinedAssignment(const Symbol &, const Procedure &);
8364ab3302SCarolineConcatto   bool CheckDefinedAssignmentArg(const Symbol &, const DummyArgument &, int);
8482edd428STim Keith   void CheckSpecificsAreDistinguishable(const Symbol &, const GenericDetails &);
8564ab3302SCarolineConcatto   void CheckEquivalenceSet(const EquivalenceSet &);
8664ab3302SCarolineConcatto   void CheckBlockData(const Scope &);
8782edd428STim Keith   void CheckGenericOps(const Scope &);
8864ab3302SCarolineConcatto   bool CheckConflicting(const Symbol &, Attr, Attr);
89c1168676Speter klausler   void WarnMissingFinal(const Symbol &);
InPure() const9064ab3302SCarolineConcatto   bool InPure() const {
9164ab3302SCarolineConcatto     return innermostSymbol_ && IsPureProcedure(*innermostSymbol_);
9264ab3302SCarolineConcatto   }
InElemental() const9343a263f5Speter klausler   bool InElemental() const {
94*6052025bSPeter Klausler     return innermostSymbol_ && IsElementalProcedure(*innermostSymbol_);
9543a263f5Speter klausler   }
InFunction() const9664ab3302SCarolineConcatto   bool InFunction() const {
9764ab3302SCarolineConcatto     return innermostSymbol_ && IsFunction(*innermostSymbol_);
9864ab3302SCarolineConcatto   }
9964ab3302SCarolineConcatto   template <typename... A>
SayWithDeclaration(const Symbol & symbol,A &&...x)10064ab3302SCarolineConcatto   void SayWithDeclaration(const Symbol &symbol, A &&...x) {
10164ab3302SCarolineConcatto     if (parser::Message * msg{messages_.Say(std::forward<A>(x)...)}) {
10261b1390eSTim Keith       if (messages_.at().begin() != symbol.name().begin()) {
10364ab3302SCarolineConcatto         evaluate::AttachDeclaration(*msg, symbol);
10464ab3302SCarolineConcatto       }
10564ab3302SCarolineConcatto     }
10664ab3302SCarolineConcatto   }
107c42f6314Speter klausler   bool IsResultOkToDiffer(const FunctionResult &);
108f3d83353SPeixinQiao   void CheckBindC(const Symbol &);
109bc56620bSPeter Steinfeld   // Check functions for defined I/O procedures
110bc56620bSPeter Steinfeld   void CheckDefinedIoProc(
111bc56620bSPeter Steinfeld       const Symbol &, const GenericDetails &, GenericKind::DefinedIo);
112bc56620bSPeter Steinfeld   bool CheckDioDummyIsData(const Symbol &, const Symbol *, std::size_t);
113dcf9ba82SPeter Klausler   void CheckDioDummyIsDerived(const Symbol &, const Symbol &,
114dcf9ba82SPeter Klausler       GenericKind::DefinedIo ioKind, const Symbol &);
115bc56620bSPeter Steinfeld   void CheckDioDummyIsDefaultInteger(const Symbol &, const Symbol &);
116bc56620bSPeter Steinfeld   void CheckDioDummyIsScalar(const Symbol &, const Symbol &);
117bc56620bSPeter Steinfeld   void CheckDioDummyAttrs(const Symbol &, const Symbol &, Attr);
118dcf9ba82SPeter Klausler   void CheckDioDtvArg(
119dcf9ba82SPeter Klausler       const Symbol &, const Symbol *, GenericKind::DefinedIo, const Symbol &);
120eb14135eSPeter Klausler   void CheckGenericVsIntrinsic(const Symbol &, const GenericDetails &);
121bc56620bSPeter Steinfeld   void CheckDefaultIntegerArg(const Symbol &, const Symbol *, Attr);
122bc56620bSPeter Steinfeld   void CheckDioAssumedLenCharacterArg(
123bc56620bSPeter Steinfeld       const Symbol &, const Symbol *, std::size_t, Attr);
124bc56620bSPeter Steinfeld   void CheckDioVlistArg(const Symbol &, const Symbol *, std::size_t);
125bc56620bSPeter Steinfeld   void CheckDioArgCount(
126bc56620bSPeter Steinfeld       const Symbol &, GenericKind::DefinedIo ioKind, std::size_t);
12722d7e298SPeter Steinfeld   struct TypeWithDefinedIo {
128dcf9ba82SPeter Klausler     const DerivedTypeSpec &type;
12922d7e298SPeter Steinfeld     GenericKind::DefinedIo ioKind;
13022d7e298SPeter Steinfeld     const Symbol &proc;
131dcf9ba82SPeter Klausler     const Symbol &generic;
13222d7e298SPeter Steinfeld   };
133dcf9ba82SPeter Klausler   void CheckAlreadySeenDefinedIo(const DerivedTypeSpec &,
134dcf9ba82SPeter Klausler       GenericKind::DefinedIo, const Symbol &, const Symbol &generic);
13564ab3302SCarolineConcatto 
13664ab3302SCarolineConcatto   SemanticsContext &context_;
13764ab3302SCarolineConcatto   evaluate::FoldingContext &foldingContext_{context_.foldingContext()};
13864ab3302SCarolineConcatto   parser::ContextualMessages &messages_{foldingContext_.messages()};
13964ab3302SCarolineConcatto   const Scope *scope_{nullptr};
140641ede93Speter klausler   bool scopeIsUninstantiatedPDT_{false};
14164ab3302SCarolineConcatto   // This symbol is the one attached to the innermost enclosing scope
14264ab3302SCarolineConcatto   // that has a symbol.
14364ab3302SCarolineConcatto   const Symbol *innermostSymbol_{nullptr};
14482edd428STim Keith   // Cache of calls to Procedure::Characterize(Symbol)
1450d8331c0Speter klausler   std::map<SymbolRef, std::optional<Procedure>, SymbolAddressCompare>
1460d8331c0Speter klausler       characterizeCache_;
147b6f22fa5Speter klausler   // Collection of symbols with BIND(C) names
148b6f22fa5Speter klausler   std::map<std::string, SymbolRef> bindC_;
14922d7e298SPeter Steinfeld   // Derived types that have defined input/output procedures
15022d7e298SPeter Steinfeld   std::vector<TypeWithDefinedIo> seenDefinedIoTypes_;
15182edd428STim Keith };
15282edd428STim Keith 
15382edd428STim Keith class DistinguishabilityHelper {
15482edd428STim Keith public:
DistinguishabilityHelper(SemanticsContext & context)15582edd428STim Keith   DistinguishabilityHelper(SemanticsContext &context) : context_{context} {}
15682edd428STim Keith   void Add(const Symbol &, GenericKind, const Symbol &, const Procedure &);
15786f59de1STim Keith   void Check(const Scope &);
15882edd428STim Keith 
15982edd428STim Keith private:
16086f59de1STim Keith   void SayNotDistinguishable(const Scope &, const SourceName &, GenericKind,
16186f59de1STim Keith       const Symbol &, const Symbol &);
16286f59de1STim Keith   void AttachDeclaration(parser::Message &, const Scope &, const Symbol &);
16382edd428STim Keith 
16482edd428STim Keith   SemanticsContext &context_;
16582edd428STim Keith   struct ProcedureInfo {
16682edd428STim Keith     GenericKind kind;
16782edd428STim Keith     const Symbol &symbol;
16882edd428STim Keith     const Procedure &procedure;
16982edd428STim Keith   };
17082edd428STim Keith   std::map<SourceName, std::vector<ProcedureInfo>> nameToInfo_;
17164ab3302SCarolineConcatto };
17264ab3302SCarolineConcatto 
Check(const ParamValue & value,bool canBeAssumed)17364ab3302SCarolineConcatto void CheckHelper::Check(const ParamValue &value, bool canBeAssumed) {
17464ab3302SCarolineConcatto   if (value.isAssumed()) {
175657aaf8bSPete Steinfeld     if (!canBeAssumed) { // C795, C721, C726
17664ab3302SCarolineConcatto       messages_.Say(
177657aaf8bSPete Steinfeld           "An assumed (*) type parameter may be used only for a (non-statement"
178657aaf8bSPete Steinfeld           " function) dummy argument, associate name, named constant, or"
179657aaf8bSPete Steinfeld           " external function result"_err_en_US);
18064ab3302SCarolineConcatto     }
18164ab3302SCarolineConcatto   } else {
18238095549SPete Steinfeld     CheckSpecExpr(value.GetExplicit());
18364ab3302SCarolineConcatto   }
18464ab3302SCarolineConcatto }
18564ab3302SCarolineConcatto 
Check(const ArraySpec & shape)18664ab3302SCarolineConcatto void CheckHelper::Check(const ArraySpec &shape) {
18764ab3302SCarolineConcatto   for (const auto &spec : shape) {
18864ab3302SCarolineConcatto     Check(spec);
18964ab3302SCarolineConcatto   }
19064ab3302SCarolineConcatto }
19164ab3302SCarolineConcatto 
Check(const DeclTypeSpec & type,bool canHaveAssumedTypeParameters)19264ab3302SCarolineConcatto void CheckHelper::Check(
19364ab3302SCarolineConcatto     const DeclTypeSpec &type, bool canHaveAssumedTypeParameters) {
19464ab3302SCarolineConcatto   if (type.category() == DeclTypeSpec::Character) {
19564ab3302SCarolineConcatto     Check(type.characterTypeSpec().length(), canHaveAssumedTypeParameters);
19664ab3302SCarolineConcatto   } else if (const DerivedTypeSpec * derived{type.AsDerived()}) {
19764ab3302SCarolineConcatto     for (auto &parm : derived->parameters()) {
19864ab3302SCarolineConcatto       Check(parm.second, canHaveAssumedTypeParameters);
19964ab3302SCarolineConcatto     }
20064ab3302SCarolineConcatto   }
20164ab3302SCarolineConcatto }
20264ab3302SCarolineConcatto 
Check(const Symbol & symbol)20364ab3302SCarolineConcatto void CheckHelper::Check(const Symbol &symbol) {
204a2ac0bb2SPeixinQiao   if (symbol.name().size() > common::maxNameLen) {
205a2ac0bb2SPeixinQiao     messages_.Say(symbol.name(),
206a2ac0bb2SPeixinQiao         "%s has length %d, which is greater than the maximum name length "
207a2ac0bb2SPeixinQiao         "%d"_port_en_US,
208a2ac0bb2SPeixinQiao         symbol.name(), symbol.name().size(), common::maxNameLen);
209a2ac0bb2SPeixinQiao   }
21064ab3302SCarolineConcatto   if (context_.HasError(symbol)) {
21164ab3302SCarolineConcatto     return;
21264ab3302SCarolineConcatto   }
21364ab3302SCarolineConcatto   auto restorer{messages_.SetLocation(symbol.name())};
21464ab3302SCarolineConcatto   context_.set_location(symbol.name());
215641ede93Speter klausler   const DeclTypeSpec *type{symbol.GetType()};
216641ede93Speter klausler   const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr};
2172de5ea3bSpeter klausler   bool isDone{false};
218cd03e96fSPeter Klausler   common::visit(
21964ab3302SCarolineConcatto       common::visitors{
2202de5ea3bSpeter klausler           [&](const UseDetails &x) { isDone = true; },
2212de5ea3bSpeter klausler           [&](const HostAssocDetails &x) {
2222de5ea3bSpeter klausler             CheckHostAssoc(symbol, x);
2232de5ea3bSpeter klausler             isDone = true;
2242de5ea3bSpeter klausler           },
2252de5ea3bSpeter klausler           [&](const ProcBindingDetails &x) {
2262de5ea3bSpeter klausler             CheckProcBinding(symbol, x);
2272de5ea3bSpeter klausler             isDone = true;
2282de5ea3bSpeter klausler           },
22964ab3302SCarolineConcatto           [&](const ObjectEntityDetails &x) { CheckObjectEntity(symbol, x); },
23064ab3302SCarolineConcatto           [&](const ProcEntityDetails &x) { CheckProcEntity(symbol, x); },
23161b1390eSTim Keith           [&](const SubprogramDetails &x) { CheckSubprogram(symbol, x); },
23264ab3302SCarolineConcatto           [&](const DerivedTypeDetails &x) { CheckDerivedType(symbol, x); },
23364ab3302SCarolineConcatto           [&](const GenericDetails &x) { CheckGeneric(symbol, x); },
23464ab3302SCarolineConcatto           [](const auto &) {},
23564ab3302SCarolineConcatto       },
23664ab3302SCarolineConcatto       symbol.details());
2372de5ea3bSpeter klausler   if (symbol.attrs().test(Attr::VOLATILE)) {
2382de5ea3bSpeter klausler     CheckVolatile(symbol, derived);
2392de5ea3bSpeter klausler   }
240f3d83353SPeixinQiao   CheckBindC(symbol);
2412de5ea3bSpeter klausler   if (isDone) {
2422de5ea3bSpeter klausler     return; // following checks do not apply
2432de5ea3bSpeter klausler   }
2442de5ea3bSpeter klausler   if (IsPointer(symbol)) {
2452de5ea3bSpeter klausler     CheckPointer(symbol);
2462de5ea3bSpeter klausler   }
24764ab3302SCarolineConcatto   if (InPure()) {
24864ab3302SCarolineConcatto     if (IsSaved(symbol)) {
2492985d562SPeter Klausler       if (IsInitialized(symbol)) {
2502985d562SPeter Klausler         messages_.Say(
2512985d562SPeter Klausler             "A pure subprogram may not initialize a variable"_err_en_US);
2522985d562SPeter Klausler       } else {
25364ab3302SCarolineConcatto         messages_.Say(
25464ab3302SCarolineConcatto             "A pure subprogram may not have a variable with the SAVE attribute"_err_en_US);
25564ab3302SCarolineConcatto       }
2562985d562SPeter Klausler     }
25764ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::VOLATILE)) {
25864ab3302SCarolineConcatto       messages_.Say(
25964ab3302SCarolineConcatto           "A pure subprogram may not have a variable with the VOLATILE attribute"_err_en_US);
26064ab3302SCarolineConcatto     }
26164ab3302SCarolineConcatto     if (IsProcedure(symbol) && !IsPureProcedure(symbol) && IsDummy(symbol)) {
26264ab3302SCarolineConcatto       messages_.Say(
26364ab3302SCarolineConcatto           "A dummy procedure of a pure subprogram must be pure"_err_en_US);
26464ab3302SCarolineConcatto     }
26564ab3302SCarolineConcatto     if (!IsDummy(symbol) && !IsFunctionResult(symbol)) {
26664ab3302SCarolineConcatto       if (IsPolymorphicAllocatable(symbol)) {
26764ab3302SCarolineConcatto         SayWithDeclaration(symbol,
26864ab3302SCarolineConcatto             "Deallocation of polymorphic object '%s' is not permitted in a pure subprogram"_err_en_US,
26964ab3302SCarolineConcatto             symbol.name());
27064ab3302SCarolineConcatto       } else if (derived) {
27164ab3302SCarolineConcatto         if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) {
27264ab3302SCarolineConcatto           SayWithDeclaration(*bad,
27364ab3302SCarolineConcatto               "Deallocation of polymorphic object '%s%s' is not permitted in a pure subprogram"_err_en_US,
27464ab3302SCarolineConcatto               symbol.name(), bad.BuildResultDesignatorName());
27564ab3302SCarolineConcatto         }
27664ab3302SCarolineConcatto       }
27764ab3302SCarolineConcatto     }
27864ab3302SCarolineConcatto   }
279657aaf8bSPete Steinfeld   if (type) { // Section 7.2, paragraph 7
28064ab3302SCarolineConcatto     bool canHaveAssumedParameter{IsNamedConstant(symbol) ||
281c42f6314Speter klausler         (IsAssumedLengthCharacter(symbol) && // C722
282c42f6314Speter klausler             IsExternal(symbol)) ||
28364ab3302SCarolineConcatto         symbol.test(Symbol::Flag::ParentComp)};
284657aaf8bSPete Steinfeld     if (!IsStmtFunctionDummy(symbol)) { // C726
28564ab3302SCarolineConcatto       if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
28664ab3302SCarolineConcatto         canHaveAssumedParameter |= object->isDummy() ||
28764ab3302SCarolineConcatto             (object->isFuncResult() &&
288657aaf8bSPete Steinfeld                 type->category() == DeclTypeSpec::Character) ||
289657aaf8bSPete Steinfeld             IsStmtFunctionResult(symbol); // Avoids multiple messages
29064ab3302SCarolineConcatto       } else {
29164ab3302SCarolineConcatto         canHaveAssumedParameter |= symbol.has<AssocEntityDetails>();
29264ab3302SCarolineConcatto       }
293657aaf8bSPete Steinfeld     }
29464ab3302SCarolineConcatto     Check(*type, canHaveAssumedParameter);
29564ab3302SCarolineConcatto     if (InPure() && InFunction() && IsFunctionResult(symbol)) {
29664ab3302SCarolineConcatto       if (derived && HasImpureFinal(*derived)) { // C1584
29764ab3302SCarolineConcatto         messages_.Say(
29864ab3302SCarolineConcatto             "Result of pure function may not have an impure FINAL subroutine"_err_en_US);
29964ab3302SCarolineConcatto       }
30064ab3302SCarolineConcatto       if (type->IsPolymorphic() && IsAllocatable(symbol)) { // C1585
30164ab3302SCarolineConcatto         messages_.Say(
30264ab3302SCarolineConcatto             "Result of pure function may not be both polymorphic and ALLOCATABLE"_err_en_US);
30364ab3302SCarolineConcatto       }
30464ab3302SCarolineConcatto       if (derived) {
30564ab3302SCarolineConcatto         if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) {
30664ab3302SCarolineConcatto           SayWithDeclaration(*bad,
30764ab3302SCarolineConcatto               "Result of pure function may not have polymorphic ALLOCATABLE ultimate component '%s'"_err_en_US,
30864ab3302SCarolineConcatto               bad.BuildResultDesignatorName());
30964ab3302SCarolineConcatto         }
31064ab3302SCarolineConcatto       }
31164ab3302SCarolineConcatto     }
31264ab3302SCarolineConcatto   }
313c42f6314Speter klausler   if (IsAssumedLengthCharacter(symbol) && IsExternal(symbol)) { // C723
31464ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::RECURSIVE)) {
31564ab3302SCarolineConcatto       messages_.Say(
31664ab3302SCarolineConcatto           "An assumed-length CHARACTER(*) function cannot be RECURSIVE"_err_en_US);
31764ab3302SCarolineConcatto     }
31864ab3302SCarolineConcatto     if (symbol.Rank() > 0) {
31964ab3302SCarolineConcatto       messages_.Say(
32064ab3302SCarolineConcatto           "An assumed-length CHARACTER(*) function cannot return an array"_err_en_US);
32164ab3302SCarolineConcatto     }
322*6052025bSPeter Klausler     if (IsElementalProcedure(symbol)) {
32364ab3302SCarolineConcatto       messages_.Say(
32464ab3302SCarolineConcatto           "An assumed-length CHARACTER(*) function cannot be ELEMENTAL"_err_en_US);
325*6052025bSPeter Klausler     } else if (IsPureProcedure(symbol)) {
326*6052025bSPeter Klausler       messages_.Say(
327*6052025bSPeter Klausler           "An assumed-length CHARACTER(*) function cannot be PURE"_err_en_US);
32864ab3302SCarolineConcatto     }
32964ab3302SCarolineConcatto     if (const Symbol * result{FindFunctionResult(symbol)}) {
33064ab3302SCarolineConcatto       if (IsPointer(*result)) {
33164ab3302SCarolineConcatto         messages_.Say(
33264ab3302SCarolineConcatto             "An assumed-length CHARACTER(*) function cannot return a POINTER"_err_en_US);
33364ab3302SCarolineConcatto       }
33464ab3302SCarolineConcatto     }
33564ab3302SCarolineConcatto   }
33664ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::VALUE)) {
33764ab3302SCarolineConcatto     CheckValue(symbol, derived);
33864ab3302SCarolineConcatto   }
33964ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::CONTIGUOUS) && IsPointer(symbol) &&
34064ab3302SCarolineConcatto       symbol.Rank() == 0) { // C830
34164ab3302SCarolineConcatto     messages_.Say("CONTIGUOUS POINTER must be an array"_err_en_US);
34264ab3302SCarolineConcatto   }
343c42f6314Speter klausler   if (IsDummy(symbol)) {
344c42f6314Speter klausler     if (IsNamedConstant(symbol)) {
345c42f6314Speter klausler       messages_.Say(
346c42f6314Speter klausler           "A dummy argument may not also be a named constant"_err_en_US);
347c42f6314Speter klausler     }
348a48e4168Speter klausler     if (!symbol.test(Symbol::Flag::InDataStmt) /*caught elsewhere*/ &&
349a48e4168Speter klausler         IsSaved(symbol)) {
350c42f6314Speter klausler       messages_.Say(
351c42f6314Speter klausler           "A dummy argument may not have the SAVE attribute"_err_en_US);
352c42f6314Speter klausler     }
3534171f80dSpeter klausler   } else if (IsFunctionResult(symbol)) {
3545491fdf5SPeixin-Qiao     if (IsNamedConstant(symbol)) {
3555491fdf5SPeixin-Qiao       messages_.Say(
3565491fdf5SPeixin-Qiao           "A function result may not also be a named constant"_err_en_US);
3575491fdf5SPeixin-Qiao     }
358a48e4168Speter klausler     if (!symbol.test(Symbol::Flag::InDataStmt) /*caught elsewhere*/ &&
359a48e4168Speter klausler         IsSaved(symbol)) {
3604171f80dSpeter klausler       messages_.Say(
3614171f80dSpeter klausler           "A function result may not have the SAVE attribute"_err_en_US);
3624171f80dSpeter klausler     }
363c42f6314Speter klausler   }
36438095549SPete Steinfeld   if (symbol.owner().IsDerivedType() &&
36538095549SPete Steinfeld       (symbol.attrs().test(Attr::CONTIGUOUS) &&
36638095549SPete Steinfeld           !(IsPointer(symbol) && symbol.Rank() > 0))) { // C752
36738095549SPete Steinfeld     messages_.Say(
36838095549SPete Steinfeld         "A CONTIGUOUS component must be an array with the POINTER attribute"_err_en_US);
36938095549SPete Steinfeld   }
370b297563aSTim Keith   if (symbol.owner().IsModule() && IsAutomatic(symbol)) {
371b297563aSTim Keith     messages_.Say(
372b297563aSTim Keith         "Automatic data object '%s' may not appear in the specification part"
373b297563aSTim Keith         " of a module"_err_en_US,
374b297563aSTim Keith         symbol.name());
375b297563aSTim Keith   }
37664ab3302SCarolineConcatto }
37764ab3302SCarolineConcatto 
CheckCommonBlock(const Symbol & symbol)378dafd3cf8SPeixin-Qiao void CheckHelper::CheckCommonBlock(const Symbol &symbol) { CheckBindC(symbol); }
379dafd3cf8SPeixin-Qiao 
CheckValue(const Symbol & symbol,const DerivedTypeSpec * derived)38064ab3302SCarolineConcatto void CheckHelper::CheckValue(
38164ab3302SCarolineConcatto     const Symbol &symbol, const DerivedTypeSpec *derived) { // C863 - C865
38264ab3302SCarolineConcatto   if (!IsDummy(symbol)) {
38364ab3302SCarolineConcatto     messages_.Say(
38464ab3302SCarolineConcatto         "VALUE attribute may apply only to a dummy argument"_err_en_US);
38564ab3302SCarolineConcatto   }
38664ab3302SCarolineConcatto   if (IsProcedure(symbol)) {
38764ab3302SCarolineConcatto     messages_.Say(
38864ab3302SCarolineConcatto         "VALUE attribute may apply only to a dummy data object"_err_en_US);
38964ab3302SCarolineConcatto   }
39064ab3302SCarolineConcatto   if (IsAssumedSizeArray(symbol)) {
39164ab3302SCarolineConcatto     messages_.Say(
39264ab3302SCarolineConcatto         "VALUE attribute may not apply to an assumed-size array"_err_en_US);
39364ab3302SCarolineConcatto   }
3941ee6f7adSPeter Klausler   if (evaluate::IsCoarray(symbol)) {
39564ab3302SCarolineConcatto     messages_.Say("VALUE attribute may not apply to a coarray"_err_en_US);
39664ab3302SCarolineConcatto   }
39764ab3302SCarolineConcatto   if (IsAllocatable(symbol)) {
39864ab3302SCarolineConcatto     messages_.Say("VALUE attribute may not apply to an ALLOCATABLE"_err_en_US);
39964ab3302SCarolineConcatto   } else if (IsPointer(symbol)) {
40064ab3302SCarolineConcatto     messages_.Say("VALUE attribute may not apply to a POINTER"_err_en_US);
40164ab3302SCarolineConcatto   }
40264ab3302SCarolineConcatto   if (IsIntentInOut(symbol)) {
40364ab3302SCarolineConcatto     messages_.Say(
40464ab3302SCarolineConcatto         "VALUE attribute may not apply to an INTENT(IN OUT) argument"_err_en_US);
40564ab3302SCarolineConcatto   } else if (IsIntentOut(symbol)) {
40664ab3302SCarolineConcatto     messages_.Say(
40764ab3302SCarolineConcatto         "VALUE attribute may not apply to an INTENT(OUT) argument"_err_en_US);
40864ab3302SCarolineConcatto   }
40964ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::VOLATILE)) {
41064ab3302SCarolineConcatto     messages_.Say("VALUE attribute may not apply to a VOLATILE"_err_en_US);
41164ab3302SCarolineConcatto   }
41264ab3302SCarolineConcatto   if (innermostSymbol_ && IsBindCProcedure(*innermostSymbol_) &&
41364ab3302SCarolineConcatto       IsOptional(symbol)) {
41464ab3302SCarolineConcatto     messages_.Say(
41564ab3302SCarolineConcatto         "VALUE attribute may not apply to an OPTIONAL in a BIND(C) procedure"_err_en_US);
41664ab3302SCarolineConcatto   }
41764ab3302SCarolineConcatto   if (derived) {
41864ab3302SCarolineConcatto     if (FindCoarrayUltimateComponent(*derived)) {
41964ab3302SCarolineConcatto       messages_.Say(
42064ab3302SCarolineConcatto           "VALUE attribute may not apply to a type with a coarray ultimate component"_err_en_US);
42164ab3302SCarolineConcatto     }
42264ab3302SCarolineConcatto   }
42364ab3302SCarolineConcatto }
42464ab3302SCarolineConcatto 
CheckAssumedTypeEntity(const Symbol & symbol,const ObjectEntityDetails & details)42564ab3302SCarolineConcatto void CheckHelper::CheckAssumedTypeEntity( // C709
42664ab3302SCarolineConcatto     const Symbol &symbol, const ObjectEntityDetails &details) {
42764ab3302SCarolineConcatto   if (const DeclTypeSpec * type{symbol.GetType()};
42864ab3302SCarolineConcatto       type && type->category() == DeclTypeSpec::TypeStar) {
42914f49599STim Keith     if (!IsDummy(symbol)) {
43064ab3302SCarolineConcatto       messages_.Say(
43164ab3302SCarolineConcatto           "Assumed-type entity '%s' must be a dummy argument"_err_en_US,
43264ab3302SCarolineConcatto           symbol.name());
43364ab3302SCarolineConcatto     } else {
43464ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::ALLOCATABLE)) {
43564ab3302SCarolineConcatto         messages_.Say("Assumed-type argument '%s' cannot have the ALLOCATABLE"
43664ab3302SCarolineConcatto                       " attribute"_err_en_US,
43764ab3302SCarolineConcatto             symbol.name());
43864ab3302SCarolineConcatto       }
43964ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::POINTER)) {
44064ab3302SCarolineConcatto         messages_.Say("Assumed-type argument '%s' cannot have the POINTER"
44164ab3302SCarolineConcatto                       " attribute"_err_en_US,
44264ab3302SCarolineConcatto             symbol.name());
44364ab3302SCarolineConcatto       }
44464ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::VALUE)) {
44564ab3302SCarolineConcatto         messages_.Say("Assumed-type argument '%s' cannot have the VALUE"
44664ab3302SCarolineConcatto                       " attribute"_err_en_US,
44764ab3302SCarolineConcatto             symbol.name());
44864ab3302SCarolineConcatto       }
44964ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::INTENT_OUT)) {
45064ab3302SCarolineConcatto         messages_.Say(
45164ab3302SCarolineConcatto             "Assumed-type argument '%s' cannot be INTENT(OUT)"_err_en_US,
45264ab3302SCarolineConcatto             symbol.name());
45364ab3302SCarolineConcatto       }
4541ee6f7adSPeter Klausler       if (evaluate::IsCoarray(symbol)) {
45564ab3302SCarolineConcatto         messages_.Say(
45664ab3302SCarolineConcatto             "Assumed-type argument '%s' cannot be a coarray"_err_en_US,
45764ab3302SCarolineConcatto             symbol.name());
45864ab3302SCarolineConcatto       }
459a0a1f519STim Keith       if (details.IsArray() && details.shape().IsExplicitShape()) {
460a0a1f519STim Keith         messages_.Say(
461a0a1f519STim Keith             "Assumed-type array argument 'arg8' must be assumed shape,"
462a0a1f519STim Keith             " assumed size, or assumed rank"_err_en_US,
46364ab3302SCarolineConcatto             symbol.name());
46464ab3302SCarolineConcatto       }
46564ab3302SCarolineConcatto     }
46664ab3302SCarolineConcatto   }
46764ab3302SCarolineConcatto }
46864ab3302SCarolineConcatto 
CheckObjectEntity(const Symbol & symbol,const ObjectEntityDetails & details)46964ab3302SCarolineConcatto void CheckHelper::CheckObjectEntity(
47064ab3302SCarolineConcatto     const Symbol &symbol, const ObjectEntityDetails &details) {
47164ab3302SCarolineConcatto   CheckArraySpec(symbol, details.shape());
47264ab3302SCarolineConcatto   Check(details.shape());
47364ab3302SCarolineConcatto   Check(details.coshape());
474940871ddSPeter Klausler   if (details.shape().Rank() > common::maxRank) {
475940871ddSPeter Klausler     messages_.Say(
476940871ddSPeter Klausler         "'%s' has rank %d, which is greater than the maximum supported rank %d"_err_en_US,
477940871ddSPeter Klausler         symbol.name(), details.shape().Rank(), common::maxRank);
478940871ddSPeter Klausler   } else if (details.shape().Rank() + details.coshape().Rank() >
479940871ddSPeter Klausler       common::maxRank) {
480940871ddSPeter Klausler     messages_.Say(
481940871ddSPeter Klausler         "'%s' has rank %d and corank %d, whose sum is greater than the maximum supported rank %d"_err_en_US,
482940871ddSPeter Klausler         symbol.name(), details.shape().Rank(), details.coshape().Rank(),
483940871ddSPeter Klausler         common::maxRank);
484940871ddSPeter Klausler   }
48564ab3302SCarolineConcatto   CheckAssumedTypeEntity(symbol, details);
486c1168676Speter klausler   WarnMissingFinal(symbol);
48764ab3302SCarolineConcatto   if (!details.coshape().empty()) {
48844bc97c8SPeter Klausler     bool isDeferredCoshape{details.coshape().CanBeDeferredShape()};
48964ab3302SCarolineConcatto     if (IsAllocatable(symbol)) {
49052711fb8Speter klausler       if (!isDeferredCoshape) { // C827
4918d0c3c05SPete Steinfeld         messages_.Say("'%s' is an ALLOCATABLE coarray and must have a deferred"
4928d0c3c05SPete Steinfeld                       " coshape"_err_en_US,
4938d0c3c05SPete Steinfeld             symbol.name());
49464ab3302SCarolineConcatto       }
4958d0c3c05SPete Steinfeld     } else if (symbol.owner().IsDerivedType()) { // C746
4968d0c3c05SPete Steinfeld       std::string deferredMsg{
49752711fb8Speter klausler           isDeferredCoshape ? "" : " and have a deferred coshape"};
4988d0c3c05SPete Steinfeld       messages_.Say("Component '%s' is a coarray and must have the ALLOCATABLE"
4998d0c3c05SPete Steinfeld                     " attribute%s"_err_en_US,
5008d0c3c05SPete Steinfeld           symbol.name(), deferredMsg);
50164ab3302SCarolineConcatto     } else {
50244bc97c8SPeter Klausler       if (!details.coshape().CanBeAssumedSize()) { // C828
50364ab3302SCarolineConcatto         messages_.Say(
50452711fb8Speter klausler             "'%s' is a non-ALLOCATABLE coarray and must have an explicit coshape"_err_en_US,
50552711fb8Speter klausler             symbol.name());
50652711fb8Speter klausler       }
50752711fb8Speter klausler     }
50852711fb8Speter klausler     if (const DeclTypeSpec * type{details.type()}) {
50952711fb8Speter klausler       if (IsBadCoarrayType(type->AsDerived())) { // C747 & C824
51052711fb8Speter klausler         messages_.Say(
51152711fb8Speter klausler             "Coarray '%s' may not have type TEAM_TYPE, C_PTR, or C_FUNPTR"_err_en_US,
5128d0c3c05SPete Steinfeld             symbol.name());
51364ab3302SCarolineConcatto       }
51464ab3302SCarolineConcatto     }
51564ab3302SCarolineConcatto   }
51664ab3302SCarolineConcatto   if (details.isDummy()) {
51764ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::INTENT_OUT)) {
51864ab3302SCarolineConcatto       if (FindUltimateComponent(symbol, [](const Symbol &x) {
5191ee6f7adSPeter Klausler             return evaluate::IsCoarray(x) && IsAllocatable(x);
52064ab3302SCarolineConcatto           })) { // C846
52164ab3302SCarolineConcatto         messages_.Say(
52264ab3302SCarolineConcatto             "An INTENT(OUT) dummy argument may not be, or contain, an ALLOCATABLE coarray"_err_en_US);
52364ab3302SCarolineConcatto       }
52464ab3302SCarolineConcatto       if (IsOrContainsEventOrLockComponent(symbol)) { // C847
52564ab3302SCarolineConcatto         messages_.Say(
52664ab3302SCarolineConcatto             "An INTENT(OUT) dummy argument may not be, or contain, EVENT_TYPE or LOCK_TYPE"_err_en_US);
52764ab3302SCarolineConcatto       }
52864ab3302SCarolineConcatto     }
5298d0c3c05SPete Steinfeld     if (InPure() && !IsStmtFunction(DEREF(innermostSymbol_)) &&
5308d0c3c05SPete Steinfeld         !IsPointer(symbol) && !IsIntentIn(symbol) &&
53164ab3302SCarolineConcatto         !symbol.attrs().test(Attr::VALUE)) {
53264ab3302SCarolineConcatto       if (InFunction()) { // C1583
53364ab3302SCarolineConcatto         messages_.Say(
53464ab3302SCarolineConcatto             "non-POINTER dummy argument of pure function must be INTENT(IN) or VALUE"_err_en_US);
53564ab3302SCarolineConcatto       } else if (IsIntentOut(symbol)) {
53664ab3302SCarolineConcatto         if (const DeclTypeSpec * type{details.type()}) {
53764ab3302SCarolineConcatto           if (type && type->IsPolymorphic()) { // C1588
53864ab3302SCarolineConcatto             messages_.Say(
53964ab3302SCarolineConcatto                 "An INTENT(OUT) dummy argument of a pure subroutine may not be polymorphic"_err_en_US);
54064ab3302SCarolineConcatto           } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
54164ab3302SCarolineConcatto             if (FindUltimateComponent(*derived, [](const Symbol &x) {
54264ab3302SCarolineConcatto                   const DeclTypeSpec *type{x.GetType()};
54364ab3302SCarolineConcatto                   return type && type->IsPolymorphic();
54464ab3302SCarolineConcatto                 })) { // C1588
54564ab3302SCarolineConcatto               messages_.Say(
54664ab3302SCarolineConcatto                   "An INTENT(OUT) dummy argument of a pure subroutine may not have a polymorphic ultimate component"_err_en_US);
54764ab3302SCarolineConcatto             }
54864ab3302SCarolineConcatto             if (HasImpureFinal(*derived)) { // C1587
54964ab3302SCarolineConcatto               messages_.Say(
55064ab3302SCarolineConcatto                   "An INTENT(OUT) dummy argument of a pure subroutine may not have an impure FINAL subroutine"_err_en_US);
55164ab3302SCarolineConcatto             }
55264ab3302SCarolineConcatto           }
55364ab3302SCarolineConcatto         }
55464ab3302SCarolineConcatto       } else if (!IsIntentInOut(symbol)) { // C1586
55564ab3302SCarolineConcatto         messages_.Say(
55664ab3302SCarolineConcatto             "non-POINTER dummy argument of pure subroutine must have INTENT() or VALUE attribute"_err_en_US);
55764ab3302SCarolineConcatto       }
55864ab3302SCarolineConcatto     }
559f9c0859eSPeter Steinfeld   } else if (symbol.attrs().test(Attr::INTENT_IN) ||
560f9c0859eSPeter Steinfeld       symbol.attrs().test(Attr::INTENT_OUT) ||
561f9c0859eSPeter Steinfeld       symbol.attrs().test(Attr::INTENT_INOUT)) {
562f9c0859eSPeter Steinfeld     messages_.Say("INTENT attributes may apply only to a dummy "
563f9c0859eSPeter Steinfeld                   "argument"_err_en_US); // C843
564f9c0859eSPeter Steinfeld   } else if (IsOptional(symbol)) {
565f9c0859eSPeter Steinfeld     messages_.Say("OPTIONAL attribute may apply only to a dummy "
566f9c0859eSPeter Steinfeld                   "argument"_err_en_US); // C849
56764ab3302SCarolineConcatto   }
56843a263f5Speter klausler   if (InElemental()) {
56943a263f5Speter klausler     if (details.isDummy()) { // C15100
57043a263f5Speter klausler       if (details.shape().Rank() > 0) {
57143a263f5Speter klausler         messages_.Say(
57243a263f5Speter klausler             "A dummy argument of an ELEMENTAL procedure must be scalar"_err_en_US);
57343a263f5Speter klausler       }
57443a263f5Speter klausler       if (IsAllocatable(symbol)) {
57543a263f5Speter klausler         messages_.Say(
57643a263f5Speter klausler             "A dummy argument of an ELEMENTAL procedure may not be ALLOCATABLE"_err_en_US);
57743a263f5Speter klausler       }
5781ee6f7adSPeter Klausler       if (evaluate::IsCoarray(symbol)) {
57943a263f5Speter klausler         messages_.Say(
58043a263f5Speter klausler             "A dummy argument of an ELEMENTAL procedure may not be a coarray"_err_en_US);
58143a263f5Speter klausler       }
58243a263f5Speter klausler       if (IsPointer(symbol)) {
58343a263f5Speter klausler         messages_.Say(
58443a263f5Speter klausler             "A dummy argument of an ELEMENTAL procedure may not be a POINTER"_err_en_US);
58543a263f5Speter klausler       }
58643a263f5Speter klausler       if (!symbol.attrs().HasAny(Attrs{Attr::VALUE, Attr::INTENT_IN,
58743a263f5Speter klausler               Attr::INTENT_INOUT, Attr::INTENT_OUT})) { // C15102
58843a263f5Speter klausler         messages_.Say(
58943a263f5Speter klausler             "A dummy argument of an ELEMENTAL procedure must have an INTENT() or VALUE attribute"_err_en_US);
59043a263f5Speter klausler       }
59143a263f5Speter klausler     } else if (IsFunctionResult(symbol)) { // C15101
59243a263f5Speter klausler       if (details.shape().Rank() > 0) {
59343a263f5Speter klausler         messages_.Say(
59443a263f5Speter klausler             "The result of an ELEMENTAL function must be scalar"_err_en_US);
59543a263f5Speter klausler       }
59643a263f5Speter klausler       if (IsAllocatable(symbol)) {
59743a263f5Speter klausler         messages_.Say(
59843a263f5Speter klausler             "The result of an ELEMENTAL function may not be ALLOCATABLE"_err_en_US);
59943a263f5Speter klausler       }
60043a263f5Speter klausler       if (IsPointer(symbol)) {
60143a263f5Speter klausler         messages_.Say(
60243a263f5Speter klausler             "The result of an ELEMENTAL function may not be a POINTER"_err_en_US);
60343a263f5Speter klausler       }
60443a263f5Speter klausler     }
60543a263f5Speter klausler   }
606d60a0220Speter klausler   if (HasDeclarationInitializer(symbol)) { // C808; ignore DATA initialization
607641ede93Speter klausler     CheckPointerInitialization(symbol);
6084171f80dSpeter klausler     if (IsAutomatic(symbol)) {
609641ede93Speter klausler       messages_.Say(
610641ede93Speter klausler           "An automatic variable or component must not be initialized"_err_en_US);
6114171f80dSpeter klausler     } else if (IsDummy(symbol)) {
6124171f80dSpeter klausler       messages_.Say("A dummy argument must not be initialized"_err_en_US);
6134171f80dSpeter klausler     } else if (IsFunctionResult(symbol)) {
6144171f80dSpeter klausler       messages_.Say("A function result must not be initialized"_err_en_US);
6154171f80dSpeter klausler     } else if (IsInBlankCommon(symbol)) {
61664ab3302SCarolineConcatto       messages_.Say(
617a53967cdSPeter Klausler           "A variable in blank COMMON should not be initialized"_port_en_US);
61864ab3302SCarolineConcatto     }
6194171f80dSpeter klausler   }
620641ede93Speter klausler   if (symbol.owner().kind() == Scope::Kind::BlockData) {
62164ab3302SCarolineConcatto     if (IsAllocatable(symbol)) {
62264ab3302SCarolineConcatto       messages_.Say(
62364ab3302SCarolineConcatto           "An ALLOCATABLE variable may not appear in a BLOCK DATA subprogram"_err_en_US);
624641ede93Speter klausler     } else if (IsInitialized(symbol) && !FindCommonBlockContaining(symbol)) {
62564ab3302SCarolineConcatto       messages_.Say(
62664ab3302SCarolineConcatto           "An initialized variable in BLOCK DATA must be in a COMMON block"_err_en_US);
62764ab3302SCarolineConcatto     }
62864ab3302SCarolineConcatto   }
62964ab3302SCarolineConcatto   if (const DeclTypeSpec * type{details.type()}) { // C708
63064ab3302SCarolineConcatto     if (type->IsPolymorphic() &&
63164ab3302SCarolineConcatto         !(type->IsAssumedType() || IsAllocatableOrPointer(symbol) ||
63214f49599STim Keith             IsDummy(symbol))) {
63364ab3302SCarolineConcatto       messages_.Say("CLASS entity '%s' must be a dummy argument or have "
63464ab3302SCarolineConcatto                     "ALLOCATABLE or POINTER attribute"_err_en_US,
63564ab3302SCarolineConcatto           symbol.name());
63664ab3302SCarolineConcatto     }
63764ab3302SCarolineConcatto   }
638f862d858Speter klausler }
639f862d858Speter klausler 
CheckPointerInitialization(const Symbol & symbol)640641ede93Speter klausler void CheckHelper::CheckPointerInitialization(const Symbol &symbol) {
641641ede93Speter klausler   if (IsPointer(symbol) && !context_.HasError(symbol) &&
642641ede93Speter klausler       !scopeIsUninstantiatedPDT_) {
643641ede93Speter klausler     if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
644641ede93Speter klausler       if (object->init()) { // C764, C765; C808
6456829bd3eSpeter klausler         if (auto designator{evaluate::AsGenericExpr(symbol)}) {
646641ede93Speter klausler           auto restorer{messages_.SetLocation(symbol.name())};
647641ede93Speter klausler           context_.set_location(symbol.name());
648641ede93Speter klausler           CheckInitialTarget(foldingContext_, *designator, *object->init());
649f862d858Speter klausler         }
650641ede93Speter klausler       }
651641ede93Speter klausler     } else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
652641ede93Speter klausler       if (proc->init() && *proc->init()) {
653641ede93Speter klausler         // C1519 - must be nonelemental external or module procedure,
654641ede93Speter klausler         // or an unrestricted specific intrinsic function.
655641ede93Speter klausler         const Symbol &ultimate{(*proc->init())->GetUltimate()};
656641ede93Speter klausler         if (ultimate.attrs().test(Attr::INTRINSIC)) {
657848cca6cSEmil Kieri           if (const auto intrinsic{
658848cca6cSEmil Kieri                   context_.intrinsics().IsSpecificIntrinsicFunction(
659848cca6cSEmil Kieri                       ultimate.name().ToString())};
660848cca6cSEmil Kieri               !intrinsic || intrinsic->isRestrictedSpecific) { // C1030
661f8f70028Speter klausler             context_.Say(
662848cca6cSEmil Kieri                 "Intrinsic procedure '%s' is not an unrestricted specific "
663848cca6cSEmil Kieri                 "intrinsic permitted for use as the initializer for procedure "
664848cca6cSEmil Kieri                 "pointer '%s'"_err_en_US,
665f8f70028Speter klausler                 ultimate.name(), symbol.name());
666f8f70028Speter klausler           }
667641ede93Speter klausler         } else if (!ultimate.attrs().test(Attr::EXTERNAL) &&
668641ede93Speter klausler             ultimate.owner().kind() != Scope::Kind::Module) {
669641ede93Speter klausler           context_.Say("Procedure pointer '%s' initializer '%s' is neither "
670641ede93Speter klausler                        "an external nor a module procedure"_err_en_US,
671641ede93Speter klausler               symbol.name(), ultimate.name());
672*6052025bSPeter Klausler         } else if (IsElementalProcedure(ultimate)) {
673641ede93Speter klausler           context_.Say("Procedure pointer '%s' cannot be initialized with the "
674641ede93Speter klausler                        "elemental procedure '%s"_err_en_US,
675641ede93Speter klausler               symbol.name(), ultimate.name());
676f862d858Speter klausler         } else {
677641ede93Speter klausler           // TODO: Check the "shalls" in the 15.4.3.6 paragraphs 7-10.
678f862d858Speter klausler         }
679f862d858Speter klausler       }
680f862d858Speter klausler     }
681f862d858Speter klausler   }
68264ab3302SCarolineConcatto }
68364ab3302SCarolineConcatto 
68464ab3302SCarolineConcatto // The six different kinds of array-specs:
68564ab3302SCarolineConcatto //   array-spec     -> explicit-shape-list | deferred-shape-list
68664ab3302SCarolineConcatto //                     | assumed-shape-list | implied-shape-list
68764ab3302SCarolineConcatto //                     | assumed-size | assumed-rank
68864ab3302SCarolineConcatto //   explicit-shape -> [ lb : ] ub
68964ab3302SCarolineConcatto //   deferred-shape -> :
69064ab3302SCarolineConcatto //   assumed-shape  -> [ lb ] :
69164ab3302SCarolineConcatto //   implied-shape  -> [ lb : ] *
69264ab3302SCarolineConcatto //   assumed-size   -> [ explicit-shape-list , ] [ lb : ] *
69364ab3302SCarolineConcatto //   assumed-rank   -> ..
69464ab3302SCarolineConcatto // Note:
69564ab3302SCarolineConcatto // - deferred-shape is also an assumed-shape
69664ab3302SCarolineConcatto // - A single "*" or "lb:*" might be assumed-size or implied-shape-list
CheckArraySpec(const Symbol & symbol,const ArraySpec & arraySpec)69764ab3302SCarolineConcatto void CheckHelper::CheckArraySpec(
69864ab3302SCarolineConcatto     const Symbol &symbol, const ArraySpec &arraySpec) {
69964ab3302SCarolineConcatto   if (arraySpec.Rank() == 0) {
70064ab3302SCarolineConcatto     return;
70164ab3302SCarolineConcatto   }
70264ab3302SCarolineConcatto   bool isExplicit{arraySpec.IsExplicitShape()};
70344bc97c8SPeter Klausler   bool canBeDeferred{arraySpec.CanBeDeferredShape()};
70444bc97c8SPeter Klausler   bool canBeImplied{arraySpec.CanBeImpliedShape()};
70544bc97c8SPeter Klausler   bool canBeAssumedShape{arraySpec.CanBeAssumedShape()};
70644bc97c8SPeter Klausler   bool canBeAssumedSize{arraySpec.CanBeAssumedSize()};
70764ab3302SCarolineConcatto   bool isAssumedRank{arraySpec.IsAssumedRank()};
70864ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
70944bc97c8SPeter Klausler   if (symbol.test(Symbol::Flag::CrayPointee) && !isExplicit &&
71044bc97c8SPeter Klausler       !canBeAssumedSize) {
71164ab3302SCarolineConcatto     msg = "Cray pointee '%s' must have must have explicit shape or"
71264ab3302SCarolineConcatto           " assumed size"_err_en_US;
71344bc97c8SPeter Klausler   } else if (IsAllocatableOrPointer(symbol) && !canBeDeferred &&
71444bc97c8SPeter Klausler       !isAssumedRank) {
71564ab3302SCarolineConcatto     if (symbol.owner().IsDerivedType()) { // C745
71664ab3302SCarolineConcatto       if (IsAllocatable(symbol)) {
71764ab3302SCarolineConcatto         msg = "Allocatable array component '%s' must have"
71864ab3302SCarolineConcatto               " deferred shape"_err_en_US;
71964ab3302SCarolineConcatto       } else {
72064ab3302SCarolineConcatto         msg = "Array pointer component '%s' must have deferred shape"_err_en_US;
72164ab3302SCarolineConcatto       }
72264ab3302SCarolineConcatto     } else {
72364ab3302SCarolineConcatto       if (IsAllocatable(symbol)) { // C832
72464ab3302SCarolineConcatto         msg = "Allocatable array '%s' must have deferred shape or"
72564ab3302SCarolineConcatto               " assumed rank"_err_en_US;
72664ab3302SCarolineConcatto       } else {
72764ab3302SCarolineConcatto         msg = "Array pointer '%s' must have deferred shape or"
72864ab3302SCarolineConcatto               " assumed rank"_err_en_US;
72964ab3302SCarolineConcatto       }
73064ab3302SCarolineConcatto     }
73114f49599STim Keith   } else if (IsDummy(symbol)) {
73244bc97c8SPeter Klausler     if (canBeImplied && !canBeAssumedSize) { // C836
73364ab3302SCarolineConcatto       msg = "Dummy array argument '%s' may not have implied shape"_err_en_US;
73464ab3302SCarolineConcatto     }
73544bc97c8SPeter Klausler   } else if (canBeAssumedShape && !canBeDeferred) {
73664ab3302SCarolineConcatto     msg = "Assumed-shape array '%s' must be a dummy argument"_err_en_US;
73744bc97c8SPeter Klausler   } else if (canBeAssumedSize && !canBeImplied) { // C833
73864ab3302SCarolineConcatto     msg = "Assumed-size array '%s' must be a dummy argument"_err_en_US;
73964ab3302SCarolineConcatto   } else if (isAssumedRank) { // C837
74064ab3302SCarolineConcatto     msg = "Assumed-rank array '%s' must be a dummy argument"_err_en_US;
74144bc97c8SPeter Klausler   } else if (canBeImplied) {
742f9c0859eSPeter Steinfeld     if (!IsNamedConstant(symbol)) { // C835, C836
743f9c0859eSPeter Steinfeld       msg = "Implied-shape array '%s' must be a named constant or a "
744f9c0859eSPeter Steinfeld             "dummy argument"_err_en_US;
74564ab3302SCarolineConcatto     }
74664ab3302SCarolineConcatto   } else if (IsNamedConstant(symbol)) {
74744bc97c8SPeter Klausler     if (!isExplicit && !canBeImplied) {
748641ede93Speter klausler       msg = "Named constant '%s' array must have constant or"
74964ab3302SCarolineConcatto             " implied shape"_err_en_US;
75064ab3302SCarolineConcatto     }
75164ab3302SCarolineConcatto   } else if (!IsAllocatableOrPointer(symbol) && !isExplicit) {
75264ab3302SCarolineConcatto     if (symbol.owner().IsDerivedType()) { // C749
75364ab3302SCarolineConcatto       msg = "Component array '%s' without ALLOCATABLE or POINTER attribute must"
75464ab3302SCarolineConcatto             " have explicit shape"_err_en_US;
75564ab3302SCarolineConcatto     } else { // C816
75664ab3302SCarolineConcatto       msg = "Array '%s' without ALLOCATABLE or POINTER attribute must have"
75764ab3302SCarolineConcatto             " explicit shape"_err_en_US;
75864ab3302SCarolineConcatto     }
75964ab3302SCarolineConcatto   }
76064ab3302SCarolineConcatto   if (msg) {
76164ab3302SCarolineConcatto     context_.Say(std::move(*msg), symbol.name());
76264ab3302SCarolineConcatto   }
76364ab3302SCarolineConcatto }
76464ab3302SCarolineConcatto 
CheckProcEntity(const Symbol & symbol,const ProcEntityDetails & details)76564ab3302SCarolineConcatto void CheckHelper::CheckProcEntity(
76664ab3302SCarolineConcatto     const Symbol &symbol, const ProcEntityDetails &details) {
76764ab3302SCarolineConcatto   if (details.isDummy()) {
7681e1a011bSPeter Steinfeld     if (!symbol.attrs().test(Attr::POINTER) && // C843
7691e1a011bSPeter Steinfeld         (symbol.attrs().test(Attr::INTENT_IN) ||
7701e1a011bSPeter Steinfeld             symbol.attrs().test(Attr::INTENT_OUT) ||
7711e1a011bSPeter Steinfeld             symbol.attrs().test(Attr::INTENT_INOUT))) {
7721e1a011bSPeter Steinfeld       messages_.Say("A dummy procedure without the POINTER attribute"
7731e1a011bSPeter Steinfeld                     " may not have an INTENT attribute"_err_en_US);
7741e1a011bSPeter Steinfeld     }
77543a263f5Speter klausler     if (InElemental()) { // C15100
77643a263f5Speter klausler       messages_.Say(
77743a263f5Speter klausler           "An ELEMENTAL subprogram may not have a dummy procedure"_err_en_US);
77843a263f5Speter klausler     }
77964ab3302SCarolineConcatto     const Symbol *interface { details.interface().symbol() };
78064ab3302SCarolineConcatto     if (!symbol.attrs().test(Attr::INTRINSIC) &&
781*6052025bSPeter Klausler         (IsElementalProcedure(symbol) ||
78264ab3302SCarolineConcatto             (interface && !interface->attrs().test(Attr::INTRINSIC) &&
783*6052025bSPeter Klausler                 IsElementalProcedure(*interface)))) {
78464ab3302SCarolineConcatto       // There's no explicit constraint or "shall" that we can find in the
78564ab3302SCarolineConcatto       // standard for this check, but it seems to be implied in multiple
78664ab3302SCarolineConcatto       // sites, and ELEMENTAL non-intrinsic actual arguments *are*
78764ab3302SCarolineConcatto       // explicitly forbidden.  But we allow "PROCEDURE(SIN)::dummy"
78864ab3302SCarolineConcatto       // because it is explicitly legal to *pass* the specific intrinsic
78964ab3302SCarolineConcatto       // function SIN as an actual argument.
79064ab3302SCarolineConcatto       messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US);
79164ab3302SCarolineConcatto     }
792f9c0859eSPeter Steinfeld   } else if (symbol.attrs().test(Attr::INTENT_IN) ||
793f9c0859eSPeter Steinfeld       symbol.attrs().test(Attr::INTENT_OUT) ||
794f9c0859eSPeter Steinfeld       symbol.attrs().test(Attr::INTENT_INOUT)) {
795f9c0859eSPeter Steinfeld     messages_.Say("INTENT attributes may apply only to a dummy "
796f9c0859eSPeter Steinfeld                   "argument"_err_en_US); // C843
797f9c0859eSPeter Steinfeld   } else if (IsOptional(symbol)) {
798f9c0859eSPeter Steinfeld     messages_.Say("OPTIONAL attribute may apply only to a dummy "
799f9c0859eSPeter Steinfeld                   "argument"_err_en_US); // C849
80064ab3302SCarolineConcatto   } else if (symbol.owner().IsDerivedType()) {
80138095549SPete Steinfeld     if (!symbol.attrs().test(Attr::POINTER)) { // C756
80238095549SPete Steinfeld       const auto &name{symbol.name()};
80338095549SPete Steinfeld       messages_.Say(name,
80438095549SPete Steinfeld           "Procedure component '%s' must have POINTER attribute"_err_en_US,
80538095549SPete Steinfeld           name);
80638095549SPete Steinfeld     }
80764ab3302SCarolineConcatto     CheckPassArg(symbol, details.interface().symbol(), details);
80864ab3302SCarolineConcatto   }
80964ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::POINTER)) {
810641ede93Speter klausler     CheckPointerInitialization(symbol);
81164ab3302SCarolineConcatto     if (const Symbol * interface{details.interface().symbol()}) {
812f8f70028Speter klausler       if (interface->attrs().test(Attr::INTRINSIC)) {
813848cca6cSEmil Kieri         if (const auto intrinsic{
814848cca6cSEmil Kieri                 context_.intrinsics().IsSpecificIntrinsicFunction(
815848cca6cSEmil Kieri                     interface->name().ToString())};
816848cca6cSEmil Kieri             !intrinsic || intrinsic->isRestrictedSpecific) { // C1515
817f8f70028Speter klausler           messages_.Say(
818848cca6cSEmil Kieri               "Intrinsic procedure '%s' is not an unrestricted specific "
819848cca6cSEmil Kieri               "intrinsic permitted for use as the definition of the interface "
820848cca6cSEmil Kieri               "to procedure pointer '%s'"_err_en_US,
821f8f70028Speter klausler               interface->name(), symbol.name());
822f8f70028Speter klausler         }
823*6052025bSPeter Klausler       } else if (IsElementalProcedure(*interface)) {
82464ab3302SCarolineConcatto         messages_.Say("Procedure pointer '%s' may not be ELEMENTAL"_err_en_US,
82564ab3302SCarolineConcatto             symbol.name()); // C1517
82664ab3302SCarolineConcatto       }
82764ab3302SCarolineConcatto     }
8284171f80dSpeter klausler   } else if (symbol.attrs().test(Attr::SAVE)) {
8294171f80dSpeter klausler     messages_.Say(
8304171f80dSpeter klausler         "Procedure '%s' with SAVE attribute must also have POINTER attribute"_err_en_US,
8314171f80dSpeter klausler         symbol.name());
83264ab3302SCarolineConcatto   }
83364ab3302SCarolineConcatto }
83464ab3302SCarolineConcatto 
83561b1390eSTim Keith // When a module subprogram has the MODULE prefix the following must match
83661b1390eSTim Keith // with the corresponding separate module procedure interface body:
83761b1390eSTim Keith // - C1549: characteristics and dummy argument names
83861b1390eSTim Keith // - C1550: binding label
83961b1390eSTim Keith // - C1551: NON_RECURSIVE prefix
84061b1390eSTim Keith class SubprogramMatchHelper {
84161b1390eSTim Keith public:
SubprogramMatchHelper(CheckHelper & checkHelper)84282edd428STim Keith   explicit SubprogramMatchHelper(CheckHelper &checkHelper)
84382edd428STim Keith       : checkHelper{checkHelper} {}
84461b1390eSTim Keith 
84561b1390eSTim Keith   void Check(const Symbol &, const Symbol &);
84661b1390eSTim Keith 
84761b1390eSTim Keith private:
context()84882edd428STim Keith   SemanticsContext &context() { return checkHelper.context(); }
84961b1390eSTim Keith   void CheckDummyArg(const Symbol &, const Symbol &, const DummyArgument &,
85061b1390eSTim Keith       const DummyArgument &);
85161b1390eSTim Keith   void CheckDummyDataObject(const Symbol &, const Symbol &,
85261b1390eSTim Keith       const DummyDataObject &, const DummyDataObject &);
85361b1390eSTim Keith   void CheckDummyProcedure(const Symbol &, const Symbol &,
85461b1390eSTim Keith       const DummyProcedure &, const DummyProcedure &);
85561b1390eSTim Keith   bool CheckSameIntent(
85661b1390eSTim Keith       const Symbol &, const Symbol &, common::Intent, common::Intent);
85761b1390eSTim Keith   template <typename... A>
85861b1390eSTim Keith   void Say(
85961b1390eSTim Keith       const Symbol &, const Symbol &, parser::MessageFixedText &&, A &&...);
86061b1390eSTim Keith   template <typename ATTRS>
86161b1390eSTim Keith   bool CheckSameAttrs(const Symbol &, const Symbol &, ATTRS, ATTRS);
86261b1390eSTim Keith   bool ShapesAreCompatible(const DummyDataObject &, const DummyDataObject &);
86361b1390eSTim Keith   evaluate::Shape FoldShape(const evaluate::Shape &);
AsFortran(DummyDataObject::Attr attr)86461b1390eSTim Keith   std::string AsFortran(DummyDataObject::Attr attr) {
86561b1390eSTim Keith     return parser::ToUpperCaseLetters(DummyDataObject::EnumToString(attr));
86661b1390eSTim Keith   }
AsFortran(DummyProcedure::Attr attr)86761b1390eSTim Keith   std::string AsFortran(DummyProcedure::Attr attr) {
86861b1390eSTim Keith     return parser::ToUpperCaseLetters(DummyProcedure::EnumToString(attr));
86961b1390eSTim Keith   }
87061b1390eSTim Keith 
87182edd428STim Keith   CheckHelper &checkHelper;
87261b1390eSTim Keith };
87361b1390eSTim Keith 
874c42f6314Speter klausler // 15.6.2.6 para 3 - can the result of an ENTRY differ from its function?
IsResultOkToDiffer(const FunctionResult & result)875c42f6314Speter klausler bool CheckHelper::IsResultOkToDiffer(const FunctionResult &result) {
876c42f6314Speter klausler   if (result.attrs.test(FunctionResult::Attr::Allocatable) ||
877c42f6314Speter klausler       result.attrs.test(FunctionResult::Attr::Pointer)) {
878c42f6314Speter klausler     return false;
879c42f6314Speter klausler   }
880c42f6314Speter klausler   const auto *typeAndShape{result.GetTypeAndShape()};
881c42f6314Speter klausler   if (!typeAndShape || typeAndShape->Rank() != 0) {
882c42f6314Speter klausler     return false;
883c42f6314Speter klausler   }
884c42f6314Speter klausler   auto category{typeAndShape->type().category()};
885c42f6314Speter klausler   if (category == TypeCategory::Character ||
886c42f6314Speter klausler       category == TypeCategory::Derived) {
887c42f6314Speter klausler     return false;
888c42f6314Speter klausler   }
889c42f6314Speter klausler   int kind{typeAndShape->type().kind()};
890c42f6314Speter klausler   return kind == context_.GetDefaultKind(category) ||
891c42f6314Speter klausler       (category == TypeCategory::Real &&
892c42f6314Speter klausler           kind == context_.doublePrecisionKind());
893c42f6314Speter klausler }
894c42f6314Speter klausler 
CheckSubprogram(const Symbol & symbol,const SubprogramDetails & details)89561b1390eSTim Keith void CheckHelper::CheckSubprogram(
896c42f6314Speter klausler     const Symbol &symbol, const SubprogramDetails &details) {
897c42f6314Speter klausler   if (const Symbol * iface{FindSeparateModuleSubprogramInterface(&symbol)}) {
89882edd428STim Keith     SubprogramMatchHelper{*this}.Check(symbol, *iface);
89961b1390eSTim Keith   }
900c42f6314Speter klausler   if (const Scope * entryScope{details.entryScope()}) {
901c42f6314Speter klausler     // ENTRY 15.6.2.6, esp. C1571
902c42f6314Speter klausler     std::optional<parser::MessageFixedText> error;
903c42f6314Speter klausler     const Symbol *subprogram{entryScope->symbol()};
904c42f6314Speter klausler     const SubprogramDetails *subprogramDetails{nullptr};
905c42f6314Speter klausler     if (subprogram) {
906c42f6314Speter klausler       subprogramDetails = subprogram->detailsIf<SubprogramDetails>();
907c42f6314Speter klausler     }
9087f680b26SPeter Klausler     if (!(entryScope->parent().IsGlobal() || entryScope->parent().IsModule() ||
909c42f6314Speter klausler             entryScope->parent().IsSubmodule())) {
910c42f6314Speter klausler       error = "ENTRY may not appear in an internal subprogram"_err_en_US;
911c42f6314Speter klausler     } else if (subprogramDetails && details.isFunction() &&
912562bfe12Speter klausler         subprogramDetails->isFunction() &&
913562bfe12Speter klausler         !context_.HasError(details.result()) &&
914562bfe12Speter klausler         !context_.HasError(subprogramDetails->result())) {
915c42f6314Speter klausler       auto result{FunctionResult::Characterize(
916641ede93Speter klausler           details.result(), context_.foldingContext())};
917c42f6314Speter klausler       auto subpResult{FunctionResult::Characterize(
918641ede93Speter klausler           subprogramDetails->result(), context_.foldingContext())};
919c42f6314Speter klausler       if (result && subpResult && *result != *subpResult &&
920c42f6314Speter klausler           (!IsResultOkToDiffer(*result) || !IsResultOkToDiffer(*subpResult))) {
921c42f6314Speter klausler         error =
922c42f6314Speter klausler             "Result of ENTRY is not compatible with result of containing function"_err_en_US;
923c42f6314Speter klausler       }
924c42f6314Speter klausler     }
925c42f6314Speter klausler     if (error) {
926c42f6314Speter klausler       if (auto *msg{messages_.Say(symbol.name(), *error)}) {
927c42f6314Speter klausler         if (subprogram) {
928c42f6314Speter klausler           msg->Attach(subprogram->name(), "Containing subprogram"_en_US);
929c42f6314Speter klausler         }
930c42f6314Speter klausler       }
931c42f6314Speter klausler     }
93261b1390eSTim Keith   }
933*6052025bSPeter Klausler   if (IsElementalProcedure(symbol)) {
934c4a65434Speter klausler     // See comment on the similar check in CheckProcEntity()
93543a263f5Speter klausler     if (details.isDummy()) {
936c4a65434Speter klausler       messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US);
93743a263f5Speter klausler     } else {
93843a263f5Speter klausler       for (const Symbol *dummy : details.dummyArgs()) {
93943a263f5Speter klausler         if (!dummy) { // C15100
94043a263f5Speter klausler           messages_.Say(
94143a263f5Speter klausler               "An ELEMENTAL subroutine may not have an alternate return dummy argument"_err_en_US);
94243a263f5Speter klausler         }
94343a263f5Speter klausler       }
94443a263f5Speter klausler     }
945c4a65434Speter klausler   }
94661b1390eSTim Keith }
94761b1390eSTim Keith 
CheckDerivedType(const Symbol & derivedType,const DerivedTypeDetails & details)94864ab3302SCarolineConcatto void CheckHelper::CheckDerivedType(
94937b2e2b0Speter klausler     const Symbol &derivedType, const DerivedTypeDetails &details) {
9502b795ec6SPeter Steinfeld   if (details.isForwardReferenced() && !context_.HasError(derivedType)) {
9512b795ec6SPeter Steinfeld     messages_.Say("The derived type '%s' has not been defined"_err_en_US,
9522b795ec6SPeter Steinfeld         derivedType.name());
9532b795ec6SPeter Steinfeld   }
95437b2e2b0Speter klausler   const Scope *scope{derivedType.scope()};
9552b790490SPete Steinfeld   if (!scope) {
95664ab3302SCarolineConcatto     CHECK(details.isForwardReferenced());
95764ab3302SCarolineConcatto     return;
95864ab3302SCarolineConcatto   }
95937b2e2b0Speter klausler   CHECK(scope->symbol() == &derivedType);
9602b790490SPete Steinfeld   CHECK(scope->IsDerivedType());
96137b2e2b0Speter klausler   if (derivedType.attrs().test(Attr::ABSTRACT) && // C734
96237b2e2b0Speter klausler       (derivedType.attrs().test(Attr::BIND_C) || details.sequence())) {
96364ab3302SCarolineConcatto     messages_.Say("An ABSTRACT derived type must be extensible"_err_en_US);
96464ab3302SCarolineConcatto   }
96537b2e2b0Speter klausler   if (const DeclTypeSpec * parent{FindParentTypeSpec(derivedType)}) {
96664ab3302SCarolineConcatto     const DerivedTypeSpec *parentDerived{parent->AsDerived()};
96764ab3302SCarolineConcatto     if (!IsExtensibleType(parentDerived)) { // C705
96864ab3302SCarolineConcatto       messages_.Say("The parent type is not extensible"_err_en_US);
96964ab3302SCarolineConcatto     }
97037b2e2b0Speter klausler     if (!derivedType.attrs().test(Attr::ABSTRACT) && parentDerived &&
97164ab3302SCarolineConcatto         parentDerived->typeSymbol().attrs().test(Attr::ABSTRACT)) {
97264ab3302SCarolineConcatto       ScopeComponentIterator components{*parentDerived};
97364ab3302SCarolineConcatto       for (const Symbol &component : components) {
97464ab3302SCarolineConcatto         if (component.attrs().test(Attr::DEFERRED)) {
9752b790490SPete Steinfeld           if (scope->FindComponent(component.name()) == &component) {
97664ab3302SCarolineConcatto             SayWithDeclaration(component,
97764ab3302SCarolineConcatto                 "Non-ABSTRACT extension of ABSTRACT derived type '%s' lacks a binding for DEFERRED procedure '%s'"_err_en_US,
97864ab3302SCarolineConcatto                 parentDerived->typeSymbol().name(), component.name());
97964ab3302SCarolineConcatto           }
98064ab3302SCarolineConcatto         }
98164ab3302SCarolineConcatto       }
98264ab3302SCarolineConcatto     }
98337b2e2b0Speter klausler     DerivedTypeSpec derived{derivedType.name(), derivedType};
9842b790490SPete Steinfeld     derived.set_scope(*scope);
9852b790490SPete Steinfeld     if (FindCoarrayUltimateComponent(derived) && // C736
9862b790490SPete Steinfeld         !(parentDerived && FindCoarrayUltimateComponent(*parentDerived))) {
9872b790490SPete Steinfeld       messages_.Say(
9882b790490SPete Steinfeld           "Type '%s' has a coarray ultimate component so the type at the base "
9892b790490SPete Steinfeld           "of its type extension chain ('%s') must be a type that has a "
9902b790490SPete Steinfeld           "coarray ultimate component"_err_en_US,
99137b2e2b0Speter klausler           derivedType.name(), scope->GetDerivedTypeBase().GetSymbol()->name());
9922b790490SPete Steinfeld     }
9932b790490SPete Steinfeld     if (FindEventOrLockPotentialComponent(derived) && // C737
9942b790490SPete Steinfeld         !(FindEventOrLockPotentialComponent(*parentDerived) ||
9952b790490SPete Steinfeld             IsEventTypeOrLockType(parentDerived))) {
9962b790490SPete Steinfeld       messages_.Say(
9972b790490SPete Steinfeld           "Type '%s' has an EVENT_TYPE or LOCK_TYPE component, so the type "
9982b790490SPete Steinfeld           "at the base of its type extension chain ('%s') must either have an "
9992b790490SPete Steinfeld           "EVENT_TYPE or LOCK_TYPE component, or be EVENT_TYPE or "
10002b790490SPete Steinfeld           "LOCK_TYPE"_err_en_US,
100137b2e2b0Speter klausler           derivedType.name(), scope->GetDerivedTypeBase().GetSymbol()->name());
10022b790490SPete Steinfeld     }
100364ab3302SCarolineConcatto   }
100437b2e2b0Speter klausler   if (HasIntrinsicTypeName(derivedType)) { // C729
1005e17e7173SPete Steinfeld     messages_.Say("A derived type name cannot be the name of an intrinsic"
1006e17e7173SPete Steinfeld                   " type"_err_en_US);
1007e17e7173SPete Steinfeld   }
100837b2e2b0Speter klausler   std::map<SourceName, SymbolRef> previous;
100937b2e2b0Speter klausler   for (const auto &pair : details.finals()) {
101037b2e2b0Speter klausler     SourceName source{pair.first};
101137b2e2b0Speter klausler     const Symbol &ref{*pair.second};
101237b2e2b0Speter klausler     if (CheckFinal(ref, source, derivedType) &&
101337b2e2b0Speter klausler         std::all_of(previous.begin(), previous.end(),
101437b2e2b0Speter klausler             [&](std::pair<SourceName, SymbolRef> prev) {
101537b2e2b0Speter klausler               return CheckDistinguishableFinals(
101637b2e2b0Speter klausler                   ref, source, *prev.second, prev.first, derivedType);
101737b2e2b0Speter klausler             })) {
101837b2e2b0Speter klausler       previous.emplace(source, ref);
101937b2e2b0Speter klausler     }
102037b2e2b0Speter klausler   }
102137b2e2b0Speter klausler }
102237b2e2b0Speter klausler 
102337b2e2b0Speter klausler // C786
CheckFinal(const Symbol & subroutine,SourceName finalName,const Symbol & derivedType)102437b2e2b0Speter klausler bool CheckHelper::CheckFinal(
102537b2e2b0Speter klausler     const Symbol &subroutine, SourceName finalName, const Symbol &derivedType) {
102637b2e2b0Speter klausler   if (!IsModuleProcedure(subroutine)) {
102737b2e2b0Speter klausler     SayWithDeclaration(subroutine, finalName,
102837b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must be a module procedure"_err_en_US,
102937b2e2b0Speter klausler         subroutine.name(), derivedType.name());
103037b2e2b0Speter klausler     return false;
103137b2e2b0Speter klausler   }
103237b2e2b0Speter klausler   const Procedure *proc{Characterize(subroutine)};
103337b2e2b0Speter klausler   if (!proc) {
103437b2e2b0Speter klausler     return false; // error recovery
103537b2e2b0Speter klausler   }
103637b2e2b0Speter klausler   if (!proc->IsSubroutine()) {
103737b2e2b0Speter klausler     SayWithDeclaration(subroutine, finalName,
103837b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must be a subroutine"_err_en_US,
103937b2e2b0Speter klausler         subroutine.name(), derivedType.name());
104037b2e2b0Speter klausler     return false;
104137b2e2b0Speter klausler   }
104237b2e2b0Speter klausler   if (proc->dummyArguments.size() != 1) {
104337b2e2b0Speter klausler     SayWithDeclaration(subroutine, finalName,
104437b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must have a single dummy argument"_err_en_US,
104537b2e2b0Speter klausler         subroutine.name(), derivedType.name());
104637b2e2b0Speter klausler     return false;
104737b2e2b0Speter klausler   }
104837b2e2b0Speter klausler   const auto &arg{proc->dummyArguments[0]};
104937b2e2b0Speter klausler   const Symbol *errSym{&subroutine};
105037b2e2b0Speter klausler   if (const auto *details{subroutine.detailsIf<SubprogramDetails>()}) {
105137b2e2b0Speter klausler     if (!details->dummyArgs().empty()) {
105237b2e2b0Speter klausler       if (const Symbol * argSym{details->dummyArgs()[0]}) {
105337b2e2b0Speter klausler         errSym = argSym;
105437b2e2b0Speter klausler       }
105537b2e2b0Speter klausler     }
105637b2e2b0Speter klausler   }
105737b2e2b0Speter klausler   const auto *ddo{std::get_if<DummyDataObject>(&arg.u)};
105837b2e2b0Speter klausler   if (!ddo) {
105937b2e2b0Speter klausler     SayWithDeclaration(subroutine, finalName,
106037b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must have a single dummy argument that is a data object"_err_en_US,
106137b2e2b0Speter klausler         subroutine.name(), derivedType.name());
106237b2e2b0Speter klausler     return false;
106337b2e2b0Speter klausler   }
106437b2e2b0Speter klausler   bool ok{true};
106537b2e2b0Speter klausler   if (arg.IsOptional()) {
106637b2e2b0Speter klausler     SayWithDeclaration(*errSym, finalName,
106737b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must not have an OPTIONAL dummy argument"_err_en_US,
106837b2e2b0Speter klausler         subroutine.name(), derivedType.name());
106937b2e2b0Speter klausler     ok = false;
107037b2e2b0Speter klausler   }
107137b2e2b0Speter klausler   if (ddo->attrs.test(DummyDataObject::Attr::Allocatable)) {
107237b2e2b0Speter klausler     SayWithDeclaration(*errSym, finalName,
107337b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must not have an ALLOCATABLE dummy argument"_err_en_US,
107437b2e2b0Speter klausler         subroutine.name(), derivedType.name());
107537b2e2b0Speter klausler     ok = false;
107637b2e2b0Speter klausler   }
107737b2e2b0Speter klausler   if (ddo->attrs.test(DummyDataObject::Attr::Pointer)) {
107837b2e2b0Speter klausler     SayWithDeclaration(*errSym, finalName,
107937b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must not have a POINTER dummy argument"_err_en_US,
108037b2e2b0Speter klausler         subroutine.name(), derivedType.name());
108137b2e2b0Speter klausler     ok = false;
108237b2e2b0Speter klausler   }
108337b2e2b0Speter klausler   if (ddo->intent == common::Intent::Out) {
108437b2e2b0Speter klausler     SayWithDeclaration(*errSym, finalName,
108537b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must not have a dummy argument with INTENT(OUT)"_err_en_US,
108637b2e2b0Speter klausler         subroutine.name(), derivedType.name());
108737b2e2b0Speter klausler     ok = false;
108837b2e2b0Speter klausler   }
108937b2e2b0Speter klausler   if (ddo->attrs.test(DummyDataObject::Attr::Value)) {
109037b2e2b0Speter klausler     SayWithDeclaration(*errSym, finalName,
109137b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must not have a dummy argument with the VALUE attribute"_err_en_US,
109237b2e2b0Speter klausler         subroutine.name(), derivedType.name());
109337b2e2b0Speter klausler     ok = false;
109437b2e2b0Speter klausler   }
109537b2e2b0Speter klausler   if (ddo->type.corank() > 0) {
109637b2e2b0Speter klausler     SayWithDeclaration(*errSym, finalName,
109737b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must not have a coarray dummy argument"_err_en_US,
109837b2e2b0Speter klausler         subroutine.name(), derivedType.name());
109937b2e2b0Speter klausler     ok = false;
110037b2e2b0Speter klausler   }
110137b2e2b0Speter klausler   if (ddo->type.type().IsPolymorphic()) {
110237b2e2b0Speter klausler     SayWithDeclaration(*errSym, finalName,
110337b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must not have a polymorphic dummy argument"_err_en_US,
110437b2e2b0Speter klausler         subroutine.name(), derivedType.name());
110537b2e2b0Speter klausler     ok = false;
110637b2e2b0Speter klausler   } else if (ddo->type.type().category() != TypeCategory::Derived ||
110737b2e2b0Speter klausler       &ddo->type.type().GetDerivedTypeSpec().typeSymbol() != &derivedType) {
110837b2e2b0Speter klausler     SayWithDeclaration(*errSym, finalName,
110937b2e2b0Speter klausler         "FINAL subroutine '%s' of derived type '%s' must have a TYPE(%s) dummy argument"_err_en_US,
111037b2e2b0Speter klausler         subroutine.name(), derivedType.name(), derivedType.name());
111137b2e2b0Speter klausler     ok = false;
111237b2e2b0Speter klausler   } else { // check that all LEN type parameters are assumed
111337b2e2b0Speter klausler     for (auto ref : OrderParameterDeclarations(derivedType)) {
1114641ede93Speter klausler       if (IsLenTypeParameter(*ref)) {
111537b2e2b0Speter klausler         const auto *value{
111637b2e2b0Speter klausler             ddo->type.type().GetDerivedTypeSpec().FindParameter(ref->name())};
111737b2e2b0Speter klausler         if (!value || !value->isAssumed()) {
111837b2e2b0Speter klausler           SayWithDeclaration(*errSym, finalName,
111937b2e2b0Speter klausler               "FINAL subroutine '%s' of derived type '%s' must have a dummy argument with an assumed LEN type parameter '%s=*'"_err_en_US,
112037b2e2b0Speter klausler               subroutine.name(), derivedType.name(), ref->name());
112137b2e2b0Speter klausler           ok = false;
112237b2e2b0Speter klausler         }
112337b2e2b0Speter klausler       }
112437b2e2b0Speter klausler     }
112537b2e2b0Speter klausler   }
112637b2e2b0Speter klausler   return ok;
112737b2e2b0Speter klausler }
112837b2e2b0Speter klausler 
CheckDistinguishableFinals(const Symbol & f1,SourceName f1Name,const Symbol & f2,SourceName f2Name,const Symbol & derivedType)112937b2e2b0Speter klausler bool CheckHelper::CheckDistinguishableFinals(const Symbol &f1,
113037b2e2b0Speter klausler     SourceName f1Name, const Symbol &f2, SourceName f2Name,
113137b2e2b0Speter klausler     const Symbol &derivedType) {
113237b2e2b0Speter klausler   const Procedure *p1{Characterize(f1)};
113337b2e2b0Speter klausler   const Procedure *p2{Characterize(f2)};
113437b2e2b0Speter klausler   if (p1 && p2) {
1135c4ba1108Speter klausler     if (characteristics::Distinguishable(
1136c4ba1108Speter klausler             context_.languageFeatures(), *p1, *p2)) {
113737b2e2b0Speter klausler       return true;
113837b2e2b0Speter klausler     }
113937b2e2b0Speter klausler     if (auto *msg{messages_.Say(f1Name,
114037b2e2b0Speter klausler             "FINAL subroutines '%s' and '%s' of derived type '%s' cannot be distinguished by rank or KIND type parameter value"_err_en_US,
114137b2e2b0Speter klausler             f1Name, f2Name, derivedType.name())}) {
114237b2e2b0Speter klausler       msg->Attach(f2Name, "FINAL declaration of '%s'"_en_US, f2.name())
114337b2e2b0Speter klausler           .Attach(f1.name(), "Definition of '%s'"_en_US, f1Name)
114437b2e2b0Speter klausler           .Attach(f2.name(), "Definition of '%s'"_en_US, f2Name);
114537b2e2b0Speter klausler     }
114637b2e2b0Speter klausler   }
114737b2e2b0Speter klausler   return false;
114864ab3302SCarolineConcatto }
114964ab3302SCarolineConcatto 
CheckHostAssoc(const Symbol & symbol,const HostAssocDetails & details)1150b8bfe358STim Keith void CheckHelper::CheckHostAssoc(
1151b8bfe358STim Keith     const Symbol &symbol, const HostAssocDetails &details) {
1152b8bfe358STim Keith   const Symbol &hostSymbol{details.symbol()};
1153b8bfe358STim Keith   if (hostSymbol.test(Symbol::Flag::ImplicitOrError)) {
1154b8bfe358STim Keith     if (details.implicitOrSpecExprError) {
1155b8bfe358STim Keith       messages_.Say("Implicitly typed local entity '%s' not allowed in"
1156b8bfe358STim Keith                     " specification expression"_err_en_US,
1157b8bfe358STim Keith           symbol.name());
1158b8bfe358STim Keith     } else if (details.implicitOrExplicitTypeError) {
1159b8bfe358STim Keith       messages_.Say(
1160b8bfe358STim Keith           "No explicit type declared for '%s'"_err_en_US, symbol.name());
1161b8bfe358STim Keith     }
1162b8bfe358STim Keith   }
1163b8bfe358STim Keith }
1164b8bfe358STim Keith 
CheckGeneric(const Symbol & symbol,const GenericDetails & details)116564ab3302SCarolineConcatto void CheckHelper::CheckGeneric(
116664ab3302SCarolineConcatto     const Symbol &symbol, const GenericDetails &details) {
116782edd428STim Keith   CheckSpecificsAreDistinguishable(symbol, details);
1168cd03e96fSPeter Klausler   common::visit(common::visitors{
1169bc56620bSPeter Steinfeld                     [&](const GenericKind::DefinedIo &io) {
1170bc56620bSPeter Steinfeld                       CheckDefinedIoProc(symbol, details, io);
1171bc56620bSPeter Steinfeld                     },
1172eb14135eSPeter Klausler                     [&](const GenericKind::OtherKind &other) {
1173eb14135eSPeter Klausler                       if (other == GenericKind::OtherKind::Name) {
1174eb14135eSPeter Klausler                         CheckGenericVsIntrinsic(symbol, details);
1175eb14135eSPeter Klausler                       }
1176eb14135eSPeter Klausler                     },
1177bc56620bSPeter Steinfeld                     [](const auto &) {},
1178bc56620bSPeter Steinfeld                 },
1179bc56620bSPeter Steinfeld       details.kind().u);
118064ab3302SCarolineConcatto }
118164ab3302SCarolineConcatto 
118264ab3302SCarolineConcatto // Check that the specifics of this generic are distinguishable from each other
CheckSpecificsAreDistinguishable(const Symbol & generic,const GenericDetails & details)118382edd428STim Keith void CheckHelper::CheckSpecificsAreDistinguishable(
118482edd428STim Keith     const Symbol &generic, const GenericDetails &details) {
118582edd428STim Keith   GenericKind kind{details.kind()};
118664ab3302SCarolineConcatto   const SymbolVector &specifics{details.specificProcs()};
118764ab3302SCarolineConcatto   std::size_t count{specifics.size()};
118882edd428STim Keith   if (count < 2 || !kind.IsName()) {
118964ab3302SCarolineConcatto     return;
119064ab3302SCarolineConcatto   }
119182edd428STim Keith   DistinguishabilityHelper helper{context_};
119282edd428STim Keith   for (const Symbol &specific : specifics) {
119382edd428STim Keith     if (const Procedure * procedure{Characterize(specific)}) {
119482edd428STim Keith       helper.Add(generic, kind, specific, *procedure);
119564ab3302SCarolineConcatto     }
119664ab3302SCarolineConcatto   }
119786f59de1STim Keith   helper.Check(generic.owner());
119864ab3302SCarolineConcatto }
119964ab3302SCarolineConcatto 
ConflictsWithIntrinsicAssignment(const Procedure & proc)120064ab3302SCarolineConcatto static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) {
120164ab3302SCarolineConcatto   auto lhs{std::get<DummyDataObject>(proc.dummyArguments[0].u).type};
120264ab3302SCarolineConcatto   auto rhs{std::get<DummyDataObject>(proc.dummyArguments[1].u).type};
120364ab3302SCarolineConcatto   return Tristate::No ==
120464ab3302SCarolineConcatto       IsDefinedAssignment(lhs.type(), lhs.Rank(), rhs.type(), rhs.Rank());
120564ab3302SCarolineConcatto }
120664ab3302SCarolineConcatto 
ConflictsWithIntrinsicOperator(const GenericKind & kind,const Procedure & proc)120764ab3302SCarolineConcatto static bool ConflictsWithIntrinsicOperator(
120864ab3302SCarolineConcatto     const GenericKind &kind, const Procedure &proc) {
120982edd428STim Keith   if (!kind.IsIntrinsicOperator()) {
121082edd428STim Keith     return false;
121182edd428STim Keith   }
121264ab3302SCarolineConcatto   auto arg0{std::get<DummyDataObject>(proc.dummyArguments[0].u).type};
121364ab3302SCarolineConcatto   auto type0{arg0.type()};
121464ab3302SCarolineConcatto   if (proc.dummyArguments.size() == 1) { // unary
1215cd03e96fSPeter Klausler     return common::visit(
121664ab3302SCarolineConcatto         common::visitors{
121764ab3302SCarolineConcatto             [&](common::NumericOperator) { return IsIntrinsicNumeric(type0); },
121864ab3302SCarolineConcatto             [&](common::LogicalOperator) { return IsIntrinsicLogical(type0); },
121964ab3302SCarolineConcatto             [](const auto &) -> bool { DIE("bad generic kind"); },
122064ab3302SCarolineConcatto         },
122164ab3302SCarolineConcatto         kind.u);
122264ab3302SCarolineConcatto   } else { // binary
122364ab3302SCarolineConcatto     int rank0{arg0.Rank()};
122464ab3302SCarolineConcatto     auto arg1{std::get<DummyDataObject>(proc.dummyArguments[1].u).type};
122564ab3302SCarolineConcatto     auto type1{arg1.type()};
122664ab3302SCarolineConcatto     int rank1{arg1.Rank()};
1227cd03e96fSPeter Klausler     return common::visit(
122864ab3302SCarolineConcatto         common::visitors{
122964ab3302SCarolineConcatto             [&](common::NumericOperator) {
123064ab3302SCarolineConcatto               return IsIntrinsicNumeric(type0, rank0, type1, rank1);
123164ab3302SCarolineConcatto             },
123264ab3302SCarolineConcatto             [&](common::LogicalOperator) {
123364ab3302SCarolineConcatto               return IsIntrinsicLogical(type0, rank0, type1, rank1);
123464ab3302SCarolineConcatto             },
123564ab3302SCarolineConcatto             [&](common::RelationalOperator opr) {
123664ab3302SCarolineConcatto               return IsIntrinsicRelational(opr, type0, rank0, type1, rank1);
123764ab3302SCarolineConcatto             },
123864ab3302SCarolineConcatto             [&](GenericKind::OtherKind x) {
123964ab3302SCarolineConcatto               CHECK(x == GenericKind::OtherKind::Concat);
124064ab3302SCarolineConcatto               return IsIntrinsicConcat(type0, rank0, type1, rank1);
124164ab3302SCarolineConcatto             },
124264ab3302SCarolineConcatto             [](const auto &) -> bool { DIE("bad generic kind"); },
124364ab3302SCarolineConcatto         },
124464ab3302SCarolineConcatto         kind.u);
124564ab3302SCarolineConcatto   }
124664ab3302SCarolineConcatto }
124764ab3302SCarolineConcatto 
124864ab3302SCarolineConcatto // Check if this procedure can be used for defined operators (see 15.4.3.4.2).
CheckDefinedOperator(SourceName opName,GenericKind kind,const Symbol & specific,const Procedure & proc)124982edd428STim Keith bool CheckHelper::CheckDefinedOperator(SourceName opName, GenericKind kind,
125082edd428STim Keith     const Symbol &specific, const Procedure &proc) {
125182edd428STim Keith   if (context_.HasError(specific)) {
125282edd428STim Keith     return false;
125382edd428STim Keith   }
125464ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
1255e962718dSIvan Zhechev   auto checkDefinedOperatorArgs{
1256e962718dSIvan Zhechev       [&](SourceName opName, const Symbol &specific, const Procedure &proc) {
1257e962718dSIvan Zhechev         bool arg0Defined{CheckDefinedOperatorArg(opName, specific, proc, 0)};
1258e962718dSIvan Zhechev         bool arg1Defined{CheckDefinedOperatorArg(opName, specific, proc, 1)};
1259e962718dSIvan Zhechev         return arg0Defined && arg1Defined;
1260e962718dSIvan Zhechev       }};
126164ab3302SCarolineConcatto   if (specific.attrs().test(Attr::NOPASS)) { // C774
126264ab3302SCarolineConcatto     msg = "%s procedure '%s' may not have NOPASS attribute"_err_en_US;
126364ab3302SCarolineConcatto   } else if (!proc.functionResult.has_value()) {
126464ab3302SCarolineConcatto     msg = "%s procedure '%s' must be a function"_err_en_US;
126564ab3302SCarolineConcatto   } else if (proc.functionResult->IsAssumedLengthCharacter()) {
126664ab3302SCarolineConcatto     msg = "%s function '%s' may not have assumed-length CHARACTER(*)"
126764ab3302SCarolineConcatto           " result"_err_en_US;
126864ab3302SCarolineConcatto   } else if (auto m{CheckNumberOfArgs(kind, proc.dummyArguments.size())}) {
126964ab3302SCarolineConcatto     msg = std::move(m);
1270e962718dSIvan Zhechev   } else if (!checkDefinedOperatorArgs(opName, specific, proc)) {
127164ab3302SCarolineConcatto     return false; // error was reported
127264ab3302SCarolineConcatto   } else if (ConflictsWithIntrinsicOperator(kind, proc)) {
127364ab3302SCarolineConcatto     msg = "%s function '%s' conflicts with intrinsic operator"_err_en_US;
127464ab3302SCarolineConcatto   } else {
127564ab3302SCarolineConcatto     return true; // OK
127664ab3302SCarolineConcatto   }
127782edd428STim Keith   SayWithDeclaration(
127882edd428STim Keith       specific, std::move(*msg), MakeOpName(opName), specific.name());
127982edd428STim Keith   context_.SetError(specific);
128064ab3302SCarolineConcatto   return false;
128164ab3302SCarolineConcatto }
128264ab3302SCarolineConcatto 
128364ab3302SCarolineConcatto // If the number of arguments is wrong for this intrinsic operator, return
128464ab3302SCarolineConcatto // false and return the error message in msg.
CheckNumberOfArgs(const GenericKind & kind,std::size_t nargs)128564ab3302SCarolineConcatto std::optional<parser::MessageFixedText> CheckHelper::CheckNumberOfArgs(
128664ab3302SCarolineConcatto     const GenericKind &kind, std::size_t nargs) {
128782edd428STim Keith   if (!kind.IsIntrinsicOperator()) {
128882edd428STim Keith     return std::nullopt;
128982edd428STim Keith   }
129064ab3302SCarolineConcatto   std::size_t min{2}, max{2}; // allowed number of args; default is binary
1291cd03e96fSPeter Klausler   common::visit(common::visitors{
129264ab3302SCarolineConcatto                     [&](const common::NumericOperator &x) {
129364ab3302SCarolineConcatto                       if (x == common::NumericOperator::Add ||
129464ab3302SCarolineConcatto                           x == common::NumericOperator::Subtract) {
129564ab3302SCarolineConcatto                         min = 1; // + and - are unary or binary
129664ab3302SCarolineConcatto                       }
129764ab3302SCarolineConcatto                     },
129864ab3302SCarolineConcatto                     [&](const common::LogicalOperator &x) {
129964ab3302SCarolineConcatto                       if (x == common::LogicalOperator::Not) {
130064ab3302SCarolineConcatto                         min = 1; // .NOT. is unary
130164ab3302SCarolineConcatto                         max = 1;
130264ab3302SCarolineConcatto                       }
130364ab3302SCarolineConcatto                     },
130464ab3302SCarolineConcatto                     [](const common::RelationalOperator &) {
130564ab3302SCarolineConcatto                       // all are binary
130664ab3302SCarolineConcatto                     },
130764ab3302SCarolineConcatto                     [](const GenericKind::OtherKind &x) {
130864ab3302SCarolineConcatto                       CHECK(x == GenericKind::OtherKind::Concat);
130964ab3302SCarolineConcatto                     },
131064ab3302SCarolineConcatto                     [](const auto &) { DIE("expected intrinsic operator"); },
131164ab3302SCarolineConcatto                 },
131264ab3302SCarolineConcatto       kind.u);
131364ab3302SCarolineConcatto   if (nargs >= min && nargs <= max) {
131464ab3302SCarolineConcatto     return std::nullopt;
131564ab3302SCarolineConcatto   } else if (max == 1) {
131664ab3302SCarolineConcatto     return "%s function '%s' must have one dummy argument"_err_en_US;
131764ab3302SCarolineConcatto   } else if (min == 2) {
131864ab3302SCarolineConcatto     return "%s function '%s' must have two dummy arguments"_err_en_US;
131964ab3302SCarolineConcatto   } else {
132064ab3302SCarolineConcatto     return "%s function '%s' must have one or two dummy arguments"_err_en_US;
132164ab3302SCarolineConcatto   }
132264ab3302SCarolineConcatto }
132364ab3302SCarolineConcatto 
CheckDefinedOperatorArg(const SourceName & opName,const Symbol & symbol,const Procedure & proc,std::size_t pos)132464ab3302SCarolineConcatto bool CheckHelper::CheckDefinedOperatorArg(const SourceName &opName,
132564ab3302SCarolineConcatto     const Symbol &symbol, const Procedure &proc, std::size_t pos) {
132664ab3302SCarolineConcatto   if (pos >= proc.dummyArguments.size()) {
132764ab3302SCarolineConcatto     return true;
132864ab3302SCarolineConcatto   }
132964ab3302SCarolineConcatto   auto &arg{proc.dummyArguments.at(pos)};
133064ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
133164ab3302SCarolineConcatto   if (arg.IsOptional()) {
133264ab3302SCarolineConcatto     msg = "In %s function '%s', dummy argument '%s' may not be"
133364ab3302SCarolineConcatto           " OPTIONAL"_err_en_US;
133464ab3302SCarolineConcatto   } else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)};
133564ab3302SCarolineConcatto              dataObject == nullptr) {
133664ab3302SCarolineConcatto     msg = "In %s function '%s', dummy argument '%s' must be a"
133764ab3302SCarolineConcatto           " data object"_err_en_US;
133864ab3302SCarolineConcatto   } else if (dataObject->intent != common::Intent::In &&
133964ab3302SCarolineConcatto       !dataObject->attrs.test(DummyDataObject::Attr::Value)) {
134064ab3302SCarolineConcatto     msg = "In %s function '%s', dummy argument '%s' must have INTENT(IN)"
134164ab3302SCarolineConcatto           " or VALUE attribute"_err_en_US;
134264ab3302SCarolineConcatto   }
134364ab3302SCarolineConcatto   if (msg) {
134464ab3302SCarolineConcatto     SayWithDeclaration(symbol, std::move(*msg),
134564ab3302SCarolineConcatto         parser::ToUpperCaseLetters(opName.ToString()), symbol.name(), arg.name);
134664ab3302SCarolineConcatto     return false;
134764ab3302SCarolineConcatto   }
134864ab3302SCarolineConcatto   return true;
134964ab3302SCarolineConcatto }
135064ab3302SCarolineConcatto 
135164ab3302SCarolineConcatto // Check if this procedure can be used for defined assignment (see 15.4.3.4.3).
CheckDefinedAssignment(const Symbol & specific,const Procedure & proc)135264ab3302SCarolineConcatto bool CheckHelper::CheckDefinedAssignment(
135364ab3302SCarolineConcatto     const Symbol &specific, const Procedure &proc) {
135482edd428STim Keith   if (context_.HasError(specific)) {
135582edd428STim Keith     return false;
135682edd428STim Keith   }
135764ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
135864ab3302SCarolineConcatto   if (specific.attrs().test(Attr::NOPASS)) { // C774
135964ab3302SCarolineConcatto     msg = "Defined assignment procedure '%s' may not have"
136064ab3302SCarolineConcatto           " NOPASS attribute"_err_en_US;
136164ab3302SCarolineConcatto   } else if (!proc.IsSubroutine()) {
136264ab3302SCarolineConcatto     msg = "Defined assignment procedure '%s' must be a subroutine"_err_en_US;
136364ab3302SCarolineConcatto   } else if (proc.dummyArguments.size() != 2) {
136464ab3302SCarolineConcatto     msg = "Defined assignment subroutine '%s' must have"
136564ab3302SCarolineConcatto           " two dummy arguments"_err_en_US;
1366fc3f92a8Speter klausler   } else {
1367fc3f92a8Speter klausler     // Check both arguments even if the first has an error.
1368fc3f92a8Speter klausler     bool ok0{CheckDefinedAssignmentArg(specific, proc.dummyArguments[0], 0)};
1369fc3f92a8Speter klausler     bool ok1{CheckDefinedAssignmentArg(specific, proc.dummyArguments[1], 1)};
1370fc3f92a8Speter klausler     if (!(ok0 && ok1)) {
137164ab3302SCarolineConcatto       return false; // error was reported
137264ab3302SCarolineConcatto     } else if (ConflictsWithIntrinsicAssignment(proc)) {
137364ab3302SCarolineConcatto       msg = "Defined assignment subroutine '%s' conflicts with"
137464ab3302SCarolineConcatto             " intrinsic assignment"_err_en_US;
137564ab3302SCarolineConcatto     } else {
137664ab3302SCarolineConcatto       return true; // OK
137764ab3302SCarolineConcatto     }
1378fc3f92a8Speter klausler   }
137964ab3302SCarolineConcatto   SayWithDeclaration(specific, std::move(msg.value()), specific.name());
138082edd428STim Keith   context_.SetError(specific);
138164ab3302SCarolineConcatto   return false;
138264ab3302SCarolineConcatto }
138364ab3302SCarolineConcatto 
CheckDefinedAssignmentArg(const Symbol & symbol,const DummyArgument & arg,int pos)138464ab3302SCarolineConcatto bool CheckHelper::CheckDefinedAssignmentArg(
138564ab3302SCarolineConcatto     const Symbol &symbol, const DummyArgument &arg, int pos) {
138664ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
138764ab3302SCarolineConcatto   if (arg.IsOptional()) {
138864ab3302SCarolineConcatto     msg = "In defined assignment subroutine '%s', dummy argument '%s'"
138964ab3302SCarolineConcatto           " may not be OPTIONAL"_err_en_US;
139064ab3302SCarolineConcatto   } else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)}) {
139164ab3302SCarolineConcatto     if (pos == 0) {
139264ab3302SCarolineConcatto       if (dataObject->intent != common::Intent::Out &&
139364ab3302SCarolineConcatto           dataObject->intent != common::Intent::InOut) {
139464ab3302SCarolineConcatto         msg = "In defined assignment subroutine '%s', first dummy argument '%s'"
139564ab3302SCarolineConcatto               " must have INTENT(OUT) or INTENT(INOUT)"_err_en_US;
139664ab3302SCarolineConcatto       }
139764ab3302SCarolineConcatto     } else if (pos == 1) {
139864ab3302SCarolineConcatto       if (dataObject->intent != common::Intent::In &&
139964ab3302SCarolineConcatto           !dataObject->attrs.test(DummyDataObject::Attr::Value)) {
140064ab3302SCarolineConcatto         msg =
140164ab3302SCarolineConcatto             "In defined assignment subroutine '%s', second dummy"
140264ab3302SCarolineConcatto             " argument '%s' must have INTENT(IN) or VALUE attribute"_err_en_US;
140364ab3302SCarolineConcatto       }
140464ab3302SCarolineConcatto     } else {
140564ab3302SCarolineConcatto       DIE("pos must be 0 or 1");
140664ab3302SCarolineConcatto     }
140764ab3302SCarolineConcatto   } else {
140864ab3302SCarolineConcatto     msg = "In defined assignment subroutine '%s', dummy argument '%s'"
140964ab3302SCarolineConcatto           " must be a data object"_err_en_US;
141064ab3302SCarolineConcatto   }
141164ab3302SCarolineConcatto   if (msg) {
141264ab3302SCarolineConcatto     SayWithDeclaration(symbol, std::move(*msg), symbol.name(), arg.name);
141382edd428STim Keith     context_.SetError(symbol);
141464ab3302SCarolineConcatto     return false;
141564ab3302SCarolineConcatto   }
141664ab3302SCarolineConcatto   return true;
141764ab3302SCarolineConcatto }
141864ab3302SCarolineConcatto 
141964ab3302SCarolineConcatto // Report a conflicting attribute error if symbol has both of these attributes
CheckConflicting(const Symbol & symbol,Attr a1,Attr a2)142064ab3302SCarolineConcatto bool CheckHelper::CheckConflicting(const Symbol &symbol, Attr a1, Attr a2) {
142164ab3302SCarolineConcatto   if (symbol.attrs().test(a1) && symbol.attrs().test(a2)) {
142264ab3302SCarolineConcatto     messages_.Say("'%s' may not have both the %s and %s attributes"_err_en_US,
1423bc56620bSPeter Steinfeld         symbol.name(), AttrToString(a1), AttrToString(a2));
142464ab3302SCarolineConcatto     return true;
142564ab3302SCarolineConcatto   } else {
142664ab3302SCarolineConcatto     return false;
142764ab3302SCarolineConcatto   }
142864ab3302SCarolineConcatto }
142964ab3302SCarolineConcatto 
WarnMissingFinal(const Symbol & symbol)1430c1168676Speter klausler void CheckHelper::WarnMissingFinal(const Symbol &symbol) {
1431c1168676Speter klausler   const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
1432c1168676Speter klausler   if (!object || IsPointer(symbol)) {
1433c1168676Speter klausler     return;
1434c1168676Speter klausler   }
1435c1168676Speter klausler   const DeclTypeSpec *type{object->type()};
1436c1168676Speter klausler   const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr};
1437c1168676Speter klausler   const Symbol *derivedSym{derived ? &derived->typeSymbol() : nullptr};
1438c1168676Speter klausler   int rank{object->shape().Rank()};
1439c1168676Speter klausler   const Symbol *initialDerivedSym{derivedSym};
1440c1168676Speter klausler   while (const auto *derivedDetails{
1441c1168676Speter klausler       derivedSym ? derivedSym->detailsIf<DerivedTypeDetails>() : nullptr}) {
1442c1168676Speter klausler     if (!derivedDetails->finals().empty() &&
1443c1168676Speter klausler         !derivedDetails->GetFinalForRank(rank)) {
1444c1168676Speter klausler       if (auto *msg{derivedSym == initialDerivedSym
1445c1168676Speter klausler                   ? messages_.Say(symbol.name(),
1446a53967cdSPeter Klausler                         "'%s' of derived type '%s' does not have a FINAL subroutine for its rank (%d)"_warn_en_US,
1447c1168676Speter klausler                         symbol.name(), derivedSym->name(), rank)
1448c1168676Speter klausler                   : messages_.Say(symbol.name(),
1449a53967cdSPeter Klausler                         "'%s' of derived type '%s' extended from '%s' does not have a FINAL subroutine for its rank (%d)"_warn_en_US,
1450c1168676Speter klausler                         symbol.name(), initialDerivedSym->name(),
1451c1168676Speter klausler                         derivedSym->name(), rank)}) {
1452c1168676Speter klausler         msg->Attach(derivedSym->name(),
1453c1168676Speter klausler             "Declaration of derived type '%s'"_en_US, derivedSym->name());
1454c1168676Speter klausler       }
1455c1168676Speter klausler       return;
1456c1168676Speter klausler     }
1457c1168676Speter klausler     derived = derivedSym->GetParentTypeSpec();
1458c1168676Speter klausler     derivedSym = derived ? &derived->typeSymbol() : nullptr;
1459c1168676Speter klausler   }
1460c1168676Speter klausler }
1461c1168676Speter klausler 
Characterize(const Symbol & symbol)146282edd428STim Keith const Procedure *CheckHelper::Characterize(const Symbol &symbol) {
146382edd428STim Keith   auto it{characterizeCache_.find(symbol)};
146482edd428STim Keith   if (it == characterizeCache_.end()) {
146582edd428STim Keith     auto pair{characterizeCache_.emplace(SymbolRef{symbol},
1466641ede93Speter klausler         Procedure::Characterize(symbol, context_.foldingContext()))};
146782edd428STim Keith     it = pair.first;
146864ab3302SCarolineConcatto   }
146982edd428STim Keith   return common::GetPtrFromOptional(it->second);
147064ab3302SCarolineConcatto }
147164ab3302SCarolineConcatto 
CheckVolatile(const Symbol & symbol,const DerivedTypeSpec * derived)14722de5ea3bSpeter klausler void CheckHelper::CheckVolatile(const Symbol &symbol,
147364ab3302SCarolineConcatto     const DerivedTypeSpec *derived) { // C866 - C868
147464ab3302SCarolineConcatto   if (IsIntentIn(symbol)) {
147564ab3302SCarolineConcatto     messages_.Say(
147664ab3302SCarolineConcatto         "VOLATILE attribute may not apply to an INTENT(IN) argument"_err_en_US);
147764ab3302SCarolineConcatto   }
147864ab3302SCarolineConcatto   if (IsProcedure(symbol)) {
147964ab3302SCarolineConcatto     messages_.Say("VOLATILE attribute may apply only to a variable"_err_en_US);
148064ab3302SCarolineConcatto   }
14812de5ea3bSpeter klausler   if (symbol.has<UseDetails>() || symbol.has<HostAssocDetails>()) {
148264ab3302SCarolineConcatto     const Symbol &ultimate{symbol.GetUltimate()};
14831ee6f7adSPeter Klausler     if (evaluate::IsCoarray(ultimate)) {
148464ab3302SCarolineConcatto       messages_.Say(
148564ab3302SCarolineConcatto           "VOLATILE attribute may not apply to a coarray accessed by USE or host association"_err_en_US);
148664ab3302SCarolineConcatto     }
148764ab3302SCarolineConcatto     if (derived) {
148864ab3302SCarolineConcatto       if (FindCoarrayUltimateComponent(*derived)) {
148964ab3302SCarolineConcatto         messages_.Say(
149064ab3302SCarolineConcatto             "VOLATILE attribute may not apply to a type with a coarray ultimate component accessed by USE or host association"_err_en_US);
149164ab3302SCarolineConcatto       }
149264ab3302SCarolineConcatto     }
149364ab3302SCarolineConcatto   }
149464ab3302SCarolineConcatto }
149564ab3302SCarolineConcatto 
CheckPointer(const Symbol & symbol)149664ab3302SCarolineConcatto void CheckHelper::CheckPointer(const Symbol &symbol) { // C852
149764ab3302SCarolineConcatto   CheckConflicting(symbol, Attr::POINTER, Attr::TARGET);
149838095549SPete Steinfeld   CheckConflicting(symbol, Attr::POINTER, Attr::ALLOCATABLE); // C751
149964ab3302SCarolineConcatto   CheckConflicting(symbol, Attr::POINTER, Attr::INTRINSIC);
1500f2897b8fSPeter Steinfeld   // Prohibit constant pointers.  The standard does not explicitly prohibit
1501f2897b8fSPeter Steinfeld   // them, but the PARAMETER attribute requires a entity-decl to have an
1502f2897b8fSPeter Steinfeld   // initialization that is a constant-expr, and the only form of
1503f2897b8fSPeter Steinfeld   // initialization that allows a constant-expr is the one that's not a "=>"
1504f2897b8fSPeter Steinfeld   // pointer initialization.  See C811, C807, and section 8.5.13.
1505f2897b8fSPeter Steinfeld   CheckConflicting(symbol, Attr::POINTER, Attr::PARAMETER);
150664ab3302SCarolineConcatto   if (symbol.Corank() > 0) {
150764ab3302SCarolineConcatto     messages_.Say(
150864ab3302SCarolineConcatto         "'%s' may not have the POINTER attribute because it is a coarray"_err_en_US,
150964ab3302SCarolineConcatto         symbol.name());
151064ab3302SCarolineConcatto   }
151164ab3302SCarolineConcatto }
151264ab3302SCarolineConcatto 
151364ab3302SCarolineConcatto // C760 constraints on the passed-object dummy argument
151438095549SPete Steinfeld // C757 constraints on procedure pointer components
CheckPassArg(const Symbol & proc,const Symbol * interface,const WithPassArg & details)151564ab3302SCarolineConcatto void CheckHelper::CheckPassArg(
151664ab3302SCarolineConcatto     const Symbol &proc, const Symbol *interface, const WithPassArg &details) {
151764ab3302SCarolineConcatto   if (proc.attrs().test(Attr::NOPASS)) {
151864ab3302SCarolineConcatto     return;
151964ab3302SCarolineConcatto   }
152064ab3302SCarolineConcatto   const auto &name{proc.name()};
152164ab3302SCarolineConcatto   if (!interface) {
152264ab3302SCarolineConcatto     messages_.Say(name,
152364ab3302SCarolineConcatto         "Procedure component '%s' must have NOPASS attribute or explicit interface"_err_en_US,
152464ab3302SCarolineConcatto         name);
152564ab3302SCarolineConcatto     return;
152664ab3302SCarolineConcatto   }
152764ab3302SCarolineConcatto   const auto *subprogram{interface->detailsIf<SubprogramDetails>()};
152864ab3302SCarolineConcatto   if (!subprogram) {
152964ab3302SCarolineConcatto     messages_.Say(name,
153064ab3302SCarolineConcatto         "Procedure component '%s' has invalid interface '%s'"_err_en_US, name,
153164ab3302SCarolineConcatto         interface->name());
153264ab3302SCarolineConcatto     return;
153364ab3302SCarolineConcatto   }
153464ab3302SCarolineConcatto   std::optional<SourceName> passName{details.passName()};
153564ab3302SCarolineConcatto   const auto &dummyArgs{subprogram->dummyArgs()};
153664ab3302SCarolineConcatto   if (!passName) {
153764ab3302SCarolineConcatto     if (dummyArgs.empty()) {
153864ab3302SCarolineConcatto       messages_.Say(name,
153964ab3302SCarolineConcatto           proc.has<ProcEntityDetails>()
154064ab3302SCarolineConcatto               ? "Procedure component '%s' with no dummy arguments"
154164ab3302SCarolineConcatto                 " must have NOPASS attribute"_err_en_US
154264ab3302SCarolineConcatto               : "Procedure binding '%s' with no dummy arguments"
154364ab3302SCarolineConcatto                 " must have NOPASS attribute"_err_en_US,
154464ab3302SCarolineConcatto           name);
154540e26180SPeter Steinfeld       context_.SetError(*interface);
154664ab3302SCarolineConcatto       return;
154764ab3302SCarolineConcatto     }
1548868187dfSPeter Steinfeld     Symbol *argSym{dummyArgs[0]};
1549868187dfSPeter Steinfeld     if (!argSym) {
1550868187dfSPeter Steinfeld       messages_.Say(interface->name(),
1551868187dfSPeter Steinfeld           "Cannot use an alternate return as the passed-object dummy "
1552868187dfSPeter Steinfeld           "argument"_err_en_US);
1553868187dfSPeter Steinfeld       return;
1554868187dfSPeter Steinfeld     }
155564ab3302SCarolineConcatto     passName = dummyArgs[0]->name();
155664ab3302SCarolineConcatto   }
155764ab3302SCarolineConcatto   std::optional<int> passArgIndex{};
155864ab3302SCarolineConcatto   for (std::size_t i{0}; i < dummyArgs.size(); ++i) {
155964ab3302SCarolineConcatto     if (dummyArgs[i] && dummyArgs[i]->name() == *passName) {
156064ab3302SCarolineConcatto       passArgIndex = i;
156164ab3302SCarolineConcatto       break;
156264ab3302SCarolineConcatto     }
156364ab3302SCarolineConcatto   }
156438095549SPete Steinfeld   if (!passArgIndex) { // C758
156564ab3302SCarolineConcatto     messages_.Say(*passName,
156664ab3302SCarolineConcatto         "'%s' is not a dummy argument of procedure interface '%s'"_err_en_US,
156764ab3302SCarolineConcatto         *passName, interface->name());
156864ab3302SCarolineConcatto     return;
156964ab3302SCarolineConcatto   }
157064ab3302SCarolineConcatto   const Symbol &passArg{*dummyArgs[*passArgIndex]};
157164ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
157264ab3302SCarolineConcatto   if (!passArg.has<ObjectEntityDetails>()) {
157364ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
157464ab3302SCarolineConcatto           " must be a data object"_err_en_US;
157564ab3302SCarolineConcatto   } else if (passArg.attrs().test(Attr::POINTER)) {
157664ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
157764ab3302SCarolineConcatto           " may not have the POINTER attribute"_err_en_US;
157864ab3302SCarolineConcatto   } else if (passArg.attrs().test(Attr::ALLOCATABLE)) {
157964ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
158064ab3302SCarolineConcatto           " may not have the ALLOCATABLE attribute"_err_en_US;
158164ab3302SCarolineConcatto   } else if (passArg.attrs().test(Attr::VALUE)) {
158264ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
158364ab3302SCarolineConcatto           " may not have the VALUE attribute"_err_en_US;
158464ab3302SCarolineConcatto   } else if (passArg.Rank() > 0) {
158564ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
158664ab3302SCarolineConcatto           " must be scalar"_err_en_US;
158764ab3302SCarolineConcatto   }
158864ab3302SCarolineConcatto   if (msg) {
158964ab3302SCarolineConcatto     messages_.Say(name, std::move(*msg), passName.value(), name);
159064ab3302SCarolineConcatto     return;
159164ab3302SCarolineConcatto   }
159264ab3302SCarolineConcatto   const DeclTypeSpec *type{passArg.GetType()};
159364ab3302SCarolineConcatto   if (!type) {
159464ab3302SCarolineConcatto     return; // an error already occurred
159564ab3302SCarolineConcatto   }
159664ab3302SCarolineConcatto   const Symbol &typeSymbol{*proc.owner().GetSymbol()};
159764ab3302SCarolineConcatto   const DerivedTypeSpec *derived{type->AsDerived()};
159864ab3302SCarolineConcatto   if (!derived || derived->typeSymbol() != typeSymbol) {
159964ab3302SCarolineConcatto     messages_.Say(name,
160064ab3302SCarolineConcatto         "Passed-object dummy argument '%s' of procedure '%s'"
160164ab3302SCarolineConcatto         " must be of type '%s' but is '%s'"_err_en_US,
160264ab3302SCarolineConcatto         passName.value(), name, typeSymbol.name(), type->AsFortran());
160364ab3302SCarolineConcatto     return;
160464ab3302SCarolineConcatto   }
160564ab3302SCarolineConcatto   if (IsExtensibleType(derived) != type->IsPolymorphic()) {
160664ab3302SCarolineConcatto     messages_.Say(name,
160764ab3302SCarolineConcatto         type->IsPolymorphic()
160864ab3302SCarolineConcatto             ? "Passed-object dummy argument '%s' of procedure '%s'"
160964ab3302SCarolineConcatto               " may not be polymorphic because '%s' is not extensible"_err_en_US
161064ab3302SCarolineConcatto             : "Passed-object dummy argument '%s' of procedure '%s'"
161164ab3302SCarolineConcatto               " must be polymorphic because '%s' is extensible"_err_en_US,
161264ab3302SCarolineConcatto         passName.value(), name, typeSymbol.name());
161364ab3302SCarolineConcatto     return;
161464ab3302SCarolineConcatto   }
161564ab3302SCarolineConcatto   for (const auto &[paramName, paramValue] : derived->parameters()) {
161664ab3302SCarolineConcatto     if (paramValue.isLen() && !paramValue.isAssumed()) {
161764ab3302SCarolineConcatto       messages_.Say(name,
161864ab3302SCarolineConcatto           "Passed-object dummy argument '%s' of procedure '%s'"
161964ab3302SCarolineConcatto           " has non-assumed length parameter '%s'"_err_en_US,
162064ab3302SCarolineConcatto           passName.value(), name, paramName);
162164ab3302SCarolineConcatto     }
162264ab3302SCarolineConcatto   }
162364ab3302SCarolineConcatto }
162464ab3302SCarolineConcatto 
CheckProcBinding(const Symbol & symbol,const ProcBindingDetails & binding)162564ab3302SCarolineConcatto void CheckHelper::CheckProcBinding(
162664ab3302SCarolineConcatto     const Symbol &symbol, const ProcBindingDetails &binding) {
162764ab3302SCarolineConcatto   const Scope &dtScope{symbol.owner()};
162864ab3302SCarolineConcatto   CHECK(dtScope.kind() == Scope::Kind::DerivedType);
162964ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::DEFERRED)) {
163073c3530fSpeter klausler     if (const Symbol * dtSymbol{dtScope.symbol()}) {
16312b790490SPete Steinfeld       if (!dtSymbol->attrs().test(Attr::ABSTRACT)) { // C733
163264ab3302SCarolineConcatto         SayWithDeclaration(*dtSymbol,
163364ab3302SCarolineConcatto             "Procedure bound to non-ABSTRACT derived type '%s' may not be DEFERRED"_err_en_US,
163464ab3302SCarolineConcatto             dtSymbol->name());
163564ab3302SCarolineConcatto       }
163673c3530fSpeter klausler     }
163764ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::NON_OVERRIDABLE)) {
163864ab3302SCarolineConcatto       messages_.Say(
163964ab3302SCarolineConcatto           "Type-bound procedure '%s' may not be both DEFERRED and NON_OVERRIDABLE"_err_en_US,
164064ab3302SCarolineConcatto           symbol.name());
164164ab3302SCarolineConcatto     }
164264ab3302SCarolineConcatto   }
164373c3530fSpeter klausler   if (binding.symbol().attrs().test(Attr::INTRINSIC) &&
164473c3530fSpeter klausler       !context_.intrinsics().IsSpecificIntrinsicFunction(
164573c3530fSpeter klausler           binding.symbol().name().ToString())) {
164673c3530fSpeter klausler     messages_.Say(
164773c3530fSpeter klausler         "Intrinsic procedure '%s' is not a specific intrinsic permitted for use in the definition of binding '%s'"_err_en_US,
164873c3530fSpeter klausler         binding.symbol().name(), symbol.name());
164964ab3302SCarolineConcatto   }
165064ab3302SCarolineConcatto   if (const Symbol * overridden{FindOverriddenBinding(symbol)}) {
165164ab3302SCarolineConcatto     if (overridden->attrs().test(Attr::NON_OVERRIDABLE)) {
165264ab3302SCarolineConcatto       SayWithDeclaration(*overridden,
165364ab3302SCarolineConcatto           "Override of NON_OVERRIDABLE '%s' is not permitted"_err_en_US,
165464ab3302SCarolineConcatto           symbol.name());
165564ab3302SCarolineConcatto     }
165664ab3302SCarolineConcatto     if (const auto *overriddenBinding{
165764ab3302SCarolineConcatto             overridden->detailsIf<ProcBindingDetails>()}) {
165864ab3302SCarolineConcatto       if (!IsPureProcedure(symbol) && IsPureProcedure(*overridden)) {
165964ab3302SCarolineConcatto         SayWithDeclaration(*overridden,
166064ab3302SCarolineConcatto             "An overridden pure type-bound procedure binding must also be pure"_err_en_US);
166164ab3302SCarolineConcatto         return;
166264ab3302SCarolineConcatto       }
1663*6052025bSPeter Klausler       if (!IsElementalProcedure(binding.symbol()) &&
1664*6052025bSPeter Klausler           IsElementalProcedure(overriddenBinding->symbol())) {
166564ab3302SCarolineConcatto         SayWithDeclaration(*overridden,
166664ab3302SCarolineConcatto             "A type-bound procedure and its override must both, or neither, be ELEMENTAL"_err_en_US);
166764ab3302SCarolineConcatto         return;
166864ab3302SCarolineConcatto       }
166964ab3302SCarolineConcatto       bool isNopass{symbol.attrs().test(Attr::NOPASS)};
167064ab3302SCarolineConcatto       if (isNopass != overridden->attrs().test(Attr::NOPASS)) {
167164ab3302SCarolineConcatto         SayWithDeclaration(*overridden,
167264ab3302SCarolineConcatto             isNopass
167364ab3302SCarolineConcatto                 ? "A NOPASS type-bound procedure may not override a passed-argument procedure"_err_en_US
167464ab3302SCarolineConcatto                 : "A passed-argument type-bound procedure may not override a NOPASS procedure"_err_en_US);
167564ab3302SCarolineConcatto       } else {
167682edd428STim Keith         const auto *bindingChars{Characterize(binding.symbol())};
167782edd428STim Keith         const auto *overriddenChars{Characterize(overriddenBinding->symbol())};
167864ab3302SCarolineConcatto         if (bindingChars && overriddenChars) {
167964ab3302SCarolineConcatto           if (isNopass) {
168064ab3302SCarolineConcatto             if (!bindingChars->CanOverride(*overriddenChars, std::nullopt)) {
168164ab3302SCarolineConcatto               SayWithDeclaration(*overridden,
168264ab3302SCarolineConcatto                   "A type-bound procedure and its override must have compatible interfaces"_err_en_US);
168364ab3302SCarolineConcatto             }
168440e26180SPeter Steinfeld           } else if (!context_.HasError(binding.symbol())) {
168564ab3302SCarolineConcatto             int passIndex{bindingChars->FindPassIndex(binding.passName())};
168664ab3302SCarolineConcatto             int overriddenPassIndex{
168764ab3302SCarolineConcatto                 overriddenChars->FindPassIndex(overriddenBinding->passName())};
168864ab3302SCarolineConcatto             if (passIndex != overriddenPassIndex) {
168964ab3302SCarolineConcatto               SayWithDeclaration(*overridden,
169064ab3302SCarolineConcatto                   "A type-bound procedure and its override must use the same PASS argument"_err_en_US);
169164ab3302SCarolineConcatto             } else if (!bindingChars->CanOverride(
169264ab3302SCarolineConcatto                            *overriddenChars, passIndex)) {
169364ab3302SCarolineConcatto               SayWithDeclaration(*overridden,
169464ab3302SCarolineConcatto                   "A type-bound procedure and its override must have compatible interfaces apart from their passed argument"_err_en_US);
169564ab3302SCarolineConcatto             }
169664ab3302SCarolineConcatto           }
169764ab3302SCarolineConcatto         }
169864ab3302SCarolineConcatto       }
169964ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::PRIVATE) &&
170064ab3302SCarolineConcatto           overridden->attrs().test(Attr::PUBLIC)) {
170164ab3302SCarolineConcatto         SayWithDeclaration(*overridden,
170264ab3302SCarolineConcatto             "A PRIVATE procedure may not override a PUBLIC procedure"_err_en_US);
170364ab3302SCarolineConcatto       }
170464ab3302SCarolineConcatto     } else {
170564ab3302SCarolineConcatto       SayWithDeclaration(*overridden,
170664ab3302SCarolineConcatto           "A type-bound procedure binding may not have the same name as a parent component"_err_en_US);
170764ab3302SCarolineConcatto     }
170864ab3302SCarolineConcatto   }
170964ab3302SCarolineConcatto   CheckPassArg(symbol, &binding.symbol(), binding);
171064ab3302SCarolineConcatto }
171164ab3302SCarolineConcatto 
Check(const Scope & scope)171264ab3302SCarolineConcatto void CheckHelper::Check(const Scope &scope) {
171364ab3302SCarolineConcatto   scope_ = &scope;
17142aa43358SMichael Kruse   common::Restorer<const Symbol *> restorer{innermostSymbol_, innermostSymbol_};
171564ab3302SCarolineConcatto   if (const Symbol * symbol{scope.symbol()}) {
171664ab3302SCarolineConcatto     innermostSymbol_ = symbol;
171764ab3302SCarolineConcatto   }
1718641ede93Speter klausler   if (scope.IsParameterizedDerivedTypeInstantiation()) {
1719641ede93Speter klausler     auto restorer{common::ScopedSet(scopeIsUninstantiatedPDT_, false)};
1720641ede93Speter klausler     auto restorer2{context_.foldingContext().messages().SetContext(
1721641ede93Speter klausler         scope.instantiationContext().get())};
1722641ede93Speter klausler     for (const auto &pair : scope) {
1723641ede93Speter klausler       CheckPointerInitialization(*pair.second);
1724641ede93Speter klausler     }
1725641ede93Speter klausler   } else {
1726641ede93Speter klausler     auto restorer{common::ScopedSet(
1727641ede93Speter klausler         scopeIsUninstantiatedPDT_, scope.IsParameterizedDerivedType())};
172864ab3302SCarolineConcatto     for (const auto &set : scope.equivalenceSets()) {
172964ab3302SCarolineConcatto       CheckEquivalenceSet(set);
173064ab3302SCarolineConcatto     }
173164ab3302SCarolineConcatto     for (const auto &pair : scope) {
173264ab3302SCarolineConcatto       Check(*pair.second);
173364ab3302SCarolineConcatto     }
1734dafd3cf8SPeixin-Qiao     for (const auto &pair : scope.commonBlocks()) {
1735dafd3cf8SPeixin-Qiao       CheckCommonBlock(*pair.second);
1736dafd3cf8SPeixin-Qiao     }
1737c207e360SPeixin-Qiao     int mainProgCnt{0};
173864ab3302SCarolineConcatto     for (const Scope &child : scope.children()) {
173964ab3302SCarolineConcatto       Check(child);
1740c207e360SPeixin-Qiao       // A program shall consist of exactly one main program (5.2.2).
1741c207e360SPeixin-Qiao       if (child.kind() == Scope::Kind::MainProgram) {
1742c207e360SPeixin-Qiao         ++mainProgCnt;
1743c207e360SPeixin-Qiao         if (mainProgCnt > 1) {
1744c207e360SPeixin-Qiao           messages_.Say(child.sourceRange(),
1745c207e360SPeixin-Qiao               "A source file cannot contain more than one main program"_err_en_US);
1746c207e360SPeixin-Qiao         }
1747c207e360SPeixin-Qiao       }
174864ab3302SCarolineConcatto     }
174964ab3302SCarolineConcatto     if (scope.kind() == Scope::Kind::BlockData) {
175064ab3302SCarolineConcatto       CheckBlockData(scope);
175164ab3302SCarolineConcatto     }
175282edd428STim Keith     CheckGenericOps(scope);
175364ab3302SCarolineConcatto   }
1754641ede93Speter klausler }
175564ab3302SCarolineConcatto 
CheckEquivalenceSet(const EquivalenceSet & set)175664ab3302SCarolineConcatto void CheckHelper::CheckEquivalenceSet(const EquivalenceSet &set) {
175764ab3302SCarolineConcatto   auto iter{
175864ab3302SCarolineConcatto       std::find_if(set.begin(), set.end(), [](const EquivalenceObject &object) {
175964ab3302SCarolineConcatto         return FindCommonBlockContaining(object.symbol) != nullptr;
176064ab3302SCarolineConcatto       })};
176164ab3302SCarolineConcatto   if (iter != set.end()) {
176264ab3302SCarolineConcatto     const Symbol &commonBlock{DEREF(FindCommonBlockContaining(iter->symbol))};
176364ab3302SCarolineConcatto     for (auto &object : set) {
176464ab3302SCarolineConcatto       if (&object != &*iter) {
176564ab3302SCarolineConcatto         if (auto *details{object.symbol.detailsIf<ObjectEntityDetails>()}) {
176664ab3302SCarolineConcatto           if (details->commonBlock()) {
176764ab3302SCarolineConcatto             if (details->commonBlock() != &commonBlock) { // 8.10.3 paragraph 1
176864ab3302SCarolineConcatto               if (auto *msg{messages_.Say(object.symbol.name(),
176964ab3302SCarolineConcatto                       "Two objects in the same EQUIVALENCE set may not be members of distinct COMMON blocks"_err_en_US)}) {
177064ab3302SCarolineConcatto                 msg->Attach(iter->symbol.name(),
177164ab3302SCarolineConcatto                        "Other object in EQUIVALENCE set"_en_US)
177264ab3302SCarolineConcatto                     .Attach(details->commonBlock()->name(),
177364ab3302SCarolineConcatto                         "COMMON block containing '%s'"_en_US,
177464ab3302SCarolineConcatto                         object.symbol.name())
177564ab3302SCarolineConcatto                     .Attach(commonBlock.name(),
177664ab3302SCarolineConcatto                         "COMMON block containing '%s'"_en_US,
177764ab3302SCarolineConcatto                         iter->symbol.name());
177864ab3302SCarolineConcatto               }
177964ab3302SCarolineConcatto             }
178064ab3302SCarolineConcatto           } else {
178164ab3302SCarolineConcatto             // Mark all symbols in the equivalence set with the same COMMON
178264ab3302SCarolineConcatto             // block to prevent spurious error messages about initialization
178364ab3302SCarolineConcatto             // in BLOCK DATA outside COMMON
178464ab3302SCarolineConcatto             details->set_commonBlock(commonBlock);
178564ab3302SCarolineConcatto           }
178664ab3302SCarolineConcatto         }
178764ab3302SCarolineConcatto       }
178864ab3302SCarolineConcatto     }
178964ab3302SCarolineConcatto   }
179064ab3302SCarolineConcatto   // TODO: Move C8106 (&al.) checks here from resolve-names-utils.cpp
179164ab3302SCarolineConcatto }
179264ab3302SCarolineConcatto 
CheckBlockData(const Scope & scope)179364ab3302SCarolineConcatto void CheckHelper::CheckBlockData(const Scope &scope) {
179464ab3302SCarolineConcatto   // BLOCK DATA subprograms should contain only named common blocks.
179564ab3302SCarolineConcatto   // C1415 presents a list of statements that shouldn't appear in
179664ab3302SCarolineConcatto   // BLOCK DATA, but so long as the subprogram contains no executable
179764ab3302SCarolineConcatto   // code and allocates no storage outside named COMMON, we're happy
179864ab3302SCarolineConcatto   // (e.g., an ENUM is strictly not allowed).
179964ab3302SCarolineConcatto   for (const auto &pair : scope) {
180064ab3302SCarolineConcatto     const Symbol &symbol{*pair.second};
180164ab3302SCarolineConcatto     if (!(symbol.has<CommonBlockDetails>() || symbol.has<UseDetails>() ||
180264ab3302SCarolineConcatto             symbol.has<UseErrorDetails>() || symbol.has<DerivedTypeDetails>() ||
180364ab3302SCarolineConcatto             symbol.has<SubprogramDetails>() ||
180464ab3302SCarolineConcatto             symbol.has<ObjectEntityDetails>() ||
180564ab3302SCarolineConcatto             (symbol.has<ProcEntityDetails>() &&
180664ab3302SCarolineConcatto                 !symbol.attrs().test(Attr::POINTER)))) {
180764ab3302SCarolineConcatto       messages_.Say(symbol.name(),
180864ab3302SCarolineConcatto           "'%s' may not appear in a BLOCK DATA subprogram"_err_en_US,
180964ab3302SCarolineConcatto           symbol.name());
181064ab3302SCarolineConcatto     }
181164ab3302SCarolineConcatto   }
181264ab3302SCarolineConcatto }
181364ab3302SCarolineConcatto 
181482edd428STim Keith // Check distinguishability of generic assignment and operators.
181582edd428STim Keith // For these, generics and generic bindings must be considered together.
CheckGenericOps(const Scope & scope)181682edd428STim Keith void CheckHelper::CheckGenericOps(const Scope &scope) {
181782edd428STim Keith   DistinguishabilityHelper helper{context_};
181882edd428STim Keith   auto addSpecifics{[&](const Symbol &generic) {
181982edd428STim Keith     const auto *details{generic.GetUltimate().detailsIf<GenericDetails>()};
182082edd428STim Keith     if (!details) {
18217f680b26SPeter Klausler       // Not a generic; ensure characteristics are defined if a function.
18227f680b26SPeter Klausler       auto restorer{messages_.SetLocation(generic.name())};
18237f680b26SPeter Klausler       if (IsFunction(generic) && !context_.HasError(generic)) {
18247f680b26SPeter Klausler         if (const Symbol * result{FindFunctionResult(generic)};
18257f680b26SPeter Klausler             result && !context_.HasError(*result)) {
1826488b9fd1SDaniil Dudkin           Characterize(generic);
1827488b9fd1SDaniil Dudkin         }
18287f680b26SPeter Klausler       }
182982edd428STim Keith       return;
183082edd428STim Keith     }
183182edd428STim Keith     GenericKind kind{details->kind()};
183282edd428STim Keith     if (!kind.IsAssignment() && !kind.IsOperator()) {
183382edd428STim Keith       return;
183482edd428STim Keith     }
183582edd428STim Keith     const SymbolVector &specifics{details->specificProcs()};
183682edd428STim Keith     const std::vector<SourceName> &bindingNames{details->bindingNames()};
183782edd428STim Keith     for (std::size_t i{0}; i < specifics.size(); ++i) {
183882edd428STim Keith       const Symbol &specific{*specifics[i]};
183982edd428STim Keith       auto restorer{messages_.SetLocation(bindingNames[i])};
18407f680b26SPeter Klausler       if (const Procedure * proc{Characterize(specific)}) {
184182edd428STim Keith         if (kind.IsAssignment()) {
184282edd428STim Keith           if (!CheckDefinedAssignment(specific, *proc)) {
184382edd428STim Keith             continue;
184482edd428STim Keith           }
184582edd428STim Keith         } else {
184682edd428STim Keith           if (!CheckDefinedOperator(generic.name(), kind, specific, *proc)) {
184782edd428STim Keith             continue;
184882edd428STim Keith           }
184982edd428STim Keith         }
185082edd428STim Keith         helper.Add(generic, kind, specific, *proc);
185182edd428STim Keith       }
185282edd428STim Keith     }
185382edd428STim Keith   }};
185482edd428STim Keith   for (const auto &pair : scope) {
185582edd428STim Keith     const Symbol &symbol{*pair.second};
185682edd428STim Keith     addSpecifics(symbol);
185782edd428STim Keith     const Symbol &ultimate{symbol.GetUltimate()};
185882edd428STim Keith     if (ultimate.has<DerivedTypeDetails>()) {
185982edd428STim Keith       if (const Scope * typeScope{ultimate.scope()}) {
186082edd428STim Keith         for (const auto &pair2 : *typeScope) {
186182edd428STim Keith           addSpecifics(*pair2.second);
186282edd428STim Keith         }
186382edd428STim Keith       }
186482edd428STim Keith     }
186582edd428STim Keith   }
186686f59de1STim Keith   helper.Check(scope);
186782edd428STim Keith }
186882edd428STim Keith 
DefinesBindCName(const Symbol & symbol)1869b6f22fa5Speter klausler static const std::string *DefinesBindCName(const Symbol &symbol) {
1870b6f22fa5Speter klausler   const auto *subp{symbol.detailsIf<SubprogramDetails>()};
1871cfd474e0SPeter Klausler   if ((subp && !subp->isInterface()) || symbol.has<ObjectEntityDetails>() ||
1872cfd474e0SPeter Klausler       symbol.has<CommonBlockDetails>()) {
1873b6f22fa5Speter klausler     // Symbol defines data or entry point
1874b6f22fa5Speter klausler     return symbol.GetBindName();
1875b6f22fa5Speter klausler   } else {
1876b6f22fa5Speter klausler     return nullptr;
1877b6f22fa5Speter klausler   }
1878b6f22fa5Speter klausler }
1879b6f22fa5Speter klausler 
CheckBindC(const Symbol & symbol)1880f3d83353SPeixinQiao void CheckHelper::CheckBindC(const Symbol &symbol) {
1881f3d83353SPeixinQiao   if (!symbol.attrs().test(Attr::BIND_C)) {
1882f3d83353SPeixinQiao     return;
1883f3d83353SPeixinQiao   }
1884e2ac99b7SPeixinQiao   CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER);
1885f3d83353SPeixinQiao   if (symbol.has<ObjectEntityDetails>() && !symbol.owner().IsModule()) {
1886f3d83353SPeixinQiao     messages_.Say(symbol.name(),
1887f3d83353SPeixinQiao         "A variable with BIND(C) attribute may only appear in the specification part of a module"_err_en_US);
1888779d2470SPeixin-Qiao     context_.SetError(symbol);
1889f3d83353SPeixinQiao   }
1890b6f22fa5Speter klausler   if (const std::string * name{DefinesBindCName(symbol)}) {
1891b6f22fa5Speter klausler     auto pair{bindC_.emplace(*name, symbol)};
1892b6f22fa5Speter klausler     if (!pair.second) {
1893b6f22fa5Speter klausler       const Symbol &other{*pair.first->second};
1894cfd474e0SPeter Klausler       if (symbol.has<CommonBlockDetails>() && other.has<CommonBlockDetails>() &&
1895cfd474e0SPeter Klausler           symbol.name() == other.name()) {
1896cfd474e0SPeter Klausler         // Two common blocks can have the same BIND(C) name so long as
1897cfd474e0SPeter Klausler         // they're not in the same scope.
1898cfd474e0SPeter Klausler       } else if (!context_.HasError(other)) {
1899dafd3cf8SPeixin-Qiao         if (auto *msg{messages_.Say(symbol.name(),
1900cfd474e0SPeter Klausler                 "Two entities have the same BIND(C) name '%s'"_err_en_US,
1901b6f22fa5Speter klausler                 *name)}) {
1902cfd474e0SPeter Klausler           msg->Attach(other.name(), "Conflicting declaration"_en_US);
1903b6f22fa5Speter klausler         }
1904b6f22fa5Speter klausler         context_.SetError(symbol);
1905b6f22fa5Speter klausler         context_.SetError(other);
1906b6f22fa5Speter klausler       }
1907b6f22fa5Speter klausler     }
1908b6f22fa5Speter klausler   }
1909b6713feeSPeixinQiao   if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
1910b6713feeSPeixinQiao     if (!proc->interface().symbol() ||
1911b6713feeSPeixinQiao         !proc->interface().symbol()->attrs().test(Attr::BIND_C)) {
1912b6713feeSPeixinQiao       messages_.Say(symbol.name(),
1913b6713feeSPeixinQiao           "An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US);
1914779d2470SPeixin-Qiao       context_.SetError(symbol);
1915b6713feeSPeixinQiao     }
1916b6713feeSPeixinQiao   }
1917b6f22fa5Speter klausler }
1918b6f22fa5Speter klausler 
CheckDioDummyIsData(const Symbol & subp,const Symbol * arg,std::size_t position)1919bc56620bSPeter Steinfeld bool CheckHelper::CheckDioDummyIsData(
1920bc56620bSPeter Steinfeld     const Symbol &subp, const Symbol *arg, std::size_t position) {
1921bc56620bSPeter Steinfeld   if (arg && arg->detailsIf<ObjectEntityDetails>()) {
1922bc56620bSPeter Steinfeld     return true;
1923bc56620bSPeter Steinfeld   } else {
1924bc56620bSPeter Steinfeld     if (arg) {
1925bc56620bSPeter Steinfeld       messages_.Say(arg->name(),
1926bc56620bSPeter Steinfeld           "Dummy argument '%s' must be a data object"_err_en_US, arg->name());
1927bc56620bSPeter Steinfeld     } else {
1928bc56620bSPeter Steinfeld       messages_.Say(subp.name(),
1929bc56620bSPeter Steinfeld           "Dummy argument %d of '%s' must be a data object"_err_en_US, position,
1930bc56620bSPeter Steinfeld           subp.name());
1931bc56620bSPeter Steinfeld     }
1932bc56620bSPeter Steinfeld     return false;
1933bc56620bSPeter Steinfeld   }
1934bc56620bSPeter Steinfeld }
1935bc56620bSPeter Steinfeld 
CheckAlreadySeenDefinedIo(const DerivedTypeSpec & derivedType,GenericKind::DefinedIo ioKind,const Symbol & proc,const Symbol & generic)1936dcf9ba82SPeter Klausler void CheckHelper::CheckAlreadySeenDefinedIo(const DerivedTypeSpec &derivedType,
1937dcf9ba82SPeter Klausler     GenericKind::DefinedIo ioKind, const Symbol &proc, const Symbol &generic) {
193822d7e298SPeter Steinfeld   for (TypeWithDefinedIo definedIoType : seenDefinedIoTypes_) {
1939dcf9ba82SPeter Klausler     // It's okay to have two or more distinct derived type I/O procedures
1940dcf9ba82SPeter Klausler     // for the same type if they're coming from distinct non-type-bound
1941dcf9ba82SPeter Klausler     // interfaces.  (The non-type-bound interfaces would have been merged into
1942dcf9ba82SPeter Klausler     // a single generic if both were visible in the same scope.)
1943dcf9ba82SPeter Klausler     if (derivedType == definedIoType.type && ioKind == definedIoType.ioKind &&
1944dcf9ba82SPeter Klausler         proc != definedIoType.proc &&
1945dcf9ba82SPeter Klausler         (generic.owner().IsDerivedType() ||
1946dcf9ba82SPeter Klausler             definedIoType.generic.owner().IsDerivedType())) {
194722d7e298SPeter Steinfeld       SayWithDeclaration(proc, definedIoType.proc.name(),
194822d7e298SPeter Steinfeld           "Derived type '%s' already has defined input/output procedure"
194922d7e298SPeter Steinfeld           " '%s'"_err_en_US,
1950dcf9ba82SPeter Klausler           derivedType.name(),
195122d7e298SPeter Steinfeld           parser::ToUpperCaseLetters(GenericKind::EnumToString(ioKind)));
1952bc56620bSPeter Steinfeld       return;
1953bc56620bSPeter Steinfeld     }
195422d7e298SPeter Steinfeld   }
195522d7e298SPeter Steinfeld   seenDefinedIoTypes_.emplace_back(
1956dcf9ba82SPeter Klausler       TypeWithDefinedIo{derivedType, ioKind, proc, generic});
195722d7e298SPeter Steinfeld }
195822d7e298SPeter Steinfeld 
CheckDioDummyIsDerived(const Symbol & subp,const Symbol & arg,GenericKind::DefinedIo ioKind,const Symbol & generic)1959dcf9ba82SPeter Klausler void CheckHelper::CheckDioDummyIsDerived(const Symbol &subp, const Symbol &arg,
1960dcf9ba82SPeter Klausler     GenericKind::DefinedIo ioKind, const Symbol &generic) {
196122d7e298SPeter Steinfeld   if (const DeclTypeSpec * type{arg.GetType()}) {
196243fadefbSpeter klausler     if (const DerivedTypeSpec * derivedType{type->AsDerived()}) {
1963dcf9ba82SPeter Klausler       CheckAlreadySeenDefinedIo(*derivedType, ioKind, subp, generic);
196443fadefbSpeter klausler       bool isPolymorphic{type->IsPolymorphic()};
196543fadefbSpeter klausler       if (isPolymorphic != IsExtensibleType(derivedType)) {
196643fadefbSpeter klausler         messages_.Say(arg.name(),
196743fadefbSpeter klausler             "Dummy argument '%s' of a defined input/output procedure must be %s when the derived type is %s"_err_en_US,
196843fadefbSpeter klausler             arg.name(), isPolymorphic ? "TYPE()" : "CLASS()",
196943fadefbSpeter klausler             isPolymorphic ? "not extensible" : "extensible");
197043fadefbSpeter klausler       }
197122d7e298SPeter Steinfeld     } else {
1972bc56620bSPeter Steinfeld       messages_.Say(arg.name(),
1973bc56620bSPeter Steinfeld           "Dummy argument '%s' of a defined input/output procedure must have a"
1974bc56620bSPeter Steinfeld           " derived type"_err_en_US,
1975bc56620bSPeter Steinfeld           arg.name());
1976bc56620bSPeter Steinfeld     }
197722d7e298SPeter Steinfeld   }
197822d7e298SPeter Steinfeld }
1979bc56620bSPeter Steinfeld 
CheckDioDummyIsDefaultInteger(const Symbol & subp,const Symbol & arg)1980bc56620bSPeter Steinfeld void CheckHelper::CheckDioDummyIsDefaultInteger(
1981bc56620bSPeter Steinfeld     const Symbol &subp, const Symbol &arg) {
1982bc56620bSPeter Steinfeld   if (const DeclTypeSpec * type{arg.GetType()};
1983bc56620bSPeter Steinfeld       type && type->IsNumeric(TypeCategory::Integer)) {
1984bc56620bSPeter Steinfeld     if (const auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())};
1985bc56620bSPeter Steinfeld         kind && *kind == context_.GetDefaultKind(TypeCategory::Integer)) {
1986bc56620bSPeter Steinfeld       return;
1987bc56620bSPeter Steinfeld     }
1988bc56620bSPeter Steinfeld   }
1989bc56620bSPeter Steinfeld   messages_.Say(arg.name(),
1990bc56620bSPeter Steinfeld       "Dummy argument '%s' of a defined input/output procedure"
1991bc56620bSPeter Steinfeld       " must be an INTEGER of default KIND"_err_en_US,
1992bc56620bSPeter Steinfeld       arg.name());
1993bc56620bSPeter Steinfeld }
1994bc56620bSPeter Steinfeld 
CheckDioDummyIsScalar(const Symbol & subp,const Symbol & arg)1995bc56620bSPeter Steinfeld void CheckHelper::CheckDioDummyIsScalar(const Symbol &subp, const Symbol &arg) {
1996bc56620bSPeter Steinfeld   if (arg.Rank() > 0 || arg.Corank() > 0) {
1997bc56620bSPeter Steinfeld     messages_.Say(arg.name(),
1998bc56620bSPeter Steinfeld         "Dummy argument '%s' of a defined input/output procedure"
1999bc56620bSPeter Steinfeld         " must be a scalar"_err_en_US,
2000bc56620bSPeter Steinfeld         arg.name());
2001bc56620bSPeter Steinfeld   }
2002bc56620bSPeter Steinfeld }
2003bc56620bSPeter Steinfeld 
CheckDioDtvArg(const Symbol & subp,const Symbol * arg,GenericKind::DefinedIo ioKind,const Symbol & generic)2004dcf9ba82SPeter Klausler void CheckHelper::CheckDioDtvArg(const Symbol &subp, const Symbol *arg,
2005dcf9ba82SPeter Klausler     GenericKind::DefinedIo ioKind, const Symbol &generic) {
2006bc56620bSPeter Steinfeld   // Dtv argument looks like: dtv-type-spec, INTENT(INOUT) :: dtv
2007bc56620bSPeter Steinfeld   if (CheckDioDummyIsData(subp, arg, 0)) {
2008dcf9ba82SPeter Klausler     CheckDioDummyIsDerived(subp, *arg, ioKind, generic);
2009bc56620bSPeter Steinfeld     CheckDioDummyAttrs(subp, *arg,
2010bc56620bSPeter Steinfeld         ioKind == GenericKind::DefinedIo::ReadFormatted ||
2011bc56620bSPeter Steinfeld                 ioKind == GenericKind::DefinedIo::ReadUnformatted
2012bc56620bSPeter Steinfeld             ? Attr::INTENT_INOUT
2013bc56620bSPeter Steinfeld             : Attr::INTENT_IN);
2014bc56620bSPeter Steinfeld   }
2015bc56620bSPeter Steinfeld }
2016bc56620bSPeter Steinfeld 
2017eb14135eSPeter Klausler // If an explicit INTRINSIC name is a function, so must all the specifics be,
2018eb14135eSPeter Klausler // and similarly for subroutines
CheckGenericVsIntrinsic(const Symbol & symbol,const GenericDetails & generic)2019eb14135eSPeter Klausler void CheckHelper::CheckGenericVsIntrinsic(
2020eb14135eSPeter Klausler     const Symbol &symbol, const GenericDetails &generic) {
2021eb14135eSPeter Klausler   if (symbol.attrs().test(Attr::INTRINSIC)) {
2022eb14135eSPeter Klausler     const evaluate::IntrinsicProcTable &table{
2023eb14135eSPeter Klausler         context_.foldingContext().intrinsics()};
2024eb14135eSPeter Klausler     bool isSubroutine{table.IsIntrinsicSubroutine(symbol.name().ToString())};
2025eb14135eSPeter Klausler     if (isSubroutine || table.IsIntrinsicFunction(symbol.name().ToString())) {
2026eb14135eSPeter Klausler       for (const SymbolRef &ref : generic.specificProcs()) {
2027eb14135eSPeter Klausler         const Symbol &ultimate{ref->GetUltimate()};
2028eb14135eSPeter Klausler         bool specificFunc{ultimate.test(Symbol::Flag::Function)};
2029eb14135eSPeter Klausler         bool specificSubr{ultimate.test(Symbol::Flag::Subroutine)};
2030eb14135eSPeter Klausler         if (!specificFunc && !specificSubr) {
2031eb14135eSPeter Klausler           if (const auto *proc{ultimate.detailsIf<SubprogramDetails>()}) {
2032eb14135eSPeter Klausler             if (proc->isFunction()) {
2033eb14135eSPeter Klausler               specificFunc = true;
2034eb14135eSPeter Klausler             } else {
2035eb14135eSPeter Klausler               specificSubr = true;
2036eb14135eSPeter Klausler             }
2037eb14135eSPeter Klausler           }
2038eb14135eSPeter Klausler         }
2039eb14135eSPeter Klausler         if ((specificFunc || specificSubr) &&
2040eb14135eSPeter Klausler             isSubroutine != specificSubr) { // C848
2041eb14135eSPeter Klausler           messages_.Say(symbol.name(),
2042eb14135eSPeter 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,
2043eb14135eSPeter Klausler               symbol.name(), isSubroutine ? "subroutine" : "function",
2044eb14135eSPeter Klausler               ref->name(), isSubroutine ? "function" : "subroutine");
2045eb14135eSPeter Klausler         }
2046eb14135eSPeter Klausler       }
2047eb14135eSPeter Klausler     }
2048eb14135eSPeter Klausler   }
2049eb14135eSPeter Klausler }
2050eb14135eSPeter Klausler 
CheckDefaultIntegerArg(const Symbol & subp,const Symbol * arg,Attr intent)2051bc56620bSPeter Steinfeld void CheckHelper::CheckDefaultIntegerArg(
2052bc56620bSPeter Steinfeld     const Symbol &subp, const Symbol *arg, Attr intent) {
2053bc56620bSPeter Steinfeld   // Argument looks like: INTEGER, INTENT(intent) :: arg
2054bc56620bSPeter Steinfeld   if (CheckDioDummyIsData(subp, arg, 1)) {
2055bc56620bSPeter Steinfeld     CheckDioDummyIsDefaultInteger(subp, *arg);
2056bc56620bSPeter Steinfeld     CheckDioDummyIsScalar(subp, *arg);
2057bc56620bSPeter Steinfeld     CheckDioDummyAttrs(subp, *arg, intent);
2058bc56620bSPeter Steinfeld   }
2059bc56620bSPeter Steinfeld }
2060bc56620bSPeter Steinfeld 
CheckDioAssumedLenCharacterArg(const Symbol & subp,const Symbol * arg,std::size_t argPosition,Attr intent)2061bc56620bSPeter Steinfeld void CheckHelper::CheckDioAssumedLenCharacterArg(const Symbol &subp,
2062bc56620bSPeter Steinfeld     const Symbol *arg, std::size_t argPosition, Attr intent) {
2063bc56620bSPeter Steinfeld   // Argument looks like: CHARACTER (LEN=*), INTENT(intent) :: (iotype OR iomsg)
2064bc56620bSPeter Steinfeld   if (CheckDioDummyIsData(subp, arg, argPosition)) {
2065bc56620bSPeter Steinfeld     CheckDioDummyAttrs(subp, *arg, intent);
2066bc56620bSPeter Steinfeld     if (!IsAssumedLengthCharacter(*arg)) {
2067bc56620bSPeter Steinfeld       messages_.Say(arg->name(),
2068bc56620bSPeter Steinfeld           "Dummy argument '%s' of a defined input/output procedure"
2069bc56620bSPeter Steinfeld           " must be assumed-length CHARACTER"_err_en_US,
2070bc56620bSPeter Steinfeld           arg->name());
2071bc56620bSPeter Steinfeld     }
2072bc56620bSPeter Steinfeld   }
2073bc56620bSPeter Steinfeld }
2074bc56620bSPeter Steinfeld 
CheckDioVlistArg(const Symbol & subp,const Symbol * arg,std::size_t argPosition)2075bc56620bSPeter Steinfeld void CheckHelper::CheckDioVlistArg(
2076bc56620bSPeter Steinfeld     const Symbol &subp, const Symbol *arg, std::size_t argPosition) {
2077bc56620bSPeter Steinfeld   // Vlist argument looks like: INTEGER, INTENT(IN) :: v_list(:)
2078bc56620bSPeter Steinfeld   if (CheckDioDummyIsData(subp, arg, argPosition)) {
2079bc56620bSPeter Steinfeld     CheckDioDummyIsDefaultInteger(subp, *arg);
2080bc56620bSPeter Steinfeld     CheckDioDummyAttrs(subp, *arg, Attr::INTENT_IN);
208144bc97c8SPeter Klausler     const auto *objectDetails{arg->detailsIf<ObjectEntityDetails>()};
208244bc97c8SPeter Klausler     if (!objectDetails || !objectDetails->shape().CanBeDeferredShape()) {
2083bc56620bSPeter Steinfeld       messages_.Say(arg->name(),
2084bc56620bSPeter Steinfeld           "Dummy argument '%s' of a defined input/output procedure must be"
2085bc56620bSPeter Steinfeld           " deferred shape"_err_en_US,
2086bc56620bSPeter Steinfeld           arg->name());
2087bc56620bSPeter Steinfeld     }
2088bc56620bSPeter Steinfeld   }
208944bc97c8SPeter Klausler }
2090bc56620bSPeter Steinfeld 
CheckDioArgCount(const Symbol & subp,GenericKind::DefinedIo ioKind,std::size_t argCount)2091bc56620bSPeter Steinfeld void CheckHelper::CheckDioArgCount(
2092bc56620bSPeter Steinfeld     const Symbol &subp, GenericKind::DefinedIo ioKind, std::size_t argCount) {
2093bc56620bSPeter Steinfeld   const std::size_t requiredArgCount{
2094bc56620bSPeter Steinfeld       (std::size_t)(ioKind == GenericKind::DefinedIo::ReadFormatted ||
2095bc56620bSPeter Steinfeld                   ioKind == GenericKind::DefinedIo::WriteFormatted
2096bc56620bSPeter Steinfeld               ? 6
2097bc56620bSPeter Steinfeld               : 4)};
2098bc56620bSPeter Steinfeld   if (argCount != requiredArgCount) {
2099bc56620bSPeter Steinfeld     SayWithDeclaration(subp,
2100bc56620bSPeter Steinfeld         "Defined input/output procedure '%s' must have"
2101bc56620bSPeter Steinfeld         " %d dummy arguments rather than %d"_err_en_US,
2102bc56620bSPeter Steinfeld         subp.name(), requiredArgCount, argCount);
2103bc56620bSPeter Steinfeld     context_.SetError(subp);
2104bc56620bSPeter Steinfeld   }
2105bc56620bSPeter Steinfeld }
2106bc56620bSPeter Steinfeld 
CheckDioDummyAttrs(const Symbol & subp,const Symbol & arg,Attr goodIntent)2107bc56620bSPeter Steinfeld void CheckHelper::CheckDioDummyAttrs(
2108bc56620bSPeter Steinfeld     const Symbol &subp, const Symbol &arg, Attr goodIntent) {
2109bc56620bSPeter Steinfeld   // Defined I/O procedures can't have attributes other than INTENT
2110bc56620bSPeter Steinfeld   Attrs attrs{arg.attrs()};
2111bc56620bSPeter Steinfeld   if (!attrs.test(goodIntent)) {
2112bc56620bSPeter Steinfeld     messages_.Say(arg.name(),
2113bc56620bSPeter Steinfeld         "Dummy argument '%s' of a defined input/output procedure"
2114bc56620bSPeter Steinfeld         " must have intent '%s'"_err_en_US,
2115bc56620bSPeter Steinfeld         arg.name(), AttrToString(goodIntent));
2116bc56620bSPeter Steinfeld   }
2117bc56620bSPeter Steinfeld   attrs = attrs - Attr::INTENT_IN - Attr::INTENT_OUT - Attr::INTENT_INOUT;
2118bc56620bSPeter Steinfeld   if (!attrs.empty()) {
2119bc56620bSPeter Steinfeld     messages_.Say(arg.name(),
2120bc56620bSPeter Steinfeld         "Dummy argument '%s' of a defined input/output procedure may not have"
2121bc56620bSPeter Steinfeld         " any attributes"_err_en_US,
2122bc56620bSPeter Steinfeld         arg.name());
2123bc56620bSPeter Steinfeld   }
2124bc56620bSPeter Steinfeld }
2125bc56620bSPeter Steinfeld 
2126bc56620bSPeter Steinfeld // Enforce semantics for defined input/output procedures (12.6.4.8.2) and C777
CheckDefinedIoProc(const Symbol & symbol,const GenericDetails & details,GenericKind::DefinedIo ioKind)2127bc56620bSPeter Steinfeld void CheckHelper::CheckDefinedIoProc(const Symbol &symbol,
2128bc56620bSPeter Steinfeld     const GenericDetails &details, GenericKind::DefinedIo ioKind) {
2129bc56620bSPeter Steinfeld   for (auto ref : details.specificProcs()) {
2130bc56620bSPeter Steinfeld     const auto *binding{ref->detailsIf<ProcBindingDetails>()};
2131bc56620bSPeter Steinfeld     const Symbol &specific{*(binding ? &binding->symbol() : &*ref)};
2132bc56620bSPeter Steinfeld     if (ref->attrs().test(Attr::NOPASS)) { // C774
2133bc56620bSPeter Steinfeld       messages_.Say("Defined input/output procedure '%s' may not have NOPASS "
2134bc56620bSPeter Steinfeld                     "attribute"_err_en_US,
2135bc56620bSPeter Steinfeld           ref->name());
2136bc56620bSPeter Steinfeld       context_.SetError(*ref);
2137bc56620bSPeter Steinfeld     }
2138bc56620bSPeter Steinfeld     if (const auto *subpDetails{specific.detailsIf<SubprogramDetails>()}) {
2139bc56620bSPeter Steinfeld       const std::vector<Symbol *> &dummyArgs{subpDetails->dummyArgs()};
2140bc56620bSPeter Steinfeld       CheckDioArgCount(specific, ioKind, dummyArgs.size());
2141bc56620bSPeter Steinfeld       int argCount{0};
2142bc56620bSPeter Steinfeld       for (auto *arg : dummyArgs) {
2143bc56620bSPeter Steinfeld         switch (argCount++) {
2144bc56620bSPeter Steinfeld         case 0:
2145bc56620bSPeter Steinfeld           // dtv-type-spec, INTENT(INOUT) :: dtv
2146dcf9ba82SPeter Klausler           CheckDioDtvArg(specific, arg, ioKind, symbol);
2147bc56620bSPeter Steinfeld           break;
2148bc56620bSPeter Steinfeld         case 1:
2149bc56620bSPeter Steinfeld           // INTEGER, INTENT(IN) :: unit
2150bc56620bSPeter Steinfeld           CheckDefaultIntegerArg(specific, arg, Attr::INTENT_IN);
2151bc56620bSPeter Steinfeld           break;
2152bc56620bSPeter Steinfeld         case 2:
2153bc56620bSPeter Steinfeld           if (ioKind == GenericKind::DefinedIo::ReadFormatted ||
2154bc56620bSPeter Steinfeld               ioKind == GenericKind::DefinedIo::WriteFormatted) {
2155bc56620bSPeter Steinfeld             // CHARACTER (LEN=*), INTENT(IN) :: iotype
2156bc56620bSPeter Steinfeld             CheckDioAssumedLenCharacterArg(
2157bc56620bSPeter Steinfeld                 specific, arg, argCount, Attr::INTENT_IN);
2158bc56620bSPeter Steinfeld           } else {
2159bc56620bSPeter Steinfeld             // INTEGER, INTENT(OUT) :: iostat
2160bc56620bSPeter Steinfeld             CheckDefaultIntegerArg(specific, arg, Attr::INTENT_OUT);
2161bc56620bSPeter Steinfeld           }
2162bc56620bSPeter Steinfeld           break;
2163bc56620bSPeter Steinfeld         case 3:
2164bc56620bSPeter Steinfeld           if (ioKind == GenericKind::DefinedIo::ReadFormatted ||
2165bc56620bSPeter Steinfeld               ioKind == GenericKind::DefinedIo::WriteFormatted) {
2166bc56620bSPeter Steinfeld             // INTEGER, INTENT(IN) :: v_list(:)
2167bc56620bSPeter Steinfeld             CheckDioVlistArg(specific, arg, argCount);
2168bc56620bSPeter Steinfeld           } else {
2169bc56620bSPeter Steinfeld             // CHARACTER (LEN=*), INTENT(INOUT) :: iomsg
2170bc56620bSPeter Steinfeld             CheckDioAssumedLenCharacterArg(
2171bc56620bSPeter Steinfeld                 specific, arg, argCount, Attr::INTENT_INOUT);
2172bc56620bSPeter Steinfeld           }
2173bc56620bSPeter Steinfeld           break;
2174bc56620bSPeter Steinfeld         case 4:
2175bc56620bSPeter Steinfeld           // INTEGER, INTENT(OUT) :: iostat
2176bc56620bSPeter Steinfeld           CheckDefaultIntegerArg(specific, arg, Attr::INTENT_OUT);
2177bc56620bSPeter Steinfeld           break;
2178bc56620bSPeter Steinfeld         case 5:
2179bc56620bSPeter Steinfeld           // CHARACTER (LEN=*), INTENT(INOUT) :: iomsg
2180bc56620bSPeter Steinfeld           CheckDioAssumedLenCharacterArg(
2181bc56620bSPeter Steinfeld               specific, arg, argCount, Attr::INTENT_INOUT);
2182bc56620bSPeter Steinfeld           break;
2183bc56620bSPeter Steinfeld         default:;
2184bc56620bSPeter Steinfeld         }
2185bc56620bSPeter Steinfeld       }
2186bc56620bSPeter Steinfeld     }
2187bc56620bSPeter Steinfeld   }
2188bc56620bSPeter Steinfeld }
2189bc56620bSPeter Steinfeld 
Check(const Symbol & symbol1,const Symbol & symbol2)219061b1390eSTim Keith void SubprogramMatchHelper::Check(
219161b1390eSTim Keith     const Symbol &symbol1, const Symbol &symbol2) {
219261b1390eSTim Keith   const auto details1{symbol1.get<SubprogramDetails>()};
219361b1390eSTim Keith   const auto details2{symbol2.get<SubprogramDetails>()};
219461b1390eSTim Keith   if (details1.isFunction() != details2.isFunction()) {
219561b1390eSTim Keith     Say(symbol1, symbol2,
219661b1390eSTim Keith         details1.isFunction()
219761b1390eSTim Keith             ? "Module function '%s' was declared as a subroutine in the"
219861b1390eSTim Keith               " corresponding interface body"_err_en_US
219961b1390eSTim Keith             : "Module subroutine '%s' was declared as a function in the"
220061b1390eSTim Keith               " corresponding interface body"_err_en_US);
220161b1390eSTim Keith     return;
220261b1390eSTim Keith   }
220361b1390eSTim Keith   const auto &args1{details1.dummyArgs()};
220461b1390eSTim Keith   const auto &args2{details2.dummyArgs()};
220561b1390eSTim Keith   int nargs1{static_cast<int>(args1.size())};
220661b1390eSTim Keith   int nargs2{static_cast<int>(args2.size())};
220761b1390eSTim Keith   if (nargs1 != nargs2) {
220861b1390eSTim Keith     Say(symbol1, symbol2,
220961b1390eSTim Keith         "Module subprogram '%s' has %d args but the corresponding interface"
221061b1390eSTim Keith         " body has %d"_err_en_US,
221161b1390eSTim Keith         nargs1, nargs2);
221261b1390eSTim Keith     return;
221361b1390eSTim Keith   }
221461b1390eSTim Keith   bool nonRecursive1{symbol1.attrs().test(Attr::NON_RECURSIVE)};
221561b1390eSTim Keith   if (nonRecursive1 != symbol2.attrs().test(Attr::NON_RECURSIVE)) { // C1551
221661b1390eSTim Keith     Say(symbol1, symbol2,
221761b1390eSTim Keith         nonRecursive1
221861b1390eSTim Keith             ? "Module subprogram '%s' has NON_RECURSIVE prefix but"
221961b1390eSTim Keith               " the corresponding interface body does not"_err_en_US
222061b1390eSTim Keith             : "Module subprogram '%s' does not have NON_RECURSIVE prefix but "
222161b1390eSTim Keith               "the corresponding interface body does"_err_en_US);
222261b1390eSTim Keith   }
22235d3249e9STim Keith   const std::string *bindName1{details1.bindName()};
22245d3249e9STim Keith   const std::string *bindName2{details2.bindName()};
22255d3249e9STim Keith   if (!bindName1 && !bindName2) {
22265d3249e9STim Keith     // OK - neither has a binding label
22275d3249e9STim Keith   } else if (!bindName1) {
222861b1390eSTim Keith     Say(symbol1, symbol2,
22295d3249e9STim Keith         "Module subprogram '%s' does not have a binding label but the"
223061b1390eSTim Keith         " corresponding interface body does"_err_en_US);
22315d3249e9STim Keith   } else if (!bindName2) {
223261b1390eSTim Keith     Say(symbol1, symbol2,
22335d3249e9STim Keith         "Module subprogram '%s' has a binding label but the"
22345d3249e9STim Keith         " corresponding interface body does not"_err_en_US);
22355d3249e9STim Keith   } else if (*bindName1 != *bindName2) {
22365d3249e9STim Keith     Say(symbol1, symbol2,
22375d3249e9STim Keith         "Module subprogram '%s' has binding label '%s' but the corresponding"
22385d3249e9STim Keith         " interface body has '%s'"_err_en_US,
22395d3249e9STim Keith         *details1.bindName(), *details2.bindName());
224061b1390eSTim Keith   }
224182edd428STim Keith   const Procedure *proc1{checkHelper.Characterize(symbol1)};
224282edd428STim Keith   const Procedure *proc2{checkHelper.Characterize(symbol2)};
224361b1390eSTim Keith   if (!proc1 || !proc2) {
224461b1390eSTim Keith     return;
224561b1390eSTim Keith   }
224639686557SPeter Klausler   if (proc1->attrs.test(Procedure::Attr::Pure) !=
224739686557SPeter Klausler       proc2->attrs.test(Procedure::Attr::Pure)) {
224839686557SPeter Klausler     Say(symbol1, symbol2,
224939686557SPeter Klausler         "Module subprogram '%s' and its corresponding interface body are not both PURE"_err_en_US);
225039686557SPeter Klausler   }
225139686557SPeter Klausler   if (proc1->attrs.test(Procedure::Attr::Elemental) !=
225239686557SPeter Klausler       proc2->attrs.test(Procedure::Attr::Elemental)) {
225339686557SPeter Klausler     Say(symbol1, symbol2,
225439686557SPeter Klausler         "Module subprogram '%s' and its corresponding interface body are not both ELEMENTAL"_err_en_US);
225539686557SPeter Klausler   }
225639686557SPeter Klausler   if (proc1->attrs.test(Procedure::Attr::BindC) !=
225739686557SPeter Klausler       proc2->attrs.test(Procedure::Attr::BindC)) {
225839686557SPeter Klausler     Say(symbol1, symbol2,
225939686557SPeter Klausler         "Module subprogram '%s' and its corresponding interface body are not both BIND(C)"_err_en_US);
226039686557SPeter Klausler   }
226161b1390eSTim Keith   if (proc1->functionResult && proc2->functionResult &&
226261b1390eSTim Keith       *proc1->functionResult != *proc2->functionResult) {
226361b1390eSTim Keith     Say(symbol1, symbol2,
226461b1390eSTim Keith         "Return type of function '%s' does not match return type of"
226561b1390eSTim Keith         " the corresponding interface body"_err_en_US);
226661b1390eSTim Keith   }
226761b1390eSTim Keith   for (int i{0}; i < nargs1; ++i) {
226861b1390eSTim Keith     const Symbol *arg1{args1[i]};
226961b1390eSTim Keith     const Symbol *arg2{args2[i]};
227061b1390eSTim Keith     if (arg1 && !arg2) {
227161b1390eSTim Keith       Say(symbol1, symbol2,
227261b1390eSTim Keith           "Dummy argument %2$d of '%1$s' is not an alternate return indicator"
227361b1390eSTim Keith           " but the corresponding argument in the interface body is"_err_en_US,
227461b1390eSTim Keith           i + 1);
227561b1390eSTim Keith     } else if (!arg1 && arg2) {
227661b1390eSTim Keith       Say(symbol1, symbol2,
227761b1390eSTim Keith           "Dummy argument %2$d of '%1$s' is an alternate return indicator but"
227861b1390eSTim Keith           " the corresponding argument in the interface body is not"_err_en_US,
227961b1390eSTim Keith           i + 1);
228061b1390eSTim Keith     } else if (arg1 && arg2) {
228161b1390eSTim Keith       SourceName name1{arg1->name()};
228261b1390eSTim Keith       SourceName name2{arg2->name()};
228361b1390eSTim Keith       if (name1 != name2) {
228461b1390eSTim Keith         Say(*arg1, *arg2,
228561b1390eSTim Keith             "Dummy argument name '%s' does not match corresponding name '%s'"
228661b1390eSTim Keith             " in interface body"_err_en_US,
228761b1390eSTim Keith             name2);
228861b1390eSTim Keith       } else {
228961b1390eSTim Keith         CheckDummyArg(
229061b1390eSTim Keith             *arg1, *arg2, proc1->dummyArguments[i], proc2->dummyArguments[i]);
229161b1390eSTim Keith       }
229261b1390eSTim Keith     }
229361b1390eSTim Keith   }
229461b1390eSTim Keith }
229561b1390eSTim Keith 
CheckDummyArg(const Symbol & symbol1,const Symbol & symbol2,const DummyArgument & arg1,const DummyArgument & arg2)229661b1390eSTim Keith void SubprogramMatchHelper::CheckDummyArg(const Symbol &symbol1,
229761b1390eSTim Keith     const Symbol &symbol2, const DummyArgument &arg1,
229861b1390eSTim Keith     const DummyArgument &arg2) {
2299cd03e96fSPeter Klausler   common::visit(
2300cd03e96fSPeter Klausler       common::visitors{
230161b1390eSTim Keith           [&](const DummyDataObject &obj1, const DummyDataObject &obj2) {
230261b1390eSTim Keith             CheckDummyDataObject(symbol1, symbol2, obj1, obj2);
230361b1390eSTim Keith           },
230461b1390eSTim Keith           [&](const DummyProcedure &proc1, const DummyProcedure &proc2) {
230561b1390eSTim Keith             CheckDummyProcedure(symbol1, symbol2, proc1, proc2);
230661b1390eSTim Keith           },
230761b1390eSTim Keith           [&](const DummyDataObject &, const auto &) {
230861b1390eSTim Keith             Say(symbol1, symbol2,
230961b1390eSTim Keith                 "Dummy argument '%s' is a data object; the corresponding"
231061b1390eSTim Keith                 " argument in the interface body is not"_err_en_US);
231161b1390eSTim Keith           },
231261b1390eSTim Keith           [&](const DummyProcedure &, const auto &) {
231361b1390eSTim Keith             Say(symbol1, symbol2,
231461b1390eSTim Keith                 "Dummy argument '%s' is a procedure; the corresponding"
231561b1390eSTim Keith                 " argument in the interface body is not"_err_en_US);
231661b1390eSTim Keith           },
231793626984SDavid Truby           [&](const auto &, const auto &) {
231893626984SDavid Truby             llvm_unreachable("Dummy arguments are not data objects or"
231993626984SDavid Truby                              "procedures");
232093626984SDavid Truby           },
232161b1390eSTim Keith       },
232261b1390eSTim Keith       arg1.u, arg2.u);
232361b1390eSTim Keith }
232461b1390eSTim Keith 
CheckDummyDataObject(const Symbol & symbol1,const Symbol & symbol2,const DummyDataObject & obj1,const DummyDataObject & obj2)232561b1390eSTim Keith void SubprogramMatchHelper::CheckDummyDataObject(const Symbol &symbol1,
232661b1390eSTim Keith     const Symbol &symbol2, const DummyDataObject &obj1,
232761b1390eSTim Keith     const DummyDataObject &obj2) {
232861b1390eSTim Keith   if (!CheckSameIntent(symbol1, symbol2, obj1.intent, obj2.intent)) {
232961b1390eSTim Keith   } else if (!CheckSameAttrs(symbol1, symbol2, obj1.attrs, obj2.attrs)) {
233061b1390eSTim Keith   } else if (obj1.type.type() != obj2.type.type()) {
233161b1390eSTim Keith     Say(symbol1, symbol2,
233261b1390eSTim Keith         "Dummy argument '%s' has type %s; the corresponding argument in the"
233361b1390eSTim Keith         " interface body has type %s"_err_en_US,
233461b1390eSTim Keith         obj1.type.type().AsFortran(), obj2.type.type().AsFortran());
233561b1390eSTim Keith   } else if (!ShapesAreCompatible(obj1, obj2)) {
233661b1390eSTim Keith     Say(symbol1, symbol2,
233761b1390eSTim Keith         "The shape of dummy argument '%s' does not match the shape of the"
233861b1390eSTim Keith         " corresponding argument in the interface body"_err_en_US);
233961b1390eSTim Keith   }
234061b1390eSTim Keith   // TODO: coshape
234161b1390eSTim Keith }
234261b1390eSTim Keith 
CheckDummyProcedure(const Symbol & symbol1,const Symbol & symbol2,const DummyProcedure & proc1,const DummyProcedure & proc2)234361b1390eSTim Keith void SubprogramMatchHelper::CheckDummyProcedure(const Symbol &symbol1,
234461b1390eSTim Keith     const Symbol &symbol2, const DummyProcedure &proc1,
234561b1390eSTim Keith     const DummyProcedure &proc2) {
234661b1390eSTim Keith   if (!CheckSameIntent(symbol1, symbol2, proc1.intent, proc2.intent)) {
234761b1390eSTim Keith   } else if (!CheckSameAttrs(symbol1, symbol2, proc1.attrs, proc2.attrs)) {
234861b1390eSTim Keith   } else if (proc1 != proc2) {
234961b1390eSTim Keith     Say(symbol1, symbol2,
235061b1390eSTim Keith         "Dummy procedure '%s' does not match the corresponding argument in"
235161b1390eSTim Keith         " the interface body"_err_en_US);
235261b1390eSTim Keith   }
235361b1390eSTim Keith }
235461b1390eSTim Keith 
CheckSameIntent(const Symbol & symbol1,const Symbol & symbol2,common::Intent intent1,common::Intent intent2)235561b1390eSTim Keith bool SubprogramMatchHelper::CheckSameIntent(const Symbol &symbol1,
235661b1390eSTim Keith     const Symbol &symbol2, common::Intent intent1, common::Intent intent2) {
235761b1390eSTim Keith   if (intent1 == intent2) {
235861b1390eSTim Keith     return true;
235961b1390eSTim Keith   } else {
236061b1390eSTim Keith     Say(symbol1, symbol2,
236161b1390eSTim Keith         "The intent of dummy argument '%s' does not match the intent"
236261b1390eSTim Keith         " of the corresponding argument in the interface body"_err_en_US);
236361b1390eSTim Keith     return false;
236461b1390eSTim Keith   }
236561b1390eSTim Keith }
236661b1390eSTim Keith 
236761b1390eSTim Keith // Report an error referring to first symbol with declaration of second symbol
236861b1390eSTim Keith template <typename... A>
Say(const Symbol & symbol1,const Symbol & symbol2,parser::MessageFixedText && text,A &&...args)236961b1390eSTim Keith void SubprogramMatchHelper::Say(const Symbol &symbol1, const Symbol &symbol2,
237061b1390eSTim Keith     parser::MessageFixedText &&text, A &&...args) {
237182edd428STim Keith   auto &message{context().Say(symbol1.name(), std::move(text), symbol1.name(),
237261b1390eSTim Keith       std::forward<A>(args)...)};
237361b1390eSTim Keith   evaluate::AttachDeclaration(message, symbol2);
237461b1390eSTim Keith }
237561b1390eSTim Keith 
237661b1390eSTim Keith template <typename ATTRS>
CheckSameAttrs(const Symbol & symbol1,const Symbol & symbol2,ATTRS attrs1,ATTRS attrs2)237761b1390eSTim Keith bool SubprogramMatchHelper::CheckSameAttrs(
237861b1390eSTim Keith     const Symbol &symbol1, const Symbol &symbol2, ATTRS attrs1, ATTRS attrs2) {
237961b1390eSTim Keith   if (attrs1 == attrs2) {
238061b1390eSTim Keith     return true;
238161b1390eSTim Keith   }
238261b1390eSTim Keith   attrs1.IterateOverMembers([&](auto attr) {
238361b1390eSTim Keith     if (!attrs2.test(attr)) {
238461b1390eSTim Keith       Say(symbol1, symbol2,
238561b1390eSTim Keith           "Dummy argument '%s' has the %s attribute; the corresponding"
238661b1390eSTim Keith           " argument in the interface body does not"_err_en_US,
238761b1390eSTim Keith           AsFortran(attr));
238861b1390eSTim Keith     }
238961b1390eSTim Keith   });
239061b1390eSTim Keith   attrs2.IterateOverMembers([&](auto attr) {
239161b1390eSTim Keith     if (!attrs1.test(attr)) {
239261b1390eSTim Keith       Say(symbol1, symbol2,
239361b1390eSTim Keith           "Dummy argument '%s' does not have the %s attribute; the"
239461b1390eSTim Keith           " corresponding argument in the interface body does"_err_en_US,
239561b1390eSTim Keith           AsFortran(attr));
239661b1390eSTim Keith     }
239761b1390eSTim Keith   });
239861b1390eSTim Keith   return false;
239961b1390eSTim Keith }
240061b1390eSTim Keith 
ShapesAreCompatible(const DummyDataObject & obj1,const DummyDataObject & obj2)240161b1390eSTim Keith bool SubprogramMatchHelper::ShapesAreCompatible(
240261b1390eSTim Keith     const DummyDataObject &obj1, const DummyDataObject &obj2) {
240382edd428STim Keith   return characteristics::ShapesAreCompatible(
240461b1390eSTim Keith       FoldShape(obj1.type.shape()), FoldShape(obj2.type.shape()));
240561b1390eSTim Keith }
240661b1390eSTim Keith 
FoldShape(const evaluate::Shape & shape)240761b1390eSTim Keith evaluate::Shape SubprogramMatchHelper::FoldShape(const evaluate::Shape &shape) {
240861b1390eSTim Keith   evaluate::Shape result;
240961b1390eSTim Keith   for (const auto &extent : shape) {
241061b1390eSTim Keith     result.emplace_back(
241182edd428STim Keith         evaluate::Fold(context().foldingContext(), common::Clone(extent)));
241261b1390eSTim Keith   }
241361b1390eSTim Keith   return result;
241461b1390eSTim Keith }
241561b1390eSTim Keith 
Add(const Symbol & generic,GenericKind kind,const Symbol & specific,const Procedure & procedure)241682edd428STim Keith void DistinguishabilityHelper::Add(const Symbol &generic, GenericKind kind,
241782edd428STim Keith     const Symbol &specific, const Procedure &procedure) {
241882edd428STim Keith   if (!context_.HasError(specific)) {
241982edd428STim Keith     nameToInfo_[generic.name()].emplace_back(
242082edd428STim Keith         ProcedureInfo{kind, specific, procedure});
242182edd428STim Keith   }
242282edd428STim Keith }
242382edd428STim Keith 
Check(const Scope & scope)242486f59de1STim Keith void DistinguishabilityHelper::Check(const Scope &scope) {
242582edd428STim Keith   for (const auto &[name, info] : nameToInfo_) {
242682edd428STim Keith     auto count{info.size()};
242782edd428STim Keith     for (std::size_t i1{0}; i1 < count - 1; ++i1) {
2428e3b2f1b6Speter klausler       const auto &[kind, symbol, proc]{info[i1]};
242982edd428STim Keith       for (std::size_t i2{i1 + 1}; i2 < count; ++i2) {
2430e3b2f1b6Speter klausler         auto distinguishable{kind.IsName()
243182edd428STim Keith                 ? evaluate::characteristics::Distinguishable
243282edd428STim Keith                 : evaluate::characteristics::DistinguishableOpOrAssign};
2433c4ba1108Speter klausler         if (!distinguishable(
2434c4ba1108Speter klausler                 context_.languageFeatures(), proc, info[i2].procedure)) {
2435e3b2f1b6Speter klausler           SayNotDistinguishable(GetTopLevelUnitContaining(scope), name, kind,
2436e3b2f1b6Speter klausler               symbol, info[i2].symbol);
243782edd428STim Keith         }
243882edd428STim Keith       }
243982edd428STim Keith     }
244082edd428STim Keith   }
244182edd428STim Keith }
244282edd428STim Keith 
SayNotDistinguishable(const Scope & scope,const SourceName & name,GenericKind kind,const Symbol & proc1,const Symbol & proc2)244386f59de1STim Keith void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope,
244486f59de1STim Keith     const SourceName &name, GenericKind kind, const Symbol &proc1,
244586f59de1STim Keith     const Symbol &proc2) {
244682edd428STim Keith   std::string name1{proc1.name().ToString()};
244782edd428STim Keith   std::string name2{proc2.name().ToString()};
244882edd428STim Keith   if (kind.IsOperator() || kind.IsAssignment()) {
244982edd428STim Keith     // proc1 and proc2 may come from different scopes so qualify their names
245082edd428STim Keith     if (proc1.owner().IsDerivedType()) {
245182edd428STim Keith       name1 = proc1.owner().GetName()->ToString() + '%' + name1;
245282edd428STim Keith     }
245382edd428STim Keith     if (proc2.owner().IsDerivedType()) {
245482edd428STim Keith       name2 = proc2.owner().GetName()->ToString() + '%' + name2;
245582edd428STim Keith     }
245682edd428STim Keith   }
245786f59de1STim Keith   parser::Message *msg;
245886f59de1STim Keith   if (scope.sourceRange().Contains(name)) {
245986f59de1STim Keith     msg = &context_.Say(name,
24600fcda9aeSpeter klausler         "Generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US,
246186f59de1STim Keith         MakeOpName(name), name1, name2);
246286f59de1STim Keith   } else {
246386f59de1STim Keith     msg = &context_.Say(*GetTopLevelUnitContaining(proc1).GetName(),
24640fcda9aeSpeter klausler         "USE-associated generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US,
246586f59de1STim Keith         MakeOpName(name), name1, name2);
246686f59de1STim Keith   }
246786f59de1STim Keith   AttachDeclaration(*msg, scope, proc1);
246886f59de1STim Keith   AttachDeclaration(*msg, scope, proc2);
246986f59de1STim Keith }
247086f59de1STim Keith 
247186f59de1STim Keith // `evaluate::AttachDeclaration` doesn't handle the generic case where `proc`
247286f59de1STim Keith // comes from a different module but is not necessarily use-associated.
AttachDeclaration(parser::Message & msg,const Scope & scope,const Symbol & proc)247386f59de1STim Keith void DistinguishabilityHelper::AttachDeclaration(
247486f59de1STim Keith     parser::Message &msg, const Scope &scope, const Symbol &proc) {
247586f59de1STim Keith   const Scope &unit{GetTopLevelUnitContaining(proc)};
247686f59de1STim Keith   if (unit == scope) {
247786f59de1STim Keith     evaluate::AttachDeclaration(msg, proc);
247886f59de1STim Keith   } else {
247986f59de1STim Keith     msg.Attach(unit.GetName().value(),
248086f59de1STim Keith         "'%s' is USE-associated from module '%s'"_en_US, proc.name(),
248186f59de1STim Keith         unit.GetName().value());
248286f59de1STim Keith   }
248382edd428STim Keith }
248482edd428STim Keith 
CheckDeclarations(SemanticsContext & context)248564ab3302SCarolineConcatto void CheckDeclarations(SemanticsContext &context) {
248664ab3302SCarolineConcatto   CheckHelper{context}.Check();
248764ab3302SCarolineConcatto }
24881f879005STim Keith } // namespace Fortran::semantics
2489