xref: /llvm-project/flang/lib/Semantics/definable.cpp (revision 07b3bba901e7d51b3173631d6af811eae9d84cda)
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