1573fc618SPeter Klausler //===-- lib/Semantics/definable.cpp ---------------------------------------===// 2573fc618SPeter Klausler // 3573fc618SPeter Klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4573fc618SPeter Klausler // See https://llvm.org/LICENSE.txt for license information. 5573fc618SPeter Klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6573fc618SPeter Klausler // 7573fc618SPeter Klausler //===----------------------------------------------------------------------===// 8573fc618SPeter Klausler 9573fc618SPeter Klausler #include "definable.h" 10573fc618SPeter Klausler #include "flang/Evaluate/tools.h" 11573fc618SPeter Klausler #include "flang/Semantics/tools.h" 12573fc618SPeter Klausler 13573fc618SPeter Klausler using namespace Fortran::parser::literals; 14573fc618SPeter Klausler 15573fc618SPeter Klausler namespace Fortran::semantics { 16573fc618SPeter Klausler 17573fc618SPeter Klausler template <typename... A> 18573fc618SPeter Klausler static parser::Message BlameSymbol(parser::CharBlock at, 19573fc618SPeter Klausler const parser::MessageFixedText &text, const Symbol &original, A &&...x) { 20573fc618SPeter Klausler parser::Message message{at, text, original.name(), std::forward<A>(x)...}; 21d5285fefSPeter Klausler message.set_severity(parser::Severity::Error); 22573fc618SPeter Klausler evaluate::AttachDeclaration(message, original); 23573fc618SPeter Klausler return message; 24573fc618SPeter Klausler } 25573fc618SPeter Klausler 26573fc618SPeter Klausler static bool IsPointerDummyOfPureFunction(const Symbol &x) { 27573fc618SPeter Klausler return IsPointerDummy(x) && FindPureProcedureContaining(x.owner()) && 28573fc618SPeter Klausler x.owner().symbol() && IsFunction(*x.owner().symbol()); 29573fc618SPeter Klausler } 30573fc618SPeter Klausler 31573fc618SPeter Klausler // See C1594, first paragraph. These conditions enable checks on both 32573fc618SPeter Klausler // left-hand and right-hand sides in various circumstances. 33573fc618SPeter Klausler const char *WhyBaseObjectIsSuspicious(const Symbol &x, const Scope &scope) { 34573fc618SPeter Klausler if (IsHostAssociatedIntoSubprogram(x, scope)) { 35573fc618SPeter Klausler return "host-associated"; 36573fc618SPeter Klausler } else if (IsUseAssociated(x, scope)) { 37573fc618SPeter Klausler return "USE-associated"; 38573fc618SPeter Klausler } else if (IsPointerDummyOfPureFunction(x)) { 39573fc618SPeter Klausler return "a POINTER dummy argument of a pure function"; 40573fc618SPeter Klausler } else if (IsIntentIn(x)) { 41573fc618SPeter Klausler return "an INTENT(IN) dummy argument"; 42573fc618SPeter Klausler } else if (FindCommonBlockContaining(x)) { 43573fc618SPeter Klausler return "in a COMMON block"; 44573fc618SPeter Klausler } else { 45573fc618SPeter Klausler return nullptr; 46573fc618SPeter Klausler } 47573fc618SPeter Klausler } 48573fc618SPeter Klausler 49573fc618SPeter Klausler // Checks C1594(1,2); false if check fails 50573fc618SPeter Klausler static std::optional<parser::Message> CheckDefinabilityInPureScope( 51573fc618SPeter Klausler SourceName at, const Symbol &original, const Symbol &ultimate, 52573fc618SPeter Klausler const Scope &context, const Scope &pure) { 53573fc618SPeter Klausler if (pure.symbol()) { 54573fc618SPeter Klausler if (const char *why{WhyBaseObjectIsSuspicious(ultimate, context)}) { 55573fc618SPeter Klausler return BlameSymbol(at, 56573fc618SPeter Klausler "'%s' may not be defined in pure subprogram '%s' because it is %s"_en_US, 57573fc618SPeter Klausler original, pure.symbol()->name(), why); 58573fc618SPeter Klausler } 59573fc618SPeter Klausler } 60573fc618SPeter Klausler return std::nullopt; 61573fc618SPeter Klausler } 62573fc618SPeter Klausler 636ac392b9SPeter Klausler // True when the object being defined is not a subobject of the base 646ac392b9SPeter Klausler // object, e.g. X%PTR = 1., X%PTR%PTR2 => T (but not X%PTR => T). 656ac392b9SPeter Klausler // F'2023 9.4.2p5 666ac392b9SPeter Klausler static bool DefinesComponentPointerTarget( 676ac392b9SPeter Klausler const evaluate::DataRef &dataRef, DefinabilityFlags flags) { 686ac392b9SPeter Klausler if (const evaluate::Component * 696ac392b9SPeter Klausler component{common::visit( 706ac392b9SPeter Klausler common::visitors{ 716ac392b9SPeter Klausler [](const SymbolRef &) -> const evaluate::Component * { 726ac392b9SPeter Klausler return nullptr; 736ac392b9SPeter Klausler }, 746ac392b9SPeter Klausler [](const evaluate::Component &component) { return &component; }, 756ac392b9SPeter Klausler [](const evaluate::ArrayRef &aRef) { 766ac392b9SPeter Klausler return aRef.base().UnwrapComponent(); 776ac392b9SPeter Klausler }, 786ac392b9SPeter Klausler [](const evaluate::CoarrayRef &aRef) 796ac392b9SPeter Klausler -> const evaluate::Component * { return nullptr; }, 806ac392b9SPeter Klausler }, 816ac392b9SPeter Klausler dataRef.u)}) { 826ac392b9SPeter Klausler const Symbol &compSym{component->GetLastSymbol()}; 836ac392b9SPeter Klausler if (IsPointer(compSym) || 846ac392b9SPeter Klausler (flags.test(DefinabilityFlag::AcceptAllocatable) && 856ac392b9SPeter Klausler IsAllocatable(compSym))) { 866ac392b9SPeter Klausler if (!flags.test(DefinabilityFlag::PointerDefinition)) { 876ac392b9SPeter Klausler return true; 88573fc618SPeter Klausler } 89573fc618SPeter Klausler } 906ac392b9SPeter Klausler flags.reset(DefinabilityFlag::PointerDefinition); 916ac392b9SPeter Klausler return DefinesComponentPointerTarget(component->base(), flags); 92573fc618SPeter Klausler } else { 936ac392b9SPeter Klausler return false; 94573fc618SPeter Klausler } 95573fc618SPeter Klausler } 96573fc618SPeter Klausler 97573fc618SPeter Klausler // Check the leftmost (or only) symbol from a data-ref or expression. 98573fc618SPeter Klausler static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at, 99f58f0891SPeter Klausler const Scope &scope, DefinabilityFlags flags, const Symbol &original, 1006ac392b9SPeter Klausler bool isWholeSymbol, bool isComponentPointerTarget) { 101573fc618SPeter Klausler const Symbol &ultimate{original.GetUltimate()}; 102573fc618SPeter Klausler bool isPointerDefinition{flags.test(DefinabilityFlag::PointerDefinition)}; 103fb792ebaSPeter Klausler bool acceptAllocatable{flags.test(DefinabilityFlag::AcceptAllocatable)}; 104573fc618SPeter Klausler bool isTargetDefinition{!isPointerDefinition && IsPointer(ultimate)}; 105573fc618SPeter Klausler if (const auto *association{ultimate.detailsIf<AssocEntityDetails>()}) { 106f06ea103SPeter Klausler if (!IsVariable(association->expr())) { 107573fc618SPeter Klausler return BlameSymbol(at, 108573fc618SPeter Klausler "'%s' is construct associated with an expression"_en_US, original); 109573fc618SPeter Klausler } else if (evaluate::HasVectorSubscript(association->expr().value())) { 110573fc618SPeter Klausler return BlameSymbol(at, 111573fc618SPeter Klausler "Construct association '%s' has a vector subscript"_en_US, original); 112573fc618SPeter Klausler } else if (auto dataRef{evaluate::ExtractDataRef( 113573fc618SPeter Klausler *association->expr(), true, true)}) { 1146ac392b9SPeter Klausler return WhyNotDefinableBase(at, scope, flags, dataRef->GetFirstSymbol(), 1156ac392b9SPeter Klausler isWholeSymbol && 1166ac392b9SPeter Klausler std::holds_alternative<evaluate::SymbolRef>(dataRef->u), 1176ac392b9SPeter Klausler isComponentPointerTarget || 1186ac392b9SPeter Klausler DefinesComponentPointerTarget(*dataRef, flags)); 119573fc618SPeter Klausler } 120573fc618SPeter Klausler } 1216ac392b9SPeter Klausler if (isTargetDefinition || isComponentPointerTarget) { 122573fc618SPeter Klausler } else if (!isPointerDefinition && !IsVariableName(ultimate)) { 123573fc618SPeter Klausler return BlameSymbol(at, "'%s' is not a variable"_en_US, original); 124573fc618SPeter Klausler } else if (IsProtected(ultimate) && IsUseAssociated(original, scope)) { 125573fc618SPeter Klausler return BlameSymbol(at, "'%s' is protected in this scope"_en_US, original); 126f58f0891SPeter Klausler } else if (IsIntentIn(ultimate) && 127f58f0891SPeter Klausler (!IsPointer(ultimate) || (isWholeSymbol && isPointerDefinition))) { 128573fc618SPeter Klausler return BlameSymbol( 129573fc618SPeter Klausler at, "'%s' is an INTENT(IN) dummy argument"_en_US, original); 130f0590177SPeter Klausler } else if (acceptAllocatable && IsAllocatable(ultimate) && 13133c27f28SPeter Klausler !flags.test(DefinabilityFlag::SourcedAllocation)) { 13233c27f28SPeter Klausler // allocating a function result doesn't count as a def'n 13333c27f28SPeter Klausler // unless there's SOURCE= 13433c27f28SPeter Klausler } else if (!flags.test(DefinabilityFlag::DoNotNoteDefinition)) { 13533c27f28SPeter Klausler scope.context().NoteDefinedSymbol(ultimate); 136573fc618SPeter Klausler } 137573fc618SPeter Klausler if (const Scope * pure{FindPureProcedureContaining(scope)}) { 138573fc618SPeter Klausler // Additional checking for pure subprograms. 1396ac392b9SPeter Klausler if (!isTargetDefinition || isComponentPointerTarget) { 140573fc618SPeter Klausler if (auto msg{CheckDefinabilityInPureScope( 141573fc618SPeter Klausler at, original, ultimate, scope, *pure)}) { 142573fc618SPeter Klausler return msg; 143573fc618SPeter Klausler } 144573fc618SPeter Klausler } 145573fc618SPeter Klausler if (const Symbol * 146573fc618SPeter Klausler visible{FindExternallyVisibleObject( 147573fc618SPeter Klausler ultimate, *pure, isPointerDefinition)}) { 148573fc618SPeter Klausler return BlameSymbol(at, 149573fc618SPeter Klausler "'%s' is externally visible via '%s' and not definable in a pure subprogram"_en_US, 150573fc618SPeter Klausler original, visible->name()); 151573fc618SPeter Klausler } 152573fc618SPeter Klausler } 153f513bd80SPeter Klausler if (const Scope * deviceContext{FindCUDADeviceContext(&scope)}) { 154f513bd80SPeter Klausler bool isOwnedByDeviceCode{deviceContext->Contains(ultimate.owner())}; 155f513bd80SPeter Klausler if (isPointerDefinition && !acceptAllocatable) { 156f513bd80SPeter Klausler return BlameSymbol(at, 157f513bd80SPeter Klausler "'%s' is a pointer and may not be associated in a device subprogram"_err_en_US, 158f513bd80SPeter Klausler original); 159f513bd80SPeter Klausler } else if (auto cudaDataAttr{GetCUDADataAttr(&ultimate)}) { 160f513bd80SPeter Klausler if (*cudaDataAttr == common::CUDADataAttr::Constant) { 161f513bd80SPeter Klausler return BlameSymbol(at, 162f513bd80SPeter Klausler "'%s' has ATTRIBUTES(CONSTANT) and is not definable in a device subprogram"_err_en_US, 163f513bd80SPeter Klausler original); 164f513bd80SPeter Klausler } else if (acceptAllocatable && !isOwnedByDeviceCode) { 165f513bd80SPeter Klausler return BlameSymbol(at, 166f513bd80SPeter Klausler "'%s' is a host-associated allocatable and is not definable in a device subprogram"_err_en_US, 167f513bd80SPeter Klausler original); 168f513bd80SPeter Klausler } else if (*cudaDataAttr != common::CUDADataAttr::Device && 1695c90527bSValentin Clement (バレンタイン クレメン) *cudaDataAttr != common::CUDADataAttr::Managed && 1705c90527bSValentin Clement (バレンタイン クレメン) *cudaDataAttr != common::CUDADataAttr::Shared) { 171f513bd80SPeter Klausler return BlameSymbol(at, 1725c90527bSValentin Clement (バレンタイン クレメン) "'%s' is not device or managed or shared data and is not definable in a device subprogram"_err_en_US, 173f513bd80SPeter Klausler original); 174f513bd80SPeter Klausler } 175f513bd80SPeter Klausler } else if (!isOwnedByDeviceCode) { 176f513bd80SPeter Klausler return BlameSymbol(at, 177f513bd80SPeter Klausler "'%s' is a host variable and is not definable in a device subprogram"_err_en_US, 178f513bd80SPeter Klausler original); 179f513bd80SPeter Klausler } 180f513bd80SPeter Klausler } 181573fc618SPeter Klausler return std::nullopt; 182573fc618SPeter Klausler } 183573fc618SPeter Klausler 184573fc618SPeter Klausler static std::optional<parser::Message> WhyNotDefinableLast(parser::CharBlock at, 185573fc618SPeter Klausler const Scope &scope, DefinabilityFlags flags, const Symbol &original) { 186573fc618SPeter Klausler const Symbol &ultimate{original.GetUltimate()}; 187e73d51d3SPeter Klausler if (const auto *association{ultimate.detailsIf<AssocEntityDetails>()}; 188e73d51d3SPeter Klausler association && 189e73d51d3SPeter Klausler (association->rank().has_value() || 190e73d51d3SPeter Klausler !flags.test(DefinabilityFlag::PointerDefinition))) { 191f58f0891SPeter Klausler if (auto dataRef{ 192f58f0891SPeter Klausler evaluate::ExtractDataRef(*association->expr(), true, true)}) { 193f58f0891SPeter Klausler return WhyNotDefinableLast(at, scope, flags, dataRef->GetLastSymbol()); 194f58f0891SPeter Klausler } 195f58f0891SPeter Klausler } 196573fc618SPeter Klausler if (flags.test(DefinabilityFlag::PointerDefinition)) { 197fb792ebaSPeter Klausler if (flags.test(DefinabilityFlag::AcceptAllocatable)) { 198031b4e5eSPeter Klausler if (!IsAllocatableOrObjectPointer(&ultimate)) { 199fb792ebaSPeter Klausler return BlameSymbol( 200fb792ebaSPeter Klausler at, "'%s' is neither a pointer nor an allocatable"_en_US, original); 201fb792ebaSPeter Klausler } 202fb792ebaSPeter Klausler } else if (!IsPointer(ultimate)) { 203573fc618SPeter Klausler return BlameSymbol(at, "'%s' is not a pointer"_en_US, original); 204573fc618SPeter Klausler } 205573fc618SPeter Klausler return std::nullopt; // pointer assignment - skip following checks 206573fc618SPeter Klausler } 207*07b3bba9SPeter Klausler if (!flags.test(DefinabilityFlag::AllowEventLockOrNotifyType) && 208*07b3bba9SPeter Klausler IsOrContainsEventOrLockComponent(ultimate)) { 209573fc618SPeter Klausler return BlameSymbol(at, 210573fc618SPeter Klausler "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US, 211573fc618SPeter Klausler original); 212573fc618SPeter Klausler } 213e9a8ab00SPeter Klausler if (FindPureProcedureContaining(scope)) { 214573fc618SPeter Klausler if (auto dyType{evaluate::DynamicType::From(ultimate)}) { 215e9a8ab00SPeter Klausler if (!flags.test(DefinabilityFlag::PolymorphicOkInPure)) { 216573fc618SPeter Klausler if (dyType->IsPolymorphic()) { // C1596 217d5285fefSPeter Klausler return BlameSymbol( 218d5285fefSPeter Klausler at, "'%s' is polymorphic in a pure subprogram"_en_US, original); 219e9a8ab00SPeter Klausler } 220e9a8ab00SPeter Klausler } 221e9a8ab00SPeter Klausler if (const Symbol * impure{HasImpureFinal(ultimate)}) { 222d5285fefSPeter Klausler return BlameSymbol(at, "'%s' has an impure FINAL procedure '%s'"_en_US, 223d5285fefSPeter Klausler original, impure->name()); 224573fc618SPeter Klausler } 225573fc618SPeter Klausler if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(dyType)}) { 226e9a8ab00SPeter Klausler if (!flags.test(DefinabilityFlag::PolymorphicOkInPure)) { 22725822dc3SPeter Klausler if (auto bad{ 22825822dc3SPeter Klausler FindPolymorphicAllocatablePotentialComponent(*derived)}) { 229573fc618SPeter Klausler return BlameSymbol(at, 230d5285fefSPeter Klausler "'%s' has polymorphic component '%s' in a pure subprogram"_en_US, 231573fc618SPeter Klausler original, bad.BuildResultDesignatorName()); 232573fc618SPeter Klausler } 233573fc618SPeter Klausler } 234573fc618SPeter Klausler } 235573fc618SPeter Klausler } 236e9a8ab00SPeter Klausler } 237573fc618SPeter Klausler return std::nullopt; 238573fc618SPeter Klausler } 239573fc618SPeter Klausler 240573fc618SPeter Klausler // Checks a data-ref 241573fc618SPeter Klausler static std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at, 242573fc618SPeter Klausler const Scope &scope, DefinabilityFlags flags, 243573fc618SPeter Klausler const evaluate::DataRef &dataRef) { 244d5285fefSPeter Klausler auto whyNotBase{ 2456ac392b9SPeter Klausler WhyNotDefinableBase(at, scope, flags, dataRef.GetFirstSymbol(), 2466ac392b9SPeter Klausler std::holds_alternative<evaluate::SymbolRef>(dataRef.u), 247d5285fefSPeter Klausler DefinesComponentPointerTarget(dataRef, flags))}; 248d5285fefSPeter Klausler if (!whyNotBase || !whyNotBase->IsFatal()) { 249d5285fefSPeter Klausler if (auto whyNotLast{ 250d5285fefSPeter Klausler WhyNotDefinableLast(at, scope, flags, dataRef.GetLastSymbol())}) { 251d5285fefSPeter Klausler if (whyNotLast->IsFatal() || !whyNotBase) { 252d5285fefSPeter Klausler return whyNotLast; 253573fc618SPeter Klausler } 254573fc618SPeter Klausler } 255d5285fefSPeter Klausler } 256d5285fefSPeter Klausler return whyNotBase; 257d5285fefSPeter Klausler } 258573fc618SPeter Klausler 259573fc618SPeter Klausler std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at, 260573fc618SPeter Klausler const Scope &scope, DefinabilityFlags flags, const Symbol &original) { 261d5285fefSPeter Klausler auto whyNotBase{WhyNotDefinableBase(at, scope, flags, original, 262d5285fefSPeter Klausler /*isWholeSymbol=*/true, /*isComponentPointerTarget=*/false)}; 263d5285fefSPeter Klausler if (!whyNotBase || !whyNotBase->IsFatal()) { 264d5285fefSPeter Klausler if (auto whyNotLast{WhyNotDefinableLast(at, scope, flags, original)}) { 265d5285fefSPeter Klausler if (whyNotLast->IsFatal() || !whyNotBase) { 266d5285fefSPeter Klausler return whyNotLast; 267573fc618SPeter Klausler } 2686ac392b9SPeter Klausler } 269d5285fefSPeter Klausler } 270d5285fefSPeter Klausler return whyNotBase; 271d5285fefSPeter Klausler } 272573fc618SPeter Klausler 27322ed61edSPeter Klausler class DuplicatedSubscriptFinder 27422ed61edSPeter Klausler : public evaluate::AnyTraverse<DuplicatedSubscriptFinder, bool> { 27522ed61edSPeter Klausler using Base = evaluate::AnyTraverse<DuplicatedSubscriptFinder, bool>; 27622ed61edSPeter Klausler 27722ed61edSPeter Klausler public: 27822ed61edSPeter Klausler explicit DuplicatedSubscriptFinder(evaluate::FoldingContext &foldingContext) 27922ed61edSPeter Klausler : Base{*this}, foldingContext_{foldingContext} {} 28022ed61edSPeter Klausler using Base::operator(); 28122ed61edSPeter Klausler bool operator()(const evaluate::ActualArgument &) { 28222ed61edSPeter Klausler return false; // don't descend into argument expressions 28322ed61edSPeter Klausler } 28422ed61edSPeter Klausler bool operator()(const evaluate::ArrayRef &aRef) { 28522ed61edSPeter Klausler bool anyVector{false}; 28622ed61edSPeter Klausler for (const auto &ss : aRef.subscript()) { 28722ed61edSPeter Klausler if (ss.Rank() > 0) { 28822ed61edSPeter Klausler anyVector = true; 28922ed61edSPeter Klausler if (const auto *vecExpr{ 29022ed61edSPeter Klausler std::get_if<evaluate::IndirectSubscriptIntegerExpr>(&ss.u)}) { 29122ed61edSPeter Klausler auto folded{evaluate::Fold(foldingContext_, 29222ed61edSPeter Klausler evaluate::Expr<evaluate::SubscriptInteger>{vecExpr->value()})}; 29322ed61edSPeter Klausler if (const auto *con{ 29422ed61edSPeter Klausler evaluate::UnwrapConstantValue<evaluate::SubscriptInteger>( 29522ed61edSPeter Klausler folded)}) { 29622ed61edSPeter Klausler std::set<std::int64_t> values; 29722ed61edSPeter Klausler for (const auto &j : con->values()) { 29822ed61edSPeter Klausler if (auto pair{values.emplace(j.ToInt64())}; !pair.second) { 29922ed61edSPeter Klausler return true; // duplicate 30022ed61edSPeter Klausler } 30122ed61edSPeter Klausler } 30222ed61edSPeter Klausler } 30322ed61edSPeter Klausler return false; 30422ed61edSPeter Klausler } 30522ed61edSPeter Klausler } 30622ed61edSPeter Klausler } 30722ed61edSPeter Klausler return anyVector ? false : (*this)(aRef.base()); 30822ed61edSPeter Klausler } 30922ed61edSPeter Klausler 31022ed61edSPeter Klausler private: 31122ed61edSPeter Klausler evaluate::FoldingContext &foldingContext_; 31222ed61edSPeter Klausler }; 31322ed61edSPeter Klausler 314573fc618SPeter Klausler std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at, 315573fc618SPeter Klausler const Scope &scope, DefinabilityFlags flags, 316573fc618SPeter Klausler const evaluate::Expr<evaluate::SomeType> &expr) { 317d5285fefSPeter Klausler std::optional<parser::Message> portabilityWarning; 318573fc618SPeter Klausler if (auto dataRef{evaluate::ExtractDataRef(expr, true, true)}) { 3197bd2eaceSPeter Klausler if (evaluate::HasVectorSubscript(expr)) { 3207bd2eaceSPeter Klausler if (flags.test(DefinabilityFlag::VectorSubscriptIsOk)) { 3217bd2eaceSPeter Klausler if (auto type{expr.GetType()}) { 3227bd2eaceSPeter Klausler if (!type->IsUnlimitedPolymorphic() && 3237bd2eaceSPeter Klausler type->category() == TypeCategory::Derived) { 3247bd2eaceSPeter Klausler // Seek the FINAL subroutine that should but cannot be called 3257bd2eaceSPeter Klausler // for this definition of an array with a vector-valued subscript. 3267bd2eaceSPeter Klausler // If there's an elemental FINAL subroutine, all is well; otherwise, 3277bd2eaceSPeter Klausler // if there is a FINAL subroutine with a matching or assumed rank 3287bd2eaceSPeter Klausler // dummy argument, there's no way to call it. 3297bd2eaceSPeter Klausler int rank{expr.Rank()}; 3307bd2eaceSPeter Klausler const DerivedTypeSpec *spec{&type->GetDerivedTypeSpec()}; 3317bd2eaceSPeter Klausler while (spec) { 3327bd2eaceSPeter Klausler bool anyElemental{false}; 3337bd2eaceSPeter Klausler const Symbol *anyRankMatch{nullptr}; 334d84faa42SPeter Klausler for (auto ref : FinalsForDerivedTypeInstantiation(*spec)) { 3357bd2eaceSPeter Klausler const Symbol &ultimate{ref->GetUltimate()}; 3367bd2eaceSPeter Klausler anyElemental |= ultimate.attrs().test(Attr::ELEMENTAL); 3377bd2eaceSPeter Klausler if (const auto *subp{ultimate.detailsIf<SubprogramDetails>()}) { 3387bd2eaceSPeter Klausler if (!subp->dummyArgs().empty()) { 3397bd2eaceSPeter Klausler if (const Symbol * arg{subp->dummyArgs()[0]}) { 3407bd2eaceSPeter Klausler const auto *object{arg->detailsIf<ObjectEntityDetails>()}; 3417bd2eaceSPeter Klausler if (arg->Rank() == rank || 3427bd2eaceSPeter Klausler (object && object->IsAssumedRank())) { 3437bd2eaceSPeter Klausler anyRankMatch = &*ref; 3447bd2eaceSPeter Klausler } 3457bd2eaceSPeter Klausler } 3467bd2eaceSPeter Klausler } 3477bd2eaceSPeter Klausler } 3487bd2eaceSPeter Klausler } 3497bd2eaceSPeter Klausler if (anyRankMatch && !anyElemental) { 350d5285fefSPeter Klausler if (!portabilityWarning && 351d5285fefSPeter Klausler scope.context().languageFeatures().ShouldWarn( 352d5285fefSPeter Klausler common::UsageWarning::VectorSubscriptFinalization)) { 3530f973ac7SPeter Klausler portabilityWarning = parser::Message{ 3540f973ac7SPeter Klausler common::UsageWarning::VectorSubscriptFinalization, at, 355d5285fefSPeter Klausler "Variable '%s' has a vector subscript and will be finalized by non-elemental subroutine '%s'"_port_en_US, 3567bd2eaceSPeter Klausler expr.AsFortran(), anyRankMatch->name()}; 3577bd2eaceSPeter Klausler } 358d5285fefSPeter Klausler break; 359d5285fefSPeter Klausler } 3607bd2eaceSPeter Klausler const auto *parent{FindParentTypeSpec(*spec)}; 3617bd2eaceSPeter Klausler spec = parent ? parent->AsDerived() : nullptr; 3627bd2eaceSPeter Klausler } 3637bd2eaceSPeter Klausler } 3647bd2eaceSPeter Klausler } 36522ed61edSPeter Klausler if (!flags.test(DefinabilityFlag::DuplicatesAreOk) && 36622ed61edSPeter Klausler DuplicatedSubscriptFinder{scope.context().foldingContext()}(expr)) { 36722ed61edSPeter Klausler return parser::Message{at, 368d5285fefSPeter Klausler "Variable has a vector subscript with a duplicated element"_err_en_US}; 36922ed61edSPeter Klausler } 3707bd2eaceSPeter Klausler } else { 371573fc618SPeter Klausler return parser::Message{at, 372d5285fefSPeter Klausler "Variable '%s' has a vector subscript"_err_en_US, expr.AsFortran()}; 373573fc618SPeter Klausler } 3747bd2eaceSPeter Klausler } 375573fc618SPeter Klausler if (FindPureProcedureContaining(scope) && 376573fc618SPeter Klausler evaluate::ExtractCoarrayRef(expr)) { 377573fc618SPeter Klausler return parser::Message(at, 378d5285fefSPeter Klausler "A pure subprogram may not define the coindexed object '%s'"_err_en_US, 379573fc618SPeter Klausler expr.AsFortran()); 380573fc618SPeter Klausler } 381d5285fefSPeter Klausler if (auto whyNotDataRef{WhyNotDefinable(at, scope, flags, *dataRef)}) { 382d5285fefSPeter Klausler return whyNotDataRef; 383d5285fefSPeter Klausler } 38430d93230SPeter Klausler } else if (evaluate::IsNullPointer(expr)) { 38530d93230SPeter Klausler return parser::Message{ 386d5285fefSPeter Klausler at, "'%s' is a null pointer"_err_en_US, expr.AsFortran()}; 38730d93230SPeter Klausler } else if (flags.test(DefinabilityFlag::PointerDefinition)) { 388573fc618SPeter Klausler if (const auto *procDesignator{ 389573fc618SPeter Klausler std::get_if<evaluate::ProcedureDesignator>(&expr.u)}) { 390573fc618SPeter Klausler // Defining a procedure pointer 391573fc618SPeter Klausler if (const Symbol * procSym{procDesignator->GetSymbol()}) { 392573fc618SPeter Klausler if (evaluate::ExtractCoarrayRef(expr)) { // C1027 393573fc618SPeter Klausler return BlameSymbol(at, 394d5285fefSPeter Klausler "Procedure pointer '%s' may not be a coindexed object"_err_en_US, 395573fc618SPeter Klausler *procSym, expr.AsFortran()); 396573fc618SPeter Klausler } 397573fc618SPeter Klausler if (const auto *component{procDesignator->GetComponent()}) { 3986ac392b9SPeter Klausler flags.reset(DefinabilityFlag::PointerDefinition); 3996ac392b9SPeter Klausler return WhyNotDefinableBase(at, scope, flags, 4006ac392b9SPeter Klausler component->base().GetFirstSymbol(), false, 4016ac392b9SPeter Klausler DefinesComponentPointerTarget(component->base(), flags)); 402573fc618SPeter Klausler } else { 403573fc618SPeter Klausler return WhyNotDefinable(at, scope, flags, *procSym); 404573fc618SPeter Klausler } 405573fc618SPeter Klausler } 406573fc618SPeter Klausler } 407aad5984bSPeter Klausler return parser::Message{ 408d5285fefSPeter Klausler at, "'%s' is not a definable pointer"_err_en_US, expr.AsFortran()}; 40930d93230SPeter Klausler } else if (!evaluate::IsVariable(expr)) { 410d5285fefSPeter Klausler return parser::Message{ 411d5285fefSPeter Klausler at, "'%s' is not a variable or pointer"_err_en_US, expr.AsFortran()}; 412aad5984bSPeter Klausler } 413d5285fefSPeter Klausler return portabilityWarning; 414573fc618SPeter Klausler } 415573fc618SPeter Klausler 416573fc618SPeter Klausler } // namespace Fortran::semantics 417