1 //===-- lib/Semantics/definable.cpp ---------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
10 #include "flang/Evaluate/tools.h"
11 #include "flang/Semantics/tools.h"
13 using namespace Fortran::parser::literals
;
15 namespace Fortran::semantics
{
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
);
26 static bool IsPointerDummyOfPureFunction(const Symbol
&x
) {
27 return IsPointerDummy(x
) && FindPureProcedureContaining(x
.owner()) &&
28 x
.owner().symbol() && IsFunction(*x
.owner().symbol());
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";
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
) {
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
);
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);
83 if (const Symbol
* lastPointer
{GetLastPointerSymbol(dataRef
)}) {
86 return dataRef
.GetFirstSymbol();
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
));
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
)) {
119 at
, "'%s' is an INTENT(IN) dummy argument"_en_US
, original
);
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
)}) {
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());
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
)) {
147 at
, "'%s' is neither a pointer nor an allocatable"_en_US
, original
);
149 } else if (!IsPointer(ultimate
)) {
150 return BlameSymbol(at
, "'%s' is not a pointer"_en_US
, original
);
152 return std::nullopt
; // pointer assignment - skip following checks
154 if (IsOrContainsEventOrLockComponent(ultimate
)) {
155 return BlameSymbol(at
,
156 "'%s' is an entity with either an EVENT_TYPE or LOCK_TYPE"_en_US
,
159 if (!flags
.test(DefinabilityFlag::PolymorphicOkInPure
) &&
160 FindPureProcedureContaining(scope
)) {
161 if (auto dyType
{evaluate::DynamicType::From(ultimate
)}) {
162 if (dyType
->IsPolymorphic()) { // C1596
163 return BlameSymbol(at
,
164 "'%s' is polymorphic in a pure subprogram"_because_en_US
, original
);
166 if (const DerivedTypeSpec
* derived
{GetDerivedTypeSpec(dyType
)}) {
167 if (auto bad
{FindPolymorphicAllocatableNonCoarrayUltimateComponent(
169 return BlameSymbol(at
,
170 "'%s' has polymorphic non-coarray component '%s' in a pure subprogram"_because_en_US
,
171 original
, bad
.BuildResultDesignatorName());
180 static std::optional
<parser::Message
> WhyNotDefinable(parser::CharBlock at
,
181 const Scope
&scope
, DefinabilityFlags flags
,
182 const evaluate::DataRef
&dataRef
) {
183 const Symbol
&base
{GetRelevantSymbol(dataRef
,
184 flags
.test(DefinabilityFlag::PointerDefinition
),
185 flags
.test(DefinabilityFlag::AcceptAllocatable
))};
186 if (auto whyNot
{WhyNotDefinableBase(at
, scope
, flags
, base
)}) {
189 return WhyNotDefinableLast(at
, scope
, flags
, dataRef
.GetLastSymbol());
193 // Checks a NOPASS procedure pointer component
194 static std::optional
<parser::Message
> WhyNotDefinable(parser::CharBlock at
,
195 const Scope
&scope
, DefinabilityFlags flags
,
196 const evaluate::Component
&component
) {
197 const evaluate::DataRef
&dataRef
{component
.base()};
198 const Symbol
&base
{GetRelevantSymbol(dataRef
, false, false)};
199 DefinabilityFlags baseFlags
{flags
};
200 baseFlags
.reset(DefinabilityFlag::PointerDefinition
);
201 return WhyNotDefinableBase(at
, scope
, baseFlags
, base
);
204 std::optional
<parser::Message
> WhyNotDefinable(parser::CharBlock at
,
205 const Scope
&scope
, DefinabilityFlags flags
, const Symbol
&original
) {
206 if (auto base
{WhyNotDefinableBase(at
, scope
, flags
, original
)}) {
209 return WhyNotDefinableLast(at
, scope
, flags
, original
);
212 std::optional
<parser::Message
> WhyNotDefinable(parser::CharBlock at
,
213 const Scope
&scope
, DefinabilityFlags flags
,
214 const evaluate::Expr
<evaluate::SomeType
> &expr
) {
215 if (auto dataRef
{evaluate::ExtractDataRef(expr
, true, true)}) {
216 if (evaluate::HasVectorSubscript(expr
)) {
217 if (flags
.test(DefinabilityFlag::VectorSubscriptIsOk
)) {
218 if (auto type
{expr
.GetType()}) {
219 if (!type
->IsUnlimitedPolymorphic() &&
220 type
->category() == TypeCategory::Derived
) {
221 // Seek the FINAL subroutine that should but cannot be called
222 // for this definition of an array with a vector-valued subscript.
223 // If there's an elemental FINAL subroutine, all is well; otherwise,
224 // if there is a FINAL subroutine with a matching or assumed rank
225 // dummy argument, there's no way to call it.
226 int rank
{expr
.Rank()};
227 const DerivedTypeSpec
*spec
{&type
->GetDerivedTypeSpec()};
229 bool anyElemental
{false};
230 const Symbol
*anyRankMatch
{nullptr};
231 for (auto ref
: FinalsForDerivedTypeInstantiation(*spec
)) {
232 const Symbol
&ultimate
{ref
->GetUltimate()};
233 anyElemental
|= ultimate
.attrs().test(Attr::ELEMENTAL
);
234 if (const auto *subp
{ultimate
.detailsIf
<SubprogramDetails
>()}) {
235 if (!subp
->dummyArgs().empty()) {
236 if (const Symbol
* arg
{subp
->dummyArgs()[0]}) {
237 const auto *object
{arg
->detailsIf
<ObjectEntityDetails
>()};
238 if (arg
->Rank() == rank
||
239 (object
&& object
->IsAssumedRank())) {
240 anyRankMatch
= &*ref
;
246 if (anyRankMatch
&& !anyElemental
) {
247 return parser::Message
{at
,
248 "Variable '%s' has a vector subscript and cannot be finalized by non-elemental subroutine '%s'"_because_en_US
,
249 expr
.AsFortran(), anyRankMatch
->name()};
251 const auto *parent
{FindParentTypeSpec(*spec
)};
252 spec
= parent
? parent
->AsDerived() : nullptr;
257 return parser::Message
{at
,
258 "Variable '%s' has a vector subscript"_because_en_US
,
262 if (FindPureProcedureContaining(scope
) &&
263 evaluate::ExtractCoarrayRef(expr
)) {
264 return parser::Message(at
,
265 "A pure subprogram may not define the coindexed object '%s'"_because_en_US
,
268 return WhyNotDefinable(at
, scope
, flags
, *dataRef
);
269 } else if (evaluate::IsNullPointer(expr
)) {
270 return parser::Message
{
271 at
, "'%s' is a null pointer"_because_en_US
, expr
.AsFortran()};
272 } else if (flags
.test(DefinabilityFlag::PointerDefinition
)) {
273 if (const auto *procDesignator
{
274 std::get_if
<evaluate::ProcedureDesignator
>(&expr
.u
)}) {
275 // Defining a procedure pointer
276 if (const Symbol
* procSym
{procDesignator
->GetSymbol()}) {
277 if (evaluate::ExtractCoarrayRef(expr
)) { // C1027
278 return BlameSymbol(at
,
279 "Procedure pointer '%s' may not be a coindexed object"_because_en_US
,
280 *procSym
, expr
.AsFortran());
282 if (const auto *component
{procDesignator
->GetComponent()}) {
283 return WhyNotDefinable(at
, scope
, flags
, *component
);
285 return WhyNotDefinable(at
, scope
, flags
, *procSym
);
289 return parser::Message
{
290 at
, "'%s' is not a definable pointer"_because_en_US
, expr
.AsFortran()};
291 } else if (!evaluate::IsVariable(expr
)) {
292 return parser::Message
{at
,
293 "'%s' is not a variable or pointer"_because_en_US
, expr
.AsFortran()};
299 } // namespace Fortran::semantics