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