1 //===-- lib/Evaluate/tools.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 "flang/Evaluate/tools.h"
10 #include "flang/Common/idioms.h"
11 #include "flang/Evaluate/characteristics.h"
12 #include "flang/Evaluate/traverse.h"
13 #include "flang/Parser/message.h"
14 #include "flang/Semantics/tools.h"
15 #include <algorithm>
16 #include <variant>
17
18 using namespace Fortran::parser::literals;
19
20 namespace Fortran::evaluate {
21
22 // Can x*(a,b) be represented as (x*a,x*b)? This code duplication
23 // of the subexpression "x" cannot (yet?) be reliably undone by
24 // common subexpression elimination in lowering, so it's disabled
25 // here for now to avoid the risk of potential duplication of
26 // expensive subexpressions (e.g., large array expressions, references
27 // to expensive functions) in generate code.
28 static constexpr bool allowOperandDuplication{false};
29
AsGenericExpr(DataRef && ref)30 std::optional<Expr<SomeType>> AsGenericExpr(DataRef &&ref) {
31 const Symbol &symbol{ref.GetLastSymbol()};
32 if (auto dyType{DynamicType::From(symbol)}) {
33 return TypedWrapper<Designator, DataRef>(*dyType, std::move(ref));
34 }
35 return std::nullopt;
36 }
37
AsGenericExpr(const Symbol & symbol)38 std::optional<Expr<SomeType>> AsGenericExpr(const Symbol &symbol) {
39 return AsGenericExpr(DataRef{symbol});
40 }
41
Parenthesize(Expr<SomeType> && expr)42 Expr<SomeType> Parenthesize(Expr<SomeType> &&expr) {
43 return common::visit(
44 [&](auto &&x) {
45 using T = std::decay_t<decltype(x)>;
46 if constexpr (common::HasMember<T, TypelessExpression>) {
47 return expr; // no parentheses around typeless
48 } else if constexpr (std::is_same_v<T, Expr<SomeDerived>>) {
49 return AsGenericExpr(Parentheses<SomeDerived>{std::move(x)});
50 } else {
51 return common::visit(
52 [](auto &&y) {
53 using T = ResultType<decltype(y)>;
54 return AsGenericExpr(Parentheses<T>{std::move(y)});
55 },
56 std::move(x.u));
57 }
58 },
59 std::move(expr.u));
60 }
61
ExtractDataRef(const ActualArgument & arg,bool intoSubstring)62 std::optional<DataRef> ExtractDataRef(
63 const ActualArgument &arg, bool intoSubstring) {
64 if (const Expr<SomeType> *expr{arg.UnwrapExpr()}) {
65 return ExtractDataRef(*expr, intoSubstring);
66 } else {
67 return std::nullopt;
68 }
69 }
70
ExtractSubstringBase(const Substring & substring)71 std::optional<DataRef> ExtractSubstringBase(const Substring &substring) {
72 return common::visit(
73 common::visitors{
74 [&](const DataRef &x) -> std::optional<DataRef> { return x; },
75 [&](const StaticDataObject::Pointer &) -> std::optional<DataRef> {
76 return std::nullopt;
77 },
78 },
79 substring.parent());
80 }
81
82 // IsVariable()
83
operator ()(const Symbol & symbol) const84 auto IsVariableHelper::operator()(const Symbol &symbol) const -> Result {
85 const Symbol &root{GetAssociationRoot(symbol)};
86 return !IsNamedConstant(root) && root.has<semantics::ObjectEntityDetails>();
87 }
operator ()(const Component & x) const88 auto IsVariableHelper::operator()(const Component &x) const -> Result {
89 const Symbol &comp{x.GetLastSymbol()};
90 return (*this)(comp) && (IsPointer(comp) || (*this)(x.base()));
91 }
operator ()(const ArrayRef & x) const92 auto IsVariableHelper::operator()(const ArrayRef &x) const -> Result {
93 return (*this)(x.base());
94 }
operator ()(const Substring & x) const95 auto IsVariableHelper::operator()(const Substring &x) const -> Result {
96 return (*this)(x.GetBaseObject());
97 }
operator ()(const ProcedureDesignator & x) const98 auto IsVariableHelper::operator()(const ProcedureDesignator &x) const
99 -> Result {
100 if (const Symbol * symbol{x.GetSymbol()}) {
101 const Symbol *result{FindFunctionResult(*symbol)};
102 return result && IsPointer(*result) && !IsProcedurePointer(*result);
103 }
104 return false;
105 }
106
107 // Conversions of COMPLEX component expressions to REAL.
ConvertRealOperands(parser::ContextualMessages & messages,Expr<SomeType> && x,Expr<SomeType> && y,int defaultRealKind)108 ConvertRealOperandsResult ConvertRealOperands(
109 parser::ContextualMessages &messages, Expr<SomeType> &&x,
110 Expr<SomeType> &&y, int defaultRealKind) {
111 return common::visit(
112 common::visitors{
113 [&](Expr<SomeInteger> &&ix,
114 Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
115 // Can happen in a CMPLX() constructor. Per F'2018,
116 // both integer operands are converted to default REAL.
117 return {AsSameKindExprs<TypeCategory::Real>(
118 ConvertToKind<TypeCategory::Real>(
119 defaultRealKind, std::move(ix)),
120 ConvertToKind<TypeCategory::Real>(
121 defaultRealKind, std::move(iy)))};
122 },
123 [&](Expr<SomeInteger> &&ix,
124 Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
125 return {AsSameKindExprs<TypeCategory::Real>(
126 ConvertTo(ry, std::move(ix)), std::move(ry))};
127 },
128 [&](Expr<SomeReal> &&rx,
129 Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
130 return {AsSameKindExprs<TypeCategory::Real>(
131 std::move(rx), ConvertTo(rx, std::move(iy)))};
132 },
133 [&](Expr<SomeReal> &&rx,
134 Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
135 return {AsSameKindExprs<TypeCategory::Real>(
136 std::move(rx), std::move(ry))};
137 },
138 [&](Expr<SomeInteger> &&ix,
139 BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
140 return {AsSameKindExprs<TypeCategory::Real>(
141 ConvertToKind<TypeCategory::Real>(
142 defaultRealKind, std::move(ix)),
143 ConvertToKind<TypeCategory::Real>(
144 defaultRealKind, std::move(by)))};
145 },
146 [&](BOZLiteralConstant &&bx,
147 Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
148 return {AsSameKindExprs<TypeCategory::Real>(
149 ConvertToKind<TypeCategory::Real>(
150 defaultRealKind, std::move(bx)),
151 ConvertToKind<TypeCategory::Real>(
152 defaultRealKind, std::move(iy)))};
153 },
154 [&](Expr<SomeReal> &&rx,
155 BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
156 return {AsSameKindExprs<TypeCategory::Real>(
157 std::move(rx), ConvertTo(rx, std::move(by)))};
158 },
159 [&](BOZLiteralConstant &&bx,
160 Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
161 return {AsSameKindExprs<TypeCategory::Real>(
162 ConvertTo(ry, std::move(bx)), std::move(ry))};
163 },
164 [&](auto &&, auto &&) -> ConvertRealOperandsResult { // C718
165 messages.Say("operands must be INTEGER or REAL"_err_en_US);
166 return std::nullopt;
167 },
168 },
169 std::move(x.u), std::move(y.u));
170 }
171
172 // Helpers for NumericOperation and its subroutines below.
NoExpr()173 static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; }
174
175 template <TypeCategory CAT>
Package(Expr<SomeKind<CAT>> && catExpr)176 std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
177 return {AsGenericExpr(std::move(catExpr))};
178 }
179 template <TypeCategory CAT>
Package(std::optional<Expr<SomeKind<CAT>>> && catExpr)180 std::optional<Expr<SomeType>> Package(
181 std::optional<Expr<SomeKind<CAT>>> &&catExpr) {
182 if (catExpr) {
183 return {AsGenericExpr(std::move(*catExpr))};
184 }
185 return NoExpr();
186 }
187
188 // Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that
189 // does not require conversion of the exponent expression.
190 template <template <typename> class OPR>
MixedRealLeft(Expr<SomeReal> && rx,Expr<SomeInteger> && iy)191 std::optional<Expr<SomeType>> MixedRealLeft(
192 Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
193 return Package(common::visit(
194 [&](auto &&rxk) -> Expr<SomeReal> {
195 using resultType = ResultType<decltype(rxk)>;
196 if constexpr (std::is_same_v<OPR<resultType>, Power<resultType>>) {
197 return AsCategoryExpr(
198 RealToIntPower<resultType>{std::move(rxk), std::move(iy)});
199 }
200 // G++ 8.1.0 emits bogus warnings about missing return statements if
201 // this statement is wrapped in an "else", as it should be.
202 return AsCategoryExpr(OPR<resultType>{
203 std::move(rxk), ConvertToType<resultType>(std::move(iy))});
204 },
205 std::move(rx.u)));
206 }
207
ConstructComplex(parser::ContextualMessages & messages,Expr<SomeType> && real,Expr<SomeType> && imaginary,int defaultRealKind)208 std::optional<Expr<SomeComplex>> ConstructComplex(
209 parser::ContextualMessages &messages, Expr<SomeType> &&real,
210 Expr<SomeType> &&imaginary, int defaultRealKind) {
211 if (auto converted{ConvertRealOperands(
212 messages, std::move(real), std::move(imaginary), defaultRealKind)}) {
213 return {common::visit(
214 [](auto &&pair) {
215 return MakeComplex(std::move(pair[0]), std::move(pair[1]));
216 },
217 std::move(*converted))};
218 }
219 return std::nullopt;
220 }
221
ConstructComplex(parser::ContextualMessages & messages,std::optional<Expr<SomeType>> && real,std::optional<Expr<SomeType>> && imaginary,int defaultRealKind)222 std::optional<Expr<SomeComplex>> ConstructComplex(
223 parser::ContextualMessages &messages, std::optional<Expr<SomeType>> &&real,
224 std::optional<Expr<SomeType>> &&imaginary, int defaultRealKind) {
225 if (auto parts{common::AllPresent(std::move(real), std::move(imaginary))}) {
226 return ConstructComplex(messages, std::get<0>(std::move(*parts)),
227 std::get<1>(std::move(*parts)), defaultRealKind);
228 }
229 return std::nullopt;
230 }
231
GetComplexPart(const Expr<SomeComplex> & z,bool isImaginary)232 Expr<SomeReal> GetComplexPart(const Expr<SomeComplex> &z, bool isImaginary) {
233 return common::visit(
234 [&](const auto &zk) {
235 static constexpr int kind{ResultType<decltype(zk)>::kind};
236 return AsCategoryExpr(ComplexComponent<kind>{isImaginary, zk});
237 },
238 z.u);
239 }
240
GetComplexPart(Expr<SomeComplex> && z,bool isImaginary)241 Expr<SomeReal> GetComplexPart(Expr<SomeComplex> &&z, bool isImaginary) {
242 return common::visit(
243 [&](auto &&zk) {
244 static constexpr int kind{ResultType<decltype(zk)>::kind};
245 return AsCategoryExpr(
246 ComplexComponent<kind>{isImaginary, std::move(zk)});
247 },
248 z.u);
249 }
250
251 // Convert REAL to COMPLEX of the same kind. Preserving the real operand kind
252 // and then applying complex operand promotion rules allows the result to have
253 // the highest precision of REAL and COMPLEX operands as required by Fortran
254 // 2018 10.9.1.3.
PromoteRealToComplex(Expr<SomeReal> && someX)255 Expr<SomeComplex> PromoteRealToComplex(Expr<SomeReal> &&someX) {
256 return common::visit(
257 [](auto &&x) {
258 using RT = ResultType<decltype(x)>;
259 return AsCategoryExpr(ComplexConstructor<RT::kind>{
260 std::move(x), AsExpr(Constant<RT>{Scalar<RT>{}})});
261 },
262 std::move(someX.u));
263 }
264
265 // Handle mixed COMPLEX+REAL (or INTEGER) operations in a better way
266 // than just converting the second operand to COMPLEX and performing the
267 // corresponding COMPLEX+COMPLEX operation.
268 template <template <typename> class OPR, TypeCategory RCAT>
MixedComplexLeft(parser::ContextualMessages & messages,Expr<SomeComplex> && zx,Expr<SomeKind<RCAT>> && iry,int defaultRealKind)269 std::optional<Expr<SomeType>> MixedComplexLeft(
270 parser::ContextualMessages &messages, Expr<SomeComplex> &&zx,
271 Expr<SomeKind<RCAT>> &&iry, [[maybe_unused]] int defaultRealKind) {
272 Expr<SomeReal> zr{GetComplexPart(zx, false)};
273 Expr<SomeReal> zi{GetComplexPart(zx, true)};
274 if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>> ||
275 std::is_same_v<OPR<LargestReal>, Subtract<LargestReal>>) {
276 // (a,b) + x -> (a+x, b)
277 // (a,b) - x -> (a-x, b)
278 if (std::optional<Expr<SomeType>> rr{
279 NumericOperation<OPR>(messages, AsGenericExpr(std::move(zr)),
280 AsGenericExpr(std::move(iry)), defaultRealKind)}) {
281 return Package(ConstructComplex(messages, std::move(*rr),
282 AsGenericExpr(std::move(zi)), defaultRealKind));
283 }
284 } else if constexpr (allowOperandDuplication &&
285 (std::is_same_v<OPR<LargestReal>, Multiply<LargestReal>> ||
286 std::is_same_v<OPR<LargestReal>, Divide<LargestReal>>)) {
287 // (a,b) * x -> (a*x, b*x)
288 // (a,b) / x -> (a/x, b/x)
289 auto copy{iry};
290 auto rr{NumericOperation<OPR>(messages, AsGenericExpr(std::move(zr)),
291 AsGenericExpr(std::move(iry)), defaultRealKind)};
292 auto ri{NumericOperation<OPR>(messages, AsGenericExpr(std::move(zi)),
293 AsGenericExpr(std::move(copy)), defaultRealKind)};
294 if (auto parts{common::AllPresent(std::move(rr), std::move(ri))}) {
295 return Package(ConstructComplex(messages, std::get<0>(std::move(*parts)),
296 std::get<1>(std::move(*parts)), defaultRealKind));
297 }
298 } else if constexpr (RCAT == TypeCategory::Integer &&
299 std::is_same_v<OPR<LargestReal>, Power<LargestReal>>) {
300 // COMPLEX**INTEGER is a special case that doesn't convert the exponent.
301 static_assert(RCAT == TypeCategory::Integer);
302 return Package(common::visit(
303 [&](auto &&zxk) {
304 using Ty = ResultType<decltype(zxk)>;
305 return AsCategoryExpr(
306 AsExpr(RealToIntPower<Ty>{std::move(zxk), std::move(iry)}));
307 },
308 std::move(zx.u)));
309 } else {
310 // (a,b) ** x -> (a,b) ** (x,0)
311 if constexpr (RCAT == TypeCategory::Integer) {
312 Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
313 return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
314 } else {
315 Expr<SomeComplex> zy{PromoteRealToComplex(std::move(iry))};
316 return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
317 }
318 }
319 return NoExpr();
320 }
321
322 // Mixed COMPLEX operations with the COMPLEX operand on the right.
323 // x + (a,b) -> (x+a, b)
324 // x - (a,b) -> (x-a, -b)
325 // x * (a,b) -> (x*a, x*b)
326 // x / (a,b) -> (x,0) / (a,b) (and **)
327 template <template <typename> class OPR, TypeCategory LCAT>
MixedComplexRight(parser::ContextualMessages & messages,Expr<SomeKind<LCAT>> && irx,Expr<SomeComplex> && zy,int defaultRealKind)328 std::optional<Expr<SomeType>> MixedComplexRight(
329 parser::ContextualMessages &messages, Expr<SomeKind<LCAT>> &&irx,
330 Expr<SomeComplex> &&zy, [[maybe_unused]] int defaultRealKind) {
331 if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>>) {
332 // x + (a,b) -> (a,b) + x -> (a+x, b)
333 return MixedComplexLeft<OPR, LCAT>(
334 messages, std::move(zy), std::move(irx), defaultRealKind);
335 } else if constexpr (allowOperandDuplication &&
336 std::is_same_v<OPR<LargestReal>, Multiply<LargestReal>>) {
337 // x * (a,b) -> (a,b) * x -> (a*x, b*x)
338 return MixedComplexLeft<OPR, LCAT>(
339 messages, std::move(zy), std::move(irx), defaultRealKind);
340 } else if constexpr (std::is_same_v<OPR<LargestReal>,
341 Subtract<LargestReal>>) {
342 // x - (a,b) -> (x-a, -b)
343 Expr<SomeReal> zr{GetComplexPart(zy, false)};
344 Expr<SomeReal> zi{GetComplexPart(zy, true)};
345 if (std::optional<Expr<SomeType>> rr{
346 NumericOperation<Subtract>(messages, AsGenericExpr(std::move(irx)),
347 AsGenericExpr(std::move(zr)), defaultRealKind)}) {
348 return Package(ConstructComplex(messages, std::move(*rr),
349 AsGenericExpr(-std::move(zi)), defaultRealKind));
350 }
351 } else {
352 // x / (a,b) -> (x,0) / (a,b)
353 if constexpr (LCAT == TypeCategory::Integer) {
354 Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
355 return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
356 } else {
357 Expr<SomeComplex> zx{PromoteRealToComplex(std::move(irx))};
358 return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
359 }
360 }
361 return NoExpr();
362 }
363
364 // N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
365 // the operands to a dyadic operation where one is permitted, it assumes the
366 // type and kind of the other operand.
367 template <template <typename> class OPR>
NumericOperation(parser::ContextualMessages & messages,Expr<SomeType> && x,Expr<SomeType> && y,int defaultRealKind)368 std::optional<Expr<SomeType>> NumericOperation(
369 parser::ContextualMessages &messages, Expr<SomeType> &&x,
370 Expr<SomeType> &&y, int defaultRealKind) {
371 return common::visit(
372 common::visitors{
373 [](Expr<SomeInteger> &&ix, Expr<SomeInteger> &&iy) {
374 return Package(PromoteAndCombine<OPR, TypeCategory::Integer>(
375 std::move(ix), std::move(iy)));
376 },
377 [](Expr<SomeReal> &&rx, Expr<SomeReal> &&ry) {
378 return Package(PromoteAndCombine<OPR, TypeCategory::Real>(
379 std::move(rx), std::move(ry)));
380 },
381 // Mixed REAL/INTEGER operations
382 [](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
383 return MixedRealLeft<OPR>(std::move(rx), std::move(iy));
384 },
385 [](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
386 return Package(common::visit(
387 [&](auto &&ryk) -> Expr<SomeReal> {
388 using resultType = ResultType<decltype(ryk)>;
389 return AsCategoryExpr(
390 OPR<resultType>{ConvertToType<resultType>(std::move(ix)),
391 std::move(ryk)});
392 },
393 std::move(ry.u)));
394 },
395 // Homogeneous and mixed COMPLEX operations
396 [](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
397 return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
398 std::move(zx), std::move(zy)));
399 },
400 [&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
401 return MixedComplexLeft<OPR>(
402 messages, std::move(zx), std::move(iy), defaultRealKind);
403 },
404 [&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
405 return MixedComplexLeft<OPR>(
406 messages, std::move(zx), std::move(ry), defaultRealKind);
407 },
408 [&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
409 return MixedComplexRight<OPR>(
410 messages, std::move(ix), std::move(zy), defaultRealKind);
411 },
412 [&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
413 return MixedComplexRight<OPR>(
414 messages, std::move(rx), std::move(zy), defaultRealKind);
415 },
416 // Operations with one typeless operand
417 [&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
418 return NumericOperation<OPR>(messages,
419 AsGenericExpr(ConvertTo(iy, std::move(bx))), std::move(y),
420 defaultRealKind);
421 },
422 [&](BOZLiteralConstant &&bx, Expr<SomeReal> &&ry) {
423 return NumericOperation<OPR>(messages,
424 AsGenericExpr(ConvertTo(ry, std::move(bx))), std::move(y),
425 defaultRealKind);
426 },
427 [&](Expr<SomeInteger> &&ix, BOZLiteralConstant &&by) {
428 return NumericOperation<OPR>(messages, std::move(x),
429 AsGenericExpr(ConvertTo(ix, std::move(by))), defaultRealKind);
430 },
431 [&](Expr<SomeReal> &&rx, BOZLiteralConstant &&by) {
432 return NumericOperation<OPR>(messages, std::move(x),
433 AsGenericExpr(ConvertTo(rx, std::move(by))), defaultRealKind);
434 },
435 // Default case
436 [&](auto &&, auto &&) {
437 // TODO: defined operator
438 messages.Say("non-numeric operands to numeric operation"_err_en_US);
439 return NoExpr();
440 },
441 },
442 std::move(x.u), std::move(y.u));
443 }
444
445 template std::optional<Expr<SomeType>> NumericOperation<Power>(
446 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
447 int defaultRealKind);
448 template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
449 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
450 int defaultRealKind);
451 template std::optional<Expr<SomeType>> NumericOperation<Divide>(
452 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
453 int defaultRealKind);
454 template std::optional<Expr<SomeType>> NumericOperation<Add>(
455 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
456 int defaultRealKind);
457 template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
458 parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
459 int defaultRealKind);
460
Negation(parser::ContextualMessages & messages,Expr<SomeType> && x)461 std::optional<Expr<SomeType>> Negation(
462 parser::ContextualMessages &messages, Expr<SomeType> &&x) {
463 return common::visit(
464 common::visitors{
465 [&](BOZLiteralConstant &&) {
466 messages.Say("BOZ literal cannot be negated"_err_en_US);
467 return NoExpr();
468 },
469 [&](NullPointer &&) {
470 messages.Say("NULL() cannot be negated"_err_en_US);
471 return NoExpr();
472 },
473 [&](ProcedureDesignator &&) {
474 messages.Say("Subroutine cannot be negated"_err_en_US);
475 return NoExpr();
476 },
477 [&](ProcedureRef &&) {
478 messages.Say("Pointer to subroutine cannot be negated"_err_en_US);
479 return NoExpr();
480 },
481 [&](Expr<SomeInteger> &&x) { return Package(-std::move(x)); },
482 [&](Expr<SomeReal> &&x) { return Package(-std::move(x)); },
483 [&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); },
484 [&](Expr<SomeCharacter> &&) {
485 // TODO: defined operator
486 messages.Say("CHARACTER cannot be negated"_err_en_US);
487 return NoExpr();
488 },
489 [&](Expr<SomeLogical> &&) {
490 // TODO: defined operator
491 messages.Say("LOGICAL cannot be negated"_err_en_US);
492 return NoExpr();
493 },
494 [&](Expr<SomeDerived> &&) {
495 // TODO: defined operator
496 messages.Say("Operand cannot be negated"_err_en_US);
497 return NoExpr();
498 },
499 },
500 std::move(x.u));
501 }
502
LogicalNegation(Expr<SomeLogical> && x)503 Expr<SomeLogical> LogicalNegation(Expr<SomeLogical> &&x) {
504 return common::visit(
505 [](auto &&xk) { return AsCategoryExpr(LogicalNegation(std::move(xk))); },
506 std::move(x.u));
507 }
508
509 template <TypeCategory CAT>
PromoteAndRelate(RelationalOperator opr,Expr<SomeKind<CAT>> && x,Expr<SomeKind<CAT>> && y)510 Expr<LogicalResult> PromoteAndRelate(
511 RelationalOperator opr, Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
512 return common::visit(
513 [=](auto &&xy) {
514 return PackageRelation(opr, std::move(xy[0]), std::move(xy[1]));
515 },
516 AsSameKindExprs(std::move(x), std::move(y)));
517 }
518
Relate(parser::ContextualMessages & messages,RelationalOperator opr,Expr<SomeType> && x,Expr<SomeType> && y)519 std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
520 RelationalOperator opr, Expr<SomeType> &&x, Expr<SomeType> &&y) {
521 return common::visit(
522 common::visitors{
523 [=](Expr<SomeInteger> &&ix,
524 Expr<SomeInteger> &&iy) -> std::optional<Expr<LogicalResult>> {
525 return PromoteAndRelate(opr, std::move(ix), std::move(iy));
526 },
527 [=](Expr<SomeReal> &&rx,
528 Expr<SomeReal> &&ry) -> std::optional<Expr<LogicalResult>> {
529 return PromoteAndRelate(opr, std::move(rx), std::move(ry));
530 },
531 [&](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
532 return Relate(messages, opr, std::move(x),
533 AsGenericExpr(ConvertTo(rx, std::move(iy))));
534 },
535 [&](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
536 return Relate(messages, opr,
537 AsGenericExpr(ConvertTo(ry, std::move(ix))), std::move(y));
538 },
539 [&](Expr<SomeComplex> &&zx,
540 Expr<SomeComplex> &&zy) -> std::optional<Expr<LogicalResult>> {
541 if (opr == RelationalOperator::EQ ||
542 opr == RelationalOperator::NE) {
543 return PromoteAndRelate(opr, std::move(zx), std::move(zy));
544 } else {
545 messages.Say(
546 "COMPLEX data may be compared only for equality"_err_en_US);
547 return std::nullopt;
548 }
549 },
550 [&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
551 return Relate(messages, opr, std::move(x),
552 AsGenericExpr(ConvertTo(zx, std::move(iy))));
553 },
554 [&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
555 return Relate(messages, opr, std::move(x),
556 AsGenericExpr(ConvertTo(zx, std::move(ry))));
557 },
558 [&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
559 return Relate(messages, opr,
560 AsGenericExpr(ConvertTo(zy, std::move(ix))), std::move(y));
561 },
562 [&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
563 return Relate(messages, opr,
564 AsGenericExpr(ConvertTo(zy, std::move(rx))), std::move(y));
565 },
566 [&](Expr<SomeCharacter> &&cx, Expr<SomeCharacter> &&cy) {
567 return common::visit(
568 [&](auto &&cxk,
569 auto &&cyk) -> std::optional<Expr<LogicalResult>> {
570 using Ty = ResultType<decltype(cxk)>;
571 if constexpr (std::is_same_v<Ty, ResultType<decltype(cyk)>>) {
572 return PackageRelation(opr, std::move(cxk), std::move(cyk));
573 } else {
574 messages.Say(
575 "CHARACTER operands do not have same KIND"_err_en_US);
576 return std::nullopt;
577 }
578 },
579 std::move(cx.u), std::move(cy.u));
580 },
581 // Default case
582 [&](auto &&, auto &&) {
583 DIE("invalid types for relational operator");
584 return std::optional<Expr<LogicalResult>>{};
585 },
586 },
587 std::move(x.u), std::move(y.u));
588 }
589
BinaryLogicalOperation(LogicalOperator opr,Expr<SomeLogical> && x,Expr<SomeLogical> && y)590 Expr<SomeLogical> BinaryLogicalOperation(
591 LogicalOperator opr, Expr<SomeLogical> &&x, Expr<SomeLogical> &&y) {
592 CHECK(opr != LogicalOperator::Not);
593 return common::visit(
594 [=](auto &&xy) {
595 using Ty = ResultType<decltype(xy[0])>;
596 return Expr<SomeLogical>{BinaryLogicalOperation<Ty::kind>(
597 opr, std::move(xy[0]), std::move(xy[1]))};
598 },
599 AsSameKindExprs(std::move(x), std::move(y)));
600 }
601
602 template <TypeCategory TO>
ConvertToNumeric(int kind,Expr<SomeType> && x)603 std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) {
604 static_assert(common::IsNumericTypeCategory(TO));
605 return common::visit(
606 [=](auto &&cx) -> std::optional<Expr<SomeType>> {
607 using cxType = std::decay_t<decltype(cx)>;
608 if constexpr (!common::HasMember<cxType, TypelessExpression>) {
609 if constexpr (IsNumericTypeCategory(ResultType<cxType>::category)) {
610 return Expr<SomeType>{ConvertToKind<TO>(kind, std::move(cx))};
611 }
612 }
613 return std::nullopt;
614 },
615 std::move(x.u));
616 }
617
ConvertToType(const DynamicType & type,Expr<SomeType> && x)618 std::optional<Expr<SomeType>> ConvertToType(
619 const DynamicType &type, Expr<SomeType> &&x) {
620 if (type.IsTypelessIntrinsicArgument()) {
621 return std::nullopt;
622 }
623 switch (type.category()) {
624 case TypeCategory::Integer:
625 if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
626 // Extension to C7109: allow BOZ literals to appear in integer contexts
627 // when the type is unambiguous.
628 return Expr<SomeType>{
629 ConvertToKind<TypeCategory::Integer>(type.kind(), std::move(*boz))};
630 }
631 return ConvertToNumeric<TypeCategory::Integer>(type.kind(), std::move(x));
632 case TypeCategory::Real:
633 if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
634 return Expr<SomeType>{
635 ConvertToKind<TypeCategory::Real>(type.kind(), std::move(*boz))};
636 }
637 return ConvertToNumeric<TypeCategory::Real>(type.kind(), std::move(x));
638 case TypeCategory::Complex:
639 return ConvertToNumeric<TypeCategory::Complex>(type.kind(), std::move(x));
640 case TypeCategory::Character:
641 if (auto *cx{UnwrapExpr<Expr<SomeCharacter>>(x)}) {
642 auto converted{
643 ConvertToKind<TypeCategory::Character>(type.kind(), std::move(*cx))};
644 if (auto length{type.GetCharLength()}) {
645 converted = common::visit(
646 [&](auto &&x) {
647 using Ty = std::decay_t<decltype(x)>;
648 using CharacterType = typename Ty::Result;
649 return Expr<SomeCharacter>{
650 Expr<CharacterType>{SetLength<CharacterType::kind>{
651 std::move(x), std::move(*length)}}};
652 },
653 std::move(converted.u));
654 }
655 return Expr<SomeType>{std::move(converted)};
656 }
657 break;
658 case TypeCategory::Logical:
659 if (auto *cx{UnwrapExpr<Expr<SomeLogical>>(x)}) {
660 return Expr<SomeType>{
661 ConvertToKind<TypeCategory::Logical>(type.kind(), std::move(*cx))};
662 }
663 break;
664 case TypeCategory::Derived:
665 if (auto fromType{x.GetType()}) {
666 if (type.IsTkCompatibleWith(*fromType)) {
667 // "x" could be assigned or passed to "type", or appear in a
668 // structure constructor as a value for a component with "type"
669 return std::move(x);
670 }
671 }
672 break;
673 }
674 return std::nullopt;
675 }
676
ConvertToType(const DynamicType & to,std::optional<Expr<SomeType>> && x)677 std::optional<Expr<SomeType>> ConvertToType(
678 const DynamicType &to, std::optional<Expr<SomeType>> &&x) {
679 if (x) {
680 return ConvertToType(to, std::move(*x));
681 } else {
682 return std::nullopt;
683 }
684 }
685
ConvertToType(const Symbol & symbol,Expr<SomeType> && x)686 std::optional<Expr<SomeType>> ConvertToType(
687 const Symbol &symbol, Expr<SomeType> &&x) {
688 if (auto symType{DynamicType::From(symbol)}) {
689 return ConvertToType(*symType, std::move(x));
690 }
691 return std::nullopt;
692 }
693
ConvertToType(const Symbol & to,std::optional<Expr<SomeType>> && x)694 std::optional<Expr<SomeType>> ConvertToType(
695 const Symbol &to, std::optional<Expr<SomeType>> &&x) {
696 if (x) {
697 return ConvertToType(to, std::move(*x));
698 } else {
699 return std::nullopt;
700 }
701 }
702
IsAssumedRank(const Symbol & original)703 bool IsAssumedRank(const Symbol &original) {
704 if (const auto *assoc{original.detailsIf<semantics::AssocEntityDetails>()}) {
705 if (assoc->rank()) {
706 return false; // in SELECT RANK case
707 }
708 }
709 const Symbol &symbol{semantics::ResolveAssociations(original)};
710 if (const auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
711 return details->IsAssumedRank();
712 } else {
713 return false;
714 }
715 }
716
IsAssumedRank(const ActualArgument & arg)717 bool IsAssumedRank(const ActualArgument &arg) {
718 if (const auto *expr{arg.UnwrapExpr()}) {
719 return IsAssumedRank(*expr);
720 } else {
721 const Symbol *assumedTypeDummy{arg.GetAssumedTypeDummy()};
722 CHECK(assumedTypeDummy);
723 return IsAssumedRank(*assumedTypeDummy);
724 }
725 }
726
IsCoarray(const ActualArgument & arg)727 bool IsCoarray(const ActualArgument &arg) {
728 const auto *expr{arg.UnwrapExpr()};
729 return expr && IsCoarray(*expr);
730 }
731
IsCoarray(const Symbol & symbol)732 bool IsCoarray(const Symbol &symbol) {
733 return GetAssociationRoot(symbol).Corank() > 0;
734 }
735
IsProcedure(const Expr<SomeType> & expr)736 bool IsProcedure(const Expr<SomeType> &expr) {
737 return std::holds_alternative<ProcedureDesignator>(expr.u);
738 }
IsFunction(const Expr<SomeType> & expr)739 bool IsFunction(const Expr<SomeType> &expr) {
740 const auto *designator{std::get_if<ProcedureDesignator>(&expr.u)};
741 return designator && designator->GetType().has_value();
742 }
743
IsProcedurePointerTarget(const Expr<SomeType> & expr)744 bool IsProcedurePointerTarget(const Expr<SomeType> &expr) {
745 return common::visit(common::visitors{
746 [](const NullPointer &) { return true; },
747 [](const ProcedureDesignator &) { return true; },
748 [](const ProcedureRef &) { return true; },
749 [&](const auto &) {
750 const Symbol *last{GetLastSymbol(expr)};
751 return last && IsProcedurePointer(*last);
752 },
753 },
754 expr.u);
755 }
756
UnwrapProcedureRef(const A &)757 template <typename A> inline const ProcedureRef *UnwrapProcedureRef(const A &) {
758 return nullptr;
759 }
760
761 template <typename T>
UnwrapProcedureRef(const FunctionRef<T> & func)762 inline const ProcedureRef *UnwrapProcedureRef(const FunctionRef<T> &func) {
763 return &func;
764 }
765
766 template <typename T>
UnwrapProcedureRef(const Expr<T> & expr)767 inline const ProcedureRef *UnwrapProcedureRef(const Expr<T> &expr) {
768 return common::visit(
769 [](const auto &x) { return UnwrapProcedureRef(x); }, expr.u);
770 }
771
772 // IsObjectPointer()
IsObjectPointer(const Expr<SomeType> & expr,FoldingContext & context)773 bool IsObjectPointer(const Expr<SomeType> &expr, FoldingContext &context) {
774 if (IsNullPointer(expr)) {
775 return true;
776 } else if (IsProcedurePointerTarget(expr)) {
777 return false;
778 } else if (const auto *funcRef{UnwrapProcedureRef(expr)}) {
779 return IsVariable(*funcRef);
780 } else if (const Symbol * symbol{UnwrapWholeSymbolOrComponentDataRef(expr)}) {
781 return IsPointer(symbol->GetUltimate());
782 } else {
783 return false;
784 }
785 }
786
IsBareNullPointer(const Expr<SomeType> * expr)787 bool IsBareNullPointer(const Expr<SomeType> *expr) {
788 return expr && std::holds_alternative<NullPointer>(expr->u);
789 }
790
791 // IsNullPointer()
792 struct IsNullPointerHelper {
operator ()Fortran::evaluate::IsNullPointerHelper793 template <typename A> bool operator()(const A &) const { return false; }
operator ()Fortran::evaluate::IsNullPointerHelper794 template <typename T> bool operator()(const FunctionRef<T> &call) const {
795 const auto *intrinsic{call.proc().GetSpecificIntrinsic()};
796 return intrinsic &&
797 intrinsic->characteristics.value().attrs.test(
798 characteristics::Procedure::Attr::NullPointer);
799 }
operator ()Fortran::evaluate::IsNullPointerHelper800 bool operator()(const NullPointer &) const { return true; }
operator ()Fortran::evaluate::IsNullPointerHelper801 template <typename T> bool operator()(const Parentheses<T> &x) const {
802 return (*this)(x.left());
803 }
operator ()Fortran::evaluate::IsNullPointerHelper804 template <typename T> bool operator()(const Expr<T> &x) const {
805 return common::visit(*this, x.u);
806 }
807 };
808
IsNullPointer(const Expr<SomeType> & expr)809 bool IsNullPointer(const Expr<SomeType> &expr) {
810 return IsNullPointerHelper{}(expr);
811 }
812
813 // GetSymbolVector()
operator ()(const Symbol & x) const814 auto GetSymbolVectorHelper::operator()(const Symbol &x) const -> Result {
815 if (const auto *details{x.detailsIf<semantics::AssocEntityDetails>()}) {
816 return (*this)(details->expr());
817 } else {
818 return {x.GetUltimate()};
819 }
820 }
operator ()(const Component & x) const821 auto GetSymbolVectorHelper::operator()(const Component &x) const -> Result {
822 Result result{(*this)(x.base())};
823 result.emplace_back(x.GetLastSymbol());
824 return result;
825 }
operator ()(const ArrayRef & x) const826 auto GetSymbolVectorHelper::operator()(const ArrayRef &x) const -> Result {
827 return GetSymbolVector(x.base());
828 }
operator ()(const CoarrayRef & x) const829 auto GetSymbolVectorHelper::operator()(const CoarrayRef &x) const -> Result {
830 return x.base();
831 }
832
GetLastTarget(const SymbolVector & symbols)833 const Symbol *GetLastTarget(const SymbolVector &symbols) {
834 auto end{std::crend(symbols)};
835 // N.B. Neither clang nor g++ recognizes "symbols.crbegin()" here.
836 auto iter{std::find_if(std::crbegin(symbols), end, [](const Symbol &x) {
837 return x.attrs().HasAny(
838 {semantics::Attr::POINTER, semantics::Attr::TARGET});
839 })};
840 return iter == end ? nullptr : &**iter;
841 }
842
843 struct CollectSymbolsHelper
844 : public SetTraverse<CollectSymbolsHelper, semantics::UnorderedSymbolSet> {
845 using Base = SetTraverse<CollectSymbolsHelper, semantics::UnorderedSymbolSet>;
CollectSymbolsHelperFortran::evaluate::CollectSymbolsHelper846 CollectSymbolsHelper() : Base{*this} {}
847 using Base::operator();
operator ()Fortran::evaluate::CollectSymbolsHelper848 semantics::UnorderedSymbolSet operator()(const Symbol &symbol) const {
849 return {symbol};
850 }
851 };
CollectSymbols(const A & x)852 template <typename A> semantics::UnorderedSymbolSet CollectSymbols(const A &x) {
853 return CollectSymbolsHelper{}(x);
854 }
855 template semantics::UnorderedSymbolSet CollectSymbols(const Expr<SomeType> &);
856 template semantics::UnorderedSymbolSet CollectSymbols(
857 const Expr<SomeInteger> &);
858 template semantics::UnorderedSymbolSet CollectSymbols(
859 const Expr<SubscriptInteger> &);
860
861 // HasVectorSubscript()
862 struct HasVectorSubscriptHelper : public AnyTraverse<HasVectorSubscriptHelper> {
863 using Base = AnyTraverse<HasVectorSubscriptHelper>;
HasVectorSubscriptHelperFortran::evaluate::HasVectorSubscriptHelper864 HasVectorSubscriptHelper() : Base{*this} {}
865 using Base::operator();
operator ()Fortran::evaluate::HasVectorSubscriptHelper866 bool operator()(const Subscript &ss) const {
867 return !std::holds_alternative<Triplet>(ss.u) && ss.Rank() > 0;
868 }
operator ()Fortran::evaluate::HasVectorSubscriptHelper869 bool operator()(const ProcedureRef &) const {
870 return false; // don't descend into function call arguments
871 }
872 };
873
HasVectorSubscript(const Expr<SomeType> & expr)874 bool HasVectorSubscript(const Expr<SomeType> &expr) {
875 return HasVectorSubscriptHelper{}(expr);
876 }
877
AttachDeclaration(parser::Message & message,const Symbol & symbol)878 parser::Message *AttachDeclaration(
879 parser::Message &message, const Symbol &symbol) {
880 const Symbol *unhosted{&symbol};
881 while (
882 const auto *assoc{unhosted->detailsIf<semantics::HostAssocDetails>()}) {
883 unhosted = &assoc->symbol();
884 }
885 if (const auto *binding{
886 unhosted->detailsIf<semantics::ProcBindingDetails>()}) {
887 if (binding->symbol().name() != symbol.name()) {
888 message.Attach(binding->symbol().name(),
889 "Procedure '%s' of type '%s' is bound to '%s'"_en_US, symbol.name(),
890 symbol.owner().GetName().value(), binding->symbol().name());
891 return &message;
892 }
893 unhosted = &binding->symbol();
894 }
895 if (const auto *use{symbol.detailsIf<semantics::UseDetails>()}) {
896 message.Attach(use->location(),
897 "'%s' is USE-associated with '%s' in module '%s'"_en_US, symbol.name(),
898 unhosted->name(), GetUsedModule(*use).name());
899 } else {
900 message.Attach(
901 unhosted->name(), "Declaration of '%s'"_en_US, unhosted->name());
902 }
903 return &message;
904 }
905
AttachDeclaration(parser::Message * message,const Symbol & symbol)906 parser::Message *AttachDeclaration(
907 parser::Message *message, const Symbol &symbol) {
908 return message ? AttachDeclaration(*message, symbol) : nullptr;
909 }
910
911 class FindImpureCallHelper
912 : public AnyTraverse<FindImpureCallHelper, std::optional<std::string>> {
913 using Result = std::optional<std::string>;
914 using Base = AnyTraverse<FindImpureCallHelper, Result>;
915
916 public:
FindImpureCallHelper(FoldingContext & c)917 explicit FindImpureCallHelper(FoldingContext &c) : Base{*this}, context_{c} {}
918 using Base::operator();
operator ()(const ProcedureRef & call) const919 Result operator()(const ProcedureRef &call) const {
920 if (auto chars{
921 characteristics::Procedure::Characterize(call.proc(), context_)}) {
922 if (chars->attrs.test(characteristics::Procedure::Attr::Pure)) {
923 return (*this)(call.arguments());
924 }
925 }
926 return call.proc().GetName();
927 }
928
929 private:
930 FoldingContext &context_;
931 };
932
FindImpureCall(FoldingContext & context,const Expr<SomeType> & expr)933 std::optional<std::string> FindImpureCall(
934 FoldingContext &context, const Expr<SomeType> &expr) {
935 return FindImpureCallHelper{context}(expr);
936 }
FindImpureCall(FoldingContext & context,const ProcedureRef & proc)937 std::optional<std::string> FindImpureCall(
938 FoldingContext &context, const ProcedureRef &proc) {
939 return FindImpureCallHelper{context}(proc);
940 }
941
942 // Common handling for procedure pointer compatibility of left- and right-hand
943 // sides. Returns nullopt if they're compatible. Otherwise, it returns a
944 // message that needs to be augmented by the names of the left and right sides
945 // and the content of the "whyNotCompatible" string.
CheckProcCompatibility(bool isCall,const std::optional<characteristics::Procedure> & lhsProcedure,const characteristics::Procedure * rhsProcedure,const SpecificIntrinsic * specificIntrinsic,std::string & whyNotCompatible)946 std::optional<parser::MessageFixedText> CheckProcCompatibility(bool isCall,
947 const std::optional<characteristics::Procedure> &lhsProcedure,
948 const characteristics::Procedure *rhsProcedure,
949 const SpecificIntrinsic *specificIntrinsic, std::string &whyNotCompatible) {
950 std::optional<parser::MessageFixedText> msg;
951 if (!lhsProcedure) {
952 msg = "In assignment to object %s, the target '%s' is a procedure"
953 " designator"_err_en_US;
954 } else if (!rhsProcedure) {
955 msg = "In assignment to procedure %s, the characteristics of the target"
956 " procedure '%s' could not be determined"_err_en_US;
957 } else if (lhsProcedure->IsCompatibleWith(
958 *rhsProcedure, &whyNotCompatible, specificIntrinsic)) {
959 // OK
960 } else if (isCall) {
961 msg = "Procedure %s associated with result of reference to function '%s'"
962 " that is an incompatible procedure pointer: %s"_err_en_US;
963 } else if (lhsProcedure->IsPure() && !rhsProcedure->IsPure()) {
964 msg = "PURE procedure %s may not be associated with non-PURE"
965 " procedure designator '%s'"_err_en_US;
966 } else if (lhsProcedure->IsFunction() && rhsProcedure->IsSubroutine()) {
967 msg = "Function %s may not be associated with subroutine"
968 " designator '%s'"_err_en_US;
969 } else if (lhsProcedure->IsSubroutine() && rhsProcedure->IsFunction()) {
970 msg = "Subroutine %s may not be associated with function"
971 " designator '%s'"_err_en_US;
972 } else if (lhsProcedure->HasExplicitInterface() &&
973 !rhsProcedure->HasExplicitInterface()) {
974 // Section 10.2.2.4, paragraph 3 prohibits associating a procedure pointer
975 // that has an explicit interface with a procedure whose characteristics
976 // don't match. That's the case if the target procedure has an implicit
977 // interface. But this case is allowed by several other compilers as long
978 // as the explicit interface can be called via an implicit interface.
979 if (!lhsProcedure->CanBeCalledViaImplicitInterface()) {
980 msg = "Procedure %s with explicit interface that cannot be called via "
981 "an implicit interface cannot be associated with procedure "
982 "designator with an implicit interface"_err_en_US;
983 }
984 } else if (!lhsProcedure->HasExplicitInterface() &&
985 rhsProcedure->HasExplicitInterface()) {
986 // OK if the target can be called via an implicit interface
987 if (!rhsProcedure->CanBeCalledViaImplicitInterface() &&
988 !specificIntrinsic) {
989 msg = "Procedure %s with implicit interface may not be associated "
990 "with procedure designator '%s' with explicit interface that "
991 "cannot be called via an implicit interface"_err_en_US;
992 }
993 } else {
994 msg = "Procedure %s associated with incompatible procedure"
995 " designator '%s': %s"_err_en_US;
996 }
997 return msg;
998 }
999
1000 // GetLastPointerSymbol()
GetLastPointerSymbol(const Symbol & symbol)1001 static const Symbol *GetLastPointerSymbol(const Symbol &symbol) {
1002 return IsPointer(GetAssociationRoot(symbol)) ? &symbol : nullptr;
1003 }
GetLastPointerSymbol(const SymbolRef & symbol)1004 static const Symbol *GetLastPointerSymbol(const SymbolRef &symbol) {
1005 return GetLastPointerSymbol(*symbol);
1006 }
GetLastPointerSymbol(const Component & x)1007 static const Symbol *GetLastPointerSymbol(const Component &x) {
1008 const Symbol &c{x.GetLastSymbol()};
1009 return IsPointer(c) ? &c : GetLastPointerSymbol(x.base());
1010 }
GetLastPointerSymbol(const NamedEntity & x)1011 static const Symbol *GetLastPointerSymbol(const NamedEntity &x) {
1012 const auto *c{x.UnwrapComponent()};
1013 return c ? GetLastPointerSymbol(*c) : GetLastPointerSymbol(x.GetLastSymbol());
1014 }
GetLastPointerSymbol(const ArrayRef & x)1015 static const Symbol *GetLastPointerSymbol(const ArrayRef &x) {
1016 return GetLastPointerSymbol(x.base());
1017 }
GetLastPointerSymbol(const CoarrayRef & x)1018 static const Symbol *GetLastPointerSymbol(const CoarrayRef &x) {
1019 return nullptr;
1020 }
GetLastPointerSymbol(const DataRef & x)1021 const Symbol *GetLastPointerSymbol(const DataRef &x) {
1022 return common::visit(
1023 [](const auto &y) { return GetLastPointerSymbol(y); }, x.u);
1024 }
1025
1026 template <TypeCategory TO, TypeCategory FROM>
DataConstantConversionHelper(FoldingContext & context,const DynamicType & toType,const Expr<SomeType> & expr)1027 static std::optional<Expr<SomeType>> DataConstantConversionHelper(
1028 FoldingContext &context, const DynamicType &toType,
1029 const Expr<SomeType> &expr) {
1030 DynamicType sizedType{FROM, toType.kind()};
1031 if (auto sized{
1032 Fold(context, ConvertToType(sizedType, Expr<SomeType>{expr}))}) {
1033 if (const auto *someExpr{UnwrapExpr<Expr<SomeKind<FROM>>>(*sized)}) {
1034 return common::visit(
1035 [](const auto &w) -> std::optional<Expr<SomeType>> {
1036 using FromType = typename std::decay_t<decltype(w)>::Result;
1037 static constexpr int kind{FromType::kind};
1038 if constexpr (IsValidKindOfIntrinsicType(TO, kind)) {
1039 if (const auto *fromConst{UnwrapExpr<Constant<FromType>>(w)}) {
1040 using FromWordType = typename FromType::Scalar;
1041 using LogicalType = value::Logical<FromWordType::bits>;
1042 using ElementType =
1043 std::conditional_t<TO == TypeCategory::Logical, LogicalType,
1044 typename LogicalType::Word>;
1045 std::vector<ElementType> values;
1046 auto at{fromConst->lbounds()};
1047 auto shape{fromConst->shape()};
1048 for (auto n{GetSize(shape)}; n-- > 0;
1049 fromConst->IncrementSubscripts(at)) {
1050 auto elt{fromConst->At(at)};
1051 if constexpr (TO == TypeCategory::Logical) {
1052 values.emplace_back(std::move(elt));
1053 } else {
1054 values.emplace_back(elt.word());
1055 }
1056 }
1057 return {AsGenericExpr(AsExpr(Constant<Type<TO, kind>>{
1058 std::move(values), std::move(shape)}))};
1059 }
1060 }
1061 return std::nullopt;
1062 },
1063 someExpr->u);
1064 }
1065 }
1066 return std::nullopt;
1067 }
1068
DataConstantConversionExtension(FoldingContext & context,const DynamicType & toType,const Expr<SomeType> & expr0)1069 std::optional<Expr<SomeType>> DataConstantConversionExtension(
1070 FoldingContext &context, const DynamicType &toType,
1071 const Expr<SomeType> &expr0) {
1072 Expr<SomeType> expr{Fold(context, Expr<SomeType>{expr0})};
1073 if (!IsActuallyConstant(expr)) {
1074 return std::nullopt;
1075 }
1076 if (auto fromType{expr.GetType()}) {
1077 if (toType.category() == TypeCategory::Logical &&
1078 fromType->category() == TypeCategory::Integer) {
1079 return DataConstantConversionHelper<TypeCategory::Logical,
1080 TypeCategory::Integer>(context, toType, expr);
1081 }
1082 if (toType.category() == TypeCategory::Integer &&
1083 fromType->category() == TypeCategory::Logical) {
1084 return DataConstantConversionHelper<TypeCategory::Integer,
1085 TypeCategory::Logical>(context, toType, expr);
1086 }
1087 }
1088 return std::nullopt;
1089 }
1090
IsAllocatableOrPointerObject(const Expr<SomeType> & expr,FoldingContext & context)1091 bool IsAllocatableOrPointerObject(
1092 const Expr<SomeType> &expr, FoldingContext &context) {
1093 const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
1094 return (sym && semantics::IsAllocatableOrPointer(*sym)) ||
1095 evaluate::IsObjectPointer(expr, context);
1096 }
1097
IsAllocatableDesignator(const Expr<SomeType> & expr)1098 bool IsAllocatableDesignator(const Expr<SomeType> &expr) {
1099 // Allocatable sub-objects are not themselves allocatable (9.5.3.1 NOTE 2).
1100 if (const semantics::Symbol *
1101 sym{UnwrapWholeSymbolOrComponentOrCoarrayRef(expr)}) {
1102 return semantics::IsAllocatable(*sym);
1103 }
1104 return false;
1105 }
1106
MayBePassedAsAbsentOptional(const Expr<SomeType> & expr,FoldingContext & context)1107 bool MayBePassedAsAbsentOptional(
1108 const Expr<SomeType> &expr, FoldingContext &context) {
1109 const semantics::Symbol *sym{UnwrapWholeSymbolOrComponentDataRef(expr)};
1110 // 15.5.2.12 1. is pretty clear that an unallocated allocatable/pointer actual
1111 // may be passed to a non-allocatable/non-pointer optional dummy. Note that
1112 // other compilers (like nag, nvfortran, ifort, gfortran and xlf) seems to
1113 // ignore this point in intrinsic contexts (e.g CMPLX argument).
1114 return (sym && semantics::IsOptional(*sym)) ||
1115 IsAllocatableOrPointerObject(expr, context);
1116 }
1117
HollerithToBOZ(FoldingContext & context,const Expr<SomeType> & expr,const DynamicType & type)1118 std::optional<Expr<SomeType>> HollerithToBOZ(FoldingContext &context,
1119 const Expr<SomeType> &expr, const DynamicType &type) {
1120 if (std::optional<std::string> chValue{GetScalarConstantValue<Ascii>(expr)}) {
1121 // Pad on the right with spaces when short, truncate the right if long.
1122 // TODO: big-endian targets
1123 auto bytes{static_cast<std::size_t>(
1124 ToInt64(type.MeasureSizeInBytes(context, false)).value())};
1125 BOZLiteralConstant bits{0};
1126 for (std::size_t j{0}; j < bytes; ++j) {
1127 char ch{j >= chValue->size() ? ' ' : chValue->at(j)};
1128 BOZLiteralConstant chBOZ{static_cast<unsigned char>(ch)};
1129 bits = bits.IOR(chBOZ.SHIFTL(8 * j));
1130 }
1131 return ConvertToType(type, Expr<SomeType>{bits});
1132 } else {
1133 return std::nullopt;
1134 }
1135 }
1136
1137 } // namespace Fortran::evaluate
1138
1139 namespace Fortran::semantics {
1140
ResolveAssociations(const Symbol & original)1141 const Symbol &ResolveAssociations(const Symbol &original) {
1142 const Symbol &symbol{original.GetUltimate()};
1143 if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
1144 if (const Symbol * nested{UnwrapWholeSymbolDataRef(details->expr())}) {
1145 return ResolveAssociations(*nested);
1146 }
1147 }
1148 return symbol;
1149 }
1150
1151 // When a construct association maps to a variable, and that variable
1152 // is not an array with a vector-valued subscript, return the base
1153 // Symbol of that variable, else nullptr. Descends into other construct
1154 // associations when one associations maps to another.
GetAssociatedVariable(const AssocEntityDetails & details)1155 static const Symbol *GetAssociatedVariable(const AssocEntityDetails &details) {
1156 if (const auto &expr{details.expr()}) {
1157 if (IsVariable(*expr) && !HasVectorSubscript(*expr)) {
1158 if (const Symbol * varSymbol{GetFirstSymbol(*expr)}) {
1159 return &GetAssociationRoot(*varSymbol);
1160 }
1161 }
1162 }
1163 return nullptr;
1164 }
1165
GetAssociationRoot(const Symbol & original)1166 const Symbol &GetAssociationRoot(const Symbol &original) {
1167 const Symbol &symbol{ResolveAssociations(original)};
1168 if (const auto *details{symbol.detailsIf<AssocEntityDetails>()}) {
1169 if (const Symbol * root{GetAssociatedVariable(*details)}) {
1170 return *root;
1171 }
1172 }
1173 return symbol;
1174 }
1175
GetMainEntry(const Symbol * symbol)1176 const Symbol *GetMainEntry(const Symbol *symbol) {
1177 if (symbol) {
1178 if (const auto *subpDetails{symbol->detailsIf<SubprogramDetails>()}) {
1179 if (const Scope * scope{subpDetails->entryScope()}) {
1180 if (const Symbol * main{scope->symbol()}) {
1181 return main;
1182 }
1183 }
1184 }
1185 }
1186 return symbol;
1187 }
1188
IsVariableName(const Symbol & original)1189 bool IsVariableName(const Symbol &original) {
1190 const Symbol &symbol{ResolveAssociations(original)};
1191 if (symbol.has<ObjectEntityDetails>()) {
1192 return !IsNamedConstant(symbol);
1193 } else if (const auto *assoc{symbol.detailsIf<AssocEntityDetails>()}) {
1194 const auto &expr{assoc->expr()};
1195 return expr && IsVariable(*expr) && !HasVectorSubscript(*expr);
1196 } else {
1197 return false;
1198 }
1199 }
1200
IsPureProcedure(const Symbol & original)1201 bool IsPureProcedure(const Symbol &original) {
1202 // An ENTRY is pure if its containing subprogram is
1203 const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
1204 if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
1205 if (const Symbol * procInterface{procDetails->interface().symbol()}) {
1206 // procedure with a pure interface
1207 return IsPureProcedure(*procInterface);
1208 }
1209 } else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) {
1210 return IsPureProcedure(details->symbol());
1211 } else if (!IsProcedure(symbol)) {
1212 return false;
1213 }
1214 if (IsStmtFunction(symbol)) {
1215 // Section 15.7(1) states that a statement function is PURE if it does not
1216 // reference an IMPURE procedure or a VOLATILE variable
1217 if (const auto &expr{symbol.get<SubprogramDetails>().stmtFunction()}) {
1218 for (const SymbolRef &ref : evaluate::CollectSymbols(*expr)) {
1219 if (IsFunction(*ref) && !IsPureProcedure(*ref)) {
1220 return false;
1221 }
1222 if (ref->GetUltimate().attrs().test(Attr::VOLATILE)) {
1223 return false;
1224 }
1225 }
1226 }
1227 return true; // statement function was not found to be impure
1228 }
1229 return symbol.attrs().test(Attr::PURE) ||
1230 (symbol.attrs().test(Attr::ELEMENTAL) &&
1231 !symbol.attrs().test(Attr::IMPURE));
1232 }
1233
IsPureProcedure(const Scope & scope)1234 bool IsPureProcedure(const Scope &scope) {
1235 const Symbol *symbol{scope.GetSymbol()};
1236 return symbol && IsPureProcedure(*symbol);
1237 }
1238
IsElementalProcedure(const Symbol & original)1239 bool IsElementalProcedure(const Symbol &original) {
1240 // An ENTRY is elemental if its containing subprogram is
1241 const Symbol &symbol{DEREF(GetMainEntry(&original.GetUltimate()))};
1242 if (const auto *procDetails{symbol.detailsIf<ProcEntityDetails>()}) {
1243 if (const Symbol * procInterface{procDetails->interface().symbol()}) {
1244 // procedure with an elemental interface, ignoring the elemental
1245 // aspect of intrinsic functions
1246 return !procInterface->attrs().test(Attr::INTRINSIC) &&
1247 IsElementalProcedure(*procInterface);
1248 }
1249 } else if (const auto *details{symbol.detailsIf<ProcBindingDetails>()}) {
1250 return IsElementalProcedure(details->symbol());
1251 } else if (!IsProcedure(symbol)) {
1252 return false;
1253 }
1254 return symbol.attrs().test(Attr::ELEMENTAL);
1255 }
1256
IsFunction(const Symbol & symbol)1257 bool IsFunction(const Symbol &symbol) {
1258 const Symbol &ultimate{symbol.GetUltimate()};
1259 return ultimate.test(Symbol::Flag::Function) ||
1260 (!ultimate.test(Symbol::Flag::Subroutine) &&
1261 common::visit(
1262 common::visitors{
1263 [](const SubprogramDetails &x) { return x.isFunction(); },
1264 [](const ProcEntityDetails &x) {
1265 const auto &ifc{x.interface()};
1266 return ifc.type() ||
1267 (ifc.symbol() && IsFunction(*ifc.symbol()));
1268 },
1269 [](const ProcBindingDetails &x) {
1270 return IsFunction(x.symbol());
1271 },
1272 [](const auto &) { return false; },
1273 },
1274 ultimate.details()));
1275 }
1276
IsFunction(const Scope & scope)1277 bool IsFunction(const Scope &scope) {
1278 const Symbol *symbol{scope.GetSymbol()};
1279 return symbol && IsFunction(*symbol);
1280 }
1281
IsProcedure(const Symbol & symbol)1282 bool IsProcedure(const Symbol &symbol) {
1283 return common::visit(common::visitors{
1284 [](const SubprogramDetails &) { return true; },
1285 [](const SubprogramNameDetails &) { return true; },
1286 [](const ProcEntityDetails &) { return true; },
1287 [](const GenericDetails &) { return true; },
1288 [](const ProcBindingDetails &) { return true; },
1289 [](const auto &) { return false; },
1290 },
1291 symbol.GetUltimate().details());
1292 }
1293
IsProcedure(const Scope & scope)1294 bool IsProcedure(const Scope &scope) {
1295 const Symbol *symbol{scope.GetSymbol()};
1296 return symbol && IsProcedure(*symbol);
1297 }
1298
FindCommonBlockContaining(const Symbol & original)1299 const Symbol *FindCommonBlockContaining(const Symbol &original) {
1300 const Symbol &root{GetAssociationRoot(original)};
1301 const auto *details{root.detailsIf<ObjectEntityDetails>()};
1302 return details ? details->commonBlock() : nullptr;
1303 }
1304
IsProcedurePointer(const Symbol & original)1305 bool IsProcedurePointer(const Symbol &original) {
1306 const Symbol &symbol{GetAssociationRoot(original)};
1307 return IsPointer(symbol) && IsProcedure(symbol);
1308 }
1309
1310 // 3.11 automatic data object
IsAutomatic(const Symbol & original)1311 bool IsAutomatic(const Symbol &original) {
1312 const Symbol &symbol{original.GetUltimate()};
1313 if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
1314 if (!object->isDummy() && !IsAllocatable(symbol) && !IsPointer(symbol)) {
1315 if (const DeclTypeSpec * type{symbol.GetType()}) {
1316 // If a type parameter value is not a constant expression, the
1317 // object is automatic.
1318 if (type->category() == DeclTypeSpec::Character) {
1319 if (const auto &length{
1320 type->characterTypeSpec().length().GetExplicit()}) {
1321 if (!evaluate::IsConstantExpr(*length)) {
1322 return true;
1323 }
1324 }
1325 } else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
1326 for (const auto &pair : derived->parameters()) {
1327 if (const auto &value{pair.second.GetExplicit()}) {
1328 if (!evaluate::IsConstantExpr(*value)) {
1329 return true;
1330 }
1331 }
1332 }
1333 }
1334 }
1335 // If an array bound is not a constant expression, the object is
1336 // automatic.
1337 for (const ShapeSpec &dim : object->shape()) {
1338 if (const auto &lb{dim.lbound().GetExplicit()}) {
1339 if (!evaluate::IsConstantExpr(*lb)) {
1340 return true;
1341 }
1342 }
1343 if (const auto &ub{dim.ubound().GetExplicit()}) {
1344 if (!evaluate::IsConstantExpr(*ub)) {
1345 return true;
1346 }
1347 }
1348 }
1349 }
1350 }
1351 return false;
1352 }
1353
IsSaved(const Symbol & original)1354 bool IsSaved(const Symbol &original) {
1355 const Symbol &symbol{GetAssociationRoot(original)};
1356 const Scope &scope{symbol.owner()};
1357 auto scopeKind{scope.kind()};
1358 if (symbol.has<AssocEntityDetails>()) {
1359 return false; // ASSOCIATE(non-variable)
1360 } else if (scopeKind == Scope::Kind::DerivedType) {
1361 return false; // this is a component
1362 } else if (symbol.attrs().test(Attr::SAVE)) {
1363 return true; // explicit SAVE attribute
1364 } else if (IsDummy(symbol) || IsFunctionResult(symbol) ||
1365 IsAutomatic(symbol) || IsNamedConstant(symbol)) {
1366 return false;
1367 } else if (scopeKind == Scope::Kind::Module ||
1368 (scopeKind == Scope::Kind::MainProgram &&
1369 (symbol.attrs().test(Attr::TARGET) || evaluate::IsCoarray(symbol)))) {
1370 // 8.5.16p4
1371 // In main programs, implied SAVE matters only for pointer
1372 // initialization targets and coarrays.
1373 // BLOCK DATA entities must all be in COMMON,
1374 // which was checked above.
1375 return true;
1376 } else if (scope.context().languageFeatures().IsEnabled(
1377 common::LanguageFeature::DefaultSave) &&
1378 (scopeKind == Scope::Kind::MainProgram ||
1379 (scope.kind() == Scope::Kind::Subprogram &&
1380 !(scope.symbol() &&
1381 scope.symbol()->attrs().test(Attr::RECURSIVE))))) {
1382 // -fno-automatic/-save/-Msave option applies to all objects in executable
1383 // main programs and subprograms unless they are explicitly RECURSIVE.
1384 return true;
1385 } else if (symbol.test(Symbol::Flag::InDataStmt)) {
1386 return true;
1387 } else if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
1388 object && object->init()) {
1389 return true;
1390 } else if (IsProcedurePointer(symbol) && symbol.has<ProcEntityDetails>() &&
1391 symbol.get<ProcEntityDetails>().init()) {
1392 return true;
1393 } else if (scope.hasSAVE()) {
1394 return true; // bare SAVE statement
1395 } else if (const Symbol * block{FindCommonBlockContaining(symbol)};
1396 block && block->attrs().test(Attr::SAVE)) {
1397 return true; // in COMMON with SAVE
1398 } else {
1399 return false;
1400 }
1401 }
1402
IsDummy(const Symbol & symbol)1403 bool IsDummy(const Symbol &symbol) {
1404 return common::visit(
1405 common::visitors{[](const EntityDetails &x) { return x.isDummy(); },
1406 [](const ObjectEntityDetails &x) { return x.isDummy(); },
1407 [](const ProcEntityDetails &x) { return x.isDummy(); },
1408 [](const SubprogramDetails &x) { return x.isDummy(); },
1409 [](const auto &) { return false; }},
1410 ResolveAssociations(symbol).details());
1411 }
1412
IsAssumedShape(const Symbol & symbol)1413 bool IsAssumedShape(const Symbol &symbol) {
1414 const Symbol &ultimate{ResolveAssociations(symbol)};
1415 const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
1416 return object && object->CanBeAssumedShape() &&
1417 !evaluate::IsAllocatableOrPointer(ultimate);
1418 }
1419
IsDeferredShape(const Symbol & symbol)1420 bool IsDeferredShape(const Symbol &symbol) {
1421 const Symbol &ultimate{ResolveAssociations(symbol)};
1422 const auto *object{ultimate.detailsIf<ObjectEntityDetails>()};
1423 return object && object->CanBeDeferredShape() &&
1424 evaluate::IsAllocatableOrPointer(ultimate);
1425 }
1426
IsFunctionResult(const Symbol & original)1427 bool IsFunctionResult(const Symbol &original) {
1428 const Symbol &symbol{GetAssociationRoot(original)};
1429 return common::visit(
1430 common::visitors{
1431 [](const EntityDetails &x) { return x.isFuncResult(); },
1432 [](const ObjectEntityDetails &x) { return x.isFuncResult(); },
1433 [](const ProcEntityDetails &x) { return x.isFuncResult(); },
1434 [](const auto &) { return false; },
1435 },
1436 symbol.details());
1437 }
1438
IsKindTypeParameter(const Symbol & symbol)1439 bool IsKindTypeParameter(const Symbol &symbol) {
1440 const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()};
1441 return param && param->attr() == common::TypeParamAttr::Kind;
1442 }
1443
IsLenTypeParameter(const Symbol & symbol)1444 bool IsLenTypeParameter(const Symbol &symbol) {
1445 const auto *param{symbol.GetUltimate().detailsIf<TypeParamDetails>()};
1446 return param && param->attr() == common::TypeParamAttr::Len;
1447 }
1448
IsExtensibleType(const DerivedTypeSpec * derived)1449 bool IsExtensibleType(const DerivedTypeSpec *derived) {
1450 return derived && !IsIsoCType(derived) &&
1451 !derived->typeSymbol().attrs().test(Attr::BIND_C) &&
1452 !derived->typeSymbol().get<DerivedTypeDetails>().sequence();
1453 }
1454
IsBuiltinDerivedType(const DerivedTypeSpec * derived,const char * name)1455 bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name) {
1456 if (!derived) {
1457 return false;
1458 } else {
1459 const auto &symbol{derived->typeSymbol()};
1460 return &symbol.owner() == symbol.owner().context().GetBuiltinsScope() &&
1461 symbol.name() == "__builtin_"s + name;
1462 }
1463 }
1464
IsIsoCType(const DerivedTypeSpec * derived)1465 bool IsIsoCType(const DerivedTypeSpec *derived) {
1466 return IsBuiltinDerivedType(derived, "c_ptr") ||
1467 IsBuiltinDerivedType(derived, "c_funptr");
1468 }
1469
IsTeamType(const DerivedTypeSpec * derived)1470 bool IsTeamType(const DerivedTypeSpec *derived) {
1471 return IsBuiltinDerivedType(derived, "team_type");
1472 }
1473
IsBadCoarrayType(const DerivedTypeSpec * derived)1474 bool IsBadCoarrayType(const DerivedTypeSpec *derived) {
1475 return IsTeamType(derived) || IsIsoCType(derived);
1476 }
1477
IsEventTypeOrLockType(const DerivedTypeSpec * derivedTypeSpec)1478 bool IsEventTypeOrLockType(const DerivedTypeSpec *derivedTypeSpec) {
1479 return IsBuiltinDerivedType(derivedTypeSpec, "event_type") ||
1480 IsBuiltinDerivedType(derivedTypeSpec, "lock_type");
1481 }
1482
CountLenParameters(const DerivedTypeSpec & type)1483 int CountLenParameters(const DerivedTypeSpec &type) {
1484 return std::count_if(type.parameters().begin(), type.parameters().end(),
1485 [](const auto &pair) { return pair.second.isLen(); });
1486 }
1487
CountNonConstantLenParameters(const DerivedTypeSpec & type)1488 int CountNonConstantLenParameters(const DerivedTypeSpec &type) {
1489 return std::count_if(
1490 type.parameters().begin(), type.parameters().end(), [](const auto &pair) {
1491 if (!pair.second.isLen()) {
1492 return false;
1493 } else if (const auto &expr{pair.second.GetExplicit()}) {
1494 return !IsConstantExpr(*expr);
1495 } else {
1496 return true;
1497 }
1498 });
1499 }
1500
1501 // Are the type parameters of type1 compile-time compatible with the
1502 // corresponding kind type parameters of type2? Return true if all constant
1503 // valued parameters are equal.
1504 // Used to check assignment statements and argument passing. See 15.5.2.4(4)
AreTypeParamCompatible(const semantics::DerivedTypeSpec & type1,const semantics::DerivedTypeSpec & type2)1505 bool AreTypeParamCompatible(const semantics::DerivedTypeSpec &type1,
1506 const semantics::DerivedTypeSpec &type2) {
1507 for (const auto &[name, param1] : type1.parameters()) {
1508 if (semantics::MaybeIntExpr paramExpr1{param1.GetExplicit()}) {
1509 if (IsConstantExpr(*paramExpr1)) {
1510 const semantics::ParamValue *param2{type2.FindParameter(name)};
1511 if (param2) {
1512 if (semantics::MaybeIntExpr paramExpr2{param2->GetExplicit()}) {
1513 if (IsConstantExpr(*paramExpr2)) {
1514 if (ToInt64(*paramExpr1) != ToInt64(*paramExpr2)) {
1515 return false;
1516 }
1517 }
1518 }
1519 }
1520 }
1521 }
1522 }
1523 return true;
1524 }
1525
GetUsedModule(const UseDetails & details)1526 const Symbol &GetUsedModule(const UseDetails &details) {
1527 return DEREF(details.symbol().owner().symbol());
1528 }
1529
FindFunctionResult(const Symbol & original,UnorderedSymbolSet & seen)1530 static const Symbol *FindFunctionResult(
1531 const Symbol &original, UnorderedSymbolSet &seen) {
1532 const Symbol &root{GetAssociationRoot(original)};
1533 ;
1534 if (!seen.insert(root).second) {
1535 return nullptr; // don't loop
1536 }
1537 return common::visit(
1538 common::visitors{[](const SubprogramDetails &subp) {
1539 return subp.isFunction() ? &subp.result() : nullptr;
1540 },
1541 [&](const ProcEntityDetails &proc) {
1542 const Symbol *iface{proc.interface().symbol()};
1543 return iface ? FindFunctionResult(*iface, seen) : nullptr;
1544 },
1545 [&](const ProcBindingDetails &binding) {
1546 return FindFunctionResult(binding.symbol(), seen);
1547 },
1548 [](const auto &) -> const Symbol * { return nullptr; }},
1549 root.details());
1550 }
1551
FindFunctionResult(const Symbol & symbol)1552 const Symbol *FindFunctionResult(const Symbol &symbol) {
1553 UnorderedSymbolSet seen;
1554 return FindFunctionResult(symbol, seen);
1555 }
1556
1557 // These are here in Evaluate/tools.cpp so that Evaluate can use
1558 // them; they cannot be defined in symbol.h due to the dependence
1559 // on Scope.
1560
operator ()(const SymbolRef & x,const SymbolRef & y) const1561 bool SymbolSourcePositionCompare::operator()(
1562 const SymbolRef &x, const SymbolRef &y) const {
1563 return x->GetSemanticsContext().allCookedSources().Precedes(
1564 x->name(), y->name());
1565 }
operator ()(const MutableSymbolRef & x,const MutableSymbolRef & y) const1566 bool SymbolSourcePositionCompare::operator()(
1567 const MutableSymbolRef &x, const MutableSymbolRef &y) const {
1568 return x->GetSemanticsContext().allCookedSources().Precedes(
1569 x->name(), y->name());
1570 }
1571
GetSemanticsContext() const1572 SemanticsContext &Symbol::GetSemanticsContext() const {
1573 return DEREF(owner_).context();
1574 }
1575
AreTkCompatibleTypes(const DeclTypeSpec * x,const DeclTypeSpec * y)1576 bool AreTkCompatibleTypes(const DeclTypeSpec *x, const DeclTypeSpec *y) {
1577 if (x && y) {
1578 if (auto xDt{evaluate::DynamicType::From(*x)}) {
1579 if (auto yDt{evaluate::DynamicType::From(*y)}) {
1580 return xDt->IsTkCompatibleWith(*yDt);
1581 }
1582 }
1583 }
1584 return false;
1585 }
1586
1587 } // namespace Fortran::semantics
1588