FLANG
call.h
1//===-- include/flang/Evaluate/call.h ---------------------------*- C++ -*-===//
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#ifndef FORTRAN_EVALUATE_CALL_H_
10#define FORTRAN_EVALUATE_CALL_H_
11
12#include "common.h"
13#include "constant.h"
14#include "formatting.h"
15#include "type.h"
16#include "flang/Common/indirection.h"
17#include "flang/Common/reference.h"
18#include "flang/Parser/char-block.h"
19#include "flang/Semantics/attr.h"
20#include "flang/Support/Fortran.h"
21#include <optional>
22#include <vector>
23
24namespace llvm {
25class raw_ostream;
26}
27
28namespace Fortran::semantics {
29class Symbol;
30}
31
32// Mutually referential data structures are represented here with forward
33// declarations of hitherto undefined class types and a level of indirection.
35class Component;
36class IntrinsicProcTable;
37} // namespace Fortran::evaluate
38namespace Fortran::evaluate::characteristics {
39struct DummyArgument;
40struct Procedure;
41} // namespace Fortran::evaluate::characteristics
42
44 true>;
45extern template class Fortran::common::Indirection<
47
48namespace Fortran::evaluate {
49
50using semantics::Symbol;
51using SymbolRef = common::Reference<const Symbol>;
52
53class ActualArgument {
54public:
55 ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef);
57
58 // Dummy arguments that are TYPE(*) can be forwarded as actual arguments.
59 // Since that's the only thing one may do with them in Fortran, they're
60 // represented in expressions as a special case of an actual argument.
61 class AssumedType {
62 public:
63 explicit AssumedType(const Symbol &);
64 DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(AssumedType)
65 const Symbol &symbol() const { return symbol_; }
66 int Rank() const;
67 bool operator==(const AssumedType &that) const {
68 return &*symbol_ == &*that.symbol_;
69 }
70 llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
71
72 private:
73 SymbolRef symbol_;
74 };
75
76 // F2023 R1526 conditional-arg: runtime selection of actual arguments.
77 // Recursive structure mirroring the parser: each ConditionalArg holds
78 // a condition, a consequent, and a tail that is either another
79 // ConditionalArg (continuing the chain) or a terminal Consequent.
80 // std::nullopt represents .NIL. (absent optional argument).
81 class ConditionalArg {
82 public:
83 using Consequent =
84 std::optional<common::CopyableIndirection<Expr<SomeType>>>;
85 using ConditionalArgPartOrConsequent =
86 std::variant<common::CopyableIndirection<ConditionalArg>, Consequent>;
87
88 ConditionalArg(Expr<SomeLogical> &&condition, Consequent &&consequent,
89 ConditionalArgPartOrConsequent &&tail);
90 DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ConditionalArg)
91
92 Expr<SomeLogical> &condition() { return condition_.value(); }
93 const Expr<SomeLogical> &condition() const { return condition_.value(); }
94 const Consequent &consequent() const { return consequent_; }
95 Consequent &consequent() { return consequent_; }
96 ConditionalArgPartOrConsequent &tail() { return tail_; }
97 const ConditionalArgPartOrConsequent &tail() const { return tail_; }
98
99 // Dispatch on the tail: calls onConditionalArg(const ConditionalArg &)
100 // if the tail continues the chain, or onConsequent(const Consequent &)
101 // if the tail is the terminal consequent.
102 template <typename F, typename G>
103 auto VisitTail(F onConditionalArg, G onConsequent) const {
104 return common::visit(
106 [&](const common::CopyableIndirection<ConditionalArg> &inner) {
107 return onConditionalArg(inner.value());
108 },
109 [&](const Consequent &cons) { return onConsequent(cons); },
110 },
111 tail_);
112 }
113
114 template <typename F, typename G>
115 auto VisitTail(F onConditionalArg, G onConsequent) {
116 return common::visit(
118 [&](common::CopyableIndirection<ConditionalArg> &inner) {
119 return onConditionalArg(inner.value());
120 },
121 [&](Consequent &cons) { return onConsequent(cons); },
122 },
123 tail_);
124 }
125
126 // Apply a callback to every Consequent in the chain (including .NIL.
127 // entries). This encapsulates the recurring "process consequent, then
128 // VisitTail with recursion" pattern.
129 template <typename F> void ForEachConsequent(F f) const {
130 f(consequent_);
131 VisitTail(
132 [&](const ConditionalArg &inner) { inner.ForEachConsequent(f); },
133 [&](const Consequent &cons) { f(cons); });
134 }
135 template <typename F> void ForEachConsequent(F f) {
136 f(consequent_);
137 VisitTail([&](ConditionalArg &inner) { inner.ForEachConsequent(f); },
138 [&](Consequent &cons) { f(cons); });
139 }
140
141 // Returns the first non-.NIL. consequent expression, or nullptr.
142 const Expr<SomeType> *FirstNonNilConsequent() const;
143 bool HasNilConsequent() const;
144
145 bool operator==(const ConditionalArg &) const;
146 llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
147
148 private:
149 common::CopyableIndirection<Expr<SomeLogical>> condition_;
150 Consequent consequent_;
151 ConditionalArgPartOrConsequent tail_;
152 };
153
154 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(ActualArgument)
155 explicit ActualArgument(Expr<SomeType> &&);
156 explicit ActualArgument(common::CopyableIndirection<Expr<SomeType>> &&);
157 explicit ActualArgument(AssumedType);
158 explicit ActualArgument(common::Label);
159 explicit ActualArgument(ConditionalArg &&);
160 ~ActualArgument();
161 ActualArgument &operator=(Expr<SomeType> &&);
162
163 Expr<SomeType> *UnwrapExpr() {
164 if (auto *p{
165 std::get_if<common::CopyableIndirection<Expr<SomeType>>>(&u_)}) {
166 return &p->value();
167 } else {
168 return nullptr;
169 }
170 }
171 const Expr<SomeType> *UnwrapExpr() const {
172 if (const auto *p{
173 std::get_if<common::CopyableIndirection<Expr<SomeType>>>(&u_)}) {
174 return &p->value();
175 } else {
176 return nullptr;
177 }
178 }
179
180 const Symbol *GetAssumedTypeDummy() const {
181 if (const AssumedType * aType{std::get_if<AssumedType>(&u_)}) {
182 return &aType->symbol();
183 } else {
184 return nullptr;
185 }
186 }
187
188 common::Label GetLabel() const { return std::get<common::Label>(u_); }
189
190 std::optional<DynamicType> GetType() const;
191 int Rank() const;
192 bool operator==(const ActualArgument &) const;
193 llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
194 std::string AsFortran() const;
195
196 std::optional<parser::CharBlock> keyword() const { return keyword_; }
197 ActualArgument &set_keyword(parser::CharBlock x) {
198 keyword_ = x;
199 return *this;
200 }
201 bool isAlternateReturn() const {
202 return std::holds_alternative<common::Label>(u_);
203 }
204 bool isConditionalArg() const {
205 return std::holds_alternative<ConditionalArg>(u_);
206 }
207 const ConditionalArg *GetConditionalArg() const {
208 return std::get_if<ConditionalArg>(&u_);
209 }
210 ConditionalArg *GetConditionalArg() {
211 return std::get_if<ConditionalArg>(&u_);
212 }
213 const Expr<SomeType> *GetConditionalArgExpr() const {
214 const auto *condArg{GetConditionalArg()};
215 return condArg ? condArg->FirstNonNilConsequent() : nullptr;
216 }
217 // Returns the expression from a direct Expr argument or, failing that,
218 // the first non-NIL consequent from a ConditionalArg.
219 const Expr<SomeType> *GetArgExpr() const {
220 if (const auto *expr{UnwrapExpr()}) {
221 return expr;
222 }
223 return GetConditionalArgExpr();
224 }
225 bool isPassedObject() const { return attrs_.test(Attr::PassedObject); }
226 ActualArgument &set_isPassedObject(bool yes = true) {
227 if (yes) {
228 attrs_ = attrs_ + Attr::PassedObject;
229 } else {
230 attrs_ = attrs_ - Attr::PassedObject;
231 }
232 return *this;
233 }
234
235 bool Matches(const characteristics::DummyArgument &) const;
236 common::Intent dummyIntent() const { return dummyIntent_; }
237 ActualArgument &set_dummyIntent(common::Intent intent) {
238 dummyIntent_ = intent;
239 return *this;
240 }
241 std::optional<parser::CharBlock> sourceLocation() const {
242 return sourceLocation_;
243 }
244 ActualArgument &set_sourceLocation(std::optional<parser::CharBlock> at) {
245 sourceLocation_ = at;
246 return *this;
247 }
248
249 // Wrap this argument in parentheses
250 void Parenthesize();
251
252 // Legacy %VAL.
253 bool isPercentVal() const { return attrs_.test(Attr::PercentVal); };
254 ActualArgument &set_isPercentVal() {
255 attrs_ = attrs_ + Attr::PercentVal;
256 return *this;
257 }
258 // Legacy %REF.
259 bool isPercentRef() const { return attrs_.test(Attr::PercentRef); };
260 ActualArgument &set_isPercentRef() {
261 attrs_ = attrs_ + Attr::PercentRef;
262 return *this;
263 }
264
265private:
266 // Subtlety: There is a distinction that must be maintained here between an
267 // actual argument expression that is a variable and one that is not,
268 // e.g. between X and (X). The parser attempts to parse each argument
269 // first as a variable, then as an expression, and the distinction appears
270 // in the parse tree.
271 std::variant<common::CopyableIndirection<Expr<SomeType>>, AssumedType,
272 common::Label, ConditionalArg>
273 u_;
274 std::optional<parser::CharBlock> keyword_;
275 Attrs attrs_;
276 common::Intent dummyIntent_{common::Intent::Default};
277 std::optional<parser::CharBlock> sourceLocation_;
278};
279
280using ActualArguments = std::vector<std::optional<ActualArgument>>;
281
282// Intrinsics are identified by their names and the characteristics
283// of their arguments, at least for now.
284using IntrinsicProcedure = std::string;
285
286struct SpecificIntrinsic {
287 SpecificIntrinsic(IntrinsicProcedure, characteristics::Procedure &&);
288 DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(SpecificIntrinsic)
289 ~SpecificIntrinsic();
290 bool operator==(const SpecificIntrinsic &) const;
291 llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
292
293 IntrinsicProcedure name;
294 bool isRestrictedSpecific{false}; // if true, can only call it, not pass it
295 common::CopyableIndirection<characteristics::Procedure> characteristics;
296};
297
298struct ProcedureDesignator {
299 EVALUATE_UNION_CLASS_BOILERPLATE(ProcedureDesignator)
300 explicit ProcedureDesignator(SpecificIntrinsic &&i) : u{std::move(i)} {}
301 explicit ProcedureDesignator(const Symbol &n) : u{n} {}
302 explicit ProcedureDesignator(Component &&);
303
304 // Exactly one of these will return a non-null pointer.
305 const SpecificIntrinsic *GetSpecificIntrinsic() const;
306 const Symbol *GetSymbol() const; // symbol or component symbol
307 const SymbolRef *UnwrapSymbolRef() const; // null if intrinsic or component
308
309 // For references to NOPASS components and bindings only.
310 // References to PASS components and bindings are represented
311 // with the symbol below and the base object DataRef in the
312 // passed-object ActualArgument.
313 // Always null when the procedure is intrinsic.
314 const Component *GetComponent() const;
315
316 const Symbol *GetInterfaceSymbol() const;
317
318 std::string GetName() const;
319 std::optional<DynamicType> GetType() const;
320 int Rank() const;
321 bool IsElemental() const;
322 bool IsPure() const;
323 bool IsSimple() const;
324 std::optional<Expr<SubscriptInteger>> LEN() const;
325 llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
326
327 std::variant<SpecificIntrinsic, SymbolRef,
328 common::CopyableIndirection<Component>>
329 u;
330};
331
332using Chevrons = std::vector<Expr<SomeType>>;
333
334class ProcedureRef {
335public:
336 CLASS_BOILERPLATE(ProcedureRef)
337 ProcedureRef(ProcedureDesignator &&p, ActualArguments &&a,
338 bool hasAlternateReturns = false)
339 : proc_{std::move(p)}, arguments_{std::move(a)},
340 hasAlternateReturns_{hasAlternateReturns} {}
341 ~ProcedureRef();
342 static void Deleter(ProcedureRef *);
343
344 ProcedureDesignator &proc() { return proc_; }
345 const ProcedureDesignator &proc() const { return proc_; }
346 ActualArguments &arguments() { return arguments_; }
347 const ActualArguments &arguments() const { return arguments_; }
348 // CALL subr <<< kernel launch >>> (...); not function
349 Chevrons &chevrons() { return chevrons_; }
350 const Chevrons &chevrons() const { return chevrons_; }
351 void set_chevrons(Chevrons &&chevrons) { chevrons_ = std::move(chevrons); }
352
353 std::optional<Expr<SubscriptInteger>> LEN() const;
354 int Rank() const;
355 static constexpr int Corank() { return 0; } // TODO
356 bool IsElemental() const { return proc_.IsElemental(); }
357 bool hasAlternateReturns() const { return hasAlternateReturns_; }
358
359 bool hasNoInline() const { return noInline_; }
360 void setNoInline(bool ni) { noInline_ = ni; }
361 bool hasAlwaysInline() const { return alwaysInline_; }
362 void setAlwaysInline(bool ai) { alwaysInline_ = ai; }
363 bool hasInlineHint() const { return inlineHint_; }
364 void setInlineHint(bool ih) { inlineHint_ = ih; }
365
366 Expr<SomeType> *UnwrapArgExpr(int n) {
367 if (static_cast<std::size_t>(n) < arguments_.size() && arguments_[n]) {
368 return arguments_[n]->UnwrapExpr();
369 } else {
370 return nullptr;
371 }
372 }
373 const Expr<SomeType> *UnwrapArgExpr(int n) const {
374 if (static_cast<std::size_t>(n) < arguments_.size() && arguments_[n]) {
375 return arguments_[n]->UnwrapExpr();
376 } else {
377 return nullptr;
378 }
379 }
380
381 bool operator==(const ProcedureRef &) const;
382 llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
383
384protected:
386 ActualArguments arguments_;
387 Chevrons chevrons_;
388 bool hasAlternateReturns_;
389 bool noInline_{false};
390 bool alwaysInline_{false};
391 bool inlineHint_{false};
392};
393
394template <typename A> class FunctionRef : public ProcedureRef {
395public:
396 using Result = A;
397 CLASS_BOILERPLATE(FunctionRef)
398 explicit FunctionRef(ProcedureRef &&pr) : ProcedureRef{std::move(pr)} {}
399 FunctionRef(ProcedureDesignator &&p, ActualArguments &&a)
400 : ProcedureRef{std::move(p), std::move(a)} {}
401
402 std::optional<DynamicType> GetType() const {
403 if constexpr (IsLengthlessIntrinsicType<A>) {
404 return A::GetType();
405 } else if (auto type{proc_.GetType()}) {
406 // TODO: Non constant explicit length parameters of PDTs result should
407 // likely be dropped too. This is not as easy as for characters since some
408 // long lived DerivedTypeSpec pointer would need to be created here. It is
409 // not clear if this is causing any issue so far since the storage size of
410 // PDTs is independent of length parameters.
411 return type->DropNonConstantCharacterLength();
412 } else {
413 return std::nullopt;
414 }
415 }
416};
417} // namespace Fortran::evaluate
418#endif // FORTRAN_EVALUATE_CALL_H_
Definition enum-set.h:28
Definition indirection.h:31
Definition variable.h:73
Definition common.h:215
Definition symbol.h:896
Definition call.h:34
Definition idioms.h:60
Definition characteristics.h:367