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