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/ComponentPath.h"
19 #include "flang/Lower/ConvertType.h"
20 #include "flang/Lower/ConvertVariable.h"
21 #include "flang/Lower/DumpEvaluateExpr.h"
22 #include "flang/Lower/IntrinsicCall.h"
23 #include "flang/Lower/StatementContext.h"
24 #include "flang/Lower/SymbolMap.h"
25 #include "flang/Lower/Todo.h"
26 #include "flang/Optimizer/Builder/Character.h"
27 #include "flang/Optimizer/Builder/Complex.h"
28 #include "flang/Optimizer/Builder/Factory.h"
29 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h"
30 #include "flang/Optimizer/Builder/MutableBox.h"
31 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
32 #include "flang/Semantics/expression.h"
33 #include "flang/Semantics/symbol.h"
34 #include "flang/Semantics/tools.h"
35 #include "flang/Semantics/type.h"
36 #include "mlir/Dialect/Func/IR/FuncOps.h"
37 #include "llvm/Support/Debug.h"
38 
39 #define DEBUG_TYPE "flang-lower-expr"
40 
41 //===----------------------------------------------------------------------===//
42 // The composition and structure of Fortran::evaluate::Expr is defined in
43 // the various header files in include/flang/Evaluate. You are referred
44 // there for more information on these data structures. Generally speaking,
45 // these data structures are a strongly typed family of abstract data types
46 // that, composed as trees, describe the syntax of Fortran expressions.
47 //
48 // This part of the bridge can traverse these tree structures and lower them
49 // to the correct FIR representation in SSA form.
50 //===----------------------------------------------------------------------===//
51 
52 /// The various semantics of a program constituent (or a part thereof) as it may
53 /// appear in an expression.
54 ///
55 /// Given the following Fortran declarations.
56 /// ```fortran
57 ///   REAL :: v1, v2, v3
58 ///   REAL, POINTER :: vp1
59 ///   REAL :: a1(c), a2(c)
60 ///   REAL ELEMENTAL FUNCTION f1(arg) ! array -> array
61 ///   FUNCTION f2(arg)                ! array -> array
62 ///   vp1 => v3       ! 1
63 ///   v1 = v2 * vp1   ! 2
64 ///   a1 = a1 + a2    ! 3
65 ///   a1 = f1(a2)     ! 4
66 ///   a1 = f2(a2)     ! 5
67 /// ```
68 ///
69 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is
70 /// constructed from the DataAddr of `v3`.
71 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed
72 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double
73 /// dereference in the `vp1` case.
74 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs
75 /// is CopyInCopyOut as `a1` is replaced elementally by the additions.
76 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if
77 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/
78 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut.
79 ///  In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational.
80 ///  `a1` on the lhs is again CopyInCopyOut.
81 enum class ConstituentSemantics {
82   // Scalar data reference semantics.
83   //
84   // For these let `v` be the location in memory of a variable with value `x`
85   DataValue, // refers to the value `x`
86   DataAddr,  // refers to the address `v`
87   BoxValue,  // refers to a box value containing `v`
88   BoxAddr,   // refers to the address of a box value containing `v`
89 
90   // Array data reference semantics.
91   //
92   // For these let `a` be the location in memory of a sequence of value `[xs]`.
93   // Let `x_i` be the `i`-th value in the sequence `[xs]`.
94 
95   // Referentially transparent. Refers to the array's value, `[xs]`.
96   RefTransparent,
97   // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7
98   // note 2). (Passing a copy by reference to simulate pass-by-value.)
99   ByValueArg,
100   // Refers to the merge of array value `[xs]` with another array value `[ys]`.
101   // This merged array value will be written into memory location `a`.
102   CopyInCopyOut,
103   // Similar to CopyInCopyOut but `a` may be a transient projection (rather than
104   // a whole array).
105   ProjectedCopyInCopyOut,
106   // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned
107   // automatically by the framework. Instead, and address for `[xs]` is made
108   // accessible so that custom assignments to `[xs]` can be implemented.
109   CustomCopyInCopyOut,
110   // Referentially opaque. Refers to the address of `x_i`.
111   RefOpaque
112 };
113 
114 /// Convert parser's INTEGER relational operators to MLIR.  TODO: using
115 /// unordered, but we may want to cons ordered in certain situation.
116 static mlir::arith::CmpIPredicate
117 translateRelational(Fortran::common::RelationalOperator rop) {
118   switch (rop) {
119   case Fortran::common::RelationalOperator::LT:
120     return mlir::arith::CmpIPredicate::slt;
121   case Fortran::common::RelationalOperator::LE:
122     return mlir::arith::CmpIPredicate::sle;
123   case Fortran::common::RelationalOperator::EQ:
124     return mlir::arith::CmpIPredicate::eq;
125   case Fortran::common::RelationalOperator::NE:
126     return mlir::arith::CmpIPredicate::ne;
127   case Fortran::common::RelationalOperator::GT:
128     return mlir::arith::CmpIPredicate::sgt;
129   case Fortran::common::RelationalOperator::GE:
130     return mlir::arith::CmpIPredicate::sge;
131   }
132   llvm_unreachable("unhandled INTEGER relational operator");
133 }
134 
135 /// Convert parser's REAL relational operators to MLIR.
136 /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018
137 /// requirements in the IEEE context (table 17.1 of F2018). This choice is
138 /// also applied in other contexts because it is easier and in line with
139 /// other Fortran compilers.
140 /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not
141 /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee
142 /// whether the comparison will signal or not in case of quiet NaN argument.
143 static mlir::arith::CmpFPredicate
144 translateFloatRelational(Fortran::common::RelationalOperator rop) {
145   switch (rop) {
146   case Fortran::common::RelationalOperator::LT:
147     return mlir::arith::CmpFPredicate::OLT;
148   case Fortran::common::RelationalOperator::LE:
149     return mlir::arith::CmpFPredicate::OLE;
150   case Fortran::common::RelationalOperator::EQ:
151     return mlir::arith::CmpFPredicate::OEQ;
152   case Fortran::common::RelationalOperator::NE:
153     return mlir::arith::CmpFPredicate::UNE;
154   case Fortran::common::RelationalOperator::GT:
155     return mlir::arith::CmpFPredicate::OGT;
156   case Fortran::common::RelationalOperator::GE:
157     return mlir::arith::CmpFPredicate::OGE;
158   }
159   llvm_unreachable("unhandled REAL relational operator");
160 }
161 
162 /// Place \p exv in memory if it is not already a memory reference. If
163 /// \p forceValueType is provided, the value is first casted to the provided
164 /// type before being stored (this is mainly intended for logicals whose value
165 /// may be `i1` but needed to be stored as Fortran logicals).
166 static fir::ExtendedValue
167 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
168                          const fir::ExtendedValue &exv,
169                          mlir::Type storageType) {
170   mlir::Value valBase = fir::getBase(exv);
171   if (fir::conformsWithPassByRef(valBase.getType()))
172     return exv;
173 
174   assert(!fir::hasDynamicSize(storageType) &&
175          "only expect statically sized scalars to be by value");
176 
177   // Since `a` is not itself a valid referent, determine its value and
178   // create a temporary location at the beginning of the function for
179   // referencing.
180   mlir::Value val = builder.createConvert(loc, storageType, valBase);
181   mlir::Value temp = builder.createTemporary(
182       loc, storageType,
183       llvm::ArrayRef<mlir::NamedAttribute>{
184           Fortran::lower::getAdaptToByRefAttr(builder)});
185   builder.create<fir::StoreOp>(loc, val, temp);
186   return fir::substBase(exv, temp);
187 }
188 
189 /// Is this a variable wrapped in parentheses?
190 template <typename A>
191 static bool isParenthesizedVariable(const A &) {
192   return false;
193 }
194 template <typename T>
195 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) {
196   using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
197   using Parentheses = Fortran::evaluate::Parentheses<T>;
198   if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
199     if (const auto *parentheses = std::get_if<Parentheses>(&expr.u))
200       return Fortran::evaluate::IsVariable(parentheses->left());
201     return false;
202   } else {
203     return std::visit([&](const auto &x) { return isParenthesizedVariable(x); },
204                       expr.u);
205   }
206 }
207 
208 /// Generate a load of a value from an address. Beware that this will lose
209 /// any dynamic type information for polymorphic entities (note that unlimited
210 /// polymorphic cannot be loaded and must not be provided here).
211 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
212                                   mlir::Location loc,
213                                   const fir::ExtendedValue &addr) {
214   return addr.match(
215       [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
216       [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
217         if (fir::unwrapRefType(fir::getBase(v).getType())
218                 .isa<fir::RecordType>())
219           return v;
220         return builder.create<fir::LoadOp>(loc, fir::getBase(v));
221       },
222       [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
223         TODO(loc, "genLoad for MutableBoxValue");
224       },
225       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
226         TODO(loc, "genLoad for BoxValue");
227       },
228       [&](const auto &) -> fir::ExtendedValue {
229         fir::emitFatalError(
230             loc, "attempting to load whole array or procedure address");
231       });
232 }
233 
234 /// Is this a call to an elemental procedure with at least one array argument?
235 static bool
236 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
237   if (procRef.IsElemental())
238     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
239          procRef.arguments())
240       if (arg && arg->Rank() != 0)
241         return true;
242   return false;
243 }
244 template <typename T>
245 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) {
246   return false;
247 }
248 template <>
249 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) {
250   if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u))
251     return isElementalProcWithArrayArgs(*procRef);
252   return false;
253 }
254 
255 /// Some auxiliary data for processing initialization in ScalarExprLowering
256 /// below. This is currently used for generating dense attributed global
257 /// arrays.
258 struct InitializerData {
259   explicit InitializerData(bool getRawVals = false) : genRawVals{getRawVals} {}
260   llvm::SmallVector<mlir::Attribute> rawVals; // initialization raw values
261   mlir::Type rawType; // Type of elements processed for rawVals vector.
262   bool genRawVals;    // generate the rawVals vector if set.
263 };
264 
265 /// If \p arg is the address of a function with a denoted host-association tuple
266 /// argument, then return the host-associations tuple value of the current
267 /// procedure. Otherwise, return nullptr.
268 static mlir::Value
269 argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
270                    mlir::Value arg) {
271   if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
272     auto &builder = converter.getFirOpBuilder();
273     if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
274       if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
275         return converter.hostAssocTupleValue();
276   }
277   return {};
278 }
279 
280 namespace {
281 
282 /// Lowering of Fortran::evaluate::Expr<T> expressions
283 class ScalarExprLowering {
284 public:
285   using ExtValue = fir::ExtendedValue;
286 
287   explicit ScalarExprLowering(mlir::Location loc,
288                               Fortran::lower::AbstractConverter &converter,
289                               Fortran::lower::SymMap &symMap,
290                               Fortran::lower::StatementContext &stmtCtx,
291                               InitializerData *initializer = nullptr)
292       : location{loc}, converter{converter},
293         builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap} {
294   }
295 
296   ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) {
297     return gen(expr);
298   }
299 
300   /// Lower `expr` to be passed as a fir.box argument. Do not create a temp
301   /// for the expr if it is a variable that can be described as a fir.box.
302   ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) {
303     bool saveUseBoxArg = useBoxArg;
304     useBoxArg = true;
305     ExtValue result = gen(expr);
306     useBoxArg = saveUseBoxArg;
307     return result;
308   }
309 
310   ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) {
311     return genval(expr);
312   }
313 
314   /// Lower an expression that is a pointer or an allocatable to a
315   /// MutableBoxValue.
316   fir::MutableBoxValue
317   genMutableBoxValue(const Fortran::lower::SomeExpr &expr) {
318     // Pointers and allocatables can only be:
319     //    - a simple designator "x"
320     //    - a component designator "a%b(i,j)%x"
321     //    - a function reference "foo()"
322     //    - result of NULL() or NULL(MOLD) intrinsic.
323     //    NULL() requires some context to be lowered, so it is not handled
324     //    here and must be lowered according to the context where it appears.
325     ExtValue exv = std::visit(
326         [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u);
327     const fir::MutableBoxValue *mutableBox =
328         exv.getBoxOf<fir::MutableBoxValue>();
329     if (!mutableBox)
330       fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue");
331     return *mutableBox;
332   }
333 
334   template <typename T>
335   ExtValue genMutableBoxValueImpl(const T &) {
336     // NULL() case should not be handled here.
337     fir::emitFatalError(getLoc(), "NULL() must be lowered in its context");
338   }
339 
340   template <typename T>
341   ExtValue
342   genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) {
343     return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef)));
344   }
345 
346   template <typename T>
347   ExtValue
348   genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) {
349     return std::visit(
350         Fortran::common::visitors{
351             [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue {
352               return symMap.lookupSymbol(*sym).toExtendedValue();
353             },
354             [&](const Fortran::evaluate::Component &comp) -> ExtValue {
355               return genComponent(comp);
356             },
357             [&](const auto &) -> ExtValue {
358               fir::emitFatalError(getLoc(),
359                                   "not an allocatable or pointer designator");
360             }},
361         designator.u);
362   }
363 
364   template <typename T>
365   ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) {
366     return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); },
367                       expr.u);
368   }
369 
370   mlir::Location getLoc() { return location; }
371 
372   template <typename A>
373   mlir::Value genunbox(const A &expr) {
374     ExtValue e = genval(expr);
375     if (const fir::UnboxedValue *r = e.getUnboxed())
376       return *r;
377     fir::emitFatalError(getLoc(), "unboxed expression expected");
378   }
379 
380   /// Generate an integral constant of `value`
381   template <int KIND>
382   mlir::Value genIntegerConstant(mlir::MLIRContext *context,
383                                  std::int64_t value) {
384     mlir::Type type =
385         converter.genType(Fortran::common::TypeCategory::Integer, KIND);
386     return builder.createIntegerConstant(getLoc(), type, value);
387   }
388 
389   /// Generate a logical/boolean constant of `value`
390   mlir::Value genBoolConstant(bool value) {
391     return builder.createBool(getLoc(), value);
392   }
393 
394   /// Generate a real constant with a value `value`.
395   template <int KIND>
396   mlir::Value genRealConstant(mlir::MLIRContext *context,
397                               const llvm::APFloat &value) {
398     mlir::Type fltTy = Fortran::lower::convertReal(context, KIND);
399     return builder.createRealConstant(getLoc(), fltTy, value);
400   }
401 
402   template <typename OpTy>
403   mlir::Value createCompareOp(mlir::arith::CmpIPredicate pred,
404                               const ExtValue &left, const ExtValue &right) {
405     if (const fir::UnboxedValue *lhs = left.getUnboxed())
406       if (const fir::UnboxedValue *rhs = right.getUnboxed())
407         return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
408     fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
409   }
410   template <typename OpTy, typename A>
411   mlir::Value createCompareOp(const A &ex, mlir::arith::CmpIPredicate pred) {
412     ExtValue left = genval(ex.left());
413     return createCompareOp<OpTy>(pred, left, genval(ex.right()));
414   }
415 
416   template <typename OpTy>
417   mlir::Value createFltCmpOp(mlir::arith::CmpFPredicate pred,
418                              const ExtValue &left, const ExtValue &right) {
419     if (const fir::UnboxedValue *lhs = left.getUnboxed())
420       if (const fir::UnboxedValue *rhs = right.getUnboxed())
421         return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
422     fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
423   }
424   template <typename OpTy, typename A>
425   mlir::Value createFltCmpOp(const A &ex, mlir::arith::CmpFPredicate pred) {
426     ExtValue left = genval(ex.left());
427     return createFltCmpOp<OpTy>(pred, left, genval(ex.right()));
428   }
429 
430   /// Returns a reference to a symbol or its box/boxChar descriptor if it has
431   /// one.
432   ExtValue gen(Fortran::semantics::SymbolRef sym) {
433     if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
434       return val.match(
435           [&](const Fortran::lower::SymbolBox::PointerOrAllocatable &boxAddr) {
436             return fir::factory::genMutableBoxRead(builder, getLoc(), boxAddr);
437           },
438           [&val](auto &) { return val.toExtendedValue(); });
439     LLVM_DEBUG(llvm::dbgs()
440                << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
441     fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
442   }
443 
444   ExtValue genLoad(const ExtValue &exv) {
445     return ::genLoad(builder, getLoc(), exv);
446   }
447 
448   ExtValue genval(Fortran::semantics::SymbolRef sym) {
449     ExtValue var = gen(sym);
450     if (const fir::UnboxedValue *s = var.getUnboxed())
451       if (fir::isReferenceLike(s->getType()))
452         return genLoad(*s);
453     return var;
454   }
455 
456   ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
457     TODO(getLoc(), "genval BOZ");
458   }
459 
460   /// Return indirection to function designated in ProcedureDesignator.
461   /// The type of the function indirection is not guaranteed to match the one
462   /// of the ProcedureDesignator due to Fortran implicit typing rules.
463   ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
464     TODO(getLoc(), "genval ProcedureDesignator");
465   }
466 
467   ExtValue genval(const Fortran::evaluate::NullPointer &) {
468     TODO(getLoc(), "genval NullPointer");
469   }
470 
471   ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
472     TODO(getLoc(), "genval StructureConstructor");
473   }
474 
475   /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
476   ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
477     TODO(getLoc(), "genval ImpliedDoIndex");
478   }
479 
480   ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
481     TODO(getLoc(), "genval DescriptorInquiry");
482   }
483 
484   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
485     TODO(getLoc(), "genval TypeParamInquiry");
486   }
487 
488   template <int KIND>
489   ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
490     TODO(getLoc(), "genval ComplexComponent");
491   }
492 
493   template <int KIND>
494   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
495                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
496     mlir::Value input = genunbox(op.left());
497     // Like LLVM, integer negation is the binary op "0 - value"
498     mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
499     return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
500   }
501 
502   template <int KIND>
503   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
504                       Fortran::common::TypeCategory::Real, KIND>> &op) {
505     return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
506   }
507   template <int KIND>
508   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
509                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
510     return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
511   }
512 
513   template <typename OpTy>
514   mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
515     assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
516     mlir::Value lhs = fir::getBase(left);
517     mlir::Value rhs = fir::getBase(right);
518     assert(lhs.getType() == rhs.getType() && "types must be the same");
519     return builder.create<OpTy>(getLoc(), lhs, rhs);
520   }
521 
522   template <typename OpTy, typename A>
523   mlir::Value createBinaryOp(const A &ex) {
524     ExtValue left = genval(ex.left());
525     return createBinaryOp<OpTy>(left, genval(ex.right()));
526   }
527 
528 #undef GENBIN
529 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
530   template <int KIND>                                                          \
531   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
532                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
533     return createBinaryOp<GenBinFirOp>(x);                                     \
534   }
535 
536   GENBIN(Add, Integer, mlir::arith::AddIOp)
537   GENBIN(Add, Real, mlir::arith::AddFOp)
538   GENBIN(Add, Complex, fir::AddcOp)
539   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
540   GENBIN(Subtract, Real, mlir::arith::SubFOp)
541   GENBIN(Subtract, Complex, fir::SubcOp)
542   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
543   GENBIN(Multiply, Real, mlir::arith::MulFOp)
544   GENBIN(Multiply, Complex, fir::MulcOp)
545   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
546   GENBIN(Divide, Real, mlir::arith::DivFOp)
547   GENBIN(Divide, Complex, fir::DivcOp)
548 
549   template <Fortran::common::TypeCategory TC, int KIND>
550   ExtValue genval(
551       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
552     mlir::Type ty = converter.genType(TC, KIND);
553     mlir::Value lhs = genunbox(op.left());
554     mlir::Value rhs = genunbox(op.right());
555     return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
556   }
557 
558   template <Fortran::common::TypeCategory TC, int KIND>
559   ExtValue genval(
560       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
561           &op) {
562     mlir::Type ty = converter.genType(TC, KIND);
563     mlir::Value lhs = genunbox(op.left());
564     mlir::Value rhs = genunbox(op.right());
565     return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
566   }
567 
568   template <int KIND>
569   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
570     mlir::Value realPartValue = genunbox(op.left());
571     return fir::factory::Complex{builder, getLoc()}.createComplex(
572         KIND, realPartValue, genunbox(op.right()));
573   }
574 
575   template <int KIND>
576   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
577     TODO(getLoc(), "genval Concat<KIND>");
578   }
579 
580   /// MIN and MAX operations
581   template <Fortran::common::TypeCategory TC, int KIND>
582   ExtValue
583   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
584              &op) {
585     TODO(getLoc(), "genval Extremum<TC, KIND>");
586   }
587 
588   template <int KIND>
589   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
590     TODO(getLoc(), "genval SetLength<KIND>");
591   }
592 
593   template <int KIND>
594   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
595                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
596     return createCompareOp<mlir::arith::CmpIOp>(op,
597                                                 translateRelational(op.opr));
598   }
599   template <int KIND>
600   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
601                       Fortran::common::TypeCategory::Real, KIND>> &op) {
602     return createFltCmpOp<mlir::arith::CmpFOp>(
603         op, translateFloatRelational(op.opr));
604   }
605   template <int KIND>
606   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
607                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
608     TODO(getLoc(), "genval complex comparison");
609   }
610   template <int KIND>
611   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
612                       Fortran::common::TypeCategory::Character, KIND>> &op) {
613     TODO(getLoc(), "genval char comparison");
614   }
615 
616   ExtValue
617   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
618     return std::visit([&](const auto &x) { return genval(x); }, op.u);
619   }
620 
621   template <Fortran::common::TypeCategory TC1, int KIND,
622             Fortran::common::TypeCategory TC2>
623   ExtValue
624   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
625                                           TC2> &convert) {
626     mlir::Type ty = converter.genType(TC1, KIND);
627     mlir::Value operand = genunbox(convert.left());
628     return builder.convertWithSemantics(getLoc(), ty, operand);
629   }
630 
631   template <typename A>
632   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
633     TODO(getLoc(), "genval parentheses<A>");
634   }
635 
636   template <int KIND>
637   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
638     mlir::Value logical = genunbox(op.left());
639     mlir::Value one = genBoolConstant(true);
640     mlir::Value val =
641         builder.createConvert(getLoc(), builder.getI1Type(), logical);
642     return builder.create<mlir::arith::XOrIOp>(getLoc(), val, one);
643   }
644 
645   template <int KIND>
646   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
647     mlir::IntegerType i1Type = builder.getI1Type();
648     mlir::Value slhs = genunbox(op.left());
649     mlir::Value srhs = genunbox(op.right());
650     mlir::Value lhs = builder.createConvert(getLoc(), i1Type, slhs);
651     mlir::Value rhs = builder.createConvert(getLoc(), i1Type, srhs);
652     switch (op.logicalOperator) {
653     case Fortran::evaluate::LogicalOperator::And:
654       return createBinaryOp<mlir::arith::AndIOp>(lhs, rhs);
655     case Fortran::evaluate::LogicalOperator::Or:
656       return createBinaryOp<mlir::arith::OrIOp>(lhs, rhs);
657     case Fortran::evaluate::LogicalOperator::Eqv:
658       return createCompareOp<mlir::arith::CmpIOp>(
659           mlir::arith::CmpIPredicate::eq, lhs, rhs);
660     case Fortran::evaluate::LogicalOperator::Neqv:
661       return createCompareOp<mlir::arith::CmpIOp>(
662           mlir::arith::CmpIPredicate::ne, lhs, rhs);
663     case Fortran::evaluate::LogicalOperator::Not:
664       // lib/evaluate expression for .NOT. is Fortran::evaluate::Not<KIND>.
665       llvm_unreachable(".NOT. is not a binary operator");
666     }
667     llvm_unreachable("unhandled logical operation");
668   }
669 
670   /// Convert a scalar literal constant to IR.
671   template <Fortran::common::TypeCategory TC, int KIND>
672   ExtValue genScalarLit(
673       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
674           &value) {
675     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
676       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
677     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
678       return genBoolConstant(value.IsTrue());
679     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
680       std::string str = value.DumpHexadecimal();
681       if constexpr (KIND == 2) {
682         llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str};
683         return genRealConstant<KIND>(builder.getContext(), floatVal);
684       } else if constexpr (KIND == 3) {
685         llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str};
686         return genRealConstant<KIND>(builder.getContext(), floatVal);
687       } else if constexpr (KIND == 4) {
688         llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str};
689         return genRealConstant<KIND>(builder.getContext(), floatVal);
690       } else if constexpr (KIND == 10) {
691         llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str};
692         return genRealConstant<KIND>(builder.getContext(), floatVal);
693       } else if constexpr (KIND == 16) {
694         llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str};
695         return genRealConstant<KIND>(builder.getContext(), floatVal);
696       } else {
697         // convert everything else to double
698         llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str};
699         return genRealConstant<KIND>(builder.getContext(), floatVal);
700       }
701     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
702       using TR =
703           Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>;
704       Fortran::evaluate::ComplexConstructor<KIND> ctor(
705           Fortran::evaluate::Expr<TR>{
706               Fortran::evaluate::Constant<TR>{value.REAL()}},
707           Fortran::evaluate::Expr<TR>{
708               Fortran::evaluate::Constant<TR>{value.AIMAG()}});
709       return genunbox(ctor);
710     } else /*constexpr*/ {
711       llvm_unreachable("unhandled constant");
712     }
713   }
714 
715   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
716   ExtValue
717   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
718                         Fortran::common::TypeCategory::Character, 1>> &value,
719                     int64_t len) {
720     assert(value.size() == static_cast<std::uint64_t>(len) &&
721            "value.size() doesn't match with len");
722     return fir::factory::createStringLiteral(builder, getLoc(), value);
723   }
724 
725   template <Fortran::common::TypeCategory TC, int KIND>
726   ExtValue
727   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
728              &con) {
729     if (con.Rank() > 0)
730       TODO(getLoc(), "genval array constant");
731     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
732         opt = con.GetScalarValue();
733     assert(opt.has_value() && "constant has no value");
734     if constexpr (TC == Fortran::common::TypeCategory::Character) {
735       if constexpr (KIND == 1)
736         return genAsciiScalarLit(opt.value(), con.LEN());
737       TODO(getLoc(), "genval for Character with KIND != 1");
738     } else {
739       return genScalarLit<TC, KIND>(opt.value());
740     }
741   }
742 
743   fir::ExtendedValue genval(
744       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
745     TODO(getLoc(), "genval constant derived");
746   }
747 
748   template <typename A>
749   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
750     TODO(getLoc(), "genval ArrayConstructor<A>");
751   }
752 
753   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
754     TODO(getLoc(), "gen ComplexPart");
755   }
756   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
757     TODO(getLoc(), "genval ComplexPart");
758   }
759 
760   ExtValue gen(const Fortran::evaluate::Substring &s) {
761     TODO(getLoc(), "gen Substring");
762   }
763   ExtValue genval(const Fortran::evaluate::Substring &ss) {
764     TODO(getLoc(), "genval Substring");
765   }
766 
767   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
768     if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>(
769             &subs.u)) {
770       if (s->value().Rank() > 0)
771         fir::emitFatalError(getLoc(), "vector subscript is not scalar");
772       return {genval(s->value())};
773     }
774     fir::emitFatalError(getLoc(), "subscript triple notation is not scalar");
775   }
776 
777   ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) {
778     return genval(subs);
779   }
780 
781   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
782     TODO(getLoc(), "gen DataRef");
783   }
784   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
785     TODO(getLoc(), "genval DataRef");
786   }
787 
788   // Helper function to turn the Component structure into a list of nested
789   // components, ordered from largest/leftmost to smallest/rightmost:
790   //  - where only the smallest/rightmost item may be allocatable or a pointer
791   //    (nested allocatable/pointer components require nested coordinate_of ops)
792   //  - that does not contain any parent components
793   //    (the front end places parent components directly in the object)
794   // Return the object used as the base coordinate for the component chain.
795   static Fortran::evaluate::DataRef const *
796   reverseComponents(const Fortran::evaluate::Component &cmpt,
797                     std::list<const Fortran::evaluate::Component *> &list) {
798     if (!cmpt.GetLastSymbol().test(
799             Fortran::semantics::Symbol::Flag::ParentComp))
800       list.push_front(&cmpt);
801     return std::visit(
802         Fortran::common::visitors{
803             [&](const Fortran::evaluate::Component &x) {
804               if (Fortran::semantics::IsAllocatableOrPointer(x.GetLastSymbol()))
805                 return &cmpt.base();
806               return reverseComponents(x, list);
807             },
808             [&](auto &) { return &cmpt.base(); },
809         },
810         cmpt.base().u);
811   }
812 
813   // Return the coordinate of the component reference
814   ExtValue genComponent(const Fortran::evaluate::Component &cmpt) {
815     std::list<const Fortran::evaluate::Component *> list;
816     const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list);
817     llvm::SmallVector<mlir::Value> coorArgs;
818     ExtValue obj = gen(*base);
819     mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType());
820     mlir::Location loc = getLoc();
821     auto fldTy = fir::FieldType::get(&converter.getMLIRContext());
822     // FIXME: need to thread the LEN type parameters here.
823     for (const Fortran::evaluate::Component *field : list) {
824       auto recTy = ty.cast<fir::RecordType>();
825       const Fortran::semantics::Symbol &sym = field->GetLastSymbol();
826       llvm::StringRef name = toStringRef(sym.name());
827       coorArgs.push_back(builder.create<fir::FieldIndexOp>(
828           loc, fldTy, name, recTy, fir::getTypeParams(obj)));
829       ty = recTy.getType(name);
830     }
831     ty = builder.getRefType(ty);
832     return fir::factory::componentToExtendedValue(
833         builder, loc,
834         builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj),
835                                           coorArgs));
836   }
837 
838   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
839     TODO(getLoc(), "gen Component");
840   }
841   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
842     TODO(getLoc(), "genval Component");
843   }
844 
845   ExtValue genval(const Fortran::semantics::Bound &bound) {
846     TODO(getLoc(), "genval Bound");
847   }
848 
849   /// Return lower bounds of \p box in dimension \p dim. The returned value
850   /// has type \ty.
851   mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) {
852     assert(box.rank() > 0 && "must be an array");
853     mlir::Location loc = getLoc();
854     mlir::Value one = builder.createIntegerConstant(loc, ty, 1);
855     mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one);
856     return builder.createConvert(loc, ty, lb);
857   }
858 
859   static bool isSlice(const Fortran::evaluate::ArrayRef &aref) {
860     for (const Fortran::evaluate::Subscript &sub : aref.subscript())
861       if (std::holds_alternative<Fortran::evaluate::Triplet>(sub.u))
862         return true;
863     return false;
864   }
865 
866   /// Lower an ArrayRef to a fir.coordinate_of given its lowered base.
867   ExtValue genCoordinateOp(const ExtValue &array,
868                            const Fortran::evaluate::ArrayRef &aref) {
869     mlir::Location loc = getLoc();
870     // References to array of rank > 1 with non constant shape that are not
871     // fir.box must be collapsed into an offset computation in lowering already.
872     // The same is needed with dynamic length character arrays of all ranks.
873     mlir::Type baseType =
874         fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType());
875     if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) ||
876         fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType)))
877       if (!array.getBoxOf<fir::BoxValue>())
878         return genOffsetAndCoordinateOp(array, aref);
879     // Generate a fir.coordinate_of with zero based array indexes.
880     llvm::SmallVector<mlir::Value> args;
881     for (const auto &subsc : llvm::enumerate(aref.subscript())) {
882       ExtValue subVal = genSubscript(subsc.value());
883       assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar");
884       mlir::Value val = fir::getBase(subVal);
885       mlir::Type ty = val.getType();
886       mlir::Value lb = getLBound(array, subsc.index(), ty);
887       args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb));
888     }
889 
890     mlir::Value base = fir::getBase(array);
891     auto seqTy =
892         fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>();
893     assert(args.size() == seqTy.getDimension());
894     mlir::Type ty = builder.getRefType(seqTy.getEleTy());
895     auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args);
896     return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr);
897   }
898 
899   /// Lower an ArrayRef to a fir.coordinate_of using an element offset instead
900   /// of array indexes.
901   /// This generates offset computation from the indexes and length parameters,
902   /// and use the offset to access the element with a fir.coordinate_of. This
903   /// must only be used if it is not possible to generate a normal
904   /// fir.coordinate_of using array indexes (i.e. when the shape information is
905   /// unavailable in the IR).
906   ExtValue genOffsetAndCoordinateOp(const ExtValue &array,
907                                     const Fortran::evaluate::ArrayRef &aref) {
908     mlir::Location loc = getLoc();
909     mlir::Value addr = fir::getBase(array);
910     mlir::Type arrTy = fir::dyn_cast_ptrEleTy(addr.getType());
911     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
912     mlir::Type seqTy = builder.getRefType(builder.getVarLenSeqTy(eleTy));
913     mlir::Type refTy = builder.getRefType(eleTy);
914     mlir::Value base = builder.createConvert(loc, seqTy, addr);
915     mlir::IndexType idxTy = builder.getIndexType();
916     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
917     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
918     auto getLB = [&](const auto &arr, unsigned dim) -> mlir::Value {
919       return arr.getLBounds().empty() ? one : arr.getLBounds()[dim];
920     };
921     auto genFullDim = [&](const auto &arr, mlir::Value delta) -> mlir::Value {
922       mlir::Value total = zero;
923       assert(arr.getExtents().size() == aref.subscript().size());
924       delta = builder.createConvert(loc, idxTy, delta);
925       unsigned dim = 0;
926       for (auto [ext, sub] : llvm::zip(arr.getExtents(), aref.subscript())) {
927         ExtValue subVal = genSubscript(sub);
928         assert(fir::isUnboxedValue(subVal));
929         mlir::Value val =
930             builder.createConvert(loc, idxTy, fir::getBase(subVal));
931         mlir::Value lb = builder.createConvert(loc, idxTy, getLB(arr, dim));
932         mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, val, lb);
933         mlir::Value prod =
934             builder.create<mlir::arith::MulIOp>(loc, delta, diff);
935         total = builder.create<mlir::arith::AddIOp>(loc, prod, total);
936         if (ext)
937           delta = builder.create<mlir::arith::MulIOp>(loc, delta, ext);
938         ++dim;
939       }
940       mlir::Type origRefTy = refTy;
941       if (fir::factory::CharacterExprHelper::isCharacterScalar(refTy)) {
942         fir::CharacterType chTy =
943             fir::factory::CharacterExprHelper::getCharacterType(refTy);
944         if (fir::characterWithDynamicLen(chTy)) {
945           mlir::MLIRContext *ctx = builder.getContext();
946           fir::KindTy kind =
947               fir::factory::CharacterExprHelper::getCharacterKind(chTy);
948           fir::CharacterType singleTy =
949               fir::CharacterType::getSingleton(ctx, kind);
950           refTy = builder.getRefType(singleTy);
951           mlir::Type seqRefTy =
952               builder.getRefType(builder.getVarLenSeqTy(singleTy));
953           base = builder.createConvert(loc, seqRefTy, base);
954         }
955       }
956       auto coor = builder.create<fir::CoordinateOp>(
957           loc, refTy, base, llvm::ArrayRef<mlir::Value>{total});
958       // Convert to expected, original type after address arithmetic.
959       return builder.createConvert(loc, origRefTy, coor);
960     };
961     return array.match(
962         [&](const fir::ArrayBoxValue &arr) -> ExtValue {
963           // FIXME: this check can be removed when slicing is implemented
964           if (isSlice(aref))
965             fir::emitFatalError(
966                 getLoc(),
967                 "slice should be handled in array expression context");
968           return genFullDim(arr, one);
969         },
970         [&](const fir::CharArrayBoxValue &arr) -> ExtValue {
971           mlir::Value delta = arr.getLen();
972           // If the length is known in the type, fir.coordinate_of will
973           // already take the length into account.
974           if (fir::factory::CharacterExprHelper::hasConstantLengthInType(arr))
975             delta = one;
976           return fir::CharBoxValue(genFullDim(arr, delta), arr.getLen());
977         },
978         [&](const fir::BoxValue &arr) -> ExtValue {
979           // CoordinateOp for BoxValue is not generated here. The dimensions
980           // must be kept in the fir.coordinate_op so that potential fir.box
981           // strides can be applied by codegen.
982           fir::emitFatalError(
983               loc, "internal: BoxValue in dim-collapsed fir.coordinate_of");
984         },
985         [&](const auto &) -> ExtValue {
986           fir::emitFatalError(loc, "internal: array lowering failed");
987         });
988   }
989 
990   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
991     ExtValue base = aref.base().IsSymbol() ? gen(aref.base().GetFirstSymbol())
992                                            : gen(aref.base().GetComponent());
993     return genCoordinateOp(base, aref);
994   }
995   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
996     return genLoad(gen(aref));
997   }
998 
999   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
1000     TODO(getLoc(), "gen CoarrayRef");
1001   }
1002   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
1003     TODO(getLoc(), "genval CoarrayRef");
1004   }
1005 
1006   template <typename A>
1007   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
1008     return std::visit([&](const auto &x) { return gen(x); }, des.u);
1009   }
1010   template <typename A>
1011   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
1012     return std::visit([&](const auto &x) { return genval(x); }, des.u);
1013   }
1014 
1015   mlir::Type genType(const Fortran::evaluate::DynamicType &dt) {
1016     if (dt.category() != Fortran::common::TypeCategory::Derived)
1017       return converter.genType(dt.category(), dt.kind());
1018     TODO(getLoc(), "genType Derived Type");
1019   }
1020 
1021   /// Lower a function reference
1022   template <typename A>
1023   ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1024     if (!funcRef.GetType().has_value())
1025       fir::emitFatalError(getLoc(), "internal: a function must have a type");
1026     mlir::Type resTy = genType(*funcRef.GetType());
1027     return genProcedureRef(funcRef, {resTy});
1028   }
1029 
1030   /// Lower function call `funcRef` and return a reference to the resultant
1031   /// value. This is required for lowering expressions such as `f1(f2(v))`.
1032   template <typename A>
1033   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1034     TODO(getLoc(), "gen FunctionRef<A>");
1035   }
1036 
1037   /// helper to detect statement functions
1038   static bool
1039   isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) {
1040     if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
1041       if (const auto *details =
1042               symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
1043         return details->stmtFunction().has_value();
1044     return false;
1045   }
1046 
1047   /// Helper to package a Value and its properties into an ExtendedValue.
1048   static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base,
1049                                   llvm::ArrayRef<mlir::Value> extents,
1050                                   llvm::ArrayRef<mlir::Value> lengths) {
1051     mlir::Type type = base.getType();
1052     if (type.isa<fir::BoxType>())
1053       return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);
1054     type = fir::unwrapRefType(type);
1055     if (type.isa<fir::BoxType>())
1056       return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});
1057     if (auto seqTy = type.dyn_cast<fir::SequenceType>()) {
1058       if (seqTy.getDimension() != extents.size())
1059         fir::emitFatalError(loc, "incorrect number of extents for array");
1060       if (seqTy.getEleTy().isa<fir::CharacterType>()) {
1061         if (lengths.empty())
1062           fir::emitFatalError(loc, "missing length for character");
1063         assert(lengths.size() == 1);
1064         return fir::CharArrayBoxValue(base, lengths[0], extents);
1065       }
1066       return fir::ArrayBoxValue(base, extents);
1067     }
1068     if (type.isa<fir::CharacterType>()) {
1069       if (lengths.empty())
1070         fir::emitFatalError(loc, "missing length for character");
1071       assert(lengths.size() == 1);
1072       return fir::CharBoxValue(base, lengths[0]);
1073     }
1074     return base;
1075   }
1076 
1077   // Find the argument that corresponds to the host associations.
1078   // Verify some assumptions about how the signature was built here.
1079   [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) {
1080     // Scan the argument list from last to first as the host associations are
1081     // appended for now.
1082     for (unsigned i = fn.getNumArguments(); i > 0; --i)
1083       if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
1084         // Host assoc tuple must be last argument (for now).
1085         assert(i == fn.getNumArguments() && "tuple must be last");
1086         return i - 1;
1087       }
1088     llvm_unreachable("anyFuncArgsHaveAttr failed");
1089   }
1090 
1091   /// Lower a non-elemental procedure reference and read allocatable and pointer
1092   /// results into normal values.
1093   ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
1094                            llvm::Optional<mlir::Type> resultType) {
1095     ExtValue res = genRawProcedureRef(procRef, resultType);
1096     return res;
1097   }
1098 
1099   /// Given a call site for which the arguments were already lowered, generate
1100   /// the call and return the result. This function deals with explicit result
1101   /// allocation and lowering if needed. It also deals with passing the host
1102   /// link to internal procedures.
1103   ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller,
1104                               mlir::FunctionType callSiteType,
1105                               llvm::Optional<mlir::Type> resultType) {
1106     mlir::Location loc = getLoc();
1107     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1108     // Handle cases where caller must allocate the result or a fir.box for it.
1109     bool mustPopSymMap = false;
1110     if (caller.mustMapInterfaceSymbols()) {
1111       symMap.pushScope();
1112       mustPopSymMap = true;
1113       Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap);
1114     }
1115     // If this is an indirect call, retrieve the function address. Also retrieve
1116     // the result length if this is a character function (note that this length
1117     // will be used only if there is no explicit length in the local interface).
1118     mlir::Value funcPointer;
1119     mlir::Value charFuncPointerLength;
1120     if (const Fortran::semantics::Symbol *sym =
1121             caller.getIfIndirectCallSymbol()) {
1122       funcPointer = symMap.lookupSymbol(*sym).getAddr();
1123       if (!funcPointer)
1124         fir::emitFatalError(loc, "failed to find indirect call symbol address");
1125       if (fir::isCharacterProcedureTuple(funcPointer.getType(),
1126                                          /*acceptRawFunc=*/false))
1127         std::tie(funcPointer, charFuncPointerLength) =
1128             fir::factory::extractCharacterProcedureTuple(builder, loc,
1129                                                          funcPointer);
1130     }
1131 
1132     mlir::IndexType idxTy = builder.getIndexType();
1133     auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
1134       return builder.createConvert(
1135           loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
1136     };
1137     llvm::SmallVector<mlir::Value> resultLengths;
1138     auto allocatedResult = [&]() -> llvm::Optional<ExtValue> {
1139       llvm::SmallVector<mlir::Value> extents;
1140       llvm::SmallVector<mlir::Value> lengths;
1141       if (!caller.callerAllocateResult())
1142         return {};
1143       mlir::Type type = caller.getResultStorageType();
1144       if (type.isa<fir::SequenceType>())
1145         caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) {
1146           extents.emplace_back(lowerSpecExpr(e));
1147         });
1148       caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) {
1149         lengths.emplace_back(lowerSpecExpr(e));
1150       });
1151 
1152       // Result length parameters should not be provided to box storage
1153       // allocation and save_results, but they are still useful information to
1154       // keep in the ExtendedValue if non-deferred.
1155       if (!type.isa<fir::BoxType>()) {
1156         if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
1157           // Calling an assumed length function. This is only possible if this
1158           // is a call to a character dummy procedure.
1159           if (!charFuncPointerLength)
1160             fir::emitFatalError(loc, "failed to retrieve character function "
1161                                      "length while calling it");
1162           lengths.push_back(charFuncPointerLength);
1163         }
1164         resultLengths = lengths;
1165       }
1166 
1167       if (!extents.empty() || !lengths.empty()) {
1168         auto *bldr = &converter.getFirOpBuilder();
1169         auto stackSaveFn = fir::factory::getLlvmStackSave(builder);
1170         auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName());
1171         mlir::Value sp =
1172             bldr->create<fir::CallOp>(loc, stackSaveFn.getType().getResults(),
1173                                       stackSaveSymbol, mlir::ValueRange{})
1174                 .getResult(0);
1175         stmtCtx.attachCleanup([bldr, loc, sp]() {
1176           auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr);
1177           auto stackRestoreSymbol =
1178               bldr->getSymbolRefAttr(stackRestoreFn.getName());
1179           bldr->create<fir::CallOp>(loc, stackRestoreFn.getType().getResults(),
1180                                     stackRestoreSymbol, mlir::ValueRange{sp});
1181         });
1182       }
1183       mlir::Value temp =
1184           builder.createTemporary(loc, type, ".result", extents, resultLengths);
1185       return toExtendedValue(loc, temp, extents, lengths);
1186     }();
1187 
1188     if (mustPopSymMap)
1189       symMap.popScope();
1190 
1191     // Place allocated result or prepare the fir.save_result arguments.
1192     mlir::Value arrayResultShape;
1193     if (allocatedResult) {
1194       if (std::optional<Fortran::lower::CallInterface<
1195               Fortran::lower::CallerInterface>::PassedEntity>
1196               resultArg = caller.getPassedResult()) {
1197         if (resultArg->passBy == PassBy::AddressAndLength)
1198           caller.placeAddressAndLengthInput(*resultArg,
1199                                             fir::getBase(*allocatedResult),
1200                                             fir::getLen(*allocatedResult));
1201         else if (resultArg->passBy == PassBy::BaseAddress)
1202           caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
1203         else
1204           fir::emitFatalError(
1205               loc, "only expect character scalar result to be passed by ref");
1206       } else {
1207         assert(caller.mustSaveResult());
1208         arrayResultShape = allocatedResult->match(
1209             [&](const fir::CharArrayBoxValue &) {
1210               return builder.createShape(loc, *allocatedResult);
1211             },
1212             [&](const fir::ArrayBoxValue &) {
1213               return builder.createShape(loc, *allocatedResult);
1214             },
1215             [&](const auto &) { return mlir::Value{}; });
1216       }
1217     }
1218 
1219     // In older Fortran, procedure argument types are inferred. This may lead
1220     // different view of what the function signature is in different locations.
1221     // Casts are inserted as needed below to accommodate this.
1222 
1223     // The mlir::FuncOp type prevails, unless it has a different number of
1224     // arguments which can happen in legal program if it was passed as a dummy
1225     // procedure argument earlier with no further type information.
1226     mlir::SymbolRefAttr funcSymbolAttr;
1227     bool addHostAssociations = false;
1228     if (!funcPointer) {
1229       mlir::FunctionType funcOpType = caller.getFuncOp().getType();
1230       mlir::SymbolRefAttr symbolAttr =
1231           builder.getSymbolRefAttr(caller.getMangledName());
1232       if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
1233           callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
1234           fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
1235                                    fir::getHostAssocAttrName())) {
1236         // The number of arguments is off by one, and we're lowering a function
1237         // with host associations. Modify call to include host associations
1238         // argument by appending the value at the end of the operands.
1239         assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
1240                converter.hostAssocTupleValue().getType());
1241         addHostAssociations = true;
1242       }
1243       if (!addHostAssociations &&
1244           (callSiteType.getNumResults() != funcOpType.getNumResults() ||
1245            callSiteType.getNumInputs() != funcOpType.getNumInputs())) {
1246         // Deal with argument number mismatch by making a function pointer so
1247         // that function type cast can be inserted. Do not emit a warning here
1248         // because this can happen in legal program if the function is not
1249         // defined here and it was first passed as an argument without any more
1250         // information.
1251         funcPointer =
1252             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1253       } else if (callSiteType.getResults() != funcOpType.getResults()) {
1254         // Implicit interface result type mismatch are not standard Fortran, but
1255         // some compilers are not complaining about it.  The front end is not
1256         // protecting lowering from this currently. Support this with a
1257         // discouraging warning.
1258         LLVM_DEBUG(mlir::emitWarning(
1259             loc, "a return type mismatch is not standard compliant and may "
1260                  "lead to undefined behavior."));
1261         // Cast the actual function to the current caller implicit type because
1262         // that is the behavior we would get if we could not see the definition.
1263         funcPointer =
1264             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1265       } else {
1266         funcSymbolAttr = symbolAttr;
1267       }
1268     }
1269 
1270     mlir::FunctionType funcType =
1271         funcPointer ? callSiteType : caller.getFuncOp().getType();
1272     llvm::SmallVector<mlir::Value> operands;
1273     // First operand of indirect call is the function pointer. Cast it to
1274     // required function type for the call to handle procedures that have a
1275     // compatible interface in Fortran, but that have different signatures in
1276     // FIR.
1277     if (funcPointer) {
1278       operands.push_back(
1279           funcPointer.getType().isa<fir::BoxProcType>()
1280               ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
1281               : builder.createConvert(loc, funcType, funcPointer));
1282     }
1283 
1284     // Deal with potential mismatches in arguments types. Passing an array to a
1285     // scalar argument should for instance be tolerated here.
1286     bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
1287     for (auto [fst, snd] :
1288          llvm::zip(caller.getInputs(), funcType.getInputs())) {
1289       // When passing arguments to a procedure that can be called an implicit
1290       // interface, allow character actual arguments to be passed to dummy
1291       // arguments of any type and vice versa
1292       mlir::Value cast;
1293       auto *context = builder.getContext();
1294       if (snd.isa<fir::BoxProcType>() &&
1295           fst.getType().isa<mlir::FunctionType>()) {
1296         auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None);
1297         auto boxProcTy = builder.getBoxProcType(funcTy);
1298         if (mlir::Value host = argumentHostAssocs(converter, fst)) {
1299           cast = builder.create<fir::EmboxProcOp>(
1300               loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
1301         } else {
1302           cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
1303         }
1304       } else {
1305         cast = builder.convertWithSemantics(loc, snd, fst,
1306                                             callingImplicitInterface);
1307       }
1308       operands.push_back(cast);
1309     }
1310 
1311     // Add host associations as necessary.
1312     if (addHostAssociations)
1313       operands.push_back(converter.hostAssocTupleValue());
1314 
1315     auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
1316                                             funcSymbolAttr, operands);
1317 
1318     if (caller.mustSaveResult())
1319       builder.create<fir::SaveResultOp>(
1320           loc, call.getResult(0), fir::getBase(allocatedResult.getValue()),
1321           arrayResultShape, resultLengths);
1322 
1323     if (allocatedResult) {
1324       allocatedResult->match(
1325           [&](const fir::MutableBoxValue &box) {
1326             if (box.isAllocatable()) {
1327               // 9.7.3.2 point 4. Finalize allocatables.
1328               fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
1329               stmtCtx.attachCleanup([bldr, loc, box]() {
1330                 fir::factory::genFinalization(*bldr, loc, box);
1331               });
1332             }
1333           },
1334           [](const auto &) {});
1335       return *allocatedResult;
1336     }
1337 
1338     if (!resultType.hasValue())
1339       return mlir::Value{}; // subroutine call
1340     // For now, Fortran return values are implemented with a single MLIR
1341     // function return value.
1342     assert(call.getNumResults() == 1 &&
1343            "Expected exactly one result in FUNCTION call");
1344     return call.getResult(0);
1345   }
1346 
1347   /// Like genExtAddr, but ensure the address returned is a temporary even if \p
1348   /// expr is variable inside parentheses.
1349   ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
1350     // In general, genExtAddr might not create a temp for variable inside
1351     // parentheses to avoid creating array temporary in sub-expressions. It only
1352     // ensures the sub-expression is not re-associated with other parts of the
1353     // expression. In the call semantics, there is a difference between expr and
1354     // variable (see R1524). For expressions, a variable storage must not be
1355     // argument associated since it could be modified inside the call, or the
1356     // variable could also be modified by other means during the call.
1357     if (!isParenthesizedVariable(expr))
1358       return genExtAddr(expr);
1359     mlir::Location loc = getLoc();
1360     if (expr.Rank() > 0)
1361       TODO(loc, "genTempExtAddr array");
1362     return genExtValue(expr).match(
1363         [&](const fir::CharBoxValue &boxChar) -> ExtValue {
1364           TODO(loc, "genTempExtAddr CharBoxValue");
1365         },
1366         [&](const fir::UnboxedValue &v) -> ExtValue {
1367           mlir::Type type = v.getType();
1368           mlir::Value value = v;
1369           if (fir::isa_ref_type(type))
1370             value = builder.create<fir::LoadOp>(loc, value);
1371           mlir::Value temp = builder.createTemporary(loc, value.getType());
1372           builder.create<fir::StoreOp>(loc, value, temp);
1373           return temp;
1374         },
1375         [&](const fir::BoxValue &x) -> ExtValue {
1376           // Derived type scalar that may be polymorphic.
1377           assert(!x.hasRank() && x.isDerived());
1378           if (x.isDerivedWithLengthParameters())
1379             fir::emitFatalError(
1380                 loc, "making temps for derived type with length parameters");
1381           // TODO: polymorphic aspects should be kept but for now the temp
1382           // created always has the declared type.
1383           mlir::Value var =
1384               fir::getBase(fir::factory::readBoxValue(builder, loc, x));
1385           auto value = builder.create<fir::LoadOp>(loc, var);
1386           mlir::Value temp = builder.createTemporary(loc, value.getType());
1387           builder.create<fir::StoreOp>(loc, value, temp);
1388           return temp;
1389         },
1390         [&](const auto &) -> ExtValue {
1391           fir::emitFatalError(loc, "expr is not a scalar value");
1392         });
1393   }
1394 
1395   /// Helper structure to track potential copy-in of non contiguous variable
1396   /// argument into a contiguous temp. It is used to deallocate the temp that
1397   /// may have been created as well as to the copy-out from the temp to the
1398   /// variable after the call.
1399   struct CopyOutPair {
1400     ExtValue var;
1401     ExtValue temp;
1402     // Flag to indicate if the argument may have been modified by the
1403     // callee, in which case it must be copied-out to the variable.
1404     bool argMayBeModifiedByCall;
1405     // Optional boolean value that, if present and false, prevents
1406     // the copy-out and temp deallocation.
1407     llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime;
1408   };
1409   using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>;
1410 
1411   /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories
1412   /// not based on fir.box.
1413   /// This will lose any non contiguous stride information and dynamic type and
1414   /// should only be called if \p exv is known to be contiguous or if its base
1415   /// address will be replaced by a contiguous one. If \p exv is not a
1416   /// fir::BoxValue, this is a no-op.
1417   ExtValue readIfBoxValue(const ExtValue &exv) {
1418     if (const auto *box = exv.getBoxOf<fir::BoxValue>())
1419       return fir::factory::readBoxValue(builder, getLoc(), *box);
1420     return exv;
1421   }
1422 
1423   /// Lower a non-elemental procedure reference.
1424   ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
1425                               llvm::Optional<mlir::Type> resultType) {
1426     mlir::Location loc = getLoc();
1427     if (isElementalProcWithArrayArgs(procRef))
1428       fir::emitFatalError(loc, "trying to lower elemental procedure with array "
1429                                "arguments as normal procedure");
1430     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
1431             procRef.proc().GetSpecificIntrinsic())
1432       return genIntrinsicRef(procRef, *intrinsic, resultType);
1433 
1434     if (isStatementFunctionCall(procRef))
1435       TODO(loc, "Lower statement function call");
1436 
1437     Fortran::lower::CallerInterface caller(procRef, converter);
1438     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1439 
1440     llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall;
1441     // List of <var, temp> where temp must be copied into var after the call.
1442     CopyOutPairs copyOutPairs;
1443 
1444     mlir::FunctionType callSiteType = caller.genFunctionType();
1445 
1446     // Lower the actual arguments and map the lowered values to the dummy
1447     // arguments.
1448     for (const Fortran::lower::CallInterface<
1449              Fortran::lower::CallerInterface>::PassedEntity &arg :
1450          caller.getPassedArguments()) {
1451       const auto *actual = arg.entity;
1452       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
1453       if (!actual) {
1454         // Optional dummy argument for which there is no actual argument.
1455         caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy));
1456         continue;
1457       }
1458       const auto *expr = actual->UnwrapExpr();
1459       if (!expr)
1460         TODO(loc, "assumed type actual argument lowering");
1461 
1462       if (arg.passBy == PassBy::Value) {
1463         ExtValue argVal = genval(*expr);
1464         if (!fir::isUnboxedValue(argVal))
1465           fir::emitFatalError(
1466               loc, "internal error: passing non trivial value by value");
1467         caller.placeInput(arg, fir::getBase(argVal));
1468         continue;
1469       }
1470 
1471       if (arg.passBy == PassBy::MutableBox) {
1472         if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
1473                 *expr)) {
1474           // If expr is NULL(), the mutableBox created must be a deallocated
1475           // pointer with the dummy argument characteristics (see table 16.5
1476           // in Fortran 2018 standard).
1477           // No length parameters are set for the created box because any non
1478           // deferred type parameters of the dummy will be evaluated on the
1479           // callee side, and it is illegal to use NULL without a MOLD if any
1480           // dummy length parameters are assumed.
1481           mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);
1482           assert(boxTy && boxTy.isa<fir::BoxType>() &&
1483                  "must be a fir.box type");
1484           mlir::Value boxStorage = builder.createTemporary(loc, boxTy);
1485           mlir::Value nullBox = fir::factory::createUnallocatedBox(
1486               builder, loc, boxTy, /*nonDeferredParams=*/{});
1487           builder.create<fir::StoreOp>(loc, nullBox, boxStorage);
1488           caller.placeInput(arg, boxStorage);
1489           continue;
1490         }
1491         fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
1492         mlir::Value irBox =
1493             fir::factory::getMutableIRBox(builder, loc, mutableBox);
1494         caller.placeInput(arg, irBox);
1495         if (arg.mayBeModifiedByCall())
1496           mutableModifiedByCall.emplace_back(std::move(mutableBox));
1497         continue;
1498       }
1499       const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr);
1500       if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) {
1501         auto argAddr = [&]() -> ExtValue {
1502           ExtValue baseAddr;
1503           if (actualArgIsVariable && arg.isOptional()) {
1504             if (Fortran::evaluate::IsAllocatableOrPointerObject(
1505                     *expr, converter.getFoldingContext())) {
1506               TODO(loc, "Allocatable or pointer argument");
1507             }
1508             if (const Fortran::semantics::Symbol *wholeSymbol =
1509                     Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(
1510                         *expr))
1511               if (Fortran::semantics::IsOptional(*wholeSymbol)) {
1512                 TODO(loc, "procedureref optional arg");
1513               }
1514             // Fall through: The actual argument can safely be
1515             // copied-in/copied-out without any care if needed.
1516           }
1517           if (actualArgIsVariable && expr->Rank() > 0) {
1518             TODO(loc, "procedureref arrays");
1519           }
1520           // Actual argument is a non optional/non pointer/non allocatable
1521           // scalar.
1522           if (actualArgIsVariable)
1523             return genExtAddr(*expr);
1524           // Actual argument is not a variable. Make sure a variable address is
1525           // not passed.
1526           return genTempExtAddr(*expr);
1527         }();
1528         // Scalar and contiguous expressions may be lowered to a fir.box,
1529         // either to account for potential polymorphism, or because lowering
1530         // did not account for some contiguity hints.
1531         // Here, polymorphism does not matter (an entity of the declared type
1532         // is passed, not one of the dynamic type), and the expr is known to
1533         // be simply contiguous, so it is safe to unbox it and pass the
1534         // address without making a copy.
1535         argAddr = readIfBoxValue(argAddr);
1536 
1537         if (arg.passBy == PassBy::BaseAddress) {
1538           caller.placeInput(arg, fir::getBase(argAddr));
1539         } else {
1540           assert(arg.passBy == PassBy::BoxChar);
1541           auto helper = fir::factory::CharacterExprHelper{builder, loc};
1542           auto boxChar = argAddr.match(
1543               [&](const fir::CharBoxValue &x) { return helper.createEmbox(x); },
1544               [&](const fir::CharArrayBoxValue &x) {
1545                 return helper.createEmbox(x);
1546               },
1547               [&](const auto &x) -> mlir::Value {
1548                 // Fortran allows an actual argument of a completely different
1549                 // type to be passed to a procedure expecting a CHARACTER in the
1550                 // dummy argument position. When this happens, the data pointer
1551                 // argument is simply assumed to point to CHARACTER data and the
1552                 // LEN argument used is garbage. Simulate this behavior by
1553                 // free-casting the base address to be a !fir.char reference and
1554                 // setting the LEN argument to undefined. What could go wrong?
1555                 auto dataPtr = fir::getBase(x);
1556                 assert(!dataPtr.getType().template isa<fir::BoxType>());
1557                 return builder.convertWithSemantics(
1558                     loc, argTy, dataPtr,
1559                     /*allowCharacterConversion=*/true);
1560               });
1561           caller.placeInput(arg, boxChar);
1562         }
1563       } else if (arg.passBy == PassBy::Box) {
1564         // Before lowering to an address, handle the allocatable/pointer actual
1565         // argument to optional fir.box dummy. It is legal to pass
1566         // unallocated/disassociated entity to an optional. In this case, an
1567         // absent fir.box must be created instead of a fir.box with a null value
1568         // (Fortran 2018 15.5.2.12 point 1).
1569         if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject(
1570                                     *expr, converter.getFoldingContext())) {
1571           TODO(loc, "optional allocatable or pointer argument");
1572         } else {
1573           // Make sure a variable address is only passed if the expression is
1574           // actually a variable.
1575           mlir::Value box =
1576               actualArgIsVariable
1577                   ? builder.createBox(loc, genBoxArg(*expr))
1578                   : builder.createBox(getLoc(), genTempExtAddr(*expr));
1579           caller.placeInput(arg, box);
1580         }
1581       } else if (arg.passBy == PassBy::AddressAndLength) {
1582         ExtValue argRef = genExtAddr(*expr);
1583         caller.placeAddressAndLengthInput(arg, fir::getBase(argRef),
1584                                           fir::getLen(argRef));
1585       } else if (arg.passBy == PassBy::CharProcTuple) {
1586         TODO(loc, "procedureref CharProcTuple");
1587       } else {
1588         TODO(loc, "pass by value in non elemental function call");
1589       }
1590     }
1591 
1592     ExtValue result = genCallOpAndResult(caller, callSiteType, resultType);
1593 
1594     // // Copy-out temps that were created for non contiguous variable arguments
1595     // if
1596     // // needed.
1597     // for (const auto &copyOutPair : copyOutPairs)
1598     //   genCopyOut(copyOutPair);
1599 
1600     return result;
1601   }
1602 
1603   template <typename A>
1604   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1605     ExtValue result = genFunctionRef(funcRef);
1606     if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
1607       return genLoad(result);
1608     return result;
1609   }
1610 
1611   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
1612     llvm::Optional<mlir::Type> resTy;
1613     if (procRef.hasAlternateReturns())
1614       resTy = builder.getIndexType();
1615     return genProcedureRef(procRef, resTy);
1616   }
1617 
1618   /// Generate a call to an intrinsic function.
1619   ExtValue
1620   genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
1621                   const Fortran::evaluate::SpecificIntrinsic &intrinsic,
1622                   llvm::Optional<mlir::Type> resultType) {
1623     llvm::SmallVector<ExtValue> operands;
1624 
1625     llvm::StringRef name = intrinsic.name;
1626     mlir::Location loc = getLoc();
1627 
1628     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
1629         Fortran::lower::getIntrinsicArgumentLowering(name);
1630     for (const auto &[arg, dummy] :
1631          llvm::zip(procRef.arguments(),
1632                    intrinsic.characteristics.value().dummyArguments)) {
1633       auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
1634       if (!expr) {
1635         // Absent optional.
1636         operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
1637         continue;
1638       }
1639       if (!argLowering) {
1640         // No argument lowering instruction, lower by value.
1641         operands.emplace_back(genval(*expr));
1642         continue;
1643       }
1644       // Ad-hoc argument lowering handling.
1645       Fortran::lower::ArgLoweringRule argRules =
1646           Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering,
1647                                                    dummy.name);
1648       switch (argRules.lowerAs) {
1649       case Fortran::lower::LowerIntrinsicArgAs::Value:
1650         operands.emplace_back(genval(*expr));
1651         continue;
1652       case Fortran::lower::LowerIntrinsicArgAs::Addr:
1653         TODO(getLoc(), "argument lowering for Addr");
1654         continue;
1655       case Fortran::lower::LowerIntrinsicArgAs::Box:
1656         TODO(getLoc(), "argument lowering for Box");
1657         continue;
1658       case Fortran::lower::LowerIntrinsicArgAs::Inquired:
1659         TODO(getLoc(), "argument lowering for Inquired");
1660         continue;
1661       }
1662       llvm_unreachable("bad switch");
1663     }
1664     // Let the intrinsic library lower the intrinsic procedure call
1665     return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
1666                                             operands);
1667   }
1668 
1669   template <typename A>
1670   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
1671     if (isScalar(x))
1672       return std::visit([&](const auto &e) { return genval(e); }, x.u);
1673     TODO(getLoc(), "genval Expr<A> arrays");
1674   }
1675 
1676   /// Helper to detect Transformational function reference.
1677   template <typename T>
1678   bool isTransformationalRef(const T &) {
1679     return false;
1680   }
1681   template <typename T>
1682   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
1683     return !funcRef.IsElemental() && funcRef.Rank();
1684   }
1685   template <typename T>
1686   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
1687     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
1688                       expr.u);
1689   }
1690 
1691   template <typename A>
1692   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
1693     // Whole array symbols or components, and results of transformational
1694     // functions already have a storage and the scalar expression lowering path
1695     // is used to not create a new temporary storage.
1696     if (isScalar(x) ||
1697         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
1698         isTransformationalRef(x))
1699       return std::visit([&](const auto &e) { return genref(e); }, x.u);
1700     TODO(getLoc(), "gen Expr non-scalar");
1701   }
1702 
1703   template <typename A>
1704   bool isScalar(const A &x) {
1705     return x.Rank() == 0;
1706   }
1707 
1708   template <int KIND>
1709   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
1710                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
1711     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
1712   }
1713 
1714   using RefSet =
1715       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
1716                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
1717                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
1718                  Fortran::semantics::SymbolRef>;
1719   template <typename A>
1720   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
1721 
1722   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
1723   ExtValue genref(const A &a) {
1724     return gen(a);
1725   }
1726   template <typename A>
1727   ExtValue genref(const A &a) {
1728     mlir::Type storageType = converter.genType(toEvExpr(a));
1729     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
1730   }
1731 
1732   template <typename A, template <typename> typename T,
1733             typename B = std::decay_t<T<A>>,
1734             std::enable_if_t<
1735                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
1736                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
1737                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
1738                 bool> = true>
1739   ExtValue genref(const T<A> &x) {
1740     return gen(x);
1741   }
1742 
1743 private:
1744   mlir::Location location;
1745   Fortran::lower::AbstractConverter &converter;
1746   fir::FirOpBuilder &builder;
1747   Fortran::lower::StatementContext &stmtCtx;
1748   Fortran::lower::SymMap &symMap;
1749   bool useBoxArg = false; // expression lowered as argument
1750 };
1751 } // namespace
1752 
1753 // Helper for changing the semantics in a given context. Preserves the current
1754 // semantics which is resumed when the "push" goes out of scope.
1755 #define PushSemantics(PushVal)                                                 \
1756   [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ =                 \
1757       Fortran::common::ScopedSet(semant, PushVal);
1758 
1759 static bool isAdjustedArrayElementType(mlir::Type t) {
1760   return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>();
1761 }
1762 
1763 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting
1764 /// the actual extents and lengths. This is only to allow their propagation as
1765 /// ExtendedValue without triggering verifier failures when propagating
1766 /// character/arrays as unboxed values. Only the base of the resulting
1767 /// ExtendedValue should be used, it is undefined to use the length or extents
1768 /// of the extended value returned,
1769 inline static fir::ExtendedValue
1770 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder,
1771                        mlir::Value val, mlir::Value len) {
1772   mlir::Type ty = fir::unwrapRefType(val.getType());
1773   mlir::IndexType idxTy = builder.getIndexType();
1774   auto seqTy = ty.cast<fir::SequenceType>();
1775   auto undef = builder.create<fir::UndefOp>(loc, idxTy);
1776   llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef);
1777   if (fir::isa_char(seqTy.getEleTy()))
1778     return fir::CharArrayBoxValue(val, len ? len : undef, extents);
1779   return fir::ArrayBoxValue(val, extents);
1780 }
1781 
1782 //===----------------------------------------------------------------------===//
1783 //
1784 // Lowering of scalar expressions in an explicit iteration space context.
1785 //
1786 //===----------------------------------------------------------------------===//
1787 
1788 // Shared code for creating a copy of a derived type element. This function is
1789 // called from a continuation.
1790 inline static fir::ArrayAmendOp
1791 createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad,
1792                         fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc,
1793                         const fir::ExtendedValue &elementExv, mlir::Type eleTy,
1794                         mlir::Value innerArg) {
1795   if (destLoad.getTypeparams().empty()) {
1796     fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv);
1797   } else {
1798     auto boxTy = fir::BoxType::get(eleTy);
1799     auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(),
1800                                               mlir::Value{}, mlir::Value{},
1801                                               destLoad.getTypeparams());
1802     auto fromBox = builder.create<fir::EmboxOp>(
1803         loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{},
1804         destLoad.getTypeparams());
1805     fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox),
1806                                       fir::BoxValue(fromBox));
1807   }
1808   return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg,
1809                                            destAcc);
1810 }
1811 
1812 inline static fir::ArrayAmendOp
1813 createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder,
1814                      fir::ArrayAccessOp dstOp, mlir::Value &dstLen,
1815                      const fir::ExtendedValue &srcExv, mlir::Value innerArg,
1816                      llvm::ArrayRef<mlir::Value> bounds) {
1817   fir::CharBoxValue dstChar(dstOp, dstLen);
1818   fir::factory::CharacterExprHelper helper{builder, loc};
1819   if (!bounds.empty()) {
1820     dstChar = helper.createSubstring(dstChar, bounds);
1821     fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv),
1822                                    dstChar.getAddr(), dstChar.getLen(), builder,
1823                                    loc);
1824     // Update the LEN to the substring's LEN.
1825     dstLen = dstChar.getLen();
1826   }
1827   // For a CHARACTER, we generate the element assignment loops inline.
1828   helper.createAssign(fir::ExtendedValue{dstChar}, srcExv);
1829   // Mark this array element as amended.
1830   mlir::Type ty = innerArg.getType();
1831   auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp);
1832   return amend;
1833 }
1834 
1835 //===----------------------------------------------------------------------===//
1836 //
1837 // Lowering of array expressions.
1838 //
1839 //===----------------------------------------------------------------------===//
1840 
1841 namespace {
1842 class ArrayExprLowering {
1843   using ExtValue = fir::ExtendedValue;
1844 
1845   /// Structure to keep track of lowered array operands in the
1846   /// array expression. Useful to later deduce the shape of the
1847   /// array expression.
1848   struct ArrayOperand {
1849     /// Array base (can be a fir.box).
1850     mlir::Value memref;
1851     /// ShapeOp, ShapeShiftOp or ShiftOp
1852     mlir::Value shape;
1853     /// SliceOp
1854     mlir::Value slice;
1855     /// Can this operand be absent ?
1856     bool mayBeAbsent = false;
1857   };
1858 
1859   using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts;
1860   using PathComponent = Fortran::lower::PathComponent;
1861 
1862   /// Active iteration space.
1863   using IterationSpace = Fortran::lower::IterationSpace;
1864   using IterSpace = const Fortran::lower::IterationSpace &;
1865 
1866   /// Current continuation. Function that will generate IR for a single
1867   /// iteration of the pending iterative loop structure.
1868   using CC = Fortran::lower::GenerateElementalArrayFunc;
1869 
1870   /// Projection continuation. Function that will project one iteration space
1871   /// into another.
1872   using PC = std::function<IterationSpace(IterSpace)>;
1873   using ArrayBaseTy =
1874       std::variant<std::monostate, const Fortran::evaluate::ArrayRef *,
1875                    const Fortran::evaluate::DataRef *>;
1876   using ComponentPath = Fortran::lower::ComponentPath;
1877 
1878 public:
1879   //===--------------------------------------------------------------------===//
1880   // Regular array assignment
1881   //===--------------------------------------------------------------------===//
1882 
1883   /// Entry point for array assignments. Both the left-hand and right-hand sides
1884   /// can either be ExtendedValue or evaluate::Expr.
1885   template <typename TL, typename TR>
1886   static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter,
1887                                    Fortran::lower::SymMap &symMap,
1888                                    Fortran::lower::StatementContext &stmtCtx,
1889                                    const TL &lhs, const TR &rhs) {
1890     ArrayExprLowering ael{converter, stmtCtx, symMap,
1891                           ConstituentSemantics::CopyInCopyOut};
1892     ael.lowerArrayAssignment(lhs, rhs);
1893   }
1894 
1895   template <typename TL, typename TR>
1896   void lowerArrayAssignment(const TL &lhs, const TR &rhs) {
1897     mlir::Location loc = getLoc();
1898     /// Here the target subspace is not necessarily contiguous. The ArrayUpdate
1899     /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad
1900     /// in `destination`.
1901     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
1902     ccStoreToDest = genarr(lhs);
1903     determineShapeOfDest(lhs);
1904     semant = ConstituentSemantics::RefTransparent;
1905     ExtValue exv = lowerArrayExpression(rhs);
1906     if (explicitSpaceIsActive()) {
1907       explicitSpace->finalizeContext();
1908       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
1909     } else {
1910       builder.create<fir::ArrayMergeStoreOp>(
1911           loc, destination, fir::getBase(exv), destination.getMemref(),
1912           destination.getSlice(), destination.getTypeparams());
1913     }
1914   }
1915 
1916   //===--------------------------------------------------------------------===//
1917   // Array assignment to allocatable array
1918   //===--------------------------------------------------------------------===//
1919 
1920   /// Entry point for assignment to allocatable array.
1921   static void lowerAllocatableArrayAssignment(
1922       Fortran::lower::AbstractConverter &converter,
1923       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
1924       const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
1925       Fortran::lower::ExplicitIterSpace &explicitSpace,
1926       Fortran::lower::ImplicitIterSpace &implicitSpace) {
1927     ArrayExprLowering ael(converter, stmtCtx, symMap,
1928                           ConstituentSemantics::CopyInCopyOut, &explicitSpace,
1929                           &implicitSpace);
1930     ael.lowerAllocatableArrayAssignment(lhs, rhs);
1931   }
1932 
1933   /// Assignment to allocatable array.
1934   ///
1935   /// The semantics are reverse that of a "regular" array assignment. The rhs
1936   /// defines the iteration space of the computation and the lhs is
1937   /// resized/reallocated to fit if necessary.
1938   void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs,
1939                                        const Fortran::lower::SomeExpr &rhs) {
1940     // With assignment to allocatable, we want to lower the rhs first and use
1941     // its shape to determine if we need to reallocate, etc.
1942     mlir::Location loc = getLoc();
1943     // FIXME: If the lhs is in an explicit iteration space, the assignment may
1944     // be to an array of allocatable arrays rather than a single allocatable
1945     // array.
1946     fir::MutableBoxValue mutableBox =
1947         createMutableBox(loc, converter, lhs, symMap);
1948     mlir::Type resultTy = converter.genType(rhs);
1949     if (rhs.Rank() > 0)
1950       determineShapeOfDest(rhs);
1951     auto rhsCC = [&]() {
1952       PushSemantics(ConstituentSemantics::RefTransparent);
1953       return genarr(rhs);
1954     }();
1955 
1956     llvm::SmallVector<mlir::Value> lengthParams;
1957     // Currently no safe way to gather length from rhs (at least for
1958     // character, it cannot be taken from array_loads since it may be
1959     // changed by concatenations).
1960     if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) ||
1961         mutableBox.isDerivedWithLengthParameters())
1962       TODO(loc, "gather rhs length parameters in assignment to allocatable");
1963 
1964     // The allocatable must take lower bounds from the expr if it is
1965     // reallocated and the right hand side is not a scalar.
1966     const bool takeLboundsIfRealloc = rhs.Rank() > 0;
1967     llvm::SmallVector<mlir::Value> lbounds;
1968     // When the reallocated LHS takes its lower bounds from the RHS,
1969     // they will be non default only if the RHS is a whole array
1970     // variable. Otherwise, lbounds is left empty and default lower bounds
1971     // will be used.
1972     if (takeLboundsIfRealloc &&
1973         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) {
1974       assert(arrayOperands.size() == 1 &&
1975              "lbounds can only come from one array");
1976       std::vector<mlir::Value> lbs =
1977           fir::factory::getOrigins(arrayOperands[0].shape);
1978       lbounds.append(lbs.begin(), lbs.end());
1979     }
1980     fir::factory::MutableBoxReallocation realloc =
1981         fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape,
1982                                          lengthParams);
1983     // Create ArrayLoad for the mutable box and save it into `destination`.
1984     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
1985     ccStoreToDest = genarr(realloc.newValue);
1986     // If the rhs is scalar, get shape from the allocatable ArrayLoad.
1987     if (destShape.empty())
1988       destShape = getShape(destination);
1989     // Finish lowering the loop nest.
1990     assert(destination && "destination must have been set");
1991     ExtValue exv = lowerArrayExpression(rhsCC, resultTy);
1992     if (explicitSpaceIsActive()) {
1993       explicitSpace->finalizeContext();
1994       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
1995     } else {
1996       builder.create<fir::ArrayMergeStoreOp>(
1997           loc, destination, fir::getBase(exv), destination.getMemref(),
1998           destination.getSlice(), destination.getTypeparams());
1999     }
2000     fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds,
2001                                   takeLboundsIfRealloc, realloc);
2002   }
2003 
2004   /// Entry point for when an array expression appears in a context where the
2005   /// result must be boxed. (BoxValue semantics.)
2006   static ExtValue
2007   lowerBoxedArrayExpression(Fortran::lower::AbstractConverter &converter,
2008                             Fortran::lower::SymMap &symMap,
2009                             Fortran::lower::StatementContext &stmtCtx,
2010                             const Fortran::lower::SomeExpr &expr) {
2011     ArrayExprLowering ael{converter, stmtCtx, symMap,
2012                           ConstituentSemantics::BoxValue};
2013     return ael.lowerBoxedArrayExpr(expr);
2014   }
2015 
2016   ExtValue lowerBoxedArrayExpr(const Fortran::lower::SomeExpr &exp) {
2017     return std::visit(
2018         [&](const auto &e) {
2019           auto f = genarr(e);
2020           ExtValue exv = f(IterationSpace{});
2021           if (fir::getBase(exv).getType().template isa<fir::BoxType>())
2022             return exv;
2023           fir::emitFatalError(getLoc(), "array must be emboxed");
2024         },
2025         exp.u);
2026   }
2027 
2028   /// Entry point into lowering an expression with rank. This entry point is for
2029   /// lowering a rhs expression, for example. (RefTransparent semantics.)
2030   static ExtValue
2031   lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter,
2032                           Fortran::lower::SymMap &symMap,
2033                           Fortran::lower::StatementContext &stmtCtx,
2034                           const Fortran::lower::SomeExpr &expr) {
2035     ArrayExprLowering ael{converter, stmtCtx, symMap};
2036     ael.determineShapeOfDest(expr);
2037     ExtValue loopRes = ael.lowerArrayExpression(expr);
2038     fir::ArrayLoadOp dest = ael.destination;
2039     mlir::Value tempRes = dest.getMemref();
2040     fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2041     mlir::Location loc = converter.getCurrentLocation();
2042     builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes),
2043                                            tempRes, dest.getSlice(),
2044                                            dest.getTypeparams());
2045 
2046     auto arrTy =
2047         fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>();
2048     if (auto charTy =
2049             arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) {
2050       if (fir::characterWithDynamicLen(charTy))
2051         TODO(loc, "CHARACTER does not have constant LEN");
2052       mlir::Value len = builder.createIntegerConstant(
2053           loc, builder.getCharacterLengthType(), charTy.getLen());
2054       return fir::CharArrayBoxValue(tempRes, len, dest.getExtents());
2055     }
2056     return fir::ArrayBoxValue(tempRes, dest.getExtents());
2057   }
2058 
2059   // FIXME: should take multiple inner arguments.
2060   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
2061   genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) {
2062     mlir::Location loc = getLoc();
2063     mlir::IndexType idxTy = builder.getIndexType();
2064     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
2065     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
2066     llvm::SmallVector<mlir::Value> loopUppers;
2067 
2068     // Convert any implied shape to closed interval form. The fir.do_loop will
2069     // run from 0 to `extent - 1` inclusive.
2070     for (auto extent : shape)
2071       loopUppers.push_back(
2072           builder.create<mlir::arith::SubIOp>(loc, extent, one));
2073 
2074     // Iteration space is created with outermost columns, innermost rows
2075     llvm::SmallVector<fir::DoLoopOp> loops;
2076 
2077     const std::size_t loopDepth = loopUppers.size();
2078     llvm::SmallVector<mlir::Value> ivars;
2079 
2080     for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) {
2081       if (i.index() > 0) {
2082         assert(!loops.empty());
2083         builder.setInsertionPointToStart(loops.back().getBody());
2084       }
2085       fir::DoLoopOp loop;
2086       if (innerArg) {
2087         loop = builder.create<fir::DoLoopOp>(
2088             loc, zero, i.value(), one, isUnordered(),
2089             /*finalCount=*/false, mlir::ValueRange{innerArg});
2090         innerArg = loop.getRegionIterArgs().front();
2091         if (explicitSpaceIsActive())
2092           explicitSpace->setInnerArg(0, innerArg);
2093       } else {
2094         loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one,
2095                                              isUnordered(),
2096                                              /*finalCount=*/false);
2097       }
2098       ivars.push_back(loop.getInductionVar());
2099       loops.push_back(loop);
2100     }
2101 
2102     if (innerArg)
2103       for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth;
2104            ++i) {
2105         builder.setInsertionPointToEnd(loops[i].getBody());
2106         builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0));
2107       }
2108 
2109     // Move insertion point to the start of the innermost loop in the nest.
2110     builder.setInsertionPointToStart(loops.back().getBody());
2111     // Set `afterLoopNest` to just after the entire loop nest.
2112     auto currPt = builder.saveInsertionPoint();
2113     builder.setInsertionPointAfter(loops[0]);
2114     auto afterLoopNest = builder.saveInsertionPoint();
2115     builder.restoreInsertionPoint(currPt);
2116 
2117     // Put the implicit loop variables in row to column order to match FIR's
2118     // Ops. (The loops were constructed from outermost column to innermost
2119     // row.)
2120     mlir::Value outerRes = loops[0].getResult(0);
2121     return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)),
2122             afterLoopNest};
2123   }
2124 
2125   /// Build the iteration space into which the array expression will be
2126   /// lowered. The resultType is used to create a temporary, if needed.
2127   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
2128   genIterSpace(mlir::Type resultType) {
2129     mlir::Location loc = getLoc();
2130     llvm::SmallVector<mlir::Value> shape = genIterationShape();
2131     if (!destination) {
2132       // Allocate storage for the result if it is not already provided.
2133       destination = createAndLoadSomeArrayTemp(resultType, shape);
2134     }
2135 
2136     // Generate the lazy mask allocation, if one was given.
2137     if (ccPrelude.hasValue())
2138       ccPrelude.getValue()(shape);
2139 
2140     // Now handle the implicit loops.
2141     mlir::Value inner = explicitSpaceIsActive()
2142                             ? explicitSpace->getInnerArgs().front()
2143                             : destination.getResult();
2144     auto [iters, afterLoopNest] = genImplicitLoops(shape, inner);
2145     mlir::Value innerArg = iters.innerArgument();
2146 
2147     // Generate the mask conditional structure, if there are masks. Unlike the
2148     // explicit masks, which are interleaved, these mask expression appear in
2149     // the innermost loop.
2150     if (implicitSpaceHasMasks()) {
2151       // Recover the cached condition from the mask buffer.
2152       auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) {
2153         return implicitSpace->getBoundClosure(e)(iters);
2154       };
2155 
2156       // Handle the negated conditions in topological order of the WHERE
2157       // clauses. See 10.2.3.2p4 as to why this control structure is produced.
2158       for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs :
2159            implicitSpace->getMasks()) {
2160         const std::size_t size = maskExprs.size() - 1;
2161         auto genFalseBlock = [&](const auto *e, auto &&cond) {
2162           auto ifOp = builder.create<fir::IfOp>(
2163               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
2164               /*withElseRegion=*/true);
2165           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
2166           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2167           builder.create<fir::ResultOp>(loc, innerArg);
2168           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
2169         };
2170         auto genTrueBlock = [&](const auto *e, auto &&cond) {
2171           auto ifOp = builder.create<fir::IfOp>(
2172               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
2173               /*withElseRegion=*/true);
2174           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
2175           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
2176           builder.create<fir::ResultOp>(loc, innerArg);
2177           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2178         };
2179         for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i)
2180           if (const auto *e = maskExprs[i])
2181             genFalseBlock(e, genCond(e, iters));
2182 
2183         // The last condition is either non-negated or unconditionally negated.
2184         if (const auto *e = maskExprs[size])
2185           genTrueBlock(e, genCond(e, iters));
2186       }
2187     }
2188 
2189     // We're ready to lower the body (an assignment statement) for this context
2190     // of loop nests at this point.
2191     return {iters, afterLoopNest};
2192   }
2193 
2194   fir::ArrayLoadOp
2195   createAndLoadSomeArrayTemp(mlir::Type type,
2196                              llvm::ArrayRef<mlir::Value> shape) {
2197     if (ccLoadDest.hasValue())
2198       return ccLoadDest.getValue()(shape);
2199     auto seqTy = type.dyn_cast<fir::SequenceType>();
2200     assert(seqTy && "must be an array");
2201     mlir::Location loc = getLoc();
2202     // TODO: Need to thread the length parameters here. For character, they may
2203     // differ from the operands length (e.g concatenation). So the array loads
2204     // type parameters are not enough.
2205     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>())
2206       if (charTy.hasDynamicLen())
2207         TODO(loc, "character array expression temp with dynamic length");
2208     if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>())
2209       if (recTy.getNumLenParams() > 0)
2210         TODO(loc, "derived type array expression temp with length parameters");
2211     mlir::Value temp = seqTy.hasConstantShape()
2212                            ? builder.create<fir::AllocMemOp>(loc, type)
2213                            : builder.create<fir::AllocMemOp>(
2214                                  loc, type, ".array.expr", llvm::None, shape);
2215     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
2216     stmtCtx.attachCleanup(
2217         [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); });
2218     mlir::Value shapeOp = genShapeOp(shape);
2219     return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp,
2220                                             /*slice=*/mlir::Value{},
2221                                             llvm::None);
2222   }
2223 
2224   static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder,
2225                                  llvm::ArrayRef<mlir::Value> shape) {
2226     mlir::IndexType idxTy = builder.getIndexType();
2227     llvm::SmallVector<mlir::Value> idxShape;
2228     for (auto s : shape)
2229       idxShape.push_back(builder.createConvert(loc, idxTy, s));
2230     auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size());
2231     return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape);
2232   }
2233 
2234   fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) {
2235     return genShapeOp(getLoc(), builder, shape);
2236   }
2237 
2238   //===--------------------------------------------------------------------===//
2239   // Expression traversal and lowering.
2240   //===--------------------------------------------------------------------===//
2241 
2242   /// Lower the expression, \p x, in a scalar context.
2243   template <typename A>
2244   ExtValue asScalar(const A &x) {
2245     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x);
2246   }
2247 
2248   /// Lower the expression in a scalar context to a memory reference.
2249   template <typename A>
2250   ExtValue asScalarRef(const A &x) {
2251     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x);
2252   }
2253 
2254   // An expression with non-zero rank is an array expression.
2255   template <typename A>
2256   bool isArray(const A &x) const {
2257     return x.Rank() != 0;
2258   }
2259 
2260   /// If there were temporaries created for this element evaluation, finalize
2261   /// and deallocate the resources now. This should be done just prior the the
2262   /// fir::ResultOp at the end of the innermost loop.
2263   void finalizeElementCtx() {
2264     if (elementCtx) {
2265       stmtCtx.finalize(/*popScope=*/true);
2266       elementCtx = false;
2267     }
2268   }
2269 
2270   template <typename A>
2271   CC genScalarAndForwardValue(const A &x) {
2272     ExtValue result = asScalar(x);
2273     return [=](IterSpace) { return result; };
2274   }
2275 
2276   template <typename A, typename = std::enable_if_t<Fortran::common::HasMember<
2277                             A, Fortran::evaluate::TypelessExpression>>>
2278   CC genarr(const A &x) {
2279     return genScalarAndForwardValue(x);
2280   }
2281 
2282   template <typename A>
2283   CC genarr(const Fortran::evaluate::Expr<A> &x) {
2284     LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x));
2285     if (isArray(x) || explicitSpaceIsActive() ||
2286         isElementalProcWithArrayArgs(x))
2287       return std::visit([&](const auto &e) { return genarr(e); }, x.u);
2288     return genScalarAndForwardValue(x);
2289   }
2290 
2291   template <Fortran::common::TypeCategory TC1, int KIND,
2292             Fortran::common::TypeCategory TC2>
2293   CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
2294                                              TC2> &x) {
2295     TODO(getLoc(), "");
2296   }
2297 
2298   template <int KIND>
2299   CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) {
2300     TODO(getLoc(), "");
2301   }
2302 
2303   template <typename T>
2304   CC genarr(const Fortran::evaluate::Parentheses<T> &x) {
2305     TODO(getLoc(), "");
2306   }
2307 
2308   template <int KIND>
2309   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
2310                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
2311     TODO(getLoc(), "");
2312   }
2313 
2314   template <int KIND>
2315   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
2316                 Fortran::common::TypeCategory::Real, KIND>> &x) {
2317     TODO(getLoc(), "");
2318   }
2319   template <int KIND>
2320   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
2321                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
2322     TODO(getLoc(), "");
2323   }
2324 
2325 #undef GENBIN
2326 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
2327   template <int KIND>                                                          \
2328   CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type<       \
2329                 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) {       \
2330     TODO(getLoc(), "genarr Binary");                                           \
2331   }
2332 
2333   GENBIN(Add, Integer, mlir::arith::AddIOp)
2334   GENBIN(Add, Real, mlir::arith::AddFOp)
2335   GENBIN(Add, Complex, fir::AddcOp)
2336   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
2337   GENBIN(Subtract, Real, mlir::arith::SubFOp)
2338   GENBIN(Subtract, Complex, fir::SubcOp)
2339   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
2340   GENBIN(Multiply, Real, mlir::arith::MulFOp)
2341   GENBIN(Multiply, Complex, fir::MulcOp)
2342   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
2343   GENBIN(Divide, Real, mlir::arith::DivFOp)
2344   GENBIN(Divide, Complex, fir::DivcOp)
2345 
2346   template <Fortran::common::TypeCategory TC, int KIND>
2347   CC genarr(
2348       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
2349     TODO(getLoc(), "genarr ");
2350   }
2351   template <Fortran::common::TypeCategory TC, int KIND>
2352   CC genarr(
2353       const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
2354     TODO(getLoc(), "genarr ");
2355   }
2356   template <Fortran::common::TypeCategory TC, int KIND>
2357   CC genarr(
2358       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
2359           &x) {
2360     TODO(getLoc(), "genarr ");
2361   }
2362   template <int KIND>
2363   CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
2364     TODO(getLoc(), "genarr ");
2365   }
2366 
2367   template <int KIND>
2368   CC genarr(const Fortran::evaluate::Concat<KIND> &x) {
2369     TODO(getLoc(), "genarr ");
2370   }
2371 
2372   template <int KIND>
2373   CC genarr(const Fortran::evaluate::SetLength<KIND> &x) {
2374     TODO(getLoc(), "genarr ");
2375   }
2376 
2377   template <typename A>
2378   CC genarr(const Fortran::evaluate::Constant<A> &x) {
2379     TODO(getLoc(), "genarr ");
2380   }
2381 
2382   CC genarr(const Fortran::semantics::SymbolRef &sym,
2383             ComponentPath &components) {
2384     return genarr(sym.get(), components);
2385   }
2386 
2387   ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) {
2388     return convertToArrayBoxValue(getLoc(), builder, val, len);
2389   }
2390 
2391   CC genarr(const ExtValue &extMemref) {
2392     ComponentPath dummy(/*isImplicit=*/true);
2393     return genarr(extMemref, dummy);
2394   }
2395 
2396   template <typename A>
2397   CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) {
2398     TODO(getLoc(), "genarr ArrayConstructor<A>");
2399   }
2400 
2401   CC genarr(const Fortran::evaluate::ImpliedDoIndex &) {
2402     TODO(getLoc(), "genarr ImpliedDoIndex");
2403   }
2404 
2405   CC genarr(const Fortran::evaluate::TypeParamInquiry &x) {
2406     TODO(getLoc(), "genarr TypeParamInquiry");
2407   }
2408 
2409   CC genarr(const Fortran::evaluate::DescriptorInquiry &x) {
2410     TODO(getLoc(), "genarr DescriptorInquiry");
2411   }
2412 
2413   CC genarr(const Fortran::evaluate::StructureConstructor &x) {
2414     TODO(getLoc(), "genarr StructureConstructor");
2415   }
2416 
2417   template <int KIND>
2418   CC genarr(const Fortran::evaluate::Not<KIND> &x) {
2419     TODO(getLoc(), "genarr Not");
2420   }
2421 
2422   template <int KIND>
2423   CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) {
2424     TODO(getLoc(), "genarr LogicalOperation");
2425   }
2426 
2427   template <int KIND>
2428   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
2429                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
2430     TODO(getLoc(), "genarr Relational Integer");
2431   }
2432   template <int KIND>
2433   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
2434                 Fortran::common::TypeCategory::Character, KIND>> &x) {
2435     TODO(getLoc(), "genarr Relational Character");
2436   }
2437   template <int KIND>
2438   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
2439                 Fortran::common::TypeCategory::Real, KIND>> &x) {
2440     TODO(getLoc(), "genarr Relational Real");
2441   }
2442   template <int KIND>
2443   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
2444                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
2445     TODO(getLoc(), "genarr Relational Complex");
2446   }
2447   CC genarr(
2448       const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) {
2449     TODO(getLoc(), "genarr Relational SomeType");
2450   }
2451 
2452   template <typename A>
2453   CC genarr(const Fortran::evaluate::Designator<A> &des) {
2454     ComponentPath components(des.Rank() > 0);
2455     return std::visit([&](const auto &x) { return genarr(x, components); },
2456                       des.u);
2457   }
2458 
2459   template <typename T>
2460   CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) {
2461     TODO(getLoc(), "genarr FunctionRef");
2462   }
2463 
2464   template <typename A>
2465   CC genImplicitArrayAccess(const A &x, ComponentPath &components) {
2466     components.reversePath.push_back(ImplicitSubscripts{});
2467     ExtValue exv = asScalarRef(x);
2468     // lowerPath(exv, components);
2469     auto lambda = genarr(exv, components);
2470     return [=](IterSpace iters) { return lambda(components.pc(iters)); };
2471   }
2472   CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x,
2473                             ComponentPath &components) {
2474     if (x.IsSymbol())
2475       return genImplicitArrayAccess(x.GetFirstSymbol(), components);
2476     return genImplicitArrayAccess(x.GetComponent(), components);
2477   }
2478 
2479   template <typename A>
2480   CC genAsScalar(const A &x) {
2481     mlir::Location loc = getLoc();
2482     if (isProjectedCopyInCopyOut()) {
2483       return [=, &x, builder = &converter.getFirOpBuilder()](
2484                  IterSpace iters) -> ExtValue {
2485         ExtValue exv = asScalarRef(x);
2486         mlir::Value val = fir::getBase(exv);
2487         mlir::Type eleTy = fir::unwrapRefType(val.getType());
2488         if (isAdjustedArrayElementType(eleTy)) {
2489           if (fir::isa_char(eleTy)) {
2490             TODO(getLoc(), "assignment of character type");
2491           } else if (fir::isa_derived(eleTy)) {
2492             TODO(loc, "assignment of derived type");
2493           } else {
2494             fir::emitFatalError(loc, "array type not expected in scalar");
2495           }
2496         } else {
2497           builder->create<fir::StoreOp>(loc, iters.getElement(), val);
2498         }
2499         return exv;
2500       };
2501     }
2502     return [=, &x](IterSpace) { return asScalar(x); };
2503   }
2504 
2505   CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) {
2506     if (explicitSpaceIsActive()) {
2507       TODO(getLoc(), "genarr Symbol explicitSpace");
2508     } else {
2509       return genImplicitArrayAccess(x, components);
2510     }
2511   }
2512 
2513   CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) {
2514     TODO(getLoc(), "genarr Component");
2515   }
2516 
2517   /// Array reference with subscripts. If this has rank > 0, this is a form
2518   /// of an array section (slice).
2519   ///
2520   /// There are two "slicing" primitives that may be applied on a dimension by
2521   /// dimension basis: (1) triple notation and (2) vector addressing. Since
2522   /// dimensions can be selectively sliced, some dimensions may contain
2523   /// regular scalar expressions and those dimensions do not participate in
2524   /// the array expression evaluation.
2525   CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) {
2526     if (explicitSpaceIsActive()) {
2527       TODO(getLoc(), "genarr ArrayRef explicitSpace");
2528     } else {
2529       if (Fortran::lower::isRankedArrayAccess(x)) {
2530         components.reversePath.push_back(&x);
2531         return genImplicitArrayAccess(x.base(), components);
2532       }
2533     }
2534     bool atEnd = pathIsEmpty(components);
2535     components.reversePath.push_back(&x);
2536     auto result = genarr(x.base(), components);
2537     if (components.applied)
2538       return result;
2539     mlir::Location loc = getLoc();
2540     if (atEnd) {
2541       if (x.Rank() == 0)
2542         return genAsScalar(x);
2543       fir::emitFatalError(loc, "expected scalar");
2544     }
2545     return [=](IterSpace) -> ExtValue {
2546       fir::emitFatalError(loc, "reached arrayref with path");
2547     };
2548   }
2549 
2550   CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) {
2551     TODO(getLoc(), "coarray reference");
2552   }
2553 
2554   CC genarr(const Fortran::evaluate::NamedEntity &x,
2555             ComponentPath &components) {
2556     return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components)
2557                         : genarr(x.GetComponent(), components);
2558   }
2559 
2560   CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) {
2561     return std::visit([&](const auto &v) { return genarr(v, components); },
2562                       x.u);
2563   }
2564 
2565   bool pathIsEmpty(const ComponentPath &components) {
2566     return components.reversePath.empty();
2567   }
2568 
2569   CC genarr(const Fortran::evaluate::ComplexPart &x,
2570             ComponentPath &components) {
2571     TODO(getLoc(), "genarr ComplexPart");
2572   }
2573 
2574   CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &,
2575             ComponentPath &components) {
2576     TODO(getLoc(), "genarr StaticDataObject::Pointer");
2577   }
2578 
2579   /// Substrings (see 9.4.1)
2580   CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) {
2581     TODO(getLoc(), "genarr Substring");
2582   }
2583 
2584   /// Base case of generating an array reference,
2585   CC genarr(const ExtValue &extMemref, ComponentPath &components) {
2586     mlir::Location loc = getLoc();
2587     mlir::Value memref = fir::getBase(extMemref);
2588     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType());
2589     assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array");
2590     mlir::Value shape = builder.createShape(loc, extMemref);
2591     mlir::Value slice;
2592     if (components.isSlice()) {
2593       TODO(loc, "genarr with Slices");
2594     }
2595     arrayOperands.push_back(ArrayOperand{memref, shape, slice});
2596     if (destShape.empty())
2597       destShape = getShape(arrayOperands.back());
2598     if (isBoxValue()) {
2599       // Semantics are a reference to a boxed array.
2600       // This case just requires that an embox operation be created to box the
2601       // value. The value of the box is forwarded in the continuation.
2602       mlir::Type reduceTy = reduceRank(arrTy, slice);
2603       auto boxTy = fir::BoxType::get(reduceTy);
2604       if (components.substring) {
2605         // Adjust char length to substring size.
2606         fir::CharacterType charTy =
2607             fir::factory::CharacterExprHelper::getCharType(reduceTy);
2608         auto seqTy = reduceTy.cast<fir::SequenceType>();
2609         // TODO: Use a constant for fir.char LEN if we can compute it.
2610         boxTy = fir::BoxType::get(
2611             fir::SequenceType::get(fir::CharacterType::getUnknownLen(
2612                                        builder.getContext(), charTy.getFKind()),
2613                                    seqTy.getDimension()));
2614       }
2615       mlir::Value embox =
2616           memref.getType().isa<fir::BoxType>()
2617               ? builder.create<fir::ReboxOp>(loc, boxTy, memref, shape, slice)
2618                     .getResult()
2619               : builder
2620                     .create<fir::EmboxOp>(loc, boxTy, memref, shape, slice,
2621                                           fir::getTypeParams(extMemref))
2622                     .getResult();
2623       return [=](IterSpace) -> ExtValue { return fir::BoxValue(embox); };
2624     }
2625     if (isReferentiallyOpaque()) {
2626       TODO(loc, "genarr isReferentiallyOpaque");
2627     }
2628     auto arrLoad = builder.create<fir::ArrayLoadOp>(
2629         loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref));
2630     mlir::Value arrLd = arrLoad.getResult();
2631     if (isProjectedCopyInCopyOut()) {
2632       // Semantics are projected copy-in copy-out.
2633       // The backing store of the destination of an array expression may be
2634       // partially modified. These updates are recorded in FIR by forwarding a
2635       // continuation that generates an `array_update` Op. The destination is
2636       // always loaded at the beginning of the statement and merged at the
2637       // end.
2638       destination = arrLoad;
2639       auto lambda = ccStoreToDest.hasValue()
2640                         ? ccStoreToDest.getValue()
2641                         : defaultStoreToDestination(components.substring);
2642       return [=](IterSpace iters) -> ExtValue { return lambda(iters); };
2643     }
2644     if (isCustomCopyInCopyOut()) {
2645       TODO(loc, "isCustomCopyInCopyOut");
2646     }
2647     if (isCopyInCopyOut()) {
2648       // Semantics are copy-in copy-out.
2649       // The continuation simply forwards the result of the `array_load` Op,
2650       // which is the value of the array as it was when loaded. All data
2651       // references with rank > 0 in an array expression typically have
2652       // copy-in copy-out semantics.
2653       return [=](IterSpace) -> ExtValue { return arrLd; };
2654     }
2655     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
2656     if (isValueAttribute()) {
2657       // Semantics are value attribute.
2658       // Here the continuation will `array_fetch` a value from an array and
2659       // then store that value in a temporary. One can thus imitate pass by
2660       // value even when the call is pass by reference.
2661       return [=](IterSpace iters) -> ExtValue {
2662         mlir::Value base;
2663         mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
2664         if (isAdjustedArrayElementType(eleTy)) {
2665           mlir::Type eleRefTy = builder.getRefType(eleTy);
2666           base = builder.create<fir::ArrayAccessOp>(
2667               loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
2668         } else {
2669           base = builder.create<fir::ArrayFetchOp>(
2670               loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
2671         }
2672         mlir::Value temp = builder.createTemporary(
2673             loc, base.getType(),
2674             llvm::ArrayRef<mlir::NamedAttribute>{
2675                 Fortran::lower::getAdaptToByRefAttr(builder)});
2676         builder.create<fir::StoreOp>(loc, base, temp);
2677         return fir::factory::arraySectionElementToExtendedValue(
2678             builder, loc, extMemref, temp, slice);
2679       };
2680     }
2681     // In the default case, the array reference forwards an `array_fetch` or
2682     // `array_access` Op in the continuation.
2683     return [=](IterSpace iters) -> ExtValue {
2684       mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
2685       if (isAdjustedArrayElementType(eleTy)) {
2686         mlir::Type eleRefTy = builder.getRefType(eleTy);
2687         mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>(
2688             loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
2689         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
2690           llvm::SmallVector<mlir::Value> substringBounds;
2691           populateBounds(substringBounds, components.substring);
2692           if (!substringBounds.empty()) {
2693             // mlir::Value dstLen = fir::factory::genLenOfCharacter(
2694             //     builder, loc, arrLoad, iters.iterVec(), substringBounds);
2695             // fir::CharBoxValue dstChar(arrayOp, dstLen);
2696             // return fir::factory::CharacterExprHelper{builder, loc}
2697             //     .createSubstring(dstChar, substringBounds);
2698           }
2699         }
2700         return fir::factory::arraySectionElementToExtendedValue(
2701             builder, loc, extMemref, arrayOp, slice);
2702       }
2703       auto arrFetch = builder.create<fir::ArrayFetchOp>(
2704           loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
2705       return fir::factory::arraySectionElementToExtendedValue(
2706           builder, loc, extMemref, arrFetch, slice);
2707     };
2708   }
2709 
2710   /// Reduce the rank of a array to be boxed based on the slice's operands.
2711   static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) {
2712     if (slice) {
2713       auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp());
2714       assert(slOp && "expected slice op");
2715       auto seqTy = arrTy.dyn_cast<fir::SequenceType>();
2716       assert(seqTy && "expected array type");
2717       mlir::Operation::operand_range triples = slOp.getTriples();
2718       fir::SequenceType::Shape shape;
2719       // reduce the rank for each invariant dimension
2720       for (unsigned i = 1, end = triples.size(); i < end; i += 3)
2721         if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp()))
2722           shape.push_back(fir::SequenceType::getUnknownExtent());
2723       return fir::SequenceType::get(shape, seqTy.getEleTy());
2724     }
2725     // not sliced, so no change in rank
2726     return arrTy;
2727   }
2728 
2729 private:
2730   void determineShapeOfDest(const fir::ExtendedValue &lhs) {
2731     destShape = fir::factory::getExtents(builder, getLoc(), lhs);
2732   }
2733 
2734   void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) {
2735     if (!destShape.empty())
2736       return;
2737     // if (explicitSpaceIsActive() && determineShapeWithSlice(lhs))
2738     //   return;
2739     mlir::Type idxTy = builder.getIndexType();
2740     mlir::Location loc = getLoc();
2741     if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape =
2742             Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(),
2743                                                   lhs))
2744       for (Fortran::common::ConstantSubscript extent : *constantShape)
2745         destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent));
2746   }
2747 
2748   ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) {
2749     mlir::Type resTy = converter.genType(exp);
2750     return std::visit(
2751         [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); },
2752         exp.u);
2753   }
2754   ExtValue lowerArrayExpression(const ExtValue &exv) {
2755     assert(!explicitSpace);
2756     mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType());
2757     return lowerArrayExpression(genarr(exv), resTy);
2758   }
2759 
2760   void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds,
2761                       const Fortran::evaluate::Substring *substring) {
2762     if (!substring)
2763       return;
2764     bounds.push_back(fir::getBase(asScalar(substring->lower())));
2765     if (auto upper = substring->upper())
2766       bounds.push_back(fir::getBase(asScalar(*upper)));
2767   }
2768 
2769   /// Default store to destination implementation.
2770   /// This implements the default case, which is to assign the value in
2771   /// `iters.element` into the destination array, `iters.innerArgument`. Handles
2772   /// by value and by reference assignment.
2773   CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) {
2774     return [=](IterSpace iterSpace) -> ExtValue {
2775       mlir::Location loc = getLoc();
2776       mlir::Value innerArg = iterSpace.innerArgument();
2777       fir::ExtendedValue exv = iterSpace.elementExv();
2778       mlir::Type arrTy = innerArg.getType();
2779       mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec());
2780       if (isAdjustedArrayElementType(eleTy)) {
2781         // The elemental update is in the memref domain. Under this semantics,
2782         // we must always copy the computed new element from its location in
2783         // memory into the destination array.
2784         mlir::Type resRefTy = builder.getRefType(eleTy);
2785         // Get a reference to the array element to be amended.
2786         auto arrayOp = builder.create<fir::ArrayAccessOp>(
2787             loc, resRefTy, innerArg, iterSpace.iterVec(),
2788             destination.getTypeparams());
2789         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
2790           llvm::SmallVector<mlir::Value> substringBounds;
2791           populateBounds(substringBounds, substring);
2792           mlir::Value dstLen = fir::factory::genLenOfCharacter(
2793               builder, loc, destination, iterSpace.iterVec(), substringBounds);
2794           fir::ArrayAmendOp amend = createCharArrayAmend(
2795               loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds);
2796           return abstractArrayExtValue(amend, dstLen);
2797         }
2798         if (fir::isa_derived(eleTy)) {
2799           fir::ArrayAmendOp amend = createDerivedArrayAmend(
2800               loc, destination, builder, arrayOp, exv, eleTy, innerArg);
2801           return abstractArrayExtValue(amend /*FIXME: typeparams?*/);
2802         }
2803         assert(eleTy.isa<fir::SequenceType>() && "must be an array");
2804         TODO(loc, "array (as element) assignment");
2805       }
2806       // By value semantics. The element is being assigned by value.
2807       mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv));
2808       auto update = builder.create<fir::ArrayUpdateOp>(
2809           loc, arrTy, innerArg, ele, iterSpace.iterVec(),
2810           destination.getTypeparams());
2811       return abstractArrayExtValue(update);
2812     };
2813   }
2814 
2815   /// For an elemental array expression.
2816   ///   1. Lower the scalars and array loads.
2817   ///   2. Create the iteration space.
2818   ///   3. Create the element-by-element computation in the loop.
2819   ///   4. Return the resulting array value.
2820   /// If no destination was set in the array context, a temporary of
2821   /// \p resultTy will be created to hold the evaluated expression.
2822   /// Otherwise, \p resultTy is ignored and the expression is evaluated
2823   /// in the destination. \p f is a continuation built from an
2824   /// evaluate::Expr or an ExtendedValue.
2825   ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) {
2826     mlir::Location loc = getLoc();
2827     auto [iterSpace, insPt] = genIterSpace(resultTy);
2828     auto exv = f(iterSpace);
2829     iterSpace.setElement(std::move(exv));
2830     auto lambda = ccStoreToDest.hasValue()
2831                       ? ccStoreToDest.getValue()
2832                       : defaultStoreToDestination(/*substring=*/nullptr);
2833     mlir::Value updVal = fir::getBase(lambda(iterSpace));
2834     finalizeElementCtx();
2835     builder.create<fir::ResultOp>(loc, updVal);
2836     builder.restoreInsertionPoint(insPt);
2837     return abstractArrayExtValue(iterSpace.outerResult());
2838   }
2839 
2840   /// Get the shape from an ArrayOperand. The shape of the array is adjusted if
2841   /// the array was sliced.
2842   llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) {
2843     // if (array.slice)
2844     //   return computeSliceShape(array.slice);
2845     if (array.memref.getType().isa<fir::BoxType>())
2846       return fir::factory::readExtents(builder, getLoc(),
2847                                        fir::BoxValue{array.memref});
2848     std::vector<mlir::Value, std::allocator<mlir::Value>> extents =
2849         fir::factory::getExtents(array.shape);
2850     return {extents.begin(), extents.end()};
2851   }
2852 
2853   /// Get the shape from an ArrayLoad.
2854   llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) {
2855     return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(),
2856                                  arrayLoad.getSlice()});
2857   }
2858 
2859   /// Returns the first array operand that may not be absent. If all
2860   /// array operands may be absent, return the first one.
2861   const ArrayOperand &getInducingShapeArrayOperand() const {
2862     assert(!arrayOperands.empty());
2863     for (const ArrayOperand &op : arrayOperands)
2864       if (!op.mayBeAbsent)
2865         return op;
2866     // If all arrays operand appears in optional position, then none of them
2867     // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the
2868     // first operands.
2869     // TODO: There is an opportunity to add a runtime check here that
2870     // this array is present as required.
2871     return arrayOperands[0];
2872   }
2873 
2874   /// Generate the shape of the iteration space over the array expression. The
2875   /// iteration space may be implicit, explicit, or both. If it is implied it is
2876   /// based on the destination and operand array loads, or an optional
2877   /// Fortran::evaluate::Shape from the front end. If the shape is explicit,
2878   /// this returns any implicit shape component, if it exists.
2879   llvm::SmallVector<mlir::Value> genIterationShape() {
2880     // Use the precomputed destination shape.
2881     if (!destShape.empty())
2882       return destShape;
2883     // Otherwise, use the destination's shape.
2884     if (destination)
2885       return getShape(destination);
2886     // Otherwise, use the first ArrayLoad operand shape.
2887     if (!arrayOperands.empty())
2888       return getShape(getInducingShapeArrayOperand());
2889     fir::emitFatalError(getLoc(),
2890                         "failed to compute the array expression shape");
2891   }
2892 
2893   bool explicitSpaceIsActive() const {
2894     return explicitSpace && explicitSpace->isActive();
2895   }
2896 
2897   bool implicitSpaceHasMasks() const {
2898     return implicitSpace && !implicitSpace->empty();
2899   }
2900 
2901   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
2902                              Fortran::lower::StatementContext &stmtCtx,
2903                              Fortran::lower::SymMap &symMap)
2904       : converter{converter}, builder{converter.getFirOpBuilder()},
2905         stmtCtx{stmtCtx}, symMap{symMap} {}
2906 
2907   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
2908                              Fortran::lower::StatementContext &stmtCtx,
2909                              Fortran::lower::SymMap &symMap,
2910                              ConstituentSemantics sem)
2911       : converter{converter}, builder{converter.getFirOpBuilder()},
2912         stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {}
2913 
2914   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
2915                              Fortran::lower::StatementContext &stmtCtx,
2916                              Fortran::lower::SymMap &symMap,
2917                              ConstituentSemantics sem,
2918                              Fortran::lower::ExplicitIterSpace *expSpace,
2919                              Fortran::lower::ImplicitIterSpace *impSpace)
2920       : converter{converter}, builder{converter.getFirOpBuilder()},
2921         stmtCtx{stmtCtx}, symMap{symMap},
2922         explicitSpace(expSpace->isActive() ? expSpace : nullptr),
2923         implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} {
2924     // Generate any mask expressions, as necessary. This is the compute step
2925     // that creates the effective masks. See 10.2.3.2 in particular.
2926     // genMasks();
2927   }
2928 
2929   mlir::Location getLoc() { return converter.getCurrentLocation(); }
2930 
2931   /// Array appears in a lhs context such that it is assigned after the rhs is
2932   /// fully evaluated.
2933   inline bool isCopyInCopyOut() {
2934     return semant == ConstituentSemantics::CopyInCopyOut;
2935   }
2936 
2937   /// Array appears in a lhs (or temp) context such that a projected,
2938   /// discontiguous subspace of the array is assigned after the rhs is fully
2939   /// evaluated. That is, the rhs array value is merged into a section of the
2940   /// lhs array.
2941   inline bool isProjectedCopyInCopyOut() {
2942     return semant == ConstituentSemantics::ProjectedCopyInCopyOut;
2943   }
2944 
2945   inline bool isCustomCopyInCopyOut() {
2946     return semant == ConstituentSemantics::CustomCopyInCopyOut;
2947   }
2948 
2949   /// Array appears in a context where it must be boxed.
2950   inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; }
2951 
2952   /// Array appears in a context where differences in the memory reference can
2953   /// be observable in the computational results. For example, an array
2954   /// element is passed to an impure procedure.
2955   inline bool isReferentiallyOpaque() {
2956     return semant == ConstituentSemantics::RefOpaque;
2957   }
2958 
2959   /// Array appears in a context where it is passed as a VALUE argument.
2960   inline bool isValueAttribute() {
2961     return semant == ConstituentSemantics::ByValueArg;
2962   }
2963 
2964   /// Can the loops over the expression be unordered?
2965   inline bool isUnordered() const { return unordered; }
2966 
2967   void setUnordered(bool b) { unordered = b; }
2968 
2969   Fortran::lower::AbstractConverter &converter;
2970   fir::FirOpBuilder &builder;
2971   Fortran::lower::StatementContext &stmtCtx;
2972   bool elementCtx = false;
2973   Fortran::lower::SymMap &symMap;
2974   /// The continuation to generate code to update the destination.
2975   llvm::Optional<CC> ccStoreToDest;
2976   llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude;
2977   llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>>
2978       ccLoadDest;
2979   /// The destination is the loaded array into which the results will be
2980   /// merged.
2981   fir::ArrayLoadOp destination;
2982   /// The shape of the destination.
2983   llvm::SmallVector<mlir::Value> destShape;
2984   /// List of arrays in the expression that have been loaded.
2985   llvm::SmallVector<ArrayOperand> arrayOperands;
2986   /// If there is a user-defined iteration space, explicitShape will hold the
2987   /// information from the front end.
2988   Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr;
2989   Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr;
2990   ConstituentSemantics semant = ConstituentSemantics::RefTransparent;
2991   // Can the array expression be evaluated in any order?
2992   // Will be set to false if any of the expression parts prevent this.
2993   bool unordered = true;
2994 };
2995 } // namespace
2996 
2997 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
2998     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
2999     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
3000     Fortran::lower::StatementContext &stmtCtx) {
3001   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
3002   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr);
3003 }
3004 
3005 fir::GlobalOp Fortran::lower::createDenseGlobal(
3006     mlir::Location loc, mlir::Type symTy, llvm::StringRef globalName,
3007     mlir::StringAttr linkage, bool isConst,
3008     const Fortran::lower::SomeExpr &expr,
3009     Fortran::lower::AbstractConverter &converter) {
3010 
3011   Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true);
3012   Fortran::lower::SymMap emptyMap;
3013   InitializerData initData(/*genRawVals=*/true);
3014   ScalarExprLowering sel(loc, converter, emptyMap, stmtCtx,
3015                          /*initializer=*/&initData);
3016   sel.genval(expr);
3017 
3018   size_t sz = initData.rawVals.size();
3019   llvm::ArrayRef<mlir::Attribute> ar = {initData.rawVals.data(), sz};
3020 
3021   mlir::RankedTensorType tensorTy;
3022   auto &builder = converter.getFirOpBuilder();
3023   mlir::Type iTy = initData.rawType;
3024   if (!iTy)
3025     return 0; // array extent is probably 0 in this case, so just return 0.
3026   tensorTy = mlir::RankedTensorType::get(sz, iTy);
3027   auto init = mlir::DenseElementsAttr::get(tensorTy, ar);
3028   return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst);
3029 }
3030 
3031 fir::ExtendedValue Fortran::lower::createSomeInitializerExpression(
3032     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
3033     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
3034     Fortran::lower::StatementContext &stmtCtx) {
3035   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
3036   InitializerData initData; // needed for initializations
3037   return ScalarExprLowering{loc, converter, symMap, stmtCtx,
3038                             /*initializer=*/&initData}
3039       .genval(expr);
3040 }
3041 
3042 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
3043     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
3044     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
3045     Fortran::lower::StatementContext &stmtCtx) {
3046   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
3047   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr);
3048 }
3049 
3050 fir::ExtendedValue Fortran::lower::createInitializerAddress(
3051     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
3052     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
3053     Fortran::lower::StatementContext &stmtCtx) {
3054   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
3055   InitializerData init;
3056   return ScalarExprLowering(loc, converter, symMap, stmtCtx, &init).gen(expr);
3057 }
3058 
3059 fir::ExtendedValue
3060 Fortran::lower::createSomeArrayBox(Fortran::lower::AbstractConverter &converter,
3061                                    const Fortran::lower::SomeExpr &expr,
3062                                    Fortran::lower::SymMap &symMap,
3063                                    Fortran::lower::StatementContext &stmtCtx) {
3064   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "box designator: ") << '\n');
3065   return ArrayExprLowering::lowerBoxedArrayExpression(converter, symMap,
3066                                                       stmtCtx, expr);
3067 }
3068 
3069 fir::MutableBoxValue Fortran::lower::createMutableBox(
3070     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
3071     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
3072   // MutableBox lowering StatementContext does not need to be propagated
3073   // to the caller because the result value is a variable, not a temporary
3074   // expression. The StatementContext clean-up can occur before using the
3075   // resulting MutableBoxValue. Variables of all other types are handled in the
3076   // bridge.
3077   Fortran::lower::StatementContext dummyStmtCtx;
3078   return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx}
3079       .genMutableBoxValue(expr);
3080 }
3081 
3082 mlir::Value Fortran::lower::createSubroutineCall(
3083     AbstractConverter &converter, const evaluate::ProcedureRef &call,
3084     SymMap &symMap, StatementContext &stmtCtx) {
3085   mlir::Location loc = converter.getCurrentLocation();
3086 
3087   // Simple subroutine call, with potential alternate return.
3088   auto res = Fortran::lower::createSomeExtendedExpression(
3089       loc, converter, toEvExpr(call), symMap, stmtCtx);
3090   return fir::getBase(res);
3091 }
3092 
3093 void Fortran::lower::createSomeArrayAssignment(
3094     Fortran::lower::AbstractConverter &converter,
3095     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
3096     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
3097   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
3098              rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
3099   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
3100 }
3101 
3102 void Fortran::lower::createSomeArrayAssignment(
3103     Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
3104     const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap,
3105     Fortran::lower::StatementContext &stmtCtx) {
3106   LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
3107              llvm::dbgs() << "assign expression: " << rhs << '\n';);
3108   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
3109 }
3110 
3111 void Fortran::lower::createAllocatableArrayAssignment(
3112     Fortran::lower::AbstractConverter &converter,
3113     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
3114     Fortran::lower::ExplicitIterSpace &explicitSpace,
3115     Fortran::lower::ImplicitIterSpace &implicitSpace,
3116     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
3117   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n';
3118              rhs.AsFortran(llvm::dbgs() << "assign expression: ")
3119              << " given the explicit iteration space:\n"
3120              << explicitSpace << "\n and implied mask conditions:\n"
3121              << implicitSpace << '\n';);
3122   ArrayExprLowering::lowerAllocatableArrayAssignment(
3123       converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
3124 }
3125 
3126 mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder,
3127                                            mlir::Location loc,
3128                                            mlir::Value value) {
3129   mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
3130   if (mlir::Operation *definingOp = value.getDefiningOp())
3131     if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
3132       if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>())
3133         return intAttr.getInt() < 0 ? zero : value;
3134   return Fortran::lower::genMax(builder, loc,
3135                                 llvm::SmallVector<mlir::Value>{value, zero});
3136 }
3137