xref: /llvm-project/flang/lib/Semantics/definable.cpp (revision e9a8ab004cc9aae3c45f8b3708176e584b5c23a2)
1 //===-- lib/Semantics/definable.cpp ---------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "definable.h"
10 #include "flang/Evaluate/tools.h"
11 #include "flang/Semantics/tools.h"
12 
13 using namespace Fortran::parser::literals;
14 
15 namespace Fortran::semantics {
16 
17 template <typename... A>
18 static parser::Message BlameSymbol(parser::CharBlock at,
19     const parser::MessageFixedText &text, const Symbol &original, A &&...x) {
20   parser::Message message{at, text, original.name(), std::forward<A>(x)...};
21   message.set_severity(parser::Severity::Because);
22   evaluate::AttachDeclaration(message, original);
23   return message;
24 }
25 
26 static bool IsPointerDummyOfPureFunction(const Symbol &x) {
27   return IsPointerDummy(x) && FindPureProcedureContaining(x.owner()) &&
28       x.owner().symbol() && IsFunction(*x.owner().symbol());
29 }
30 
31 // See C1594, first paragraph.  These conditions enable checks on both
32 // left-hand and right-hand sides in various circumstances.
33 const char *WhyBaseObjectIsSuspicious(const Symbol &x, const Scope &scope) {
34   if (IsHostAssociatedIntoSubprogram(x, scope)) {
35     return "host-associated";
36   } else if (IsUseAssociated(x, scope)) {
37     return "USE-associated";
38   } else if (IsPointerDummyOfPureFunction(x)) {
39     return "a POINTER dummy argument of a pure function";
40   } else if (IsIntentIn(x)) {
41     return "an INTENT(IN) dummy argument";
42   } else if (FindCommonBlockContaining(x)) {
43     return "in a COMMON block";
44   } else {
45     return nullptr;
46   }
47 }
48 
49 // Checks C1594(1,2); false if check fails
50 static std::optional<parser::Message> CheckDefinabilityInPureScope(
51     SourceName at, const Symbol &original, const Symbol &ultimate,
52     const Scope &context, const Scope &pure) {
53   if (pure.symbol()) {
54     if (const char *why{WhyBaseObjectIsSuspicious(ultimate, context)}) {
55       return BlameSymbol(at,
56           "'%s' may not be defined in pure subprogram '%s' because it is %s"_en_US,
57           original, pure.symbol()->name(), why);
58     }
59   }
60   return std::nullopt;
61 }
62 
63 // When a DataRef contains pointers, gets the rightmost one (unless it is
64 // the entity being defined, in which case the last pointer above it);
65 // otherwise, returns the leftmost symbol.  The resulting symbol is the
66 // relevant base object for definabiliy checking.  Examples:
67 //   ptr1%ptr2        => ...     -> ptr1
68 //   nonptr%ptr       => ...     -> nonptr
69 //   nonptr%ptr       =  ...     -> ptr
70 //   ptr1%ptr2        =  ...     -> ptr2
71 //   ptr1%ptr2%nonptr =  ...     -> ptr2
72 //   nonptr1%nonptr2  =  ...     -> nonptr1
73 static const Symbol &GetRelevantSymbol(const evaluate::DataRef &dataRef,
74     bool isPointerDefinition, bool acceptAllocatable) {
75   if (isPointerDefinition) {
76     if (const auto *component{std::get_if<evaluate::Component>(&dataRef.u)}) {
77       if (IsPointer(component->GetLastSymbol()) ||
78           (acceptAllocatable && IsAllocatable(component->GetLastSymbol()))) {
79         return GetRelevantSymbol(component->base(), false, false);
80       }
81     }
82   }
83   if (const Symbol * lastPointer{GetLastPointerSymbol(dataRef)}) {
84     return *lastPointer;
85   } else {
86     return dataRef.GetFirstSymbol();
87   }
88 }
89 
90 // Check the leftmost (or only) symbol from a data-ref or expression.
91 static std::optional<parser::Message> WhyNotDefinableBase(parser::CharBlock at,
92     const Scope &scope, DefinabilityFlags flags, const Symbol &original) {
93   const Symbol &ultimate{original.GetUltimate()};
94   bool isPointerDefinition{flags.test(DefinabilityFlag::PointerDefinition)};
95   bool acceptAllocatable{flags.test(DefinabilityFlag::AcceptAllocatable)};
96   bool isTargetDefinition{!isPointerDefinition && IsPointer(ultimate)};
97   if (const auto *association{ultimate.detailsIf<AssocEntityDetails>()}) {
98     if (association->rank().has_value()) {
99       return std::nullopt; // SELECT RANK always modifiable variable
100     } else if (!IsVariable(association->expr())) {
101       return BlameSymbol(at,
102           "'%s' is construct associated with an expression"_en_US, original);
103     } else if (evaluate::HasVectorSubscript(association->expr().value())) {
104       return BlameSymbol(at,
105           "Construct association '%s' has a vector subscript"_en_US, original);
106     } else if (auto dataRef{evaluate::ExtractDataRef(
107                    *association->expr(), true, true)}) {
108       return WhyNotDefinableBase(at, scope, flags,
109           GetRelevantSymbol(*dataRef, isPointerDefinition, acceptAllocatable));
110     }
111   }
112   if (isTargetDefinition) {
113   } else if (!isPointerDefinition && !IsVariableName(ultimate)) {
114     return BlameSymbol(at, "'%s' is not a variable"_en_US, original);
115   } else if (IsProtected(ultimate) && IsUseAssociated(original, scope)) {
116     return BlameSymbol(at, "'%s' is protected in this scope"_en_US, original);
117   } else if (IsIntentIn(ultimate)) {
118     return BlameSymbol(
119         at, "'%s' is an INTENT(IN) dummy argument"_en_US, original);
120   }
121   if (const Scope * pure{FindPureProcedureContaining(scope)}) {
122     // Additional checking for pure subprograms.
123     if (!isTargetDefinition) {
124       if (auto msg{CheckDefinabilityInPureScope(
125               at, original, ultimate, scope, *pure)}) {
126         return msg;
127       }
128     }
129     if (const Symbol *
130         visible{FindExternallyVisibleObject(
131             ultimate, *pure, isPointerDefinition)}) {
132       return BlameSymbol(at,
133           "'%s' is externally visible via '%s' and not definable in a pure subprogram"_en_US,
134           original, visible->name());
135     }
136   }
137   return std::nullopt;
138 }
139 
140 static std::optional<parser::Message> WhyNotDefinableLast(parser::CharBlock at,
141     const Scope &scope, DefinabilityFlags flags, const Symbol &original) {
142   const Symbol &ultimate{original.GetUltimate()};
143   if (flags.test(DefinabilityFlag::PointerDefinition)) {
144     if (flags.test(DefinabilityFlag::AcceptAllocatable)) {
145       if (!IsAllocatableOrPointer(ultimate)) {
146         return BlameSymbol(
147             at, "'%s' is neither a pointer nor an allocatable"_en_US, original);
148       }
149     } else if (!IsPointer(ultimate)) {
150       return BlameSymbol(at, "'%s' is not a pointer"_en_US, original);
151     }
152     return std::nullopt; // pointer assignment - skip following checks
153   }
154   if (IsOrContainsEventOrLockComponent(ultimate)) {
155     return BlameSymbol(at,
156         "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US,
157         original);
158   }
159   if (FindPureProcedureContaining(scope)) {
160     if (auto dyType{evaluate::DynamicType::From(ultimate)}) {
161       if (!flags.test(DefinabilityFlag::PolymorphicOkInPure)) {
162         if (dyType->IsPolymorphic()) { // C1596
163           return BlameSymbol(at,
164               "'%s' is polymorphic in a pure subprogram"_because_en_US,
165               original);
166         }
167       }
168       if (const Symbol * impure{HasImpureFinal(ultimate)}) {
169         return BlameSymbol(at,
170             "'%s' has an impure FINAL procedure '%s'"_because_en_US, original,
171             impure->name());
172       }
173       if (const DerivedTypeSpec * derived{GetDerivedTypeSpec(dyType)}) {
174         if (!flags.test(DefinabilityFlag::PolymorphicOkInPure)) {
175           if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) {
176             return BlameSymbol(at,
177                 "'%s' has polymorphic component '%s' in a pure subprogram"_because_en_US,
178                 original, bad.BuildResultDesignatorName());
179           }
180         }
181       }
182     }
183   }
184   return std::nullopt;
185 }
186 
187 // Checks a data-ref
188 static std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
189     const Scope &scope, DefinabilityFlags flags,
190     const evaluate::DataRef &dataRef) {
191   const Symbol &base{GetRelevantSymbol(dataRef,
192       flags.test(DefinabilityFlag::PointerDefinition),
193       flags.test(DefinabilityFlag::AcceptAllocatable))};
194   if (auto whyNot{WhyNotDefinableBase(at, scope, flags, base)}) {
195     return whyNot;
196   } else {
197     return WhyNotDefinableLast(at, scope, flags, dataRef.GetLastSymbol());
198   }
199 }
200 
201 // Checks a NOPASS procedure pointer component
202 static std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
203     const Scope &scope, DefinabilityFlags flags,
204     const evaluate::Component &component) {
205   const evaluate::DataRef &dataRef{component.base()};
206   const Symbol &base{GetRelevantSymbol(dataRef, false, false)};
207   DefinabilityFlags baseFlags{flags};
208   baseFlags.reset(DefinabilityFlag::PointerDefinition);
209   return WhyNotDefinableBase(at, scope, baseFlags, base);
210 }
211 
212 std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
213     const Scope &scope, DefinabilityFlags flags, const Symbol &original) {
214   if (auto base{WhyNotDefinableBase(at, scope, flags, original)}) {
215     return base;
216   }
217   return WhyNotDefinableLast(at, scope, flags, original);
218 }
219 
220 std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
221     const Scope &scope, DefinabilityFlags flags,
222     const evaluate::Expr<evaluate::SomeType> &expr) {
223   if (auto dataRef{evaluate::ExtractDataRef(expr, true, true)}) {
224     if (evaluate::HasVectorSubscript(expr)) {
225       if (flags.test(DefinabilityFlag::VectorSubscriptIsOk)) {
226         if (auto type{expr.GetType()}) {
227           if (!type->IsUnlimitedPolymorphic() &&
228               type->category() == TypeCategory::Derived) {
229             // Seek the FINAL subroutine that should but cannot be called
230             // for this definition of an array with a vector-valued subscript.
231             // If there's an elemental FINAL subroutine, all is well; otherwise,
232             // if there is a FINAL subroutine with a matching or assumed rank
233             // dummy argument, there's no way to call it.
234             int rank{expr.Rank()};
235             const DerivedTypeSpec *spec{&type->GetDerivedTypeSpec()};
236             while (spec) {
237               bool anyElemental{false};
238               const Symbol *anyRankMatch{nullptr};
239               for (auto ref : FinalsForDerivedTypeInstantiation(*spec)) {
240                 const Symbol &ultimate{ref->GetUltimate()};
241                 anyElemental |= ultimate.attrs().test(Attr::ELEMENTAL);
242                 if (const auto *subp{ultimate.detailsIf<SubprogramDetails>()}) {
243                   if (!subp->dummyArgs().empty()) {
244                     if (const Symbol * arg{subp->dummyArgs()[0]}) {
245                       const auto *object{arg->detailsIf<ObjectEntityDetails>()};
246                       if (arg->Rank() == rank ||
247                           (object && object->IsAssumedRank())) {
248                         anyRankMatch = &*ref;
249                       }
250                     }
251                   }
252                 }
253               }
254               if (anyRankMatch && !anyElemental) {
255                 return parser::Message{at,
256                     "Variable '%s' has a vector subscript and cannot be finalized by non-elemental subroutine '%s'"_because_en_US,
257                     expr.AsFortran(), anyRankMatch->name()};
258               }
259               const auto *parent{FindParentTypeSpec(*spec)};
260               spec = parent ? parent->AsDerived() : nullptr;
261             }
262           }
263         }
264       } else {
265         return parser::Message{at,
266             "Variable '%s' has a vector subscript"_because_en_US,
267             expr.AsFortran()};
268       }
269     }
270     if (FindPureProcedureContaining(scope) &&
271         evaluate::ExtractCoarrayRef(expr)) {
272       return parser::Message(at,
273           "A pure subprogram may not define the coindexed object '%s'"_because_en_US,
274           expr.AsFortran());
275     }
276     return WhyNotDefinable(at, scope, flags, *dataRef);
277   } else if (evaluate::IsNullPointer(expr)) {
278     return parser::Message{
279         at, "'%s' is a null pointer"_because_en_US, expr.AsFortran()};
280   } else if (flags.test(DefinabilityFlag::PointerDefinition)) {
281     if (const auto *procDesignator{
282             std::get_if<evaluate::ProcedureDesignator>(&expr.u)}) {
283       // Defining a procedure pointer
284       if (const Symbol * procSym{procDesignator->GetSymbol()}) {
285         if (evaluate::ExtractCoarrayRef(expr)) { // C1027
286           return BlameSymbol(at,
287               "Procedure pointer '%s' may not be a coindexed object"_because_en_US,
288               *procSym, expr.AsFortran());
289         }
290         if (const auto *component{procDesignator->GetComponent()}) {
291           return WhyNotDefinable(at, scope, flags, *component);
292         } else {
293           return WhyNotDefinable(at, scope, flags, *procSym);
294         }
295       }
296     }
297     return parser::Message{
298         at, "'%s' is not a definable pointer"_because_en_US, expr.AsFortran()};
299   } else if (!evaluate::IsVariable(expr)) {
300     return parser::Message{at,
301         "'%s' is not a variable or pointer"_because_en_US, expr.AsFortran()};
302   } else {
303     return std::nullopt;
304   }
305 }
306 
307 } // namespace Fortran::semantics
308