xref: /llvm-project/flang/lib/Semantics/check-declarations.cpp (revision 657aaf8b8d6c0df6156025bb1db73280cf3d7870)
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"
1264ab3302SCarolineConcatto #include "flang/Evaluate/check-expression.h"
1364ab3302SCarolineConcatto #include "flang/Evaluate/fold.h"
1464ab3302SCarolineConcatto #include "flang/Evaluate/tools.h"
1564ab3302SCarolineConcatto #include "flang/Semantics/scope.h"
1664ab3302SCarolineConcatto #include "flang/Semantics/semantics.h"
1764ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
1864ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
1964ab3302SCarolineConcatto #include "flang/Semantics/type.h"
2064ab3302SCarolineConcatto #include <algorithm>
2164ab3302SCarolineConcatto 
2264ab3302SCarolineConcatto namespace Fortran::semantics {
2364ab3302SCarolineConcatto 
2464ab3302SCarolineConcatto using evaluate::characteristics::DummyArgument;
2564ab3302SCarolineConcatto using evaluate::characteristics::DummyDataObject;
2664ab3302SCarolineConcatto using evaluate::characteristics::Procedure;
2764ab3302SCarolineConcatto 
2864ab3302SCarolineConcatto class CheckHelper {
2964ab3302SCarolineConcatto public:
3064ab3302SCarolineConcatto   explicit CheckHelper(SemanticsContext &c) : context_{c} {}
3164ab3302SCarolineConcatto 
3264ab3302SCarolineConcatto   void Check() { Check(context_.globalScope()); }
3364ab3302SCarolineConcatto   void Check(const ParamValue &, bool canBeAssumed);
3464ab3302SCarolineConcatto   void Check(const Bound &bound) { CheckSpecExpr(bound.GetExplicit()); }
3564ab3302SCarolineConcatto   void Check(const ShapeSpec &spec) {
3664ab3302SCarolineConcatto     Check(spec.lbound());
3764ab3302SCarolineConcatto     Check(spec.ubound());
3864ab3302SCarolineConcatto   }
3964ab3302SCarolineConcatto   void Check(const ArraySpec &);
4064ab3302SCarolineConcatto   void Check(const DeclTypeSpec &, bool canHaveAssumedTypeParameters);
4164ab3302SCarolineConcatto   void Check(const Symbol &);
4264ab3302SCarolineConcatto   void Check(const Scope &);
4364ab3302SCarolineConcatto 
4464ab3302SCarolineConcatto private:
4564ab3302SCarolineConcatto   template<typename A> void CheckSpecExpr(A &x) {
4664ab3302SCarolineConcatto     x = Fold(foldingContext_, std::move(x));
4764ab3302SCarolineConcatto     evaluate::CheckSpecificationExpr(x, messages_, DEREF(scope_));
4864ab3302SCarolineConcatto   }
4964ab3302SCarolineConcatto   template<typename A> void CheckSpecExpr(const A &x) {
5064ab3302SCarolineConcatto     evaluate::CheckSpecificationExpr(x, messages_, DEREF(scope_));
5164ab3302SCarolineConcatto   }
5264ab3302SCarolineConcatto   void CheckValue(const Symbol &, const DerivedTypeSpec *);
5364ab3302SCarolineConcatto   void CheckVolatile(
5464ab3302SCarolineConcatto       const Symbol &, bool isAssociated, const DerivedTypeSpec *);
5564ab3302SCarolineConcatto   void CheckPointer(const Symbol &);
5664ab3302SCarolineConcatto   void CheckPassArg(
5764ab3302SCarolineConcatto       const Symbol &proc, const Symbol *interface, const WithPassArg &);
5864ab3302SCarolineConcatto   void CheckProcBinding(const Symbol &, const ProcBindingDetails &);
5964ab3302SCarolineConcatto   void CheckObjectEntity(const Symbol &, const ObjectEntityDetails &);
6064ab3302SCarolineConcatto   void CheckArraySpec(const Symbol &, const ArraySpec &);
6164ab3302SCarolineConcatto   void CheckProcEntity(const Symbol &, const ProcEntityDetails &);
6264ab3302SCarolineConcatto   void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &);
6364ab3302SCarolineConcatto   void CheckDerivedType(const Symbol &, const DerivedTypeDetails &);
6464ab3302SCarolineConcatto   void CheckGeneric(const Symbol &, const GenericDetails &);
6564ab3302SCarolineConcatto   std::optional<std::vector<Procedure>> Characterize(const SymbolVector &);
6664ab3302SCarolineConcatto   bool CheckDefinedOperator(const SourceName &, const GenericKind &,
6764ab3302SCarolineConcatto       const Symbol &, const Procedure &);
6864ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> CheckNumberOfArgs(
6964ab3302SCarolineConcatto       const GenericKind &, std::size_t);
7064ab3302SCarolineConcatto   bool CheckDefinedOperatorArg(
7164ab3302SCarolineConcatto       const SourceName &, const Symbol &, const Procedure &, std::size_t);
7264ab3302SCarolineConcatto   bool CheckDefinedAssignment(const Symbol &, const Procedure &);
7364ab3302SCarolineConcatto   bool CheckDefinedAssignmentArg(const Symbol &, const DummyArgument &, int);
7464ab3302SCarolineConcatto   void CheckSpecificsAreDistinguishable(
7564ab3302SCarolineConcatto       const Symbol &, const GenericDetails &, const std::vector<Procedure> &);
7664ab3302SCarolineConcatto   void CheckEquivalenceSet(const EquivalenceSet &);
7764ab3302SCarolineConcatto   void CheckBlockData(const Scope &);
7864ab3302SCarolineConcatto 
7964ab3302SCarolineConcatto   void SayNotDistinguishable(
8064ab3302SCarolineConcatto       const SourceName &, GenericKind, const Symbol &, const Symbol &);
8164ab3302SCarolineConcatto   bool CheckConflicting(const Symbol &, Attr, Attr);
8264ab3302SCarolineConcatto   bool InPure() const {
8364ab3302SCarolineConcatto     return innermostSymbol_ && IsPureProcedure(*innermostSymbol_);
8464ab3302SCarolineConcatto   }
8564ab3302SCarolineConcatto   bool InFunction() const {
8664ab3302SCarolineConcatto     return innermostSymbol_ && IsFunction(*innermostSymbol_);
8764ab3302SCarolineConcatto   }
8864ab3302SCarolineConcatto   template<typename... A>
8964ab3302SCarolineConcatto   void SayWithDeclaration(const Symbol &symbol, A &&... x) {
9064ab3302SCarolineConcatto     if (parser::Message * msg{messages_.Say(std::forward<A>(x)...)}) {
9164ab3302SCarolineConcatto       if (messages_.at() != symbol.name()) {
9264ab3302SCarolineConcatto         evaluate::AttachDeclaration(*msg, symbol);
9364ab3302SCarolineConcatto       }
9464ab3302SCarolineConcatto     }
9564ab3302SCarolineConcatto   }
9664ab3302SCarolineConcatto 
9764ab3302SCarolineConcatto   SemanticsContext &context_;
9864ab3302SCarolineConcatto   evaluate::FoldingContext &foldingContext_{context_.foldingContext()};
9964ab3302SCarolineConcatto   parser::ContextualMessages &messages_{foldingContext_.messages()};
10064ab3302SCarolineConcatto   const Scope *scope_{nullptr};
10164ab3302SCarolineConcatto   // This symbol is the one attached to the innermost enclosing scope
10264ab3302SCarolineConcatto   // that has a symbol.
10364ab3302SCarolineConcatto   const Symbol *innermostSymbol_{nullptr};
10464ab3302SCarolineConcatto };
10564ab3302SCarolineConcatto 
10664ab3302SCarolineConcatto void CheckHelper::Check(const ParamValue &value, bool canBeAssumed) {
10764ab3302SCarolineConcatto   if (value.isAssumed()) {
108*657aaf8bSPete Steinfeld     if (!canBeAssumed) {  // C795, C721, C726
10964ab3302SCarolineConcatto       messages_.Say(
110*657aaf8bSPete Steinfeld           "An assumed (*) type parameter may be used only for a (non-statement"
111*657aaf8bSPete Steinfeld           " function) dummy argument, associate name, named constant, or"
112*657aaf8bSPete Steinfeld           " external function result"_err_en_US);
11364ab3302SCarolineConcatto     }
11464ab3302SCarolineConcatto   } else {
11564ab3302SCarolineConcatto     CheckSpecExpr(value.GetExplicit());
11664ab3302SCarolineConcatto   }
11764ab3302SCarolineConcatto }
11864ab3302SCarolineConcatto 
11964ab3302SCarolineConcatto void CheckHelper::Check(const ArraySpec &shape) {
12064ab3302SCarolineConcatto   for (const auto &spec : shape) {
12164ab3302SCarolineConcatto     Check(spec);
12264ab3302SCarolineConcatto   }
12364ab3302SCarolineConcatto }
12464ab3302SCarolineConcatto 
12564ab3302SCarolineConcatto void CheckHelper::Check(
12664ab3302SCarolineConcatto     const DeclTypeSpec &type, bool canHaveAssumedTypeParameters) {
12764ab3302SCarolineConcatto   if (type.category() == DeclTypeSpec::Character) {
12864ab3302SCarolineConcatto     Check(type.characterTypeSpec().length(), canHaveAssumedTypeParameters);
12964ab3302SCarolineConcatto   } else if (const DerivedTypeSpec * derived{type.AsDerived()}) {
13064ab3302SCarolineConcatto     for (auto &parm : derived->parameters()) {
13164ab3302SCarolineConcatto       Check(parm.second, canHaveAssumedTypeParameters);
13264ab3302SCarolineConcatto     }
13364ab3302SCarolineConcatto   }
13464ab3302SCarolineConcatto }
13564ab3302SCarolineConcatto 
13664ab3302SCarolineConcatto void CheckHelper::Check(const Symbol &symbol) {
13764ab3302SCarolineConcatto   if (context_.HasError(symbol)) {
13864ab3302SCarolineConcatto     return;
13964ab3302SCarolineConcatto   }
14064ab3302SCarolineConcatto   const DeclTypeSpec *type{symbol.GetType()};
14164ab3302SCarolineConcatto   const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr};
14264ab3302SCarolineConcatto   auto restorer{messages_.SetLocation(symbol.name())};
14364ab3302SCarolineConcatto   context_.set_location(symbol.name());
14464ab3302SCarolineConcatto   bool isAssociated{symbol.has<UseDetails>() || symbol.has<HostAssocDetails>()};
14564ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::VOLATILE)) {
14664ab3302SCarolineConcatto     CheckVolatile(symbol, isAssociated, derived);
14764ab3302SCarolineConcatto   }
14864ab3302SCarolineConcatto   if (isAssociated) {
14964ab3302SCarolineConcatto     return;  // only care about checking VOLATILE on associated symbols
15064ab3302SCarolineConcatto   }
15164ab3302SCarolineConcatto   if (IsPointer(symbol)) {
15264ab3302SCarolineConcatto     CheckPointer(symbol);
15364ab3302SCarolineConcatto   }
15464ab3302SCarolineConcatto   std::visit(
15564ab3302SCarolineConcatto       common::visitors{
15664ab3302SCarolineConcatto           [&](const ProcBindingDetails &x) { CheckProcBinding(symbol, x); },
15764ab3302SCarolineConcatto           [&](const ObjectEntityDetails &x) { CheckObjectEntity(symbol, x); },
15864ab3302SCarolineConcatto           [&](const ProcEntityDetails &x) { CheckProcEntity(symbol, x); },
15964ab3302SCarolineConcatto           [&](const DerivedTypeDetails &x) { CheckDerivedType(symbol, x); },
16064ab3302SCarolineConcatto           [&](const GenericDetails &x) { CheckGeneric(symbol, x); },
16164ab3302SCarolineConcatto           [](const auto &) {},
16264ab3302SCarolineConcatto       },
16364ab3302SCarolineConcatto       symbol.details());
16464ab3302SCarolineConcatto   if (InPure()) {
16564ab3302SCarolineConcatto     if (IsSaved(symbol)) {
16664ab3302SCarolineConcatto       messages_.Say(
16764ab3302SCarolineConcatto           "A pure subprogram may not have a variable with the SAVE attribute"_err_en_US);
16864ab3302SCarolineConcatto     }
16964ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::VOLATILE)) {
17064ab3302SCarolineConcatto       messages_.Say(
17164ab3302SCarolineConcatto           "A pure subprogram may not have a variable with the VOLATILE attribute"_err_en_US);
17264ab3302SCarolineConcatto     }
17364ab3302SCarolineConcatto     if (IsProcedure(symbol) && !IsPureProcedure(symbol) && IsDummy(symbol)) {
17464ab3302SCarolineConcatto       messages_.Say(
17564ab3302SCarolineConcatto           "A dummy procedure of a pure subprogram must be pure"_err_en_US);
17664ab3302SCarolineConcatto     }
17764ab3302SCarolineConcatto     if (!IsDummy(symbol) && !IsFunctionResult(symbol)) {
17864ab3302SCarolineConcatto       if (IsPolymorphicAllocatable(symbol)) {
17964ab3302SCarolineConcatto         SayWithDeclaration(symbol,
18064ab3302SCarolineConcatto             "Deallocation of polymorphic object '%s' is not permitted in a pure subprogram"_err_en_US,
18164ab3302SCarolineConcatto             symbol.name());
18264ab3302SCarolineConcatto       } else if (derived) {
18364ab3302SCarolineConcatto         if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) {
18464ab3302SCarolineConcatto           SayWithDeclaration(*bad,
18564ab3302SCarolineConcatto               "Deallocation of polymorphic object '%s%s' is not permitted in a pure subprogram"_err_en_US,
18664ab3302SCarolineConcatto               symbol.name(), bad.BuildResultDesignatorName());
18764ab3302SCarolineConcatto         }
18864ab3302SCarolineConcatto       }
18964ab3302SCarolineConcatto     }
19064ab3302SCarolineConcatto   }
191*657aaf8bSPete Steinfeld   if (type) {  // Section 7.2, paragraph 7
19264ab3302SCarolineConcatto     bool canHaveAssumedParameter{IsNamedConstant(symbol) ||
193*657aaf8bSPete Steinfeld         IsAssumedLengthExternalCharacterFunction(symbol) ||  // C722
19464ab3302SCarolineConcatto         symbol.test(Symbol::Flag::ParentComp)};
195*657aaf8bSPete Steinfeld     if (!IsStmtFunctionDummy(symbol)) {  // C726
19664ab3302SCarolineConcatto       if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
19764ab3302SCarolineConcatto         canHaveAssumedParameter |= object->isDummy() ||
19864ab3302SCarolineConcatto             (object->isFuncResult() &&
199*657aaf8bSPete Steinfeld                 type->category() == DeclTypeSpec::Character) ||
200*657aaf8bSPete Steinfeld             IsStmtFunctionResult(symbol);  // Avoids multiple messages
20164ab3302SCarolineConcatto       } else {
20264ab3302SCarolineConcatto         canHaveAssumedParameter |= symbol.has<AssocEntityDetails>();
20364ab3302SCarolineConcatto       }
204*657aaf8bSPete Steinfeld     }
20564ab3302SCarolineConcatto     Check(*type, canHaveAssumedParameter);
20664ab3302SCarolineConcatto     if (InPure() && InFunction() && IsFunctionResult(symbol)) {
20764ab3302SCarolineConcatto       if (derived && HasImpureFinal(*derived)) {  // C1584
20864ab3302SCarolineConcatto         messages_.Say(
20964ab3302SCarolineConcatto             "Result of pure function may not have an impure FINAL subroutine"_err_en_US);
21064ab3302SCarolineConcatto       }
21164ab3302SCarolineConcatto       if (type->IsPolymorphic() && IsAllocatable(symbol)) {  // C1585
21264ab3302SCarolineConcatto         messages_.Say(
21364ab3302SCarolineConcatto             "Result of pure function may not be both polymorphic and ALLOCATABLE"_err_en_US);
21464ab3302SCarolineConcatto       }
21564ab3302SCarolineConcatto       if (derived) {
21664ab3302SCarolineConcatto         if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) {
21764ab3302SCarolineConcatto           SayWithDeclaration(*bad,
21864ab3302SCarolineConcatto               "Result of pure function may not have polymorphic ALLOCATABLE ultimate component '%s'"_err_en_US,
21964ab3302SCarolineConcatto               bad.BuildResultDesignatorName());
22064ab3302SCarolineConcatto         }
22164ab3302SCarolineConcatto       }
22264ab3302SCarolineConcatto     }
22364ab3302SCarolineConcatto   }
224*657aaf8bSPete Steinfeld   if (IsAssumedLengthExternalCharacterFunction(symbol)) {  // C723
22564ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::RECURSIVE)) {
22664ab3302SCarolineConcatto       messages_.Say(
22764ab3302SCarolineConcatto           "An assumed-length CHARACTER(*) function cannot be RECURSIVE"_err_en_US);
22864ab3302SCarolineConcatto     }
22964ab3302SCarolineConcatto     if (symbol.Rank() > 0) {
23064ab3302SCarolineConcatto       messages_.Say(
23164ab3302SCarolineConcatto           "An assumed-length CHARACTER(*) function cannot return an array"_err_en_US);
23264ab3302SCarolineConcatto     }
23364ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::PURE)) {
23464ab3302SCarolineConcatto       messages_.Say(
23564ab3302SCarolineConcatto           "An assumed-length CHARACTER(*) function cannot be PURE"_err_en_US);
23664ab3302SCarolineConcatto     }
23764ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::ELEMENTAL)) {
23864ab3302SCarolineConcatto       messages_.Say(
23964ab3302SCarolineConcatto           "An assumed-length CHARACTER(*) function cannot be ELEMENTAL"_err_en_US);
24064ab3302SCarolineConcatto     }
24164ab3302SCarolineConcatto     if (const Symbol * result{FindFunctionResult(symbol)}) {
24264ab3302SCarolineConcatto       if (IsPointer(*result)) {
24364ab3302SCarolineConcatto         messages_.Say(
24464ab3302SCarolineConcatto             "An assumed-length CHARACTER(*) function cannot return a POINTER"_err_en_US);
24564ab3302SCarolineConcatto       }
24664ab3302SCarolineConcatto     }
24764ab3302SCarolineConcatto   }
24864ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::VALUE)) {
24964ab3302SCarolineConcatto     CheckValue(symbol, derived);
25064ab3302SCarolineConcatto   }
25164ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::CONTIGUOUS) && IsPointer(symbol) &&
25264ab3302SCarolineConcatto       symbol.Rank() == 0) {  // C830
25364ab3302SCarolineConcatto     messages_.Say("CONTIGUOUS POINTER must be an array"_err_en_US);
25464ab3302SCarolineConcatto   }
25564ab3302SCarolineConcatto }
25664ab3302SCarolineConcatto 
25764ab3302SCarolineConcatto void CheckHelper::CheckValue(
25864ab3302SCarolineConcatto     const Symbol &symbol, const DerivedTypeSpec *derived) {  // C863 - C865
25964ab3302SCarolineConcatto   if (!IsDummy(symbol)) {
26064ab3302SCarolineConcatto     messages_.Say(
26164ab3302SCarolineConcatto         "VALUE attribute may apply only to a dummy argument"_err_en_US);
26264ab3302SCarolineConcatto   }
26364ab3302SCarolineConcatto   if (IsProcedure(symbol)) {
26464ab3302SCarolineConcatto     messages_.Say(
26564ab3302SCarolineConcatto         "VALUE attribute may apply only to a dummy data object"_err_en_US);
26664ab3302SCarolineConcatto   }
26764ab3302SCarolineConcatto   if (IsAssumedSizeArray(symbol)) {
26864ab3302SCarolineConcatto     messages_.Say(
26964ab3302SCarolineConcatto         "VALUE attribute may not apply to an assumed-size array"_err_en_US);
27064ab3302SCarolineConcatto   }
27164ab3302SCarolineConcatto   if (IsCoarray(symbol)) {
27264ab3302SCarolineConcatto     messages_.Say("VALUE attribute may not apply to a coarray"_err_en_US);
27364ab3302SCarolineConcatto   }
27464ab3302SCarolineConcatto   if (IsAllocatable(symbol)) {
27564ab3302SCarolineConcatto     messages_.Say("VALUE attribute may not apply to an ALLOCATABLE"_err_en_US);
27664ab3302SCarolineConcatto   } else if (IsPointer(symbol)) {
27764ab3302SCarolineConcatto     messages_.Say("VALUE attribute may not apply to a POINTER"_err_en_US);
27864ab3302SCarolineConcatto   }
27964ab3302SCarolineConcatto   if (IsIntentInOut(symbol)) {
28064ab3302SCarolineConcatto     messages_.Say(
28164ab3302SCarolineConcatto         "VALUE attribute may not apply to an INTENT(IN OUT) argument"_err_en_US);
28264ab3302SCarolineConcatto   } else if (IsIntentOut(symbol)) {
28364ab3302SCarolineConcatto     messages_.Say(
28464ab3302SCarolineConcatto         "VALUE attribute may not apply to an INTENT(OUT) argument"_err_en_US);
28564ab3302SCarolineConcatto   }
28664ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::VOLATILE)) {
28764ab3302SCarolineConcatto     messages_.Say("VALUE attribute may not apply to a VOLATILE"_err_en_US);
28864ab3302SCarolineConcatto   }
28964ab3302SCarolineConcatto   if (innermostSymbol_ && IsBindCProcedure(*innermostSymbol_) &&
29064ab3302SCarolineConcatto       IsOptional(symbol)) {
29164ab3302SCarolineConcatto     messages_.Say(
29264ab3302SCarolineConcatto         "VALUE attribute may not apply to an OPTIONAL in a BIND(C) procedure"_err_en_US);
29364ab3302SCarolineConcatto   }
29464ab3302SCarolineConcatto   if (derived) {
29564ab3302SCarolineConcatto     if (FindCoarrayUltimateComponent(*derived)) {
29664ab3302SCarolineConcatto       messages_.Say(
29764ab3302SCarolineConcatto           "VALUE attribute may not apply to a type with a coarray ultimate component"_err_en_US);
29864ab3302SCarolineConcatto     }
29964ab3302SCarolineConcatto   }
30064ab3302SCarolineConcatto }
30164ab3302SCarolineConcatto 
30264ab3302SCarolineConcatto void CheckHelper::CheckAssumedTypeEntity(  // C709
30364ab3302SCarolineConcatto     const Symbol &symbol, const ObjectEntityDetails &details) {
30464ab3302SCarolineConcatto   if (const DeclTypeSpec * type{symbol.GetType()};
30564ab3302SCarolineConcatto       type && type->category() == DeclTypeSpec::TypeStar) {
30664ab3302SCarolineConcatto     if (!symbol.IsDummy()) {
30764ab3302SCarolineConcatto       messages_.Say(
30864ab3302SCarolineConcatto           "Assumed-type entity '%s' must be a dummy argument"_err_en_US,
30964ab3302SCarolineConcatto           symbol.name());
31064ab3302SCarolineConcatto     } else {
31164ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::ALLOCATABLE)) {
31264ab3302SCarolineConcatto         messages_.Say("Assumed-type argument '%s' cannot have the ALLOCATABLE"
31364ab3302SCarolineConcatto                       " attribute"_err_en_US,
31464ab3302SCarolineConcatto             symbol.name());
31564ab3302SCarolineConcatto       }
31664ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::POINTER)) {
31764ab3302SCarolineConcatto         messages_.Say("Assumed-type argument '%s' cannot have the POINTER"
31864ab3302SCarolineConcatto                       " attribute"_err_en_US,
31964ab3302SCarolineConcatto             symbol.name());
32064ab3302SCarolineConcatto       }
32164ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::VALUE)) {
32264ab3302SCarolineConcatto         messages_.Say("Assumed-type argument '%s' cannot have the VALUE"
32364ab3302SCarolineConcatto                       " attribute"_err_en_US,
32464ab3302SCarolineConcatto             symbol.name());
32564ab3302SCarolineConcatto       }
32664ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::INTENT_OUT)) {
32764ab3302SCarolineConcatto         messages_.Say(
32864ab3302SCarolineConcatto             "Assumed-type argument '%s' cannot be INTENT(OUT)"_err_en_US,
32964ab3302SCarolineConcatto             symbol.name());
33064ab3302SCarolineConcatto       }
33164ab3302SCarolineConcatto       if (IsCoarray(symbol)) {
33264ab3302SCarolineConcatto         messages_.Say(
33364ab3302SCarolineConcatto             "Assumed-type argument '%s' cannot be a coarray"_err_en_US,
33464ab3302SCarolineConcatto             symbol.name());
33564ab3302SCarolineConcatto       }
33664ab3302SCarolineConcatto       if (details.IsArray() &&
33764ab3302SCarolineConcatto           !(details.IsAssumedShape() || details.IsAssumedSize())) {
33864ab3302SCarolineConcatto         messages_.Say("Assumed-type argument '%s' must be assumed shape"
33964ab3302SCarolineConcatto                       " or assumed size array"_err_en_US,
34064ab3302SCarolineConcatto             symbol.name());
34164ab3302SCarolineConcatto       }
34264ab3302SCarolineConcatto     }
34364ab3302SCarolineConcatto   }
34464ab3302SCarolineConcatto }
34564ab3302SCarolineConcatto 
34664ab3302SCarolineConcatto void CheckHelper::CheckObjectEntity(
34764ab3302SCarolineConcatto     const Symbol &symbol, const ObjectEntityDetails &details) {
34864ab3302SCarolineConcatto   CheckArraySpec(symbol, details.shape());
34964ab3302SCarolineConcatto   Check(details.shape());
35064ab3302SCarolineConcatto   Check(details.coshape());
35164ab3302SCarolineConcatto   CheckAssumedTypeEntity(symbol, details);
35264ab3302SCarolineConcatto   if (!details.coshape().empty()) {
35364ab3302SCarolineConcatto     if (IsAllocatable(symbol)) {
35464ab3302SCarolineConcatto       if (!details.coshape().IsDeferredShape()) {  // C827
35564ab3302SCarolineConcatto         messages_.Say(
35664ab3302SCarolineConcatto             "ALLOCATABLE coarray must have a deferred coshape"_err_en_US);
35764ab3302SCarolineConcatto       }
35864ab3302SCarolineConcatto     } else {
35964ab3302SCarolineConcatto       if (!details.coshape().IsAssumedSize()) {  // C828
36064ab3302SCarolineConcatto         messages_.Say(
36164ab3302SCarolineConcatto             "Non-ALLOCATABLE coarray must have an explicit coshape"_err_en_US);
36264ab3302SCarolineConcatto       }
36364ab3302SCarolineConcatto     }
36464ab3302SCarolineConcatto   }
36564ab3302SCarolineConcatto   if (details.isDummy()) {
36664ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::INTENT_OUT)) {
36764ab3302SCarolineConcatto       if (FindUltimateComponent(symbol, [](const Symbol &x) {
36864ab3302SCarolineConcatto             return IsCoarray(x) && IsAllocatable(x);
36964ab3302SCarolineConcatto           })) {  // C846
37064ab3302SCarolineConcatto         messages_.Say(
37164ab3302SCarolineConcatto             "An INTENT(OUT) dummy argument may not be, or contain, an ALLOCATABLE coarray"_err_en_US);
37264ab3302SCarolineConcatto       }
37364ab3302SCarolineConcatto       if (IsOrContainsEventOrLockComponent(symbol)) {  // C847
37464ab3302SCarolineConcatto         messages_.Say(
37564ab3302SCarolineConcatto             "An INTENT(OUT) dummy argument may not be, or contain, EVENT_TYPE or LOCK_TYPE"_err_en_US);
37664ab3302SCarolineConcatto       }
37764ab3302SCarolineConcatto     }
37864ab3302SCarolineConcatto     if (InPure() && !IsPointer(symbol) && !IsIntentIn(symbol) &&
37964ab3302SCarolineConcatto         !symbol.attrs().test(Attr::VALUE)) {
38064ab3302SCarolineConcatto       if (InFunction()) {  // C1583
38164ab3302SCarolineConcatto         messages_.Say(
38264ab3302SCarolineConcatto             "non-POINTER dummy argument of pure function must be INTENT(IN) or VALUE"_err_en_US);
38364ab3302SCarolineConcatto       } else if (IsIntentOut(symbol)) {
38464ab3302SCarolineConcatto         if (const DeclTypeSpec * type{details.type()}) {
38564ab3302SCarolineConcatto           if (type && type->IsPolymorphic()) {  // C1588
38664ab3302SCarolineConcatto             messages_.Say(
38764ab3302SCarolineConcatto                 "An INTENT(OUT) dummy argument of a pure subroutine may not be polymorphic"_err_en_US);
38864ab3302SCarolineConcatto           } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
38964ab3302SCarolineConcatto             if (FindUltimateComponent(*derived, [](const Symbol &x) {
39064ab3302SCarolineConcatto                   const DeclTypeSpec *type{x.GetType()};
39164ab3302SCarolineConcatto                   return type && type->IsPolymorphic();
39264ab3302SCarolineConcatto                 })) {  // C1588
39364ab3302SCarolineConcatto               messages_.Say(
39464ab3302SCarolineConcatto                   "An INTENT(OUT) dummy argument of a pure subroutine may not have a polymorphic ultimate component"_err_en_US);
39564ab3302SCarolineConcatto             }
39664ab3302SCarolineConcatto             if (HasImpureFinal(*derived)) {  // C1587
39764ab3302SCarolineConcatto               messages_.Say(
39864ab3302SCarolineConcatto                   "An INTENT(OUT) dummy argument of a pure subroutine may not have an impure FINAL subroutine"_err_en_US);
39964ab3302SCarolineConcatto             }
40064ab3302SCarolineConcatto           }
40164ab3302SCarolineConcatto         }
40264ab3302SCarolineConcatto       } else if (!IsIntentInOut(symbol)) {  // C1586
40364ab3302SCarolineConcatto         messages_.Say(
40464ab3302SCarolineConcatto             "non-POINTER dummy argument of pure subroutine must have INTENT() or VALUE attribute"_err_en_US);
40564ab3302SCarolineConcatto       }
40664ab3302SCarolineConcatto     }
40764ab3302SCarolineConcatto   }
40864ab3302SCarolineConcatto   if (symbol.owner().kind() != Scope::Kind::DerivedType &&
40964ab3302SCarolineConcatto       IsInitialized(symbol)) {
41064ab3302SCarolineConcatto     if (details.commonBlock()) {
41164ab3302SCarolineConcatto       if (details.commonBlock()->name().empty()) {
41264ab3302SCarolineConcatto         messages_.Say(
41364ab3302SCarolineConcatto             "A variable in blank COMMON should not be initialized"_en_US);
41464ab3302SCarolineConcatto       }
41564ab3302SCarolineConcatto     } else if (symbol.owner().kind() == Scope::Kind::BlockData) {
41664ab3302SCarolineConcatto       if (IsAllocatable(symbol)) {
41764ab3302SCarolineConcatto         messages_.Say(
41864ab3302SCarolineConcatto             "An ALLOCATABLE variable may not appear in a BLOCK DATA subprogram"_err_en_US);
41964ab3302SCarolineConcatto       } else {
42064ab3302SCarolineConcatto         messages_.Say(
42164ab3302SCarolineConcatto             "An initialized variable in BLOCK DATA must be in a COMMON block"_err_en_US);
42264ab3302SCarolineConcatto       }
42364ab3302SCarolineConcatto     }
42464ab3302SCarolineConcatto   }
42564ab3302SCarolineConcatto   if (const DeclTypeSpec * type{details.type()}) {  // C708
42664ab3302SCarolineConcatto     if (type->IsPolymorphic() &&
42764ab3302SCarolineConcatto         !(type->IsAssumedType() || IsAllocatableOrPointer(symbol) ||
42864ab3302SCarolineConcatto             symbol.IsDummy())) {
42964ab3302SCarolineConcatto       messages_.Say("CLASS entity '%s' must be a dummy argument or have "
43064ab3302SCarolineConcatto                     "ALLOCATABLE or POINTER attribute"_err_en_US,
43164ab3302SCarolineConcatto           symbol.name());
43264ab3302SCarolineConcatto     }
43364ab3302SCarolineConcatto   }
43464ab3302SCarolineConcatto }
43564ab3302SCarolineConcatto 
43664ab3302SCarolineConcatto // The six different kinds of array-specs:
43764ab3302SCarolineConcatto //   array-spec     -> explicit-shape-list | deferred-shape-list
43864ab3302SCarolineConcatto //                     | assumed-shape-list | implied-shape-list
43964ab3302SCarolineConcatto //                     | assumed-size | assumed-rank
44064ab3302SCarolineConcatto //   explicit-shape -> [ lb : ] ub
44164ab3302SCarolineConcatto //   deferred-shape -> :
44264ab3302SCarolineConcatto //   assumed-shape  -> [ lb ] :
44364ab3302SCarolineConcatto //   implied-shape  -> [ lb : ] *
44464ab3302SCarolineConcatto //   assumed-size   -> [ explicit-shape-list , ] [ lb : ] *
44564ab3302SCarolineConcatto //   assumed-rank   -> ..
44664ab3302SCarolineConcatto // Note:
44764ab3302SCarolineConcatto // - deferred-shape is also an assumed-shape
44864ab3302SCarolineConcatto // - A single "*" or "lb:*" might be assumed-size or implied-shape-list
44964ab3302SCarolineConcatto void CheckHelper::CheckArraySpec(
45064ab3302SCarolineConcatto     const Symbol &symbol, const ArraySpec &arraySpec) {
45164ab3302SCarolineConcatto   if (arraySpec.Rank() == 0) {
45264ab3302SCarolineConcatto     return;
45364ab3302SCarolineConcatto   }
45464ab3302SCarolineConcatto   bool isExplicit{arraySpec.IsExplicitShape()};
45564ab3302SCarolineConcatto   bool isDeferred{arraySpec.IsDeferredShape()};
45664ab3302SCarolineConcatto   bool isImplied{arraySpec.IsImpliedShape()};
45764ab3302SCarolineConcatto   bool isAssumedShape{arraySpec.IsAssumedShape()};
45864ab3302SCarolineConcatto   bool isAssumedSize{arraySpec.IsAssumedSize()};
45964ab3302SCarolineConcatto   bool isAssumedRank{arraySpec.IsAssumedRank()};
46064ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
46164ab3302SCarolineConcatto   if (symbol.test(Symbol::Flag::CrayPointee) && !isExplicit && !isAssumedSize) {
46264ab3302SCarolineConcatto     msg = "Cray pointee '%s' must have must have explicit shape or"
46364ab3302SCarolineConcatto           " assumed size"_err_en_US;
46464ab3302SCarolineConcatto   } else if (IsAllocatableOrPointer(symbol) && !isDeferred && !isAssumedRank) {
46564ab3302SCarolineConcatto     if (symbol.owner().IsDerivedType()) {  // C745
46664ab3302SCarolineConcatto       if (IsAllocatable(symbol)) {
46764ab3302SCarolineConcatto         msg = "Allocatable array component '%s' must have"
46864ab3302SCarolineConcatto               " deferred shape"_err_en_US;
46964ab3302SCarolineConcatto       } else {
47064ab3302SCarolineConcatto         msg = "Array pointer component '%s' must have deferred shape"_err_en_US;
47164ab3302SCarolineConcatto       }
47264ab3302SCarolineConcatto     } else {
47364ab3302SCarolineConcatto       if (IsAllocatable(symbol)) {  // C832
47464ab3302SCarolineConcatto         msg = "Allocatable array '%s' must have deferred shape or"
47564ab3302SCarolineConcatto               " assumed rank"_err_en_US;
47664ab3302SCarolineConcatto       } else {
47764ab3302SCarolineConcatto         msg = "Array pointer '%s' must have deferred shape or"
47864ab3302SCarolineConcatto               " assumed rank"_err_en_US;
47964ab3302SCarolineConcatto       }
48064ab3302SCarolineConcatto     }
48164ab3302SCarolineConcatto   } else if (symbol.IsDummy()) {
48264ab3302SCarolineConcatto     if (isImplied && !isAssumedSize) {  // C836
48364ab3302SCarolineConcatto       msg = "Dummy array argument '%s' may not have implied shape"_err_en_US;
48464ab3302SCarolineConcatto     }
48564ab3302SCarolineConcatto   } else if (isAssumedShape && !isDeferred) {
48664ab3302SCarolineConcatto     msg = "Assumed-shape array '%s' must be a dummy argument"_err_en_US;
48764ab3302SCarolineConcatto   } else if (isAssumedSize && !isImplied) {  // C833
48864ab3302SCarolineConcatto     msg = "Assumed-size array '%s' must be a dummy argument"_err_en_US;
48964ab3302SCarolineConcatto   } else if (isAssumedRank) {  // C837
49064ab3302SCarolineConcatto     msg = "Assumed-rank array '%s' must be a dummy argument"_err_en_US;
49164ab3302SCarolineConcatto   } else if (isImplied) {
49264ab3302SCarolineConcatto     if (!IsNamedConstant(symbol)) {  // C836
49364ab3302SCarolineConcatto       msg = "Implied-shape array '%s' must be a named constant"_err_en_US;
49464ab3302SCarolineConcatto     }
49564ab3302SCarolineConcatto   } else if (IsNamedConstant(symbol)) {
49664ab3302SCarolineConcatto     if (!isExplicit && !isImplied) {
49764ab3302SCarolineConcatto       msg = "Named constant '%s' array must have explicit or"
49864ab3302SCarolineConcatto             " implied shape"_err_en_US;
49964ab3302SCarolineConcatto     }
50064ab3302SCarolineConcatto   } else if (!IsAllocatableOrPointer(symbol) && !isExplicit) {
50164ab3302SCarolineConcatto     if (symbol.owner().IsDerivedType()) {  // C749
50264ab3302SCarolineConcatto       msg = "Component array '%s' without ALLOCATABLE or POINTER attribute must"
50364ab3302SCarolineConcatto             " have explicit shape"_err_en_US;
50464ab3302SCarolineConcatto     } else {  // C816
50564ab3302SCarolineConcatto       msg = "Array '%s' without ALLOCATABLE or POINTER attribute must have"
50664ab3302SCarolineConcatto             " explicit shape"_err_en_US;
50764ab3302SCarolineConcatto     }
50864ab3302SCarolineConcatto   }
50964ab3302SCarolineConcatto   if (msg) {
51064ab3302SCarolineConcatto     context_.Say(std::move(*msg), symbol.name());
51164ab3302SCarolineConcatto   }
51264ab3302SCarolineConcatto }
51364ab3302SCarolineConcatto 
51464ab3302SCarolineConcatto void CheckHelper::CheckProcEntity(
51564ab3302SCarolineConcatto     const Symbol &symbol, const ProcEntityDetails &details) {
51664ab3302SCarolineConcatto   if (details.isDummy()) {
51764ab3302SCarolineConcatto     const Symbol *interface{details.interface().symbol()};
51864ab3302SCarolineConcatto     if (!symbol.attrs().test(Attr::INTRINSIC) &&
51964ab3302SCarolineConcatto         (symbol.attrs().test(Attr::ELEMENTAL) ||
52064ab3302SCarolineConcatto             (interface && !interface->attrs().test(Attr::INTRINSIC) &&
52164ab3302SCarolineConcatto                 interface->attrs().test(Attr::ELEMENTAL)))) {
52264ab3302SCarolineConcatto       // There's no explicit constraint or "shall" that we can find in the
52364ab3302SCarolineConcatto       // standard for this check, but it seems to be implied in multiple
52464ab3302SCarolineConcatto       // sites, and ELEMENTAL non-intrinsic actual arguments *are*
52564ab3302SCarolineConcatto       // explicitly forbidden.  But we allow "PROCEDURE(SIN)::dummy"
52664ab3302SCarolineConcatto       // because it is explicitly legal to *pass* the specific intrinsic
52764ab3302SCarolineConcatto       // function SIN as an actual argument.
52864ab3302SCarolineConcatto       messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US);
52964ab3302SCarolineConcatto     }
53064ab3302SCarolineConcatto   } else if (symbol.owner().IsDerivedType()) {
53164ab3302SCarolineConcatto     CheckPassArg(symbol, details.interface().symbol(), details);
53264ab3302SCarolineConcatto   }
53364ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::POINTER)) {
53464ab3302SCarolineConcatto     if (const Symbol * interface{details.interface().symbol()}) {
53564ab3302SCarolineConcatto       if (interface->attrs().test(Attr::ELEMENTAL) &&
53664ab3302SCarolineConcatto           !interface->attrs().test(Attr::INTRINSIC)) {
53764ab3302SCarolineConcatto         messages_.Say("Procedure pointer '%s' may not be ELEMENTAL"_err_en_US,
53864ab3302SCarolineConcatto             symbol.name());  // C1517
53964ab3302SCarolineConcatto       }
54064ab3302SCarolineConcatto     }
54164ab3302SCarolineConcatto   }
54264ab3302SCarolineConcatto }
54364ab3302SCarolineConcatto 
54464ab3302SCarolineConcatto void CheckHelper::CheckDerivedType(
54564ab3302SCarolineConcatto     const Symbol &symbol, const DerivedTypeDetails &details) {
54664ab3302SCarolineConcatto   if (!symbol.scope()) {
54764ab3302SCarolineConcatto     CHECK(details.isForwardReferenced());
54864ab3302SCarolineConcatto     return;
54964ab3302SCarolineConcatto   }
55064ab3302SCarolineConcatto   CHECK(symbol.scope()->symbol() == &symbol);
55164ab3302SCarolineConcatto   CHECK(symbol.scope()->IsDerivedType());
55264ab3302SCarolineConcatto   if (symbol.attrs().test(Attr::ABSTRACT) &&
55364ab3302SCarolineConcatto       (symbol.attrs().test(Attr::BIND_C) || details.sequence())) {
55464ab3302SCarolineConcatto     messages_.Say("An ABSTRACT derived type must be extensible"_err_en_US);
55564ab3302SCarolineConcatto   }
55664ab3302SCarolineConcatto   if (const DeclTypeSpec * parent{FindParentTypeSpec(symbol)}) {
55764ab3302SCarolineConcatto     const DerivedTypeSpec *parentDerived{parent->AsDerived()};
55864ab3302SCarolineConcatto     if (!IsExtensibleType(parentDerived)) {  // C705
55964ab3302SCarolineConcatto       messages_.Say("The parent type is not extensible"_err_en_US);
56064ab3302SCarolineConcatto     }
56164ab3302SCarolineConcatto     if (!symbol.attrs().test(Attr::ABSTRACT) && parentDerived &&
56264ab3302SCarolineConcatto         parentDerived->typeSymbol().attrs().test(Attr::ABSTRACT)) {
56364ab3302SCarolineConcatto       ScopeComponentIterator components{*parentDerived};
56464ab3302SCarolineConcatto       for (const Symbol &component : components) {
56564ab3302SCarolineConcatto         if (component.attrs().test(Attr::DEFERRED)) {
56664ab3302SCarolineConcatto           if (symbol.scope()->FindComponent(component.name()) == &component) {
56764ab3302SCarolineConcatto             SayWithDeclaration(component,
56864ab3302SCarolineConcatto                 "Non-ABSTRACT extension of ABSTRACT derived type '%s' lacks a binding for DEFERRED procedure '%s'"_err_en_US,
56964ab3302SCarolineConcatto                 parentDerived->typeSymbol().name(), component.name());
57064ab3302SCarolineConcatto           }
57164ab3302SCarolineConcatto         }
57264ab3302SCarolineConcatto       }
57364ab3302SCarolineConcatto     }
57464ab3302SCarolineConcatto   }
57564ab3302SCarolineConcatto }
57664ab3302SCarolineConcatto 
57764ab3302SCarolineConcatto void CheckHelper::CheckGeneric(
57864ab3302SCarolineConcatto     const Symbol &symbol, const GenericDetails &details) {
57964ab3302SCarolineConcatto   const SymbolVector &specifics{details.specificProcs()};
58064ab3302SCarolineConcatto   const auto &bindingNames{details.bindingNames()};
58164ab3302SCarolineConcatto   std::optional<std::vector<Procedure>> procs{Characterize(specifics)};
58264ab3302SCarolineConcatto   if (!procs) {
58364ab3302SCarolineConcatto     return;
58464ab3302SCarolineConcatto   }
58564ab3302SCarolineConcatto   bool ok{true};
58664ab3302SCarolineConcatto   if (details.kind().IsIntrinsicOperator()) {
58764ab3302SCarolineConcatto     for (std::size_t i{0}; i < specifics.size(); ++i) {
58864ab3302SCarolineConcatto       auto restorer{messages_.SetLocation(bindingNames[i])};
58964ab3302SCarolineConcatto       ok &= CheckDefinedOperator(
59064ab3302SCarolineConcatto           symbol.name(), details.kind(), specifics[i], (*procs)[i]);
59164ab3302SCarolineConcatto     }
59264ab3302SCarolineConcatto   }
59364ab3302SCarolineConcatto   if (details.kind().IsAssignment()) {
59464ab3302SCarolineConcatto     for (std::size_t i{0}; i < specifics.size(); ++i) {
59564ab3302SCarolineConcatto       auto restorer{messages_.SetLocation(bindingNames[i])};
59664ab3302SCarolineConcatto       ok &= CheckDefinedAssignment(specifics[i], (*procs)[i]);
59764ab3302SCarolineConcatto     }
59864ab3302SCarolineConcatto   }
59964ab3302SCarolineConcatto   if (ok) {
60064ab3302SCarolineConcatto     CheckSpecificsAreDistinguishable(symbol, details, *procs);
60164ab3302SCarolineConcatto   }
60264ab3302SCarolineConcatto }
60364ab3302SCarolineConcatto 
60464ab3302SCarolineConcatto // Check that the specifics of this generic are distinguishable from each other
60564ab3302SCarolineConcatto void CheckHelper::CheckSpecificsAreDistinguishable(const Symbol &generic,
60664ab3302SCarolineConcatto     const GenericDetails &details, const std::vector<Procedure> &procs) {
60764ab3302SCarolineConcatto   const SymbolVector &specifics{details.specificProcs()};
60864ab3302SCarolineConcatto   std::size_t count{specifics.size()};
60964ab3302SCarolineConcatto   if (count < 2) {
61064ab3302SCarolineConcatto     return;
61164ab3302SCarolineConcatto   }
61264ab3302SCarolineConcatto   GenericKind kind{details.kind()};
61364ab3302SCarolineConcatto   auto distinguishable{kind.IsAssignment() || kind.IsOperator()
61464ab3302SCarolineConcatto           ? evaluate::characteristics::DistinguishableOpOrAssign
61564ab3302SCarolineConcatto           : evaluate::characteristics::Distinguishable};
61664ab3302SCarolineConcatto   for (std::size_t i1{0}; i1 < count - 1; ++i1) {
61764ab3302SCarolineConcatto     auto &proc1{procs[i1]};
61864ab3302SCarolineConcatto     for (std::size_t i2{i1 + 1}; i2 < count; ++i2) {
61964ab3302SCarolineConcatto       auto &proc2{procs[i2]};
62064ab3302SCarolineConcatto       if (!distinguishable(proc1, proc2)) {
62164ab3302SCarolineConcatto         SayNotDistinguishable(
62264ab3302SCarolineConcatto             generic.name(), kind, specifics[i1], specifics[i2]);
62364ab3302SCarolineConcatto       }
62464ab3302SCarolineConcatto     }
62564ab3302SCarolineConcatto   }
62664ab3302SCarolineConcatto }
62764ab3302SCarolineConcatto 
62864ab3302SCarolineConcatto void CheckHelper::SayNotDistinguishable(const SourceName &name,
62964ab3302SCarolineConcatto     GenericKind kind, const Symbol &proc1, const Symbol &proc2) {
63064ab3302SCarolineConcatto   auto &&text{kind.IsDefinedOperator()
63164ab3302SCarolineConcatto           ? "Generic operator '%s' may not have specific procedures '%s'"
63264ab3302SCarolineConcatto             " and '%s' as their interfaces are not distinguishable"_err_en_US
63364ab3302SCarolineConcatto           : "Generic '%s' may not have specific procedures '%s'"
63464ab3302SCarolineConcatto             " and '%s' as their interfaces are not distinguishable"_err_en_US};
63564ab3302SCarolineConcatto   auto &msg{
63664ab3302SCarolineConcatto       context_.Say(name, std::move(text), name, proc1.name(), proc2.name())};
63764ab3302SCarolineConcatto   evaluate::AttachDeclaration(msg, proc1);
63864ab3302SCarolineConcatto   evaluate::AttachDeclaration(msg, proc2);
63964ab3302SCarolineConcatto }
64064ab3302SCarolineConcatto 
64164ab3302SCarolineConcatto static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) {
64264ab3302SCarolineConcatto   auto lhs{std::get<DummyDataObject>(proc.dummyArguments[0].u).type};
64364ab3302SCarolineConcatto   auto rhs{std::get<DummyDataObject>(proc.dummyArguments[1].u).type};
64464ab3302SCarolineConcatto   return Tristate::No ==
64564ab3302SCarolineConcatto       IsDefinedAssignment(lhs.type(), lhs.Rank(), rhs.type(), rhs.Rank());
64664ab3302SCarolineConcatto }
64764ab3302SCarolineConcatto 
64864ab3302SCarolineConcatto static bool ConflictsWithIntrinsicOperator(
64964ab3302SCarolineConcatto     const GenericKind &kind, const Procedure &proc) {
65064ab3302SCarolineConcatto   auto arg0{std::get<DummyDataObject>(proc.dummyArguments[0].u).type};
65164ab3302SCarolineConcatto   auto type0{arg0.type()};
65264ab3302SCarolineConcatto   if (proc.dummyArguments.size() == 1) {  // unary
65364ab3302SCarolineConcatto     return std::visit(
65464ab3302SCarolineConcatto         common::visitors{
65564ab3302SCarolineConcatto             [&](common::NumericOperator) { return IsIntrinsicNumeric(type0); },
65664ab3302SCarolineConcatto             [&](common::LogicalOperator) { return IsIntrinsicLogical(type0); },
65764ab3302SCarolineConcatto             [](const auto &) -> bool { DIE("bad generic kind"); },
65864ab3302SCarolineConcatto         },
65964ab3302SCarolineConcatto         kind.u);
66064ab3302SCarolineConcatto   } else {  // binary
66164ab3302SCarolineConcatto     int rank0{arg0.Rank()};
66264ab3302SCarolineConcatto     auto arg1{std::get<DummyDataObject>(proc.dummyArguments[1].u).type};
66364ab3302SCarolineConcatto     auto type1{arg1.type()};
66464ab3302SCarolineConcatto     int rank1{arg1.Rank()};
66564ab3302SCarolineConcatto     return std::visit(
66664ab3302SCarolineConcatto         common::visitors{
66764ab3302SCarolineConcatto             [&](common::NumericOperator) {
66864ab3302SCarolineConcatto               return IsIntrinsicNumeric(type0, rank0, type1, rank1);
66964ab3302SCarolineConcatto             },
67064ab3302SCarolineConcatto             [&](common::LogicalOperator) {
67164ab3302SCarolineConcatto               return IsIntrinsicLogical(type0, rank0, type1, rank1);
67264ab3302SCarolineConcatto             },
67364ab3302SCarolineConcatto             [&](common::RelationalOperator opr) {
67464ab3302SCarolineConcatto               return IsIntrinsicRelational(opr, type0, rank0, type1, rank1);
67564ab3302SCarolineConcatto             },
67664ab3302SCarolineConcatto             [&](GenericKind::OtherKind x) {
67764ab3302SCarolineConcatto               CHECK(x == GenericKind::OtherKind::Concat);
67864ab3302SCarolineConcatto               return IsIntrinsicConcat(type0, rank0, type1, rank1);
67964ab3302SCarolineConcatto             },
68064ab3302SCarolineConcatto             [](const auto &) -> bool { DIE("bad generic kind"); },
68164ab3302SCarolineConcatto         },
68264ab3302SCarolineConcatto         kind.u);
68364ab3302SCarolineConcatto   }
68464ab3302SCarolineConcatto }
68564ab3302SCarolineConcatto 
68664ab3302SCarolineConcatto // Check if this procedure can be used for defined operators (see 15.4.3.4.2).
68764ab3302SCarolineConcatto bool CheckHelper::CheckDefinedOperator(const SourceName &opName,
68864ab3302SCarolineConcatto     const GenericKind &kind, const Symbol &specific, const Procedure &proc) {
68964ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
69064ab3302SCarolineConcatto   if (specific.attrs().test(Attr::NOPASS)) {  // C774
69164ab3302SCarolineConcatto     msg = "%s procedure '%s' may not have NOPASS attribute"_err_en_US;
69264ab3302SCarolineConcatto   } else if (!proc.functionResult.has_value()) {
69364ab3302SCarolineConcatto     msg = "%s procedure '%s' must be a function"_err_en_US;
69464ab3302SCarolineConcatto   } else if (proc.functionResult->IsAssumedLengthCharacter()) {
69564ab3302SCarolineConcatto     msg = "%s function '%s' may not have assumed-length CHARACTER(*)"
69664ab3302SCarolineConcatto           " result"_err_en_US;
69764ab3302SCarolineConcatto   } else if (auto m{CheckNumberOfArgs(kind, proc.dummyArguments.size())}) {
69864ab3302SCarolineConcatto     msg = std::move(m);
69964ab3302SCarolineConcatto   } else if (!CheckDefinedOperatorArg(opName, specific, proc, 0) |
70064ab3302SCarolineConcatto       !CheckDefinedOperatorArg(opName, specific, proc, 1)) {
70164ab3302SCarolineConcatto     return false;  // error was reported
70264ab3302SCarolineConcatto   } else if (ConflictsWithIntrinsicOperator(kind, proc)) {
70364ab3302SCarolineConcatto     msg = "%s function '%s' conflicts with intrinsic operator"_err_en_US;
70464ab3302SCarolineConcatto   } else {
70564ab3302SCarolineConcatto     return true;  // OK
70664ab3302SCarolineConcatto   }
70764ab3302SCarolineConcatto   SayWithDeclaration(specific, std::move(msg.value()),
70864ab3302SCarolineConcatto       parser::ToUpperCaseLetters(opName.ToString()), specific.name());
70964ab3302SCarolineConcatto   return false;
71064ab3302SCarolineConcatto }
71164ab3302SCarolineConcatto 
71264ab3302SCarolineConcatto // If the number of arguments is wrong for this intrinsic operator, return
71364ab3302SCarolineConcatto // false and return the error message in msg.
71464ab3302SCarolineConcatto std::optional<parser::MessageFixedText> CheckHelper::CheckNumberOfArgs(
71564ab3302SCarolineConcatto     const GenericKind &kind, std::size_t nargs) {
71664ab3302SCarolineConcatto   std::size_t min{2}, max{2};  // allowed number of args; default is binary
71764ab3302SCarolineConcatto   std::visit(
71864ab3302SCarolineConcatto       common::visitors{
71964ab3302SCarolineConcatto           [&](const common::NumericOperator &x) {
72064ab3302SCarolineConcatto             if (x == common::NumericOperator::Add ||
72164ab3302SCarolineConcatto                 x == common::NumericOperator::Subtract) {
72264ab3302SCarolineConcatto               min = 1;  // + and - are unary or binary
72364ab3302SCarolineConcatto             }
72464ab3302SCarolineConcatto           },
72564ab3302SCarolineConcatto           [&](const common::LogicalOperator &x) {
72664ab3302SCarolineConcatto             if (x == common::LogicalOperator::Not) {
72764ab3302SCarolineConcatto               min = 1;  // .NOT. is unary
72864ab3302SCarolineConcatto               max = 1;
72964ab3302SCarolineConcatto             }
73064ab3302SCarolineConcatto           },
73164ab3302SCarolineConcatto           [](const common::RelationalOperator &) {
73264ab3302SCarolineConcatto             // all are binary
73364ab3302SCarolineConcatto           },
73464ab3302SCarolineConcatto           [](const GenericKind::OtherKind &x) {
73564ab3302SCarolineConcatto             CHECK(x == GenericKind::OtherKind::Concat);
73664ab3302SCarolineConcatto           },
73764ab3302SCarolineConcatto           [](const auto &) { DIE("expected intrinsic operator"); },
73864ab3302SCarolineConcatto       },
73964ab3302SCarolineConcatto       kind.u);
74064ab3302SCarolineConcatto   if (nargs >= min && nargs <= max) {
74164ab3302SCarolineConcatto     return std::nullopt;
74264ab3302SCarolineConcatto   } else if (max == 1) {
74364ab3302SCarolineConcatto     return "%s function '%s' must have one dummy argument"_err_en_US;
74464ab3302SCarolineConcatto   } else if (min == 2) {
74564ab3302SCarolineConcatto     return "%s function '%s' must have two dummy arguments"_err_en_US;
74664ab3302SCarolineConcatto   } else {
74764ab3302SCarolineConcatto     return "%s function '%s' must have one or two dummy arguments"_err_en_US;
74864ab3302SCarolineConcatto   }
74964ab3302SCarolineConcatto }
75064ab3302SCarolineConcatto 
75164ab3302SCarolineConcatto bool CheckHelper::CheckDefinedOperatorArg(const SourceName &opName,
75264ab3302SCarolineConcatto     const Symbol &symbol, const Procedure &proc, std::size_t pos) {
75364ab3302SCarolineConcatto   if (pos >= proc.dummyArguments.size()) {
75464ab3302SCarolineConcatto     return true;
75564ab3302SCarolineConcatto   }
75664ab3302SCarolineConcatto   auto &arg{proc.dummyArguments.at(pos)};
75764ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
75864ab3302SCarolineConcatto   if (arg.IsOptional()) {
75964ab3302SCarolineConcatto     msg = "In %s function '%s', dummy argument '%s' may not be"
76064ab3302SCarolineConcatto           " OPTIONAL"_err_en_US;
76164ab3302SCarolineConcatto   } else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)};
76264ab3302SCarolineConcatto              dataObject == nullptr) {
76364ab3302SCarolineConcatto     msg = "In %s function '%s', dummy argument '%s' must be a"
76464ab3302SCarolineConcatto           " data object"_err_en_US;
76564ab3302SCarolineConcatto   } else if (dataObject->intent != common::Intent::In &&
76664ab3302SCarolineConcatto       !dataObject->attrs.test(DummyDataObject::Attr::Value)) {
76764ab3302SCarolineConcatto     msg = "In %s function '%s', dummy argument '%s' must have INTENT(IN)"
76864ab3302SCarolineConcatto           " or VALUE attribute"_err_en_US;
76964ab3302SCarolineConcatto   }
77064ab3302SCarolineConcatto   if (msg) {
77164ab3302SCarolineConcatto     SayWithDeclaration(symbol, std::move(*msg),
77264ab3302SCarolineConcatto         parser::ToUpperCaseLetters(opName.ToString()), symbol.name(), arg.name);
77364ab3302SCarolineConcatto     return false;
77464ab3302SCarolineConcatto   }
77564ab3302SCarolineConcatto   return true;
77664ab3302SCarolineConcatto }
77764ab3302SCarolineConcatto 
77864ab3302SCarolineConcatto // Check if this procedure can be used for defined assignment (see 15.4.3.4.3).
77964ab3302SCarolineConcatto bool CheckHelper::CheckDefinedAssignment(
78064ab3302SCarolineConcatto     const Symbol &specific, const Procedure &proc) {
78164ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
78264ab3302SCarolineConcatto   if (specific.attrs().test(Attr::NOPASS)) {  // C774
78364ab3302SCarolineConcatto     msg = "Defined assignment procedure '%s' may not have"
78464ab3302SCarolineConcatto           " NOPASS attribute"_err_en_US;
78564ab3302SCarolineConcatto   } else if (!proc.IsSubroutine()) {
78664ab3302SCarolineConcatto     msg = "Defined assignment procedure '%s' must be a subroutine"_err_en_US;
78764ab3302SCarolineConcatto   } else if (proc.dummyArguments.size() != 2) {
78864ab3302SCarolineConcatto     msg = "Defined assignment subroutine '%s' must have"
78964ab3302SCarolineConcatto           " two dummy arguments"_err_en_US;
79064ab3302SCarolineConcatto   } else if (!CheckDefinedAssignmentArg(specific, proc.dummyArguments[0], 0) |
79164ab3302SCarolineConcatto       !CheckDefinedAssignmentArg(specific, proc.dummyArguments[1], 1)) {
79264ab3302SCarolineConcatto     return false;  // error was reported
79364ab3302SCarolineConcatto   } else if (ConflictsWithIntrinsicAssignment(proc)) {
79464ab3302SCarolineConcatto     msg = "Defined assignment subroutine '%s' conflicts with"
79564ab3302SCarolineConcatto           " intrinsic assignment"_err_en_US;
79664ab3302SCarolineConcatto   } else {
79764ab3302SCarolineConcatto     return true;  // OK
79864ab3302SCarolineConcatto   }
79964ab3302SCarolineConcatto   SayWithDeclaration(specific, std::move(msg.value()), specific.name());
80064ab3302SCarolineConcatto   return false;
80164ab3302SCarolineConcatto }
80264ab3302SCarolineConcatto 
80364ab3302SCarolineConcatto bool CheckHelper::CheckDefinedAssignmentArg(
80464ab3302SCarolineConcatto     const Symbol &symbol, const DummyArgument &arg, int pos) {
80564ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
80664ab3302SCarolineConcatto   if (arg.IsOptional()) {
80764ab3302SCarolineConcatto     msg = "In defined assignment subroutine '%s', dummy argument '%s'"
80864ab3302SCarolineConcatto           " may not be OPTIONAL"_err_en_US;
80964ab3302SCarolineConcatto   } else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)}) {
81064ab3302SCarolineConcatto     if (pos == 0) {
81164ab3302SCarolineConcatto       if (dataObject->intent != common::Intent::Out &&
81264ab3302SCarolineConcatto           dataObject->intent != common::Intent::InOut) {
81364ab3302SCarolineConcatto         msg = "In defined assignment subroutine '%s', first dummy argument '%s'"
81464ab3302SCarolineConcatto               " must have INTENT(OUT) or INTENT(INOUT)"_err_en_US;
81564ab3302SCarolineConcatto       }
81664ab3302SCarolineConcatto     } else if (pos == 1) {
81764ab3302SCarolineConcatto       if (dataObject->intent != common::Intent::In &&
81864ab3302SCarolineConcatto           !dataObject->attrs.test(DummyDataObject::Attr::Value)) {
81964ab3302SCarolineConcatto         msg =
82064ab3302SCarolineConcatto             "In defined assignment subroutine '%s', second dummy"
82164ab3302SCarolineConcatto             " argument '%s' must have INTENT(IN) or VALUE attribute"_err_en_US;
82264ab3302SCarolineConcatto       }
82364ab3302SCarolineConcatto     } else {
82464ab3302SCarolineConcatto       DIE("pos must be 0 or 1");
82564ab3302SCarolineConcatto     }
82664ab3302SCarolineConcatto   } else {
82764ab3302SCarolineConcatto     msg = "In defined assignment subroutine '%s', dummy argument '%s'"
82864ab3302SCarolineConcatto           " must be a data object"_err_en_US;
82964ab3302SCarolineConcatto   }
83064ab3302SCarolineConcatto   if (msg) {
83164ab3302SCarolineConcatto     SayWithDeclaration(symbol, std::move(*msg), symbol.name(), arg.name);
83264ab3302SCarolineConcatto     return false;
83364ab3302SCarolineConcatto   }
83464ab3302SCarolineConcatto   return true;
83564ab3302SCarolineConcatto }
83664ab3302SCarolineConcatto 
83764ab3302SCarolineConcatto // Report a conflicting attribute error if symbol has both of these attributes
83864ab3302SCarolineConcatto bool CheckHelper::CheckConflicting(const Symbol &symbol, Attr a1, Attr a2) {
83964ab3302SCarolineConcatto   if (symbol.attrs().test(a1) && symbol.attrs().test(a2)) {
84064ab3302SCarolineConcatto     messages_.Say("'%s' may not have both the %s and %s attributes"_err_en_US,
84164ab3302SCarolineConcatto         symbol.name(), EnumToString(a1), EnumToString(a2));
84264ab3302SCarolineConcatto     return true;
84364ab3302SCarolineConcatto   } else {
84464ab3302SCarolineConcatto     return false;
84564ab3302SCarolineConcatto   }
84664ab3302SCarolineConcatto }
84764ab3302SCarolineConcatto 
84864ab3302SCarolineConcatto std::optional<std::vector<Procedure>> CheckHelper::Characterize(
84964ab3302SCarolineConcatto     const SymbolVector &specifics) {
85064ab3302SCarolineConcatto   std::vector<Procedure> result;
85164ab3302SCarolineConcatto   for (const Symbol &specific : specifics) {
85264ab3302SCarolineConcatto     auto proc{Procedure::Characterize(specific, context_.intrinsics())};
85364ab3302SCarolineConcatto     if (!proc || context_.HasError(specific)) {
85464ab3302SCarolineConcatto       return std::nullopt;
85564ab3302SCarolineConcatto     }
85664ab3302SCarolineConcatto     result.emplace_back(*proc);
85764ab3302SCarolineConcatto   }
85864ab3302SCarolineConcatto   return result;
85964ab3302SCarolineConcatto }
86064ab3302SCarolineConcatto 
86164ab3302SCarolineConcatto void CheckHelper::CheckVolatile(const Symbol &symbol, bool isAssociated,
86264ab3302SCarolineConcatto     const DerivedTypeSpec *derived) {  // C866 - C868
86364ab3302SCarolineConcatto   if (IsIntentIn(symbol)) {
86464ab3302SCarolineConcatto     messages_.Say(
86564ab3302SCarolineConcatto         "VOLATILE attribute may not apply to an INTENT(IN) argument"_err_en_US);
86664ab3302SCarolineConcatto   }
86764ab3302SCarolineConcatto   if (IsProcedure(symbol)) {
86864ab3302SCarolineConcatto     messages_.Say("VOLATILE attribute may apply only to a variable"_err_en_US);
86964ab3302SCarolineConcatto   }
87064ab3302SCarolineConcatto   if (isAssociated) {
87164ab3302SCarolineConcatto     const Symbol &ultimate{symbol.GetUltimate()};
87264ab3302SCarolineConcatto     if (IsCoarray(ultimate)) {
87364ab3302SCarolineConcatto       messages_.Say(
87464ab3302SCarolineConcatto           "VOLATILE attribute may not apply to a coarray accessed by USE or host association"_err_en_US);
87564ab3302SCarolineConcatto     }
87664ab3302SCarolineConcatto     if (derived) {
87764ab3302SCarolineConcatto       if (FindCoarrayUltimateComponent(*derived)) {
87864ab3302SCarolineConcatto         messages_.Say(
87964ab3302SCarolineConcatto             "VOLATILE attribute may not apply to a type with a coarray ultimate component accessed by USE or host association"_err_en_US);
88064ab3302SCarolineConcatto       }
88164ab3302SCarolineConcatto     }
88264ab3302SCarolineConcatto   }
88364ab3302SCarolineConcatto }
88464ab3302SCarolineConcatto 
88564ab3302SCarolineConcatto void CheckHelper::CheckPointer(const Symbol &symbol) {  // C852
88664ab3302SCarolineConcatto   CheckConflicting(symbol, Attr::POINTER, Attr::TARGET);
88764ab3302SCarolineConcatto   CheckConflicting(symbol, Attr::POINTER, Attr::ALLOCATABLE);
88864ab3302SCarolineConcatto   CheckConflicting(symbol, Attr::POINTER, Attr::INTRINSIC);
88964ab3302SCarolineConcatto   if (symbol.Corank() > 0) {
89064ab3302SCarolineConcatto     messages_.Say(
89164ab3302SCarolineConcatto         "'%s' may not have the POINTER attribute because it is a coarray"_err_en_US,
89264ab3302SCarolineConcatto         symbol.name());
89364ab3302SCarolineConcatto   }
89464ab3302SCarolineConcatto }
89564ab3302SCarolineConcatto 
89664ab3302SCarolineConcatto // C760 constraints on the passed-object dummy argument
89764ab3302SCarolineConcatto void CheckHelper::CheckPassArg(
89864ab3302SCarolineConcatto     const Symbol &proc, const Symbol *interface, const WithPassArg &details) {
89964ab3302SCarolineConcatto   if (proc.attrs().test(Attr::NOPASS)) {
90064ab3302SCarolineConcatto     return;
90164ab3302SCarolineConcatto   }
90264ab3302SCarolineConcatto   const auto &name{proc.name()};
90364ab3302SCarolineConcatto   if (!interface) {
90464ab3302SCarolineConcatto     messages_.Say(name,
90564ab3302SCarolineConcatto         "Procedure component '%s' must have NOPASS attribute or explicit interface"_err_en_US,
90664ab3302SCarolineConcatto         name);
90764ab3302SCarolineConcatto     return;
90864ab3302SCarolineConcatto   }
90964ab3302SCarolineConcatto   const auto *subprogram{interface->detailsIf<SubprogramDetails>()};
91064ab3302SCarolineConcatto   if (!subprogram) {
91164ab3302SCarolineConcatto     messages_.Say(name,
91264ab3302SCarolineConcatto         "Procedure component '%s' has invalid interface '%s'"_err_en_US, name,
91364ab3302SCarolineConcatto         interface->name());
91464ab3302SCarolineConcatto     return;
91564ab3302SCarolineConcatto   }
91664ab3302SCarolineConcatto   std::optional<SourceName> passName{details.passName()};
91764ab3302SCarolineConcatto   const auto &dummyArgs{subprogram->dummyArgs()};
91864ab3302SCarolineConcatto   if (!passName) {
91964ab3302SCarolineConcatto     if (dummyArgs.empty()) {
92064ab3302SCarolineConcatto       messages_.Say(name,
92164ab3302SCarolineConcatto           proc.has<ProcEntityDetails>()
92264ab3302SCarolineConcatto               ? "Procedure component '%s' with no dummy arguments"
92364ab3302SCarolineConcatto                 " must have NOPASS attribute"_err_en_US
92464ab3302SCarolineConcatto               : "Procedure binding '%s' with no dummy arguments"
92564ab3302SCarolineConcatto                 " must have NOPASS attribute"_err_en_US,
92664ab3302SCarolineConcatto           name);
92764ab3302SCarolineConcatto       return;
92864ab3302SCarolineConcatto     }
92964ab3302SCarolineConcatto     passName = dummyArgs[0]->name();
93064ab3302SCarolineConcatto   }
93164ab3302SCarolineConcatto   std::optional<int> passArgIndex{};
93264ab3302SCarolineConcatto   for (std::size_t i{0}; i < dummyArgs.size(); ++i) {
93364ab3302SCarolineConcatto     if (dummyArgs[i] && dummyArgs[i]->name() == *passName) {
93464ab3302SCarolineConcatto       passArgIndex = i;
93564ab3302SCarolineConcatto       break;
93664ab3302SCarolineConcatto     }
93764ab3302SCarolineConcatto   }
93864ab3302SCarolineConcatto   if (!passArgIndex) {
93964ab3302SCarolineConcatto     messages_.Say(*passName,
94064ab3302SCarolineConcatto         "'%s' is not a dummy argument of procedure interface '%s'"_err_en_US,
94164ab3302SCarolineConcatto         *passName, interface->name());
94264ab3302SCarolineConcatto     return;
94364ab3302SCarolineConcatto   }
94464ab3302SCarolineConcatto   const Symbol &passArg{*dummyArgs[*passArgIndex]};
94564ab3302SCarolineConcatto   std::optional<parser::MessageFixedText> msg;
94664ab3302SCarolineConcatto   if (!passArg.has<ObjectEntityDetails>()) {
94764ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
94864ab3302SCarolineConcatto           " must be a data object"_err_en_US;
94964ab3302SCarolineConcatto   } else if (passArg.attrs().test(Attr::POINTER)) {
95064ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
95164ab3302SCarolineConcatto           " may not have the POINTER attribute"_err_en_US;
95264ab3302SCarolineConcatto   } else if (passArg.attrs().test(Attr::ALLOCATABLE)) {
95364ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
95464ab3302SCarolineConcatto           " may not have the ALLOCATABLE attribute"_err_en_US;
95564ab3302SCarolineConcatto   } else if (passArg.attrs().test(Attr::VALUE)) {
95664ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
95764ab3302SCarolineConcatto           " may not have the VALUE attribute"_err_en_US;
95864ab3302SCarolineConcatto   } else if (passArg.Rank() > 0) {
95964ab3302SCarolineConcatto     msg = "Passed-object dummy argument '%s' of procedure '%s'"
96064ab3302SCarolineConcatto           " must be scalar"_err_en_US;
96164ab3302SCarolineConcatto   }
96264ab3302SCarolineConcatto   if (msg) {
96364ab3302SCarolineConcatto     messages_.Say(name, std::move(*msg), passName.value(), name);
96464ab3302SCarolineConcatto     return;
96564ab3302SCarolineConcatto   }
96664ab3302SCarolineConcatto   const DeclTypeSpec *type{passArg.GetType()};
96764ab3302SCarolineConcatto   if (!type) {
96864ab3302SCarolineConcatto     return;  // an error already occurred
96964ab3302SCarolineConcatto   }
97064ab3302SCarolineConcatto   const Symbol &typeSymbol{*proc.owner().GetSymbol()};
97164ab3302SCarolineConcatto   const DerivedTypeSpec *derived{type->AsDerived()};
97264ab3302SCarolineConcatto   if (!derived || derived->typeSymbol() != typeSymbol) {
97364ab3302SCarolineConcatto     messages_.Say(name,
97464ab3302SCarolineConcatto         "Passed-object dummy argument '%s' of procedure '%s'"
97564ab3302SCarolineConcatto         " must be of type '%s' but is '%s'"_err_en_US,
97664ab3302SCarolineConcatto         passName.value(), name, typeSymbol.name(), type->AsFortran());
97764ab3302SCarolineConcatto     return;
97864ab3302SCarolineConcatto   }
97964ab3302SCarolineConcatto   if (IsExtensibleType(derived) != type->IsPolymorphic()) {
98064ab3302SCarolineConcatto     messages_.Say(name,
98164ab3302SCarolineConcatto         type->IsPolymorphic()
98264ab3302SCarolineConcatto             ? "Passed-object dummy argument '%s' of procedure '%s'"
98364ab3302SCarolineConcatto               " may not be polymorphic because '%s' is not extensible"_err_en_US
98464ab3302SCarolineConcatto             : "Passed-object dummy argument '%s' of procedure '%s'"
98564ab3302SCarolineConcatto               " must be polymorphic because '%s' is extensible"_err_en_US,
98664ab3302SCarolineConcatto         passName.value(), name, typeSymbol.name());
98764ab3302SCarolineConcatto     return;
98864ab3302SCarolineConcatto   }
98964ab3302SCarolineConcatto   for (const auto &[paramName, paramValue] : derived->parameters()) {
99064ab3302SCarolineConcatto     if (paramValue.isLen() && !paramValue.isAssumed()) {
99164ab3302SCarolineConcatto       messages_.Say(name,
99264ab3302SCarolineConcatto           "Passed-object dummy argument '%s' of procedure '%s'"
99364ab3302SCarolineConcatto           " has non-assumed length parameter '%s'"_err_en_US,
99464ab3302SCarolineConcatto           passName.value(), name, paramName);
99564ab3302SCarolineConcatto     }
99664ab3302SCarolineConcatto   }
99764ab3302SCarolineConcatto }
99864ab3302SCarolineConcatto 
99964ab3302SCarolineConcatto void CheckHelper::CheckProcBinding(
100064ab3302SCarolineConcatto     const Symbol &symbol, const ProcBindingDetails &binding) {
100164ab3302SCarolineConcatto   const Scope &dtScope{symbol.owner()};
100264ab3302SCarolineConcatto   CHECK(dtScope.kind() == Scope::Kind::DerivedType);
100364ab3302SCarolineConcatto   if (const Symbol * dtSymbol{dtScope.symbol()}) {
100464ab3302SCarolineConcatto     if (symbol.attrs().test(Attr::DEFERRED)) {
100564ab3302SCarolineConcatto       if (!dtSymbol->attrs().test(Attr::ABSTRACT)) {
100664ab3302SCarolineConcatto         SayWithDeclaration(*dtSymbol,
100764ab3302SCarolineConcatto             "Procedure bound to non-ABSTRACT derived type '%s' may not be DEFERRED"_err_en_US,
100864ab3302SCarolineConcatto             dtSymbol->name());
100964ab3302SCarolineConcatto       }
101064ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::NON_OVERRIDABLE)) {
101164ab3302SCarolineConcatto         messages_.Say(
101264ab3302SCarolineConcatto             "Type-bound procedure '%s' may not be both DEFERRED and NON_OVERRIDABLE"_err_en_US,
101364ab3302SCarolineConcatto             symbol.name());
101464ab3302SCarolineConcatto       }
101564ab3302SCarolineConcatto     }
101664ab3302SCarolineConcatto   }
101764ab3302SCarolineConcatto   if (const Symbol * overridden{FindOverriddenBinding(symbol)}) {
101864ab3302SCarolineConcatto     if (overridden->attrs().test(Attr::NON_OVERRIDABLE)) {
101964ab3302SCarolineConcatto       SayWithDeclaration(*overridden,
102064ab3302SCarolineConcatto           "Override of NON_OVERRIDABLE '%s' is not permitted"_err_en_US,
102164ab3302SCarolineConcatto           symbol.name());
102264ab3302SCarolineConcatto     }
102364ab3302SCarolineConcatto     if (const auto *overriddenBinding{
102464ab3302SCarolineConcatto             overridden->detailsIf<ProcBindingDetails>()}) {
102564ab3302SCarolineConcatto       if (!IsPureProcedure(symbol) && IsPureProcedure(*overridden)) {
102664ab3302SCarolineConcatto         SayWithDeclaration(*overridden,
102764ab3302SCarolineConcatto             "An overridden pure type-bound procedure binding must also be pure"_err_en_US);
102864ab3302SCarolineConcatto         return;
102964ab3302SCarolineConcatto       }
103064ab3302SCarolineConcatto       if (!binding.symbol().attrs().test(Attr::ELEMENTAL) &&
103164ab3302SCarolineConcatto           overriddenBinding->symbol().attrs().test(Attr::ELEMENTAL)) {
103264ab3302SCarolineConcatto         SayWithDeclaration(*overridden,
103364ab3302SCarolineConcatto             "A type-bound procedure and its override must both, or neither, be ELEMENTAL"_err_en_US);
103464ab3302SCarolineConcatto         return;
103564ab3302SCarolineConcatto       }
103664ab3302SCarolineConcatto       bool isNopass{symbol.attrs().test(Attr::NOPASS)};
103764ab3302SCarolineConcatto       if (isNopass != overridden->attrs().test(Attr::NOPASS)) {
103864ab3302SCarolineConcatto         SayWithDeclaration(*overridden,
103964ab3302SCarolineConcatto             isNopass
104064ab3302SCarolineConcatto                 ? "A NOPASS type-bound procedure may not override a passed-argument procedure"_err_en_US
104164ab3302SCarolineConcatto                 : "A passed-argument type-bound procedure may not override a NOPASS procedure"_err_en_US);
104264ab3302SCarolineConcatto       } else {
104364ab3302SCarolineConcatto         auto bindingChars{evaluate::characteristics::Procedure::Characterize(
104464ab3302SCarolineConcatto             binding.symbol(), context_.intrinsics())};
104564ab3302SCarolineConcatto         auto overriddenChars{evaluate::characteristics::Procedure::Characterize(
104664ab3302SCarolineConcatto             overriddenBinding->symbol(), context_.intrinsics())};
104764ab3302SCarolineConcatto         if (bindingChars && overriddenChars) {
104864ab3302SCarolineConcatto           if (isNopass) {
104964ab3302SCarolineConcatto             if (!bindingChars->CanOverride(*overriddenChars, std::nullopt)) {
105064ab3302SCarolineConcatto               SayWithDeclaration(*overridden,
105164ab3302SCarolineConcatto                   "A type-bound procedure and its override must have compatible interfaces"_err_en_US);
105264ab3302SCarolineConcatto             }
105364ab3302SCarolineConcatto           } else {
105464ab3302SCarolineConcatto             int passIndex{bindingChars->FindPassIndex(binding.passName())};
105564ab3302SCarolineConcatto             int overriddenPassIndex{
105664ab3302SCarolineConcatto                 overriddenChars->FindPassIndex(overriddenBinding->passName())};
105764ab3302SCarolineConcatto             if (passIndex != overriddenPassIndex) {
105864ab3302SCarolineConcatto               SayWithDeclaration(*overridden,
105964ab3302SCarolineConcatto                   "A type-bound procedure and its override must use the same PASS argument"_err_en_US);
106064ab3302SCarolineConcatto             } else if (!bindingChars->CanOverride(
106164ab3302SCarolineConcatto                            *overriddenChars, passIndex)) {
106264ab3302SCarolineConcatto               SayWithDeclaration(*overridden,
106364ab3302SCarolineConcatto                   "A type-bound procedure and its override must have compatible interfaces apart from their passed argument"_err_en_US);
106464ab3302SCarolineConcatto             }
106564ab3302SCarolineConcatto           }
106664ab3302SCarolineConcatto         }
106764ab3302SCarolineConcatto       }
106864ab3302SCarolineConcatto       if (symbol.attrs().test(Attr::PRIVATE) &&
106964ab3302SCarolineConcatto           overridden->attrs().test(Attr::PUBLIC)) {
107064ab3302SCarolineConcatto         SayWithDeclaration(*overridden,
107164ab3302SCarolineConcatto             "A PRIVATE procedure may not override a PUBLIC procedure"_err_en_US);
107264ab3302SCarolineConcatto       }
107364ab3302SCarolineConcatto     } else {
107464ab3302SCarolineConcatto       SayWithDeclaration(*overridden,
107564ab3302SCarolineConcatto           "A type-bound procedure binding may not have the same name as a parent component"_err_en_US);
107664ab3302SCarolineConcatto     }
107764ab3302SCarolineConcatto   }
107864ab3302SCarolineConcatto   CheckPassArg(symbol, &binding.symbol(), binding);
107964ab3302SCarolineConcatto }
108064ab3302SCarolineConcatto 
108164ab3302SCarolineConcatto void CheckHelper::Check(const Scope &scope) {
108264ab3302SCarolineConcatto   scope_ = &scope;
108364ab3302SCarolineConcatto   common::Restorer<const Symbol *> restorer{innermostSymbol_};
108464ab3302SCarolineConcatto   if (const Symbol * symbol{scope.symbol()}) {
108564ab3302SCarolineConcatto     innermostSymbol_ = symbol;
108664ab3302SCarolineConcatto   } else if (scope.IsDerivedType()) {
108764ab3302SCarolineConcatto     return;  // PDT instantiations have null symbol()
108864ab3302SCarolineConcatto   }
108964ab3302SCarolineConcatto   for (const auto &set : scope.equivalenceSets()) {
109064ab3302SCarolineConcatto     CheckEquivalenceSet(set);
109164ab3302SCarolineConcatto   }
109264ab3302SCarolineConcatto   for (const auto &pair : scope) {
109364ab3302SCarolineConcatto     Check(*pair.second);
109464ab3302SCarolineConcatto   }
109564ab3302SCarolineConcatto   for (const Scope &child : scope.children()) {
109664ab3302SCarolineConcatto     Check(child);
109764ab3302SCarolineConcatto   }
109864ab3302SCarolineConcatto   if (scope.kind() == Scope::Kind::BlockData) {
109964ab3302SCarolineConcatto     CheckBlockData(scope);
110064ab3302SCarolineConcatto   }
110164ab3302SCarolineConcatto }
110264ab3302SCarolineConcatto 
110364ab3302SCarolineConcatto void CheckHelper::CheckEquivalenceSet(const EquivalenceSet &set) {
110464ab3302SCarolineConcatto   auto iter{
110564ab3302SCarolineConcatto       std::find_if(set.begin(), set.end(), [](const EquivalenceObject &object) {
110664ab3302SCarolineConcatto         return FindCommonBlockContaining(object.symbol) != nullptr;
110764ab3302SCarolineConcatto       })};
110864ab3302SCarolineConcatto   if (iter != set.end()) {
110964ab3302SCarolineConcatto     const Symbol &commonBlock{DEREF(FindCommonBlockContaining(iter->symbol))};
111064ab3302SCarolineConcatto     for (auto &object : set) {
111164ab3302SCarolineConcatto       if (&object != &*iter) {
111264ab3302SCarolineConcatto         if (auto *details{object.symbol.detailsIf<ObjectEntityDetails>()}) {
111364ab3302SCarolineConcatto           if (details->commonBlock()) {
111464ab3302SCarolineConcatto             if (details->commonBlock() != &commonBlock) {  // 8.10.3 paragraph 1
111564ab3302SCarolineConcatto               if (auto *msg{messages_.Say(object.symbol.name(),
111664ab3302SCarolineConcatto                       "Two objects in the same EQUIVALENCE set may not be members of distinct COMMON blocks"_err_en_US)}) {
111764ab3302SCarolineConcatto                 msg->Attach(iter->symbol.name(),
111864ab3302SCarolineConcatto                        "Other object in EQUIVALENCE set"_en_US)
111964ab3302SCarolineConcatto                     .Attach(details->commonBlock()->name(),
112064ab3302SCarolineConcatto                         "COMMON block containing '%s'"_en_US,
112164ab3302SCarolineConcatto                         object.symbol.name())
112264ab3302SCarolineConcatto                     .Attach(commonBlock.name(),
112364ab3302SCarolineConcatto                         "COMMON block containing '%s'"_en_US,
112464ab3302SCarolineConcatto                         iter->symbol.name());
112564ab3302SCarolineConcatto               }
112664ab3302SCarolineConcatto             }
112764ab3302SCarolineConcatto           } else {
112864ab3302SCarolineConcatto             // Mark all symbols in the equivalence set with the same COMMON
112964ab3302SCarolineConcatto             // block to prevent spurious error messages about initialization
113064ab3302SCarolineConcatto             // in BLOCK DATA outside COMMON
113164ab3302SCarolineConcatto             details->set_commonBlock(commonBlock);
113264ab3302SCarolineConcatto           }
113364ab3302SCarolineConcatto         }
113464ab3302SCarolineConcatto       }
113564ab3302SCarolineConcatto     }
113664ab3302SCarolineConcatto   }
113764ab3302SCarolineConcatto   // TODO: Move C8106 (&al.) checks here from resolve-names-utils.cpp
113864ab3302SCarolineConcatto }
113964ab3302SCarolineConcatto 
114064ab3302SCarolineConcatto void CheckHelper::CheckBlockData(const Scope &scope) {
114164ab3302SCarolineConcatto   // BLOCK DATA subprograms should contain only named common blocks.
114264ab3302SCarolineConcatto   // C1415 presents a list of statements that shouldn't appear in
114364ab3302SCarolineConcatto   // BLOCK DATA, but so long as the subprogram contains no executable
114464ab3302SCarolineConcatto   // code and allocates no storage outside named COMMON, we're happy
114564ab3302SCarolineConcatto   // (e.g., an ENUM is strictly not allowed).
114664ab3302SCarolineConcatto   for (const auto &pair : scope) {
114764ab3302SCarolineConcatto     const Symbol &symbol{*pair.second};
114864ab3302SCarolineConcatto     if (!(symbol.has<CommonBlockDetails>() || symbol.has<UseDetails>() ||
114964ab3302SCarolineConcatto             symbol.has<UseErrorDetails>() || symbol.has<DerivedTypeDetails>() ||
115064ab3302SCarolineConcatto             symbol.has<SubprogramDetails>() ||
115164ab3302SCarolineConcatto             symbol.has<ObjectEntityDetails>() ||
115264ab3302SCarolineConcatto             (symbol.has<ProcEntityDetails>() &&
115364ab3302SCarolineConcatto                 !symbol.attrs().test(Attr::POINTER)))) {
115464ab3302SCarolineConcatto       messages_.Say(symbol.name(),
115564ab3302SCarolineConcatto           "'%s' may not appear in a BLOCK DATA subprogram"_err_en_US,
115664ab3302SCarolineConcatto           symbol.name());
115764ab3302SCarolineConcatto     }
115864ab3302SCarolineConcatto   }
115964ab3302SCarolineConcatto }
116064ab3302SCarolineConcatto 
116164ab3302SCarolineConcatto void CheckDeclarations(SemanticsContext &context) {
116264ab3302SCarolineConcatto   CheckHelper{context}.Check();
116364ab3302SCarolineConcatto }
116464ab3302SCarolineConcatto }
1165