1 //===-- ConvertExpr.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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "flang/Lower/ConvertExpr.h"
14 #include "flang/Evaluate/fold.h"
15 #include "flang/Evaluate/traverse.h"
16 #include "flang/Lower/AbstractConverter.h"
17 #include "flang/Lower/CallInterface.h"
18 #include "flang/Lower/ConvertType.h"
19 #include "flang/Lower/ConvertVariable.h"
20 #include "flang/Lower/IntrinsicCall.h"
21 #include "flang/Lower/StatementContext.h"
22 #include "flang/Lower/SymbolMap.h"
23 #include "flang/Lower/Todo.h"
24 #include "flang/Optimizer/Builder/Complex.h"
25 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
26 #include "flang/Semantics/expression.h"
27 #include "flang/Semantics/symbol.h"
28 #include "flang/Semantics/tools.h"
29 #include "flang/Semantics/type.h"
30 #include "mlir/Dialect/StandardOps/IR/Ops.h"
31 #include "llvm/Support/Debug.h"
32 
33 #define DEBUG_TYPE "flang-lower-expr"
34 
35 //===----------------------------------------------------------------------===//
36 // The composition and structure of Fortran::evaluate::Expr is defined in
37 // the various header files in include/flang/Evaluate. You are referred
38 // there for more information on these data structures. Generally speaking,
39 // these data structures are a strongly typed family of abstract data types
40 // that, composed as trees, describe the syntax of Fortran expressions.
41 //
42 // This part of the bridge can traverse these tree structures and lower them
43 // to the correct FIR representation in SSA form.
44 //===----------------------------------------------------------------------===//
45 
46 /// Place \p exv in memory if it is not already a memory reference. If
47 /// \p forceValueType is provided, the value is first casted to the provided
48 /// type before being stored (this is mainly intended for logicals whose value
49 /// may be `i1` but needed to be stored as Fortran logicals).
50 static fir::ExtendedValue
51 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
52                          const fir::ExtendedValue &exv,
53                          mlir::Type storageType) {
54   mlir::Value valBase = fir::getBase(exv);
55   if (fir::conformsWithPassByRef(valBase.getType()))
56     return exv;
57 
58   assert(!fir::hasDynamicSize(storageType) &&
59          "only expect statically sized scalars to be by value");
60 
61   // Since `a` is not itself a valid referent, determine its value and
62   // create a temporary location at the beginning of the function for
63   // referencing.
64   mlir::Value val = builder.createConvert(loc, storageType, valBase);
65   mlir::Value temp = builder.createTemporary(
66       loc, storageType,
67       llvm::ArrayRef<mlir::NamedAttribute>{
68           Fortran::lower::getAdaptToByRefAttr(builder)});
69   builder.create<fir::StoreOp>(loc, val, temp);
70   return fir::substBase(exv, temp);
71 }
72 
73 /// Is this a variable wrapped in parentheses?
74 template <typename A>
75 static bool isParenthesizedVariable(const A &) {
76   return false;
77 }
78 template <typename T>
79 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) {
80   using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
81   using Parentheses = Fortran::evaluate::Parentheses<T>;
82   if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
83     if (const auto *parentheses = std::get_if<Parentheses>(&expr.u))
84       return Fortran::evaluate::IsVariable(parentheses->left());
85     return false;
86   } else {
87     return std::visit([&](const auto &x) { return isParenthesizedVariable(x); },
88                       expr.u);
89   }
90 }
91 
92 /// Generate a load of a value from an address. Beware that this will lose
93 /// any dynamic type information for polymorphic entities (note that unlimited
94 /// polymorphic cannot be loaded and must not be provided here).
95 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
96                                   mlir::Location loc,
97                                   const fir::ExtendedValue &addr) {
98   return addr.match(
99       [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
100       [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
101         if (fir::unwrapRefType(fir::getBase(v).getType())
102                 .isa<fir::RecordType>())
103           return v;
104         return builder.create<fir::LoadOp>(loc, fir::getBase(v));
105       },
106       [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
107         TODO(loc, "genLoad for MutableBoxValue");
108       },
109       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
110         TODO(loc, "genLoad for BoxValue");
111       },
112       [&](const auto &) -> fir::ExtendedValue {
113         fir::emitFatalError(
114             loc, "attempting to load whole array or procedure address");
115       });
116 }
117 
118 /// Is this a call to an elemental procedure with at least one array argument?
119 static bool
120 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
121   if (procRef.IsElemental())
122     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
123          procRef.arguments())
124       if (arg && arg->Rank() != 0)
125         return true;
126   return false;
127 }
128 
129 /// If \p arg is the address of a function with a denoted host-association tuple
130 /// argument, then return the host-associations tuple value of the current
131 /// procedure. Otherwise, return nullptr.
132 static mlir::Value
133 argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
134                    mlir::Value arg) {
135   if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
136     auto &builder = converter.getFirOpBuilder();
137     if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
138       if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
139         return converter.hostAssocTupleValue();
140   }
141   return {};
142 }
143 
144 namespace {
145 
146 /// Lowering of Fortran::evaluate::Expr<T> expressions
147 class ScalarExprLowering {
148 public:
149   using ExtValue = fir::ExtendedValue;
150 
151   explicit ScalarExprLowering(mlir::Location loc,
152                               Fortran::lower::AbstractConverter &converter,
153                               Fortran::lower::SymMap &symMap,
154                               Fortran::lower::StatementContext &stmtCtx)
155       : location{loc}, converter{converter},
156         builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap} {
157   }
158 
159   ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) {
160     return gen(expr);
161   }
162 
163   /// Lower `expr` to be passed as a fir.box argument. Do not create a temp
164   /// for the expr if it is a variable that can be described as a fir.box.
165   ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) {
166     bool saveUseBoxArg = useBoxArg;
167     useBoxArg = true;
168     ExtValue result = gen(expr);
169     useBoxArg = saveUseBoxArg;
170     return result;
171   }
172 
173   ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) {
174     return genval(expr);
175   }
176 
177   mlir::Location getLoc() { return location; }
178 
179   template <typename A>
180   mlir::Value genunbox(const A &expr) {
181     ExtValue e = genval(expr);
182     if (const fir::UnboxedValue *r = e.getUnboxed())
183       return *r;
184     fir::emitFatalError(getLoc(), "unboxed expression expected");
185   }
186 
187   /// Generate an integral constant of `value`
188   template <int KIND>
189   mlir::Value genIntegerConstant(mlir::MLIRContext *context,
190                                  std::int64_t value) {
191     mlir::Type type =
192         converter.genType(Fortran::common::TypeCategory::Integer, KIND);
193     return builder.createIntegerConstant(getLoc(), type, value);
194   }
195 
196   /// Generate a logical/boolean constant of `value`
197   mlir::Value genBoolConstant(bool value) {
198     return builder.createBool(getLoc(), value);
199   }
200 
201   /// Generate a real constant with a value `value`.
202   template <int KIND>
203   mlir::Value genRealConstant(mlir::MLIRContext *context,
204                               const llvm::APFloat &value) {
205     mlir::Type fltTy = Fortran::lower::convertReal(context, KIND);
206     return builder.createRealConstant(getLoc(), fltTy, value);
207   }
208 
209   /// Returns a reference to a symbol or its box/boxChar descriptor if it has
210   /// one.
211   ExtValue gen(Fortran::semantics::SymbolRef sym) {
212     if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
213       return val.match([&val](auto &) { return val.toExtendedValue(); });
214     LLVM_DEBUG(llvm::dbgs()
215                << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
216     fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
217   }
218 
219   ExtValue genLoad(const ExtValue &exv) {
220     return ::genLoad(builder, getLoc(), exv);
221   }
222 
223   ExtValue genval(Fortran::semantics::SymbolRef sym) {
224     ExtValue var = gen(sym);
225     if (const fir::UnboxedValue *s = var.getUnboxed())
226       if (fir::isReferenceLike(s->getType()))
227         return genLoad(*s);
228     return var;
229   }
230 
231   ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
232     TODO(getLoc(), "genval BOZ");
233   }
234 
235   /// Return indirection to function designated in ProcedureDesignator.
236   /// The type of the function indirection is not guaranteed to match the one
237   /// of the ProcedureDesignator due to Fortran implicit typing rules.
238   ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
239     TODO(getLoc(), "genval ProcedureDesignator");
240   }
241 
242   ExtValue genval(const Fortran::evaluate::NullPointer &) {
243     TODO(getLoc(), "genval NullPointer");
244   }
245 
246   ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
247     TODO(getLoc(), "genval StructureConstructor");
248   }
249 
250   /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
251   ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
252     TODO(getLoc(), "genval ImpliedDoIndex");
253   }
254 
255   ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
256     TODO(getLoc(), "genval DescriptorInquiry");
257   }
258 
259   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
260     TODO(getLoc(), "genval TypeParamInquiry");
261   }
262 
263   template <int KIND>
264   ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
265     TODO(getLoc(), "genval ComplexComponent");
266   }
267 
268   template <int KIND>
269   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
270                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
271     mlir::Value input = genunbox(op.left());
272     // Like LLVM, integer negation is the binary op "0 - value"
273     mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
274     return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
275   }
276 
277   template <int KIND>
278   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
279                       Fortran::common::TypeCategory::Real, KIND>> &op) {
280     return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
281   }
282   template <int KIND>
283   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
284                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
285     return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
286   }
287 
288   template <typename OpTy>
289   mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
290     assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
291     mlir::Value lhs = fir::getBase(left);
292     mlir::Value rhs = fir::getBase(right);
293     assert(lhs.getType() == rhs.getType() && "types must be the same");
294     return builder.create<OpTy>(getLoc(), lhs, rhs);
295   }
296 
297   template <typename OpTy, typename A>
298   mlir::Value createBinaryOp(const A &ex) {
299     ExtValue left = genval(ex.left());
300     return createBinaryOp<OpTy>(left, genval(ex.right()));
301   }
302 
303 #undef GENBIN
304 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
305   template <int KIND>                                                          \
306   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
307                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
308     return createBinaryOp<GenBinFirOp>(x);                                     \
309   }
310 
311   GENBIN(Add, Integer, mlir::arith::AddIOp)
312   GENBIN(Add, Real, mlir::arith::AddFOp)
313   GENBIN(Add, Complex, fir::AddcOp)
314   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
315   GENBIN(Subtract, Real, mlir::arith::SubFOp)
316   GENBIN(Subtract, Complex, fir::SubcOp)
317   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
318   GENBIN(Multiply, Real, mlir::arith::MulFOp)
319   GENBIN(Multiply, Complex, fir::MulcOp)
320   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
321   GENBIN(Divide, Real, mlir::arith::DivFOp)
322   GENBIN(Divide, Complex, fir::DivcOp)
323 
324   template <Fortran::common::TypeCategory TC, int KIND>
325   ExtValue genval(
326       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
327     TODO(getLoc(), "genval Power");
328   }
329 
330   template <Fortran::common::TypeCategory TC, int KIND>
331   ExtValue genval(
332       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
333           &op) {
334     TODO(getLoc(), "genval RealToInt");
335   }
336 
337   template <int KIND>
338   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
339     mlir::Value realPartValue = genunbox(op.left());
340     return fir::factory::Complex{builder, getLoc()}.createComplex(
341         KIND, realPartValue, genunbox(op.right()));
342   }
343 
344   template <int KIND>
345   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
346     TODO(getLoc(), "genval Concat<KIND>");
347   }
348 
349   /// MIN and MAX operations
350   template <Fortran::common::TypeCategory TC, int KIND>
351   ExtValue
352   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
353              &op) {
354     TODO(getLoc(), "genval Extremum<TC, KIND>");
355   }
356 
357   template <int KIND>
358   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
359     TODO(getLoc(), "genval SetLength<KIND>");
360   }
361 
362   template <int KIND>
363   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
364                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
365     TODO(getLoc(), "genval integer comparison");
366   }
367   template <int KIND>
368   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
369                       Fortran::common::TypeCategory::Real, KIND>> &op) {
370     TODO(getLoc(), "genval real comparison");
371   }
372   template <int KIND>
373   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
374                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
375     TODO(getLoc(), "genval complex comparison");
376   }
377   template <int KIND>
378   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
379                       Fortran::common::TypeCategory::Character, KIND>> &op) {
380     TODO(getLoc(), "genval char comparison");
381   }
382 
383   ExtValue
384   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
385     TODO(getLoc(), "genval comparison");
386   }
387 
388   template <Fortran::common::TypeCategory TC1, int KIND,
389             Fortran::common::TypeCategory TC2>
390   ExtValue
391   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
392                                           TC2> &convert) {
393     mlir::Type ty = converter.genType(TC1, KIND);
394     mlir::Value operand = genunbox(convert.left());
395     return builder.convertWithSemantics(getLoc(), ty, operand);
396   }
397 
398   template <typename A>
399   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
400     TODO(getLoc(), "genval parentheses<A>");
401   }
402 
403   template <int KIND>
404   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
405     TODO(getLoc(), "genval Not<KIND>");
406   }
407 
408   template <int KIND>
409   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
410     TODO(getLoc(), "genval LogicalOperation<KIND>");
411   }
412 
413   /// Convert a scalar literal constant to IR.
414   template <Fortran::common::TypeCategory TC, int KIND>
415   ExtValue genScalarLit(
416       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
417           &value) {
418     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
419       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
420     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
421       return genBoolConstant(value.IsTrue());
422     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
423       std::string str = value.DumpHexadecimal();
424       if constexpr (KIND == 2) {
425         llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str};
426         return genRealConstant<KIND>(builder.getContext(), floatVal);
427       } else if constexpr (KIND == 3) {
428         llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str};
429         return genRealConstant<KIND>(builder.getContext(), floatVal);
430       } else if constexpr (KIND == 4) {
431         llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str};
432         return genRealConstant<KIND>(builder.getContext(), floatVal);
433       } else if constexpr (KIND == 10) {
434         llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str};
435         return genRealConstant<KIND>(builder.getContext(), floatVal);
436       } else if constexpr (KIND == 16) {
437         llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str};
438         return genRealConstant<KIND>(builder.getContext(), floatVal);
439       } else {
440         // convert everything else to double
441         llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str};
442         return genRealConstant<KIND>(builder.getContext(), floatVal);
443       }
444     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
445       using TR =
446           Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>;
447       Fortran::evaluate::ComplexConstructor<KIND> ctor(
448           Fortran::evaluate::Expr<TR>{
449               Fortran::evaluate::Constant<TR>{value.REAL()}},
450           Fortran::evaluate::Expr<TR>{
451               Fortran::evaluate::Constant<TR>{value.AIMAG()}});
452       return genunbox(ctor);
453     } else /*constexpr*/ {
454       llvm_unreachable("unhandled constant");
455     }
456   }
457 
458   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
459   ExtValue
460   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
461                         Fortran::common::TypeCategory::Character, 1>> &value,
462                     int64_t len) {
463     assert(value.size() == static_cast<std::uint64_t>(len) &&
464            "value.size() doesn't match with len");
465     return fir::factory::createStringLiteral(builder, getLoc(), value);
466   }
467 
468   template <Fortran::common::TypeCategory TC, int KIND>
469   ExtValue
470   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
471              &con) {
472     if (con.Rank() > 0)
473       TODO(getLoc(), "genval array constant");
474     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
475         opt = con.GetScalarValue();
476     assert(opt.has_value() && "constant has no value");
477     if constexpr (TC == Fortran::common::TypeCategory::Character) {
478       if constexpr (KIND == 1)
479         return genAsciiScalarLit(opt.value(), con.LEN());
480       TODO(getLoc(), "genval for Character with KIND != 1");
481     } else {
482       return genScalarLit<TC, KIND>(opt.value());
483     }
484   }
485 
486   fir::ExtendedValue genval(
487       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
488     TODO(getLoc(), "genval constant derived");
489   }
490 
491   template <typename A>
492   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
493     TODO(getLoc(), "genval ArrayConstructor<A>");
494   }
495 
496   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
497     TODO(getLoc(), "gen ComplexPart");
498   }
499   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
500     TODO(getLoc(), "genval ComplexPart");
501   }
502 
503   ExtValue gen(const Fortran::evaluate::Substring &s) {
504     TODO(getLoc(), "gen Substring");
505   }
506   ExtValue genval(const Fortran::evaluate::Substring &ss) {
507     TODO(getLoc(), "genval Substring");
508   }
509 
510   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
511     TODO(getLoc(), "genval Subscript");
512   }
513 
514   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
515     TODO(getLoc(), "gen DataRef");
516   }
517   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
518     TODO(getLoc(), "genval DataRef");
519   }
520 
521   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
522     TODO(getLoc(), "gen Component");
523   }
524   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
525     TODO(getLoc(), "genval Component");
526   }
527 
528   ExtValue genval(const Fortran::semantics::Bound &bound) {
529     TODO(getLoc(), "genval Bound");
530   }
531 
532   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
533     TODO(getLoc(), "gen ArrayRef");
534   }
535   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
536     TODO(getLoc(), "genval ArrayRef");
537   }
538 
539   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
540     TODO(getLoc(), "gen CoarrayRef");
541   }
542   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
543     TODO(getLoc(), "genval CoarrayRef");
544   }
545 
546   template <typename A>
547   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
548     return std::visit([&](const auto &x) { return gen(x); }, des.u);
549   }
550   template <typename A>
551   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
552     return std::visit([&](const auto &x) { return genval(x); }, des.u);
553   }
554 
555   mlir::Type genType(const Fortran::evaluate::DynamicType &dt) {
556     if (dt.category() != Fortran::common::TypeCategory::Derived)
557       return converter.genType(dt.category(), dt.kind());
558     TODO(getLoc(), "genType Derived Type");
559   }
560 
561   /// Lower a function reference
562   template <typename A>
563   ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) {
564     if (!funcRef.GetType().has_value())
565       fir::emitFatalError(getLoc(), "internal: a function must have a type");
566     mlir::Type resTy = genType(*funcRef.GetType());
567     return genProcedureRef(funcRef, {resTy});
568   }
569 
570   /// Lower function call `funcRef` and return a reference to the resultant
571   /// value. This is required for lowering expressions such as `f1(f2(v))`.
572   template <typename A>
573   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
574     TODO(getLoc(), "gen FunctionRef<A>");
575   }
576 
577   /// helper to detect statement functions
578   static bool
579   isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) {
580     if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
581       if (const auto *details =
582               symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
583         return details->stmtFunction().has_value();
584     return false;
585   }
586 
587   /// Helper to package a Value and its properties into an ExtendedValue.
588   static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base,
589                                   llvm::ArrayRef<mlir::Value> extents,
590                                   llvm::ArrayRef<mlir::Value> lengths) {
591     mlir::Type type = base.getType();
592     if (type.isa<fir::BoxType>())
593       return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);
594     type = fir::unwrapRefType(type);
595     if (type.isa<fir::BoxType>())
596       return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});
597     if (auto seqTy = type.dyn_cast<fir::SequenceType>()) {
598       if (seqTy.getDimension() != extents.size())
599         fir::emitFatalError(loc, "incorrect number of extents for array");
600       if (seqTy.getEleTy().isa<fir::CharacterType>()) {
601         if (lengths.empty())
602           fir::emitFatalError(loc, "missing length for character");
603         assert(lengths.size() == 1);
604         return fir::CharArrayBoxValue(base, lengths[0], extents);
605       }
606       return fir::ArrayBoxValue(base, extents);
607     }
608     if (type.isa<fir::CharacterType>()) {
609       if (lengths.empty())
610         fir::emitFatalError(loc, "missing length for character");
611       assert(lengths.size() == 1);
612       return fir::CharBoxValue(base, lengths[0]);
613     }
614     return base;
615   }
616 
617   // Find the argument that corresponds to the host associations.
618   // Verify some assumptions about how the signature was built here.
619   [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) {
620     // Scan the argument list from last to first as the host associations are
621     // appended for now.
622     for (unsigned i = fn.getNumArguments(); i > 0; --i)
623       if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
624         // Host assoc tuple must be last argument (for now).
625         assert(i == fn.getNumArguments() && "tuple must be last");
626         return i - 1;
627       }
628     llvm_unreachable("anyFuncArgsHaveAttr failed");
629   }
630 
631   /// Lower a non-elemental procedure reference and read allocatable and pointer
632   /// results into normal values.
633   ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
634                            llvm::Optional<mlir::Type> resultType) {
635     ExtValue res = genRawProcedureRef(procRef, resultType);
636     return res;
637   }
638 
639   /// Given a call site for which the arguments were already lowered, generate
640   /// the call and return the result. This function deals with explicit result
641   /// allocation and lowering if needed. It also deals with passing the host
642   /// link to internal procedures.
643   ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller,
644                               mlir::FunctionType callSiteType,
645                               llvm::Optional<mlir::Type> resultType) {
646     mlir::Location loc = getLoc();
647     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
648     // Handle cases where caller must allocate the result or a fir.box for it.
649     bool mustPopSymMap = false;
650     if (caller.mustMapInterfaceSymbols()) {
651       symMap.pushScope();
652       mustPopSymMap = true;
653       Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap);
654     }
655     // If this is an indirect call, retrieve the function address. Also retrieve
656     // the result length if this is a character function (note that this length
657     // will be used only if there is no explicit length in the local interface).
658     mlir::Value funcPointer;
659     mlir::Value charFuncPointerLength;
660     if (caller.getIfIndirectCallSymbol()) {
661       TODO(loc, "genCallOpAndResult indirect call");
662     }
663 
664     mlir::IndexType idxTy = builder.getIndexType();
665     auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
666       return builder.createConvert(
667           loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
668     };
669     llvm::SmallVector<mlir::Value> resultLengths;
670     auto allocatedResult = [&]() -> llvm::Optional<ExtValue> {
671       llvm::SmallVector<mlir::Value> extents;
672       llvm::SmallVector<mlir::Value> lengths;
673       if (!caller.callerAllocateResult())
674         return {};
675       mlir::Type type = caller.getResultStorageType();
676       if (type.isa<fir::SequenceType>())
677         caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) {
678           extents.emplace_back(lowerSpecExpr(e));
679         });
680       caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) {
681         lengths.emplace_back(lowerSpecExpr(e));
682       });
683 
684       // Result length parameters should not be provided to box storage
685       // allocation and save_results, but they are still useful information to
686       // keep in the ExtendedValue if non-deferred.
687       if (!type.isa<fir::BoxType>()) {
688         if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
689           // Calling an assumed length function. This is only possible if this
690           // is a call to a character dummy procedure.
691           if (!charFuncPointerLength)
692             fir::emitFatalError(loc, "failed to retrieve character function "
693                                      "length while calling it");
694           lengths.push_back(charFuncPointerLength);
695         }
696         resultLengths = lengths;
697       }
698 
699       if (!extents.empty() || !lengths.empty()) {
700         TODO(loc, "genCallOpResult extents and length");
701       }
702       mlir::Value temp =
703           builder.createTemporary(loc, type, ".result", extents, resultLengths);
704       return toExtendedValue(loc, temp, extents, lengths);
705     }();
706 
707     if (mustPopSymMap)
708       symMap.popScope();
709 
710     // Place allocated result or prepare the fir.save_result arguments.
711     mlir::Value arrayResultShape;
712     if (allocatedResult) {
713       if (std::optional<Fortran::lower::CallInterface<
714               Fortran::lower::CallerInterface>::PassedEntity>
715               resultArg = caller.getPassedResult()) {
716         if (resultArg->passBy == PassBy::AddressAndLength)
717           caller.placeAddressAndLengthInput(*resultArg,
718                                             fir::getBase(*allocatedResult),
719                                             fir::getLen(*allocatedResult));
720         else if (resultArg->passBy == PassBy::BaseAddress)
721           caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
722         else
723           fir::emitFatalError(
724               loc, "only expect character scalar result to be passed by ref");
725       } else {
726         assert(caller.mustSaveResult());
727         arrayResultShape = allocatedResult->match(
728             [&](const fir::CharArrayBoxValue &) {
729               return builder.createShape(loc, *allocatedResult);
730             },
731             [&](const fir::ArrayBoxValue &) {
732               return builder.createShape(loc, *allocatedResult);
733             },
734             [&](const auto &) { return mlir::Value{}; });
735       }
736     }
737 
738     // In older Fortran, procedure argument types are inferred. This may lead
739     // different view of what the function signature is in different locations.
740     // Casts are inserted as needed below to accommodate this.
741 
742     // The mlir::FuncOp type prevails, unless it has a different number of
743     // arguments which can happen in legal program if it was passed as a dummy
744     // procedure argument earlier with no further type information.
745     mlir::SymbolRefAttr funcSymbolAttr;
746     bool addHostAssociations = false;
747     if (!funcPointer) {
748       mlir::FunctionType funcOpType = caller.getFuncOp().getType();
749       mlir::SymbolRefAttr symbolAttr =
750           builder.getSymbolRefAttr(caller.getMangledName());
751       if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
752           callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
753           fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
754                                    fir::getHostAssocAttrName())) {
755         // The number of arguments is off by one, and we're lowering a function
756         // with host associations. Modify call to include host associations
757         // argument by appending the value at the end of the operands.
758         assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
759                converter.hostAssocTupleValue().getType());
760         addHostAssociations = true;
761       }
762       if (!addHostAssociations &&
763           (callSiteType.getNumResults() != funcOpType.getNumResults() ||
764            callSiteType.getNumInputs() != funcOpType.getNumInputs())) {
765         // Deal with argument number mismatch by making a function pointer so
766         // that function type cast can be inserted. Do not emit a warning here
767         // because this can happen in legal program if the function is not
768         // defined here and it was first passed as an argument without any more
769         // information.
770         funcPointer =
771             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
772       } else if (callSiteType.getResults() != funcOpType.getResults()) {
773         // Implicit interface result type mismatch are not standard Fortran, but
774         // some compilers are not complaining about it.  The front end is not
775         // protecting lowering from this currently. Support this with a
776         // discouraging warning.
777         LLVM_DEBUG(mlir::emitWarning(
778             loc, "a return type mismatch is not standard compliant and may "
779                  "lead to undefined behavior."));
780         // Cast the actual function to the current caller implicit type because
781         // that is the behavior we would get if we could not see the definition.
782         funcPointer =
783             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
784       } else {
785         funcSymbolAttr = symbolAttr;
786       }
787     }
788 
789     mlir::FunctionType funcType =
790         funcPointer ? callSiteType : caller.getFuncOp().getType();
791     llvm::SmallVector<mlir::Value> operands;
792     // First operand of indirect call is the function pointer. Cast it to
793     // required function type for the call to handle procedures that have a
794     // compatible interface in Fortran, but that have different signatures in
795     // FIR.
796     if (funcPointer) {
797       operands.push_back(
798           funcPointer.getType().isa<fir::BoxProcType>()
799               ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
800               : builder.createConvert(loc, funcType, funcPointer));
801     }
802 
803     // Deal with potential mismatches in arguments types. Passing an array to a
804     // scalar argument should for instance be tolerated here.
805     bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
806     for (auto [fst, snd] :
807          llvm::zip(caller.getInputs(), funcType.getInputs())) {
808       // When passing arguments to a procedure that can be called an implicit
809       // interface, allow character actual arguments to be passed to dummy
810       // arguments of any type and vice versa
811       mlir::Value cast;
812       auto *context = builder.getContext();
813       if (snd.isa<fir::BoxProcType>() &&
814           fst.getType().isa<mlir::FunctionType>()) {
815         auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None);
816         auto boxProcTy = builder.getBoxProcType(funcTy);
817         if (mlir::Value host = argumentHostAssocs(converter, fst)) {
818           cast = builder.create<fir::EmboxProcOp>(
819               loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
820         } else {
821           cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
822         }
823       } else {
824         cast = builder.convertWithSemantics(loc, snd, fst,
825                                             callingImplicitInterface);
826       }
827       operands.push_back(cast);
828     }
829 
830     // Add host associations as necessary.
831     if (addHostAssociations)
832       operands.push_back(converter.hostAssocTupleValue());
833 
834     auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
835                                             funcSymbolAttr, operands);
836 
837     if (caller.mustSaveResult())
838       builder.create<fir::SaveResultOp>(
839           loc, call.getResult(0), fir::getBase(allocatedResult.getValue()),
840           arrayResultShape, resultLengths);
841 
842     if (allocatedResult) {
843       allocatedResult->match(
844           [&](const fir::MutableBoxValue &box) {
845             if (box.isAllocatable()) {
846               TODO(loc, "allocatedResult for allocatable");
847             }
848           },
849           [](const auto &) {});
850       return *allocatedResult;
851     }
852 
853     if (!resultType.hasValue())
854       return mlir::Value{}; // subroutine call
855     // For now, Fortran return values are implemented with a single MLIR
856     // function return value.
857     assert(call.getNumResults() == 1 &&
858            "Expected exactly one result in FUNCTION call");
859     return call.getResult(0);
860   }
861 
862   /// Like genExtAddr, but ensure the address returned is a temporary even if \p
863   /// expr is variable inside parentheses.
864   ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
865     // In general, genExtAddr might not create a temp for variable inside
866     // parentheses to avoid creating array temporary in sub-expressions. It only
867     // ensures the sub-expression is not re-associated with other parts of the
868     // expression. In the call semantics, there is a difference between expr and
869     // variable (see R1524). For expressions, a variable storage must not be
870     // argument associated since it could be modified inside the call, or the
871     // variable could also be modified by other means during the call.
872     if (!isParenthesizedVariable(expr))
873       return genExtAddr(expr);
874     mlir::Location loc = getLoc();
875     if (expr.Rank() > 0)
876       TODO(loc, "genTempExtAddr array");
877     return genExtValue(expr).match(
878         [&](const fir::CharBoxValue &boxChar) -> ExtValue {
879           TODO(loc, "genTempExtAddr CharBoxValue");
880         },
881         [&](const fir::UnboxedValue &v) -> ExtValue {
882           mlir::Type type = v.getType();
883           mlir::Value value = v;
884           if (fir::isa_ref_type(type))
885             value = builder.create<fir::LoadOp>(loc, value);
886           mlir::Value temp = builder.createTemporary(loc, value.getType());
887           builder.create<fir::StoreOp>(loc, value, temp);
888           return temp;
889         },
890         [&](const fir::BoxValue &x) -> ExtValue {
891           // Derived type scalar that may be polymorphic.
892           assert(!x.hasRank() && x.isDerived());
893           if (x.isDerivedWithLengthParameters())
894             fir::emitFatalError(
895                 loc, "making temps for derived type with length parameters");
896           // TODO: polymorphic aspects should be kept but for now the temp
897           // created always has the declared type.
898           mlir::Value var =
899               fir::getBase(fir::factory::readBoxValue(builder, loc, x));
900           auto value = builder.create<fir::LoadOp>(loc, var);
901           mlir::Value temp = builder.createTemporary(loc, value.getType());
902           builder.create<fir::StoreOp>(loc, value, temp);
903           return temp;
904         },
905         [&](const auto &) -> ExtValue {
906           fir::emitFatalError(loc, "expr is not a scalar value");
907         });
908   }
909 
910   /// Helper structure to track potential copy-in of non contiguous variable
911   /// argument into a contiguous temp. It is used to deallocate the temp that
912   /// may have been created as well as to the copy-out from the temp to the
913   /// variable after the call.
914   struct CopyOutPair {
915     ExtValue var;
916     ExtValue temp;
917     // Flag to indicate if the argument may have been modified by the
918     // callee, in which case it must be copied-out to the variable.
919     bool argMayBeModifiedByCall;
920     // Optional boolean value that, if present and false, prevents
921     // the copy-out and temp deallocation.
922     llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime;
923   };
924   using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>;
925 
926   /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories
927   /// not based on fir.box.
928   /// This will lose any non contiguous stride information and dynamic type and
929   /// should only be called if \p exv is known to be contiguous or if its base
930   /// address will be replaced by a contiguous one. If \p exv is not a
931   /// fir::BoxValue, this is a no-op.
932   ExtValue readIfBoxValue(const ExtValue &exv) {
933     if (const auto *box = exv.getBoxOf<fir::BoxValue>())
934       return fir::factory::readBoxValue(builder, getLoc(), *box);
935     return exv;
936   }
937 
938   /// Lower a non-elemental procedure reference.
939   ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
940                               llvm::Optional<mlir::Type> resultType) {
941     mlir::Location loc = getLoc();
942     if (isElementalProcWithArrayArgs(procRef))
943       fir::emitFatalError(loc, "trying to lower elemental procedure with array "
944                                "arguments as normal procedure");
945     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
946             procRef.proc().GetSpecificIntrinsic())
947       return genIntrinsicRef(procRef, *intrinsic, resultType);
948 
949     if (isStatementFunctionCall(procRef))
950       TODO(loc, "Lower statement function call");
951 
952     Fortran::lower::CallerInterface caller(procRef, converter);
953     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
954 
955     llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall;
956     // List of <var, temp> where temp must be copied into var after the call.
957     CopyOutPairs copyOutPairs;
958 
959     mlir::FunctionType callSiteType = caller.genFunctionType();
960 
961     // Lower the actual arguments and map the lowered values to the dummy
962     // arguments.
963     for (const Fortran::lower::CallInterface<
964              Fortran::lower::CallerInterface>::PassedEntity &arg :
965          caller.getPassedArguments()) {
966       const auto *actual = arg.entity;
967       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
968       if (!actual) {
969         // Optional dummy argument for which there is no actual argument.
970         caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy));
971         continue;
972       }
973       const auto *expr = actual->UnwrapExpr();
974       if (!expr)
975         TODO(loc, "assumed type actual argument lowering");
976 
977       if (arg.passBy == PassBy::Value) {
978         ExtValue argVal = genval(*expr);
979         if (!fir::isUnboxedValue(argVal))
980           fir::emitFatalError(
981               loc, "internal error: passing non trivial value by value");
982         caller.placeInput(arg, fir::getBase(argVal));
983         continue;
984       }
985 
986       if (arg.passBy == PassBy::MutableBox) {
987         TODO(loc, "arg passby MutableBox");
988       }
989       const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr);
990       if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) {
991         auto argAddr = [&]() -> ExtValue {
992           ExtValue baseAddr;
993           if (actualArgIsVariable && arg.isOptional()) {
994             if (Fortran::evaluate::IsAllocatableOrPointerObject(
995                     *expr, converter.getFoldingContext())) {
996               TODO(loc, "Allocatable or pointer argument");
997             }
998             if (const Fortran::semantics::Symbol *wholeSymbol =
999                     Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(
1000                         *expr))
1001               if (Fortran::semantics::IsOptional(*wholeSymbol)) {
1002                 TODO(loc, "procedureref optional arg");
1003               }
1004             // Fall through: The actual argument can safely be
1005             // copied-in/copied-out without any care if needed.
1006           }
1007           if (actualArgIsVariable && expr->Rank() > 0) {
1008             TODO(loc, "procedureref arrays");
1009           }
1010           // Actual argument is a non optional/non pointer/non allocatable
1011           // scalar.
1012           if (actualArgIsVariable)
1013             return genExtAddr(*expr);
1014           // Actual argument is not a variable. Make sure a variable address is
1015           // not passed.
1016           return genTempExtAddr(*expr);
1017         }();
1018         // Scalar and contiguous expressions may be lowered to a fir.box,
1019         // either to account for potential polymorphism, or because lowering
1020         // did not account for some contiguity hints.
1021         // Here, polymorphism does not matter (an entity of the declared type
1022         // is passed, not one of the dynamic type), and the expr is known to
1023         // be simply contiguous, so it is safe to unbox it and pass the
1024         // address without making a copy.
1025         argAddr = readIfBoxValue(argAddr);
1026 
1027         if (arg.passBy == PassBy::BaseAddress) {
1028           caller.placeInput(arg, fir::getBase(argAddr));
1029         } else {
1030           TODO(loc, "procedureref PassBy::BoxChar");
1031         }
1032       } else if (arg.passBy == PassBy::Box) {
1033         // Before lowering to an address, handle the allocatable/pointer actual
1034         // argument to optional fir.box dummy. It is legal to pass
1035         // unallocated/disassociated entity to an optional. In this case, an
1036         // absent fir.box must be created instead of a fir.box with a null value
1037         // (Fortran 2018 15.5.2.12 point 1).
1038         if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject(
1039                                     *expr, converter.getFoldingContext())) {
1040           TODO(loc, "optional allocatable or pointer argument");
1041         } else {
1042           // Make sure a variable address is only passed if the expression is
1043           // actually a variable.
1044           mlir::Value box =
1045               actualArgIsVariable
1046                   ? builder.createBox(loc, genBoxArg(*expr))
1047                   : builder.createBox(getLoc(), genTempExtAddr(*expr));
1048           caller.placeInput(arg, box);
1049         }
1050       } else if (arg.passBy == PassBy::AddressAndLength) {
1051         ExtValue argRef = genExtAddr(*expr);
1052         caller.placeAddressAndLengthInput(arg, fir::getBase(argRef),
1053                                           fir::getLen(argRef));
1054       } else if (arg.passBy == PassBy::CharProcTuple) {
1055         TODO(loc, "procedureref CharProcTuple");
1056       } else {
1057         TODO(loc, "pass by value in non elemental function call");
1058       }
1059     }
1060 
1061     ExtValue result = genCallOpAndResult(caller, callSiteType, resultType);
1062 
1063     // // Copy-out temps that were created for non contiguous variable arguments
1064     // if
1065     // // needed.
1066     // for (const auto &copyOutPair : copyOutPairs)
1067     //   genCopyOut(copyOutPair);
1068 
1069     return result;
1070   }
1071 
1072   template <typename A>
1073   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1074     ExtValue result = genFunctionRef(funcRef);
1075     if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
1076       return genLoad(result);
1077     return result;
1078   }
1079 
1080   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
1081     llvm::Optional<mlir::Type> resTy;
1082     if (procRef.hasAlternateReturns())
1083       resTy = builder.getIndexType();
1084     return genProcedureRef(procRef, resTy);
1085   }
1086 
1087   /// Generate a call to an intrinsic function.
1088   ExtValue
1089   genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
1090                   const Fortran::evaluate::SpecificIntrinsic &intrinsic,
1091                   llvm::Optional<mlir::Type> resultType) {
1092     llvm::SmallVector<ExtValue> operands;
1093 
1094     llvm::StringRef name = intrinsic.name;
1095     mlir::Location loc = getLoc();
1096 
1097     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
1098         Fortran::lower::getIntrinsicArgumentLowering(name);
1099     for (const auto &[arg, dummy] :
1100          llvm::zip(procRef.arguments(),
1101                    intrinsic.characteristics.value().dummyArguments)) {
1102       auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
1103       if (!expr) {
1104         // Absent optional.
1105         operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
1106         continue;
1107       }
1108       if (!argLowering) {
1109         // No argument lowering instruction, lower by value.
1110         operands.emplace_back(genval(*expr));
1111         continue;
1112       }
1113       // Ad-hoc argument lowering handling.
1114       Fortran::lower::ArgLoweringRule argRules =
1115           Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering,
1116                                                    dummy.name);
1117       switch (argRules.lowerAs) {
1118       case Fortran::lower::LowerIntrinsicArgAs::Value:
1119         operands.emplace_back(genval(*expr));
1120         continue;
1121       case Fortran::lower::LowerIntrinsicArgAs::Addr:
1122         TODO(getLoc(), "argument lowering for Addr");
1123         continue;
1124       case Fortran::lower::LowerIntrinsicArgAs::Box:
1125         TODO(getLoc(), "argument lowering for Box");
1126         continue;
1127       case Fortran::lower::LowerIntrinsicArgAs::Inquired:
1128         TODO(getLoc(), "argument lowering for Inquired");
1129         continue;
1130       }
1131       llvm_unreachable("bad switch");
1132     }
1133     // Let the intrinsic library lower the intrinsic procedure call
1134     return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
1135                                             operands);
1136   }
1137 
1138   template <typename A>
1139   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
1140     if (isScalar(x))
1141       return std::visit([&](const auto &e) { return genval(e); }, x.u);
1142     TODO(getLoc(), "genval Expr<A> arrays");
1143   }
1144 
1145   /// Helper to detect Transformational function reference.
1146   template <typename T>
1147   bool isTransformationalRef(const T &) {
1148     return false;
1149   }
1150   template <typename T>
1151   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
1152     return !funcRef.IsElemental() && funcRef.Rank();
1153   }
1154   template <typename T>
1155   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
1156     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
1157                       expr.u);
1158   }
1159 
1160   template <typename A>
1161   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
1162     // Whole array symbols or components, and results of transformational
1163     // functions already have a storage and the scalar expression lowering path
1164     // is used to not create a new temporary storage.
1165     if (isScalar(x) ||
1166         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
1167         isTransformationalRef(x))
1168       return std::visit([&](const auto &e) { return genref(e); }, x.u);
1169     TODO(getLoc(), "gen Expr non-scalar");
1170   }
1171 
1172   template <typename A>
1173   bool isScalar(const A &x) {
1174     return x.Rank() == 0;
1175   }
1176 
1177   template <int KIND>
1178   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
1179                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
1180     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
1181   }
1182 
1183   using RefSet =
1184       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
1185                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
1186                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
1187                  Fortran::semantics::SymbolRef>;
1188   template <typename A>
1189   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
1190 
1191   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
1192   ExtValue genref(const A &a) {
1193     return gen(a);
1194   }
1195   template <typename A>
1196   ExtValue genref(const A &a) {
1197     mlir::Type storageType = converter.genType(toEvExpr(a));
1198     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
1199   }
1200 
1201   template <typename A, template <typename> typename T,
1202             typename B = std::decay_t<T<A>>,
1203             std::enable_if_t<
1204                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
1205                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
1206                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
1207                 bool> = true>
1208   ExtValue genref(const T<A> &x) {
1209     return gen(x);
1210   }
1211 
1212 private:
1213   mlir::Location location;
1214   Fortran::lower::AbstractConverter &converter;
1215   fir::FirOpBuilder &builder;
1216   Fortran::lower::StatementContext &stmtCtx;
1217   Fortran::lower::SymMap &symMap;
1218   bool useBoxArg = false; // expression lowered as argument
1219 };
1220 } // namespace
1221 
1222 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
1223     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
1224     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
1225     Fortran::lower::StatementContext &stmtCtx) {
1226   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
1227   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr);
1228 }
1229 
1230 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
1231     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
1232     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
1233     Fortran::lower::StatementContext &stmtCtx) {
1234   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
1235   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr);
1236 }
1237 
1238 mlir::Value Fortran::lower::createSubroutineCall(
1239     AbstractConverter &converter, const evaluate::ProcedureRef &call,
1240     SymMap &symMap, StatementContext &stmtCtx) {
1241   mlir::Location loc = converter.getCurrentLocation();
1242 
1243   // Simple subroutine call, with potential alternate return.
1244   auto res = Fortran::lower::createSomeExtendedExpression(
1245       loc, converter, toEvExpr(call), symMap, stmtCtx);
1246   return fir::getBase(res);
1247 }
1248