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/CustomIntrinsicCall.h"
22 #include "flang/Lower/DumpEvaluateExpr.h"
23 #include "flang/Lower/IntrinsicCall.h"
24 #include "flang/Lower/StatementContext.h"
25 #include "flang/Lower/SymbolMap.h"
26 #include "flang/Lower/Todo.h"
27 #include "flang/Optimizer/Builder/Character.h"
28 #include "flang/Optimizer/Builder/Complex.h"
29 #include "flang/Optimizer/Builder/Factory.h"
30 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h"
31 #include "flang/Optimizer/Builder/MutableBox.h"
32 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
33 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
34 #include "flang/Semantics/expression.h"
35 #include "flang/Semantics/symbol.h"
36 #include "flang/Semantics/tools.h"
37 #include "flang/Semantics/type.h"
38 #include "mlir/Dialect/Func/IR/FuncOps.h"
39 #include "llvm/Support/CommandLine.h"
40 #include "llvm/Support/Debug.h"
41 
42 #define DEBUG_TYPE "flang-lower-expr"
43 
44 //===----------------------------------------------------------------------===//
45 // The composition and structure of Fortran::evaluate::Expr is defined in
46 // the various header files in include/flang/Evaluate. You are referred
47 // there for more information on these data structures. Generally speaking,
48 // these data structures are a strongly typed family of abstract data types
49 // that, composed as trees, describe the syntax of Fortran expressions.
50 //
51 // This part of the bridge can traverse these tree structures and lower them
52 // to the correct FIR representation in SSA form.
53 //===----------------------------------------------------------------------===//
54 
55 // The default attempts to balance a modest allocation size with expected user
56 // input to minimize bounds checks and reallocations during dynamic array
57 // construction. Some user codes may have very large array constructors for
58 // which the default can be increased.
59 static llvm::cl::opt<unsigned> clInitialBufferSize(
60     "array-constructor-initial-buffer-size",
61     llvm::cl::desc(
62         "set the incremental array construction buffer size (default=32)"),
63     llvm::cl::init(32u));
64 
65 /// The various semantics of a program constituent (or a part thereof) as it may
66 /// appear in an expression.
67 ///
68 /// Given the following Fortran declarations.
69 /// ```fortran
70 ///   REAL :: v1, v2, v3
71 ///   REAL, POINTER :: vp1
72 ///   REAL :: a1(c), a2(c)
73 ///   REAL ELEMENTAL FUNCTION f1(arg) ! array -> array
74 ///   FUNCTION f2(arg)                ! array -> array
75 ///   vp1 => v3       ! 1
76 ///   v1 = v2 * vp1   ! 2
77 ///   a1 = a1 + a2    ! 3
78 ///   a1 = f1(a2)     ! 4
79 ///   a1 = f2(a2)     ! 5
80 /// ```
81 ///
82 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is
83 /// constructed from the DataAddr of `v3`.
84 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed
85 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double
86 /// dereference in the `vp1` case.
87 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs
88 /// is CopyInCopyOut as `a1` is replaced elementally by the additions.
89 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if
90 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/
91 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut.
92 ///  In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational.
93 ///  `a1` on the lhs is again CopyInCopyOut.
94 enum class ConstituentSemantics {
95   // Scalar data reference semantics.
96   //
97   // For these let `v` be the location in memory of a variable with value `x`
98   DataValue, // refers to the value `x`
99   DataAddr,  // refers to the address `v`
100   BoxValue,  // refers to a box value containing `v`
101   BoxAddr,   // refers to the address of a box value containing `v`
102 
103   // Array data reference semantics.
104   //
105   // For these let `a` be the location in memory of a sequence of value `[xs]`.
106   // Let `x_i` be the `i`-th value in the sequence `[xs]`.
107 
108   // Referentially transparent. Refers to the array's value, `[xs]`.
109   RefTransparent,
110   // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7
111   // note 2). (Passing a copy by reference to simulate pass-by-value.)
112   ByValueArg,
113   // Refers to the merge of array value `[xs]` with another array value `[ys]`.
114   // This merged array value will be written into memory location `a`.
115   CopyInCopyOut,
116   // Similar to CopyInCopyOut but `a` may be a transient projection (rather than
117   // a whole array).
118   ProjectedCopyInCopyOut,
119   // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned
120   // automatically by the framework. Instead, and address for `[xs]` is made
121   // accessible so that custom assignments to `[xs]` can be implemented.
122   CustomCopyInCopyOut,
123   // Referentially opaque. Refers to the address of `x_i`.
124   RefOpaque
125 };
126 
127 /// Convert parser's INTEGER relational operators to MLIR.  TODO: using
128 /// unordered, but we may want to cons ordered in certain situation.
129 static mlir::arith::CmpIPredicate
130 translateRelational(Fortran::common::RelationalOperator rop) {
131   switch (rop) {
132   case Fortran::common::RelationalOperator::LT:
133     return mlir::arith::CmpIPredicate::slt;
134   case Fortran::common::RelationalOperator::LE:
135     return mlir::arith::CmpIPredicate::sle;
136   case Fortran::common::RelationalOperator::EQ:
137     return mlir::arith::CmpIPredicate::eq;
138   case Fortran::common::RelationalOperator::NE:
139     return mlir::arith::CmpIPredicate::ne;
140   case Fortran::common::RelationalOperator::GT:
141     return mlir::arith::CmpIPredicate::sgt;
142   case Fortran::common::RelationalOperator::GE:
143     return mlir::arith::CmpIPredicate::sge;
144   }
145   llvm_unreachable("unhandled INTEGER relational operator");
146 }
147 
148 /// Convert parser's REAL relational operators to MLIR.
149 /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018
150 /// requirements in the IEEE context (table 17.1 of F2018). This choice is
151 /// also applied in other contexts because it is easier and in line with
152 /// other Fortran compilers.
153 /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not
154 /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee
155 /// whether the comparison will signal or not in case of quiet NaN argument.
156 static mlir::arith::CmpFPredicate
157 translateFloatRelational(Fortran::common::RelationalOperator rop) {
158   switch (rop) {
159   case Fortran::common::RelationalOperator::LT:
160     return mlir::arith::CmpFPredicate::OLT;
161   case Fortran::common::RelationalOperator::LE:
162     return mlir::arith::CmpFPredicate::OLE;
163   case Fortran::common::RelationalOperator::EQ:
164     return mlir::arith::CmpFPredicate::OEQ;
165   case Fortran::common::RelationalOperator::NE:
166     return mlir::arith::CmpFPredicate::UNE;
167   case Fortran::common::RelationalOperator::GT:
168     return mlir::arith::CmpFPredicate::OGT;
169   case Fortran::common::RelationalOperator::GE:
170     return mlir::arith::CmpFPredicate::OGE;
171   }
172   llvm_unreachable("unhandled REAL relational operator");
173 }
174 
175 static mlir::Value genActualIsPresentTest(fir::FirOpBuilder &builder,
176                                           mlir::Location loc,
177                                           fir::ExtendedValue actual) {
178   if (const auto *ptrOrAlloc = actual.getBoxOf<fir::MutableBoxValue>())
179     return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc,
180                                                         *ptrOrAlloc);
181   // Optional case (not that optional allocatable/pointer cannot be absent
182   // when passed to CMPLX as per 15.5.2.12 point 3 (7) and (8)). It is
183   // therefore possible to catch them in the `then` case above.
184   return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
185                                           fir::getBase(actual));
186 }
187 
188 /// Place \p exv in memory if it is not already a memory reference. If
189 /// \p forceValueType is provided, the value is first casted to the provided
190 /// type before being stored (this is mainly intended for logicals whose value
191 /// may be `i1` but needed to be stored as Fortran logicals).
192 static fir::ExtendedValue
193 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
194                          const fir::ExtendedValue &exv,
195                          mlir::Type storageType) {
196   mlir::Value valBase = fir::getBase(exv);
197   if (fir::conformsWithPassByRef(valBase.getType()))
198     return exv;
199 
200   assert(!fir::hasDynamicSize(storageType) &&
201          "only expect statically sized scalars to be by value");
202 
203   // Since `a` is not itself a valid referent, determine its value and
204   // create a temporary location at the beginning of the function for
205   // referencing.
206   mlir::Value val = builder.createConvert(loc, storageType, valBase);
207   mlir::Value temp = builder.createTemporary(
208       loc, storageType,
209       llvm::ArrayRef<mlir::NamedAttribute>{
210           Fortran::lower::getAdaptToByRefAttr(builder)});
211   builder.create<fir::StoreOp>(loc, val, temp);
212   return fir::substBase(exv, temp);
213 }
214 
215 // Copy a copy of scalar \p exv in a new temporary.
216 static fir::ExtendedValue
217 createInMemoryScalarCopy(fir::FirOpBuilder &builder, mlir::Location loc,
218                          const fir::ExtendedValue &exv) {
219   assert(exv.rank() == 0 && "input to scalar memory copy must be a scalar");
220   if (exv.getCharBox() != nullptr)
221     return fir::factory::CharacterExprHelper{builder, loc}.createTempFrom(exv);
222   if (fir::isDerivedWithLengthParameters(exv))
223     TODO(loc, "copy derived type with length parameters");
224   mlir::Type type = fir::unwrapPassByRefType(fir::getBase(exv).getType());
225   fir::ExtendedValue temp = builder.createTemporary(loc, type);
226   fir::factory::genScalarAssignment(builder, loc, temp, exv);
227   return temp;
228 }
229 
230 /// Is this a variable wrapped in parentheses?
231 template <typename A>
232 static bool isParenthesizedVariable(const A &) {
233   return false;
234 }
235 template <typename T>
236 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) {
237   using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
238   using Parentheses = Fortran::evaluate::Parentheses<T>;
239   if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
240     if (const auto *parentheses = std::get_if<Parentheses>(&expr.u))
241       return Fortran::evaluate::IsVariable(parentheses->left());
242     return false;
243   } else {
244     return std::visit([&](const auto &x) { return isParenthesizedVariable(x); },
245                       expr.u);
246   }
247 }
248 
249 /// Generate a load of a value from an address. Beware that this will lose
250 /// any dynamic type information for polymorphic entities (note that unlimited
251 /// polymorphic cannot be loaded and must not be provided here).
252 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
253                                   mlir::Location loc,
254                                   const fir::ExtendedValue &addr) {
255   return addr.match(
256       [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
257       [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
258         if (fir::unwrapRefType(fir::getBase(v).getType())
259                 .isa<fir::RecordType>())
260           return v;
261         return builder.create<fir::LoadOp>(loc, fir::getBase(v));
262       },
263       [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
264         TODO(loc, "genLoad for MutableBoxValue");
265       },
266       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
267         TODO(loc, "genLoad for BoxValue");
268       },
269       [&](const auto &) -> fir::ExtendedValue {
270         fir::emitFatalError(
271             loc, "attempting to load whole array or procedure address");
272       });
273 }
274 
275 /// Create an optional dummy argument value from entity \p exv that may be
276 /// absent. This can only be called with numerical or logical scalar \p exv.
277 /// If \p exv is considered absent according to 15.5.2.12 point 1., the returned
278 /// value is zero (or false), otherwise it is the value of \p exv.
279 static fir::ExtendedValue genOptionalValue(fir::FirOpBuilder &builder,
280                                            mlir::Location loc,
281                                            const fir::ExtendedValue &exv,
282                                            mlir::Value isPresent) {
283   mlir::Type eleType = fir::getBaseTypeOf(exv);
284   assert(exv.rank() == 0 && fir::isa_trivial(eleType) &&
285          "must be a numerical or logical scalar");
286   return builder
287       .genIfOp(loc, {eleType}, isPresent,
288                /*withElseRegion=*/true)
289       .genThen([&]() {
290         mlir::Value val = fir::getBase(genLoad(builder, loc, exv));
291         builder.create<fir::ResultOp>(loc, val);
292       })
293       .genElse([&]() {
294         mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType);
295         builder.create<fir::ResultOp>(loc, zero);
296       })
297       .getResults()[0];
298 }
299 
300 /// Create an optional dummy argument address from entity \p exv that may be
301 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the
302 /// returned value is a null pointer, otherwise it is the address of \p exv.
303 static fir::ExtendedValue genOptionalAddr(fir::FirOpBuilder &builder,
304                                           mlir::Location loc,
305                                           const fir::ExtendedValue &exv,
306                                           mlir::Value isPresent) {
307   // If it is an exv pointer/allocatable, then it cannot be absent
308   // because it is passed to a non-pointer/non-allocatable.
309   if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
310     return fir::factory::genMutableBoxRead(builder, loc, *box);
311   // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL
312   // address and can be passed directly.
313   return exv;
314 }
315 
316 /// Create an optional dummy argument address from entity \p exv that may be
317 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the
318 /// returned value is an absent fir.box, otherwise it is a fir.box describing \p
319 /// exv.
320 static fir::ExtendedValue genOptionalBox(fir::FirOpBuilder &builder,
321                                          mlir::Location loc,
322                                          const fir::ExtendedValue &exv,
323                                          mlir::Value isPresent) {
324   // Non allocatable/pointer optional box -> simply forward
325   if (exv.getBoxOf<fir::BoxValue>())
326     return exv;
327 
328   fir::ExtendedValue newExv = exv;
329   // Optional allocatable/pointer -> Cannot be absent, but need to translate
330   // unallocated/diassociated into absent fir.box.
331   if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
332     newExv = fir::factory::genMutableBoxRead(builder, loc, *box);
333 
334   // createBox will not do create any invalid memory dereferences if exv is
335   // absent. The created fir.box will not be usable, but the SelectOp below
336   // ensures it won't be.
337   mlir::Value box = builder.createBox(loc, newExv);
338   mlir::Type boxType = box.getType();
339   auto absent = builder.create<fir::AbsentOp>(loc, boxType);
340   auto boxOrAbsent = builder.create<mlir::arith::SelectOp>(
341       loc, boxType, isPresent, box, absent);
342   return fir::BoxValue(boxOrAbsent);
343 }
344 
345 /// Is this a call to an elemental procedure with at least one array argument?
346 static bool
347 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
348   if (procRef.IsElemental())
349     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
350          procRef.arguments())
351       if (arg && arg->Rank() != 0)
352         return true;
353   return false;
354 }
355 template <typename T>
356 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) {
357   return false;
358 }
359 template <>
360 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) {
361   if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u))
362     return isElementalProcWithArrayArgs(*procRef);
363   return false;
364 }
365 
366 /// Some auxiliary data for processing initialization in ScalarExprLowering
367 /// below. This is currently used for generating dense attributed global
368 /// arrays.
369 struct InitializerData {
370   explicit InitializerData(bool getRawVals = false) : genRawVals{getRawVals} {}
371   llvm::SmallVector<mlir::Attribute> rawVals; // initialization raw values
372   mlir::Type rawType; // Type of elements processed for rawVals vector.
373   bool genRawVals;    // generate the rawVals vector if set.
374 };
375 
376 /// If \p arg is the address of a function with a denoted host-association tuple
377 /// argument, then return the host-associations tuple value of the current
378 /// procedure. Otherwise, return nullptr.
379 static mlir::Value
380 argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
381                    mlir::Value arg) {
382   if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
383     auto &builder = converter.getFirOpBuilder();
384     if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
385       if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
386         return converter.hostAssocTupleValue();
387   }
388   return {};
389 }
390 
391 namespace {
392 
393 /// Lowering of Fortran::evaluate::Expr<T> expressions
394 class ScalarExprLowering {
395 public:
396   using ExtValue = fir::ExtendedValue;
397 
398   explicit ScalarExprLowering(mlir::Location loc,
399                               Fortran::lower::AbstractConverter &converter,
400                               Fortran::lower::SymMap &symMap,
401                               Fortran::lower::StatementContext &stmtCtx,
402                               InitializerData *initializer = nullptr)
403       : location{loc}, converter{converter},
404         builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap},
405         inInitializer{initializer} {}
406 
407   ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) {
408     return gen(expr);
409   }
410 
411   /// Lower `expr` to be passed as a fir.box argument. Do not create a temp
412   /// for the expr if it is a variable that can be described as a fir.box.
413   ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) {
414     bool saveUseBoxArg = useBoxArg;
415     useBoxArg = true;
416     ExtValue result = gen(expr);
417     useBoxArg = saveUseBoxArg;
418     return result;
419   }
420 
421   ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) {
422     return genval(expr);
423   }
424 
425   /// Lower an expression that is a pointer or an allocatable to a
426   /// MutableBoxValue.
427   fir::MutableBoxValue
428   genMutableBoxValue(const Fortran::lower::SomeExpr &expr) {
429     // Pointers and allocatables can only be:
430     //    - a simple designator "x"
431     //    - a component designator "a%b(i,j)%x"
432     //    - a function reference "foo()"
433     //    - result of NULL() or NULL(MOLD) intrinsic.
434     //    NULL() requires some context to be lowered, so it is not handled
435     //    here and must be lowered according to the context where it appears.
436     ExtValue exv = std::visit(
437         [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u);
438     const fir::MutableBoxValue *mutableBox =
439         exv.getBoxOf<fir::MutableBoxValue>();
440     if (!mutableBox)
441       fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue");
442     return *mutableBox;
443   }
444 
445   template <typename T>
446   ExtValue genMutableBoxValueImpl(const T &) {
447     // NULL() case should not be handled here.
448     fir::emitFatalError(getLoc(), "NULL() must be lowered in its context");
449   }
450 
451   template <typename T>
452   ExtValue
453   genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) {
454     return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef)));
455   }
456 
457   template <typename T>
458   ExtValue
459   genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) {
460     return std::visit(
461         Fortran::common::visitors{
462             [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue {
463               return symMap.lookupSymbol(*sym).toExtendedValue();
464             },
465             [&](const Fortran::evaluate::Component &comp) -> ExtValue {
466               return genComponent(comp);
467             },
468             [&](const auto &) -> ExtValue {
469               fir::emitFatalError(getLoc(),
470                                   "not an allocatable or pointer designator");
471             }},
472         designator.u);
473   }
474 
475   template <typename T>
476   ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) {
477     return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); },
478                       expr.u);
479   }
480 
481   mlir::Location getLoc() { return location; }
482 
483   template <typename A>
484   mlir::Value genunbox(const A &expr) {
485     ExtValue e = genval(expr);
486     if (const fir::UnboxedValue *r = e.getUnboxed())
487       return *r;
488     fir::emitFatalError(getLoc(), "unboxed expression expected");
489   }
490 
491   /// Generate an integral constant of `value`
492   template <int KIND>
493   mlir::Value genIntegerConstant(mlir::MLIRContext *context,
494                                  std::int64_t value) {
495     mlir::Type type =
496         converter.genType(Fortran::common::TypeCategory::Integer, KIND);
497     return builder.createIntegerConstant(getLoc(), type, value);
498   }
499 
500   /// Generate a logical/boolean constant of `value`
501   mlir::Value genBoolConstant(bool value) {
502     return builder.createBool(getLoc(), value);
503   }
504 
505   /// Generate a real constant with a value `value`.
506   template <int KIND>
507   mlir::Value genRealConstant(mlir::MLIRContext *context,
508                               const llvm::APFloat &value) {
509     mlir::Type fltTy = Fortran::lower::convertReal(context, KIND);
510     return builder.createRealConstant(getLoc(), fltTy, value);
511   }
512 
513   template <typename OpTy>
514   mlir::Value createCompareOp(mlir::arith::CmpIPredicate pred,
515                               const ExtValue &left, const ExtValue &right) {
516     if (const fir::UnboxedValue *lhs = left.getUnboxed())
517       if (const fir::UnboxedValue *rhs = right.getUnboxed())
518         return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
519     fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
520   }
521   template <typename OpTy, typename A>
522   mlir::Value createCompareOp(const A &ex, mlir::arith::CmpIPredicate pred) {
523     ExtValue left = genval(ex.left());
524     return createCompareOp<OpTy>(pred, left, genval(ex.right()));
525   }
526 
527   template <typename OpTy>
528   mlir::Value createFltCmpOp(mlir::arith::CmpFPredicate pred,
529                              const ExtValue &left, const ExtValue &right) {
530     if (const fir::UnboxedValue *lhs = left.getUnboxed())
531       if (const fir::UnboxedValue *rhs = right.getUnboxed())
532         return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
533     fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
534   }
535   template <typename OpTy, typename A>
536   mlir::Value createFltCmpOp(const A &ex, mlir::arith::CmpFPredicate pred) {
537     ExtValue left = genval(ex.left());
538     return createFltCmpOp<OpTy>(pred, left, genval(ex.right()));
539   }
540 
541   /// Returns a reference to a symbol or its box/boxChar descriptor if it has
542   /// one.
543   ExtValue gen(Fortran::semantics::SymbolRef sym) {
544     if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
545       return val.match(
546           [&](const Fortran::lower::SymbolBox::PointerOrAllocatable &boxAddr) {
547             return fir::factory::genMutableBoxRead(builder, getLoc(), boxAddr);
548           },
549           [&val](auto &) { return val.toExtendedValue(); });
550     LLVM_DEBUG(llvm::dbgs()
551                << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
552     fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
553   }
554 
555   ExtValue genLoad(const ExtValue &exv) {
556     return ::genLoad(builder, getLoc(), exv);
557   }
558 
559   ExtValue genval(Fortran::semantics::SymbolRef sym) {
560     ExtValue var = gen(sym);
561     if (const fir::UnboxedValue *s = var.getUnboxed())
562       if (fir::isReferenceLike(s->getType()))
563         return genLoad(*s);
564     return var;
565   }
566 
567   ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
568     TODO(getLoc(), "genval BOZ");
569   }
570 
571   /// Return indirection to function designated in ProcedureDesignator.
572   /// The type of the function indirection is not guaranteed to match the one
573   /// of the ProcedureDesignator due to Fortran implicit typing rules.
574   ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
575     TODO(getLoc(), "genval ProcedureDesignator");
576   }
577 
578   ExtValue genval(const Fortran::evaluate::NullPointer &) {
579     TODO(getLoc(), "genval NullPointer");
580   }
581 
582   ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
583     TODO(getLoc(), "genval StructureConstructor");
584   }
585 
586   /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
587   ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
588     return converter.impliedDoBinding(toStringRef(var.name));
589   }
590 
591   ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
592     ExtValue exv = desc.base().IsSymbol() ? gen(desc.base().GetLastSymbol())
593                                           : gen(desc.base().GetComponent());
594     mlir::IndexType idxTy = builder.getIndexType();
595     mlir::Location loc = getLoc();
596     auto castResult = [&](mlir::Value v) {
597       using ResTy = Fortran::evaluate::DescriptorInquiry::Result;
598       return builder.createConvert(
599           loc, converter.genType(ResTy::category, ResTy::kind), v);
600     };
601     switch (desc.field()) {
602     case Fortran::evaluate::DescriptorInquiry::Field::Len:
603       return castResult(fir::factory::readCharLen(builder, loc, exv));
604     case Fortran::evaluate::DescriptorInquiry::Field::LowerBound:
605       return castResult(fir::factory::readLowerBound(
606           builder, loc, exv, desc.dimension(),
607           builder.createIntegerConstant(loc, idxTy, 1)));
608     case Fortran::evaluate::DescriptorInquiry::Field::Extent:
609       return castResult(
610           fir::factory::readExtent(builder, loc, exv, desc.dimension()));
611     case Fortran::evaluate::DescriptorInquiry::Field::Rank:
612       TODO(loc, "rank inquiry on assumed rank");
613     case Fortran::evaluate::DescriptorInquiry::Field::Stride:
614       // So far the front end does not generate this inquiry.
615       TODO(loc, "Stride inquiry");
616     }
617     llvm_unreachable("unknown descriptor inquiry");
618   }
619 
620   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
621     TODO(getLoc(), "genval TypeParamInquiry");
622   }
623 
624   template <int KIND>
625   ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
626     TODO(getLoc(), "genval ComplexComponent");
627   }
628 
629   template <int KIND>
630   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
631                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
632     mlir::Value input = genunbox(op.left());
633     // Like LLVM, integer negation is the binary op "0 - value"
634     mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
635     return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
636   }
637 
638   template <int KIND>
639   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
640                       Fortran::common::TypeCategory::Real, KIND>> &op) {
641     return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
642   }
643   template <int KIND>
644   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
645                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
646     return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
647   }
648 
649   template <typename OpTy>
650   mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
651     assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
652     mlir::Value lhs = fir::getBase(left);
653     mlir::Value rhs = fir::getBase(right);
654     assert(lhs.getType() == rhs.getType() && "types must be the same");
655     return builder.create<OpTy>(getLoc(), lhs, rhs);
656   }
657 
658   template <typename OpTy, typename A>
659   mlir::Value createBinaryOp(const A &ex) {
660     ExtValue left = genval(ex.left());
661     return createBinaryOp<OpTy>(left, genval(ex.right()));
662   }
663 
664 #undef GENBIN
665 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
666   template <int KIND>                                                          \
667   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
668                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
669     return createBinaryOp<GenBinFirOp>(x);                                     \
670   }
671 
672   GENBIN(Add, Integer, mlir::arith::AddIOp)
673   GENBIN(Add, Real, mlir::arith::AddFOp)
674   GENBIN(Add, Complex, fir::AddcOp)
675   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
676   GENBIN(Subtract, Real, mlir::arith::SubFOp)
677   GENBIN(Subtract, Complex, fir::SubcOp)
678   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
679   GENBIN(Multiply, Real, mlir::arith::MulFOp)
680   GENBIN(Multiply, Complex, fir::MulcOp)
681   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
682   GENBIN(Divide, Real, mlir::arith::DivFOp)
683   GENBIN(Divide, Complex, fir::DivcOp)
684 
685   template <Fortran::common::TypeCategory TC, int KIND>
686   ExtValue genval(
687       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
688     mlir::Type ty = converter.genType(TC, KIND);
689     mlir::Value lhs = genunbox(op.left());
690     mlir::Value rhs = genunbox(op.right());
691     return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
692   }
693 
694   template <Fortran::common::TypeCategory TC, int KIND>
695   ExtValue genval(
696       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
697           &op) {
698     mlir::Type ty = converter.genType(TC, KIND);
699     mlir::Value lhs = genunbox(op.left());
700     mlir::Value rhs = genunbox(op.right());
701     return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
702   }
703 
704   template <int KIND>
705   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
706     mlir::Value realPartValue = genunbox(op.left());
707     return fir::factory::Complex{builder, getLoc()}.createComplex(
708         KIND, realPartValue, genunbox(op.right()));
709   }
710 
711   template <int KIND>
712   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
713     TODO(getLoc(), "genval Concat<KIND>");
714   }
715 
716   /// MIN and MAX operations
717   template <Fortran::common::TypeCategory TC, int KIND>
718   ExtValue
719   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
720              &op) {
721     TODO(getLoc(), "genval Extremum<TC, KIND>");
722   }
723 
724   template <int KIND>
725   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
726     TODO(getLoc(), "genval SetLength<KIND>");
727   }
728 
729   template <int KIND>
730   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
731                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
732     return createCompareOp<mlir::arith::CmpIOp>(op,
733                                                 translateRelational(op.opr));
734   }
735   template <int KIND>
736   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
737                       Fortran::common::TypeCategory::Real, KIND>> &op) {
738     return createFltCmpOp<mlir::arith::CmpFOp>(
739         op, translateFloatRelational(op.opr));
740   }
741   template <int KIND>
742   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
743                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
744     TODO(getLoc(), "genval complex comparison");
745   }
746   template <int KIND>
747   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
748                       Fortran::common::TypeCategory::Character, KIND>> &op) {
749     TODO(getLoc(), "genval char comparison");
750   }
751 
752   ExtValue
753   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
754     return std::visit([&](const auto &x) { return genval(x); }, op.u);
755   }
756 
757   template <Fortran::common::TypeCategory TC1, int KIND,
758             Fortran::common::TypeCategory TC2>
759   ExtValue
760   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
761                                           TC2> &convert) {
762     mlir::Type ty = converter.genType(TC1, KIND);
763     mlir::Value operand = genunbox(convert.left());
764     return builder.convertWithSemantics(getLoc(), ty, operand);
765   }
766 
767   template <typename A>
768   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
769     TODO(getLoc(), "genval parentheses<A>");
770   }
771 
772   template <int KIND>
773   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
774     mlir::Value logical = genunbox(op.left());
775     mlir::Value one = genBoolConstant(true);
776     mlir::Value val =
777         builder.createConvert(getLoc(), builder.getI1Type(), logical);
778     return builder.create<mlir::arith::XOrIOp>(getLoc(), val, one);
779   }
780 
781   template <int KIND>
782   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
783     mlir::IntegerType i1Type = builder.getI1Type();
784     mlir::Value slhs = genunbox(op.left());
785     mlir::Value srhs = genunbox(op.right());
786     mlir::Value lhs = builder.createConvert(getLoc(), i1Type, slhs);
787     mlir::Value rhs = builder.createConvert(getLoc(), i1Type, srhs);
788     switch (op.logicalOperator) {
789     case Fortran::evaluate::LogicalOperator::And:
790       return createBinaryOp<mlir::arith::AndIOp>(lhs, rhs);
791     case Fortran::evaluate::LogicalOperator::Or:
792       return createBinaryOp<mlir::arith::OrIOp>(lhs, rhs);
793     case Fortran::evaluate::LogicalOperator::Eqv:
794       return createCompareOp<mlir::arith::CmpIOp>(
795           mlir::arith::CmpIPredicate::eq, lhs, rhs);
796     case Fortran::evaluate::LogicalOperator::Neqv:
797       return createCompareOp<mlir::arith::CmpIOp>(
798           mlir::arith::CmpIPredicate::ne, lhs, rhs);
799     case Fortran::evaluate::LogicalOperator::Not:
800       // lib/evaluate expression for .NOT. is Fortran::evaluate::Not<KIND>.
801       llvm_unreachable(".NOT. is not a binary operator");
802     }
803     llvm_unreachable("unhandled logical operation");
804   }
805 
806   /// Convert a scalar literal constant to IR.
807   template <Fortran::common::TypeCategory TC, int KIND>
808   ExtValue genScalarLit(
809       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
810           &value) {
811     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
812       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
813     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
814       return genBoolConstant(value.IsTrue());
815     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
816       std::string str = value.DumpHexadecimal();
817       if constexpr (KIND == 2) {
818         llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str};
819         return genRealConstant<KIND>(builder.getContext(), floatVal);
820       } else if constexpr (KIND == 3) {
821         llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str};
822         return genRealConstant<KIND>(builder.getContext(), floatVal);
823       } else if constexpr (KIND == 4) {
824         llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str};
825         return genRealConstant<KIND>(builder.getContext(), floatVal);
826       } else if constexpr (KIND == 10) {
827         llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str};
828         return genRealConstant<KIND>(builder.getContext(), floatVal);
829       } else if constexpr (KIND == 16) {
830         llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str};
831         return genRealConstant<KIND>(builder.getContext(), floatVal);
832       } else {
833         // convert everything else to double
834         llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str};
835         return genRealConstant<KIND>(builder.getContext(), floatVal);
836       }
837     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
838       using TR =
839           Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>;
840       Fortran::evaluate::ComplexConstructor<KIND> ctor(
841           Fortran::evaluate::Expr<TR>{
842               Fortran::evaluate::Constant<TR>{value.REAL()}},
843           Fortran::evaluate::Expr<TR>{
844               Fortran::evaluate::Constant<TR>{value.AIMAG()}});
845       return genunbox(ctor);
846     } else /*constexpr*/ {
847       llvm_unreachable("unhandled constant");
848     }
849   }
850 
851   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
852   ExtValue
853   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
854                         Fortran::common::TypeCategory::Character, 1>> &value,
855                     int64_t len) {
856     assert(value.size() == static_cast<std::uint64_t>(len) &&
857            "value.size() doesn't match with len");
858     return fir::factory::createStringLiteral(builder, getLoc(), value);
859   }
860 
861   template <Fortran::common::TypeCategory TC, int KIND>
862   ExtValue
863   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
864              &con) {
865     if (con.Rank() > 0)
866       TODO(getLoc(), "genval array constant");
867     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
868         opt = con.GetScalarValue();
869     assert(opt.has_value() && "constant has no value");
870     if constexpr (TC == Fortran::common::TypeCategory::Character) {
871       if constexpr (KIND == 1)
872         return genAsciiScalarLit(opt.value(), con.LEN());
873       TODO(getLoc(), "genval for Character with KIND != 1");
874     } else {
875       return genScalarLit<TC, KIND>(opt.value());
876     }
877   }
878 
879   fir::ExtendedValue genval(
880       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
881     TODO(getLoc(), "genval constant derived");
882   }
883 
884   template <typename A>
885   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
886     TODO(getLoc(), "genval ArrayConstructor<A>");
887   }
888 
889   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
890     TODO(getLoc(), "gen ComplexPart");
891   }
892   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
893     TODO(getLoc(), "genval ComplexPart");
894   }
895 
896   ExtValue gen(const Fortran::evaluate::Substring &s) {
897     TODO(getLoc(), "gen Substring");
898   }
899   ExtValue genval(const Fortran::evaluate::Substring &ss) {
900     TODO(getLoc(), "genval Substring");
901   }
902 
903   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
904     if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>(
905             &subs.u)) {
906       if (s->value().Rank() > 0)
907         fir::emitFatalError(getLoc(), "vector subscript is not scalar");
908       return {genval(s->value())};
909     }
910     fir::emitFatalError(getLoc(), "subscript triple notation is not scalar");
911   }
912 
913   ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) {
914     return genval(subs);
915   }
916 
917   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
918     TODO(getLoc(), "gen DataRef");
919   }
920   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
921     TODO(getLoc(), "genval DataRef");
922   }
923 
924   // Helper function to turn the Component structure into a list of nested
925   // components, ordered from largest/leftmost to smallest/rightmost:
926   //  - where only the smallest/rightmost item may be allocatable or a pointer
927   //    (nested allocatable/pointer components require nested coordinate_of ops)
928   //  - that does not contain any parent components
929   //    (the front end places parent components directly in the object)
930   // Return the object used as the base coordinate for the component chain.
931   static Fortran::evaluate::DataRef const *
932   reverseComponents(const Fortran::evaluate::Component &cmpt,
933                     std::list<const Fortran::evaluate::Component *> &list) {
934     if (!cmpt.GetLastSymbol().test(
935             Fortran::semantics::Symbol::Flag::ParentComp))
936       list.push_front(&cmpt);
937     return std::visit(
938         Fortran::common::visitors{
939             [&](const Fortran::evaluate::Component &x) {
940               if (Fortran::semantics::IsAllocatableOrPointer(x.GetLastSymbol()))
941                 return &cmpt.base();
942               return reverseComponents(x, list);
943             },
944             [&](auto &) { return &cmpt.base(); },
945         },
946         cmpt.base().u);
947   }
948 
949   // Return the coordinate of the component reference
950   ExtValue genComponent(const Fortran::evaluate::Component &cmpt) {
951     std::list<const Fortran::evaluate::Component *> list;
952     const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list);
953     llvm::SmallVector<mlir::Value> coorArgs;
954     ExtValue obj = gen(*base);
955     mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType());
956     mlir::Location loc = getLoc();
957     auto fldTy = fir::FieldType::get(&converter.getMLIRContext());
958     // FIXME: need to thread the LEN type parameters here.
959     for (const Fortran::evaluate::Component *field : list) {
960       auto recTy = ty.cast<fir::RecordType>();
961       const Fortran::semantics::Symbol &sym = field->GetLastSymbol();
962       llvm::StringRef name = toStringRef(sym.name());
963       coorArgs.push_back(builder.create<fir::FieldIndexOp>(
964           loc, fldTy, name, recTy, fir::getTypeParams(obj)));
965       ty = recTy.getType(name);
966     }
967     ty = builder.getRefType(ty);
968     return fir::factory::componentToExtendedValue(
969         builder, loc,
970         builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj),
971                                           coorArgs));
972   }
973 
974   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
975     TODO(getLoc(), "gen Component");
976   }
977   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
978     TODO(getLoc(), "genval Component");
979   }
980 
981   ExtValue genval(const Fortran::semantics::Bound &bound) {
982     TODO(getLoc(), "genval Bound");
983   }
984 
985   /// Return lower bounds of \p box in dimension \p dim. The returned value
986   /// has type \ty.
987   mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) {
988     assert(box.rank() > 0 && "must be an array");
989     mlir::Location loc = getLoc();
990     mlir::Value one = builder.createIntegerConstant(loc, ty, 1);
991     mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one);
992     return builder.createConvert(loc, ty, lb);
993   }
994 
995   static bool isSlice(const Fortran::evaluate::ArrayRef &aref) {
996     for (const Fortran::evaluate::Subscript &sub : aref.subscript())
997       if (std::holds_alternative<Fortran::evaluate::Triplet>(sub.u))
998         return true;
999     return false;
1000   }
1001 
1002   /// Lower an ArrayRef to a fir.coordinate_of given its lowered base.
1003   ExtValue genCoordinateOp(const ExtValue &array,
1004                            const Fortran::evaluate::ArrayRef &aref) {
1005     mlir::Location loc = getLoc();
1006     // References to array of rank > 1 with non constant shape that are not
1007     // fir.box must be collapsed into an offset computation in lowering already.
1008     // The same is needed with dynamic length character arrays of all ranks.
1009     mlir::Type baseType =
1010         fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType());
1011     if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) ||
1012         fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType)))
1013       if (!array.getBoxOf<fir::BoxValue>())
1014         return genOffsetAndCoordinateOp(array, aref);
1015     // Generate a fir.coordinate_of with zero based array indexes.
1016     llvm::SmallVector<mlir::Value> args;
1017     for (const auto &subsc : llvm::enumerate(aref.subscript())) {
1018       ExtValue subVal = genSubscript(subsc.value());
1019       assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar");
1020       mlir::Value val = fir::getBase(subVal);
1021       mlir::Type ty = val.getType();
1022       mlir::Value lb = getLBound(array, subsc.index(), ty);
1023       args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb));
1024     }
1025 
1026     mlir::Value base = fir::getBase(array);
1027     auto seqTy =
1028         fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>();
1029     assert(args.size() == seqTy.getDimension());
1030     mlir::Type ty = builder.getRefType(seqTy.getEleTy());
1031     auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args);
1032     return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr);
1033   }
1034 
1035   /// Lower an ArrayRef to a fir.coordinate_of using an element offset instead
1036   /// of array indexes.
1037   /// This generates offset computation from the indexes and length parameters,
1038   /// and use the offset to access the element with a fir.coordinate_of. This
1039   /// must only be used if it is not possible to generate a normal
1040   /// fir.coordinate_of using array indexes (i.e. when the shape information is
1041   /// unavailable in the IR).
1042   ExtValue genOffsetAndCoordinateOp(const ExtValue &array,
1043                                     const Fortran::evaluate::ArrayRef &aref) {
1044     mlir::Location loc = getLoc();
1045     mlir::Value addr = fir::getBase(array);
1046     mlir::Type arrTy = fir::dyn_cast_ptrEleTy(addr.getType());
1047     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
1048     mlir::Type seqTy = builder.getRefType(builder.getVarLenSeqTy(eleTy));
1049     mlir::Type refTy = builder.getRefType(eleTy);
1050     mlir::Value base = builder.createConvert(loc, seqTy, addr);
1051     mlir::IndexType idxTy = builder.getIndexType();
1052     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
1053     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
1054     auto getLB = [&](const auto &arr, unsigned dim) -> mlir::Value {
1055       return arr.getLBounds().empty() ? one : arr.getLBounds()[dim];
1056     };
1057     auto genFullDim = [&](const auto &arr, mlir::Value delta) -> mlir::Value {
1058       mlir::Value total = zero;
1059       assert(arr.getExtents().size() == aref.subscript().size());
1060       delta = builder.createConvert(loc, idxTy, delta);
1061       unsigned dim = 0;
1062       for (auto [ext, sub] : llvm::zip(arr.getExtents(), aref.subscript())) {
1063         ExtValue subVal = genSubscript(sub);
1064         assert(fir::isUnboxedValue(subVal));
1065         mlir::Value val =
1066             builder.createConvert(loc, idxTy, fir::getBase(subVal));
1067         mlir::Value lb = builder.createConvert(loc, idxTy, getLB(arr, dim));
1068         mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, val, lb);
1069         mlir::Value prod =
1070             builder.create<mlir::arith::MulIOp>(loc, delta, diff);
1071         total = builder.create<mlir::arith::AddIOp>(loc, prod, total);
1072         if (ext)
1073           delta = builder.create<mlir::arith::MulIOp>(loc, delta, ext);
1074         ++dim;
1075       }
1076       mlir::Type origRefTy = refTy;
1077       if (fir::factory::CharacterExprHelper::isCharacterScalar(refTy)) {
1078         fir::CharacterType chTy =
1079             fir::factory::CharacterExprHelper::getCharacterType(refTy);
1080         if (fir::characterWithDynamicLen(chTy)) {
1081           mlir::MLIRContext *ctx = builder.getContext();
1082           fir::KindTy kind =
1083               fir::factory::CharacterExprHelper::getCharacterKind(chTy);
1084           fir::CharacterType singleTy =
1085               fir::CharacterType::getSingleton(ctx, kind);
1086           refTy = builder.getRefType(singleTy);
1087           mlir::Type seqRefTy =
1088               builder.getRefType(builder.getVarLenSeqTy(singleTy));
1089           base = builder.createConvert(loc, seqRefTy, base);
1090         }
1091       }
1092       auto coor = builder.create<fir::CoordinateOp>(
1093           loc, refTy, base, llvm::ArrayRef<mlir::Value>{total});
1094       // Convert to expected, original type after address arithmetic.
1095       return builder.createConvert(loc, origRefTy, coor);
1096     };
1097     return array.match(
1098         [&](const fir::ArrayBoxValue &arr) -> ExtValue {
1099           // FIXME: this check can be removed when slicing is implemented
1100           if (isSlice(aref))
1101             fir::emitFatalError(
1102                 getLoc(),
1103                 "slice should be handled in array expression context");
1104           return genFullDim(arr, one);
1105         },
1106         [&](const fir::CharArrayBoxValue &arr) -> ExtValue {
1107           mlir::Value delta = arr.getLen();
1108           // If the length is known in the type, fir.coordinate_of will
1109           // already take the length into account.
1110           if (fir::factory::CharacterExprHelper::hasConstantLengthInType(arr))
1111             delta = one;
1112           return fir::CharBoxValue(genFullDim(arr, delta), arr.getLen());
1113         },
1114         [&](const fir::BoxValue &arr) -> ExtValue {
1115           // CoordinateOp for BoxValue is not generated here. The dimensions
1116           // must be kept in the fir.coordinate_op so that potential fir.box
1117           // strides can be applied by codegen.
1118           fir::emitFatalError(
1119               loc, "internal: BoxValue in dim-collapsed fir.coordinate_of");
1120         },
1121         [&](const auto &) -> ExtValue {
1122           fir::emitFatalError(loc, "internal: array lowering failed");
1123         });
1124   }
1125 
1126   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
1127     ExtValue base = aref.base().IsSymbol() ? gen(aref.base().GetFirstSymbol())
1128                                            : gen(aref.base().GetComponent());
1129     return genCoordinateOp(base, aref);
1130   }
1131   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
1132     return genLoad(gen(aref));
1133   }
1134 
1135   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
1136     TODO(getLoc(), "gen CoarrayRef");
1137   }
1138   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
1139     TODO(getLoc(), "genval CoarrayRef");
1140   }
1141 
1142   template <typename A>
1143   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
1144     return std::visit([&](const auto &x) { return gen(x); }, des.u);
1145   }
1146   template <typename A>
1147   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
1148     return std::visit([&](const auto &x) { return genval(x); }, des.u);
1149   }
1150 
1151   mlir::Type genType(const Fortran::evaluate::DynamicType &dt) {
1152     if (dt.category() != Fortran::common::TypeCategory::Derived)
1153       return converter.genType(dt.category(), dt.kind());
1154     TODO(getLoc(), "genType Derived Type");
1155   }
1156 
1157   /// Lower a function reference
1158   template <typename A>
1159   ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1160     if (!funcRef.GetType().has_value())
1161       fir::emitFatalError(getLoc(), "internal: a function must have a type");
1162     mlir::Type resTy = genType(*funcRef.GetType());
1163     return genProcedureRef(funcRef, {resTy});
1164   }
1165 
1166   /// Lower function call `funcRef` and return a reference to the resultant
1167   /// value. This is required for lowering expressions such as `f1(f2(v))`.
1168   template <typename A>
1169   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1170     ExtValue retVal = genFunctionRef(funcRef);
1171     mlir::Value retValBase = fir::getBase(retVal);
1172     if (fir::conformsWithPassByRef(retValBase.getType()))
1173       return retVal;
1174     auto mem = builder.create<fir::AllocaOp>(getLoc(), retValBase.getType());
1175     builder.create<fir::StoreOp>(getLoc(), retValBase, mem);
1176     return fir::substBase(retVal, mem.getResult());
1177   }
1178 
1179   /// helper to detect statement functions
1180   static bool
1181   isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) {
1182     if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
1183       if (const auto *details =
1184               symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
1185         return details->stmtFunction().has_value();
1186     return false;
1187   }
1188 
1189   /// Helper to package a Value and its properties into an ExtendedValue.
1190   static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base,
1191                                   llvm::ArrayRef<mlir::Value> extents,
1192                                   llvm::ArrayRef<mlir::Value> lengths) {
1193     mlir::Type type = base.getType();
1194     if (type.isa<fir::BoxType>())
1195       return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);
1196     type = fir::unwrapRefType(type);
1197     if (type.isa<fir::BoxType>())
1198       return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});
1199     if (auto seqTy = type.dyn_cast<fir::SequenceType>()) {
1200       if (seqTy.getDimension() != extents.size())
1201         fir::emitFatalError(loc, "incorrect number of extents for array");
1202       if (seqTy.getEleTy().isa<fir::CharacterType>()) {
1203         if (lengths.empty())
1204           fir::emitFatalError(loc, "missing length for character");
1205         assert(lengths.size() == 1);
1206         return fir::CharArrayBoxValue(base, lengths[0], extents);
1207       }
1208       return fir::ArrayBoxValue(base, extents);
1209     }
1210     if (type.isa<fir::CharacterType>()) {
1211       if (lengths.empty())
1212         fir::emitFatalError(loc, "missing length for character");
1213       assert(lengths.size() == 1);
1214       return fir::CharBoxValue(base, lengths[0]);
1215     }
1216     return base;
1217   }
1218 
1219   // Find the argument that corresponds to the host associations.
1220   // Verify some assumptions about how the signature was built here.
1221   [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) {
1222     // Scan the argument list from last to first as the host associations are
1223     // appended for now.
1224     for (unsigned i = fn.getNumArguments(); i > 0; --i)
1225       if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
1226         // Host assoc tuple must be last argument (for now).
1227         assert(i == fn.getNumArguments() && "tuple must be last");
1228         return i - 1;
1229       }
1230     llvm_unreachable("anyFuncArgsHaveAttr failed");
1231   }
1232 
1233   /// Create a contiguous temporary array with the same shape,
1234   /// length parameters and type as mold. It is up to the caller to deallocate
1235   /// the temporary.
1236   ExtValue genArrayTempFromMold(const ExtValue &mold,
1237                                 llvm::StringRef tempName) {
1238     mlir::Type type = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(mold).getType());
1239     assert(type && "expected descriptor or memory type");
1240     mlir::Location loc = getLoc();
1241     llvm::SmallVector<mlir::Value> extents =
1242         fir::factory::getExtents(builder, loc, mold);
1243     llvm::SmallVector<mlir::Value> allocMemTypeParams =
1244         fir::getTypeParams(mold);
1245     mlir::Value charLen;
1246     mlir::Type elementType = fir::unwrapSequenceType(type);
1247     if (auto charType = elementType.dyn_cast<fir::CharacterType>()) {
1248       charLen = allocMemTypeParams.empty()
1249                     ? fir::factory::readCharLen(builder, loc, mold)
1250                     : allocMemTypeParams[0];
1251       if (charType.hasDynamicLen() && allocMemTypeParams.empty())
1252         allocMemTypeParams.push_back(charLen);
1253     } else if (fir::hasDynamicSize(elementType)) {
1254       TODO(loc, "Creating temporary for derived type with length parameters");
1255     }
1256 
1257     mlir::Value temp = builder.create<fir::AllocMemOp>(
1258         loc, type, tempName, allocMemTypeParams, extents);
1259     if (fir::unwrapSequenceType(type).isa<fir::CharacterType>())
1260       return fir::CharArrayBoxValue{temp, charLen, extents};
1261     return fir::ArrayBoxValue{temp, extents};
1262   }
1263 
1264   /// Copy \p source array into \p dest array. Both arrays must be
1265   /// conforming, but neither array must be contiguous.
1266   void genArrayCopy(ExtValue dest, ExtValue source) {
1267     return createSomeArrayAssignment(converter, dest, source, symMap, stmtCtx);
1268   }
1269 
1270   /// Lower a non-elemental procedure reference and read allocatable and pointer
1271   /// results into normal values.
1272   ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
1273                            llvm::Optional<mlir::Type> resultType) {
1274     ExtValue res = genRawProcedureRef(procRef, resultType);
1275     return res;
1276   }
1277 
1278   /// Given a call site for which the arguments were already lowered, generate
1279   /// the call and return the result. This function deals with explicit result
1280   /// allocation and lowering if needed. It also deals with passing the host
1281   /// link to internal procedures.
1282   ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller,
1283                               mlir::FunctionType callSiteType,
1284                               llvm::Optional<mlir::Type> resultType) {
1285     mlir::Location loc = getLoc();
1286     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1287     // Handle cases where caller must allocate the result or a fir.box for it.
1288     bool mustPopSymMap = false;
1289     if (caller.mustMapInterfaceSymbols()) {
1290       symMap.pushScope();
1291       mustPopSymMap = true;
1292       Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap);
1293     }
1294     // If this is an indirect call, retrieve the function address. Also retrieve
1295     // the result length if this is a character function (note that this length
1296     // will be used only if there is no explicit length in the local interface).
1297     mlir::Value funcPointer;
1298     mlir::Value charFuncPointerLength;
1299     if (const Fortran::semantics::Symbol *sym =
1300             caller.getIfIndirectCallSymbol()) {
1301       funcPointer = symMap.lookupSymbol(*sym).getAddr();
1302       if (!funcPointer)
1303         fir::emitFatalError(loc, "failed to find indirect call symbol address");
1304       if (fir::isCharacterProcedureTuple(funcPointer.getType(),
1305                                          /*acceptRawFunc=*/false))
1306         std::tie(funcPointer, charFuncPointerLength) =
1307             fir::factory::extractCharacterProcedureTuple(builder, loc,
1308                                                          funcPointer);
1309     }
1310 
1311     mlir::IndexType idxTy = builder.getIndexType();
1312     auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
1313       return builder.createConvert(
1314           loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
1315     };
1316     llvm::SmallVector<mlir::Value> resultLengths;
1317     auto allocatedResult = [&]() -> llvm::Optional<ExtValue> {
1318       llvm::SmallVector<mlir::Value> extents;
1319       llvm::SmallVector<mlir::Value> lengths;
1320       if (!caller.callerAllocateResult())
1321         return {};
1322       mlir::Type type = caller.getResultStorageType();
1323       if (type.isa<fir::SequenceType>())
1324         caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) {
1325           extents.emplace_back(lowerSpecExpr(e));
1326         });
1327       caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) {
1328         lengths.emplace_back(lowerSpecExpr(e));
1329       });
1330 
1331       // Result length parameters should not be provided to box storage
1332       // allocation and save_results, but they are still useful information to
1333       // keep in the ExtendedValue if non-deferred.
1334       if (!type.isa<fir::BoxType>()) {
1335         if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
1336           // Calling an assumed length function. This is only possible if this
1337           // is a call to a character dummy procedure.
1338           if (!charFuncPointerLength)
1339             fir::emitFatalError(loc, "failed to retrieve character function "
1340                                      "length while calling it");
1341           lengths.push_back(charFuncPointerLength);
1342         }
1343         resultLengths = lengths;
1344       }
1345 
1346       if (!extents.empty() || !lengths.empty()) {
1347         auto *bldr = &converter.getFirOpBuilder();
1348         auto stackSaveFn = fir::factory::getLlvmStackSave(builder);
1349         auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName());
1350         mlir::Value sp =
1351             bldr->create<fir::CallOp>(loc, stackSaveFn.getType().getResults(),
1352                                       stackSaveSymbol, mlir::ValueRange{})
1353                 .getResult(0);
1354         stmtCtx.attachCleanup([bldr, loc, sp]() {
1355           auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr);
1356           auto stackRestoreSymbol =
1357               bldr->getSymbolRefAttr(stackRestoreFn.getName());
1358           bldr->create<fir::CallOp>(loc, stackRestoreFn.getType().getResults(),
1359                                     stackRestoreSymbol, mlir::ValueRange{sp});
1360         });
1361       }
1362       mlir::Value temp =
1363           builder.createTemporary(loc, type, ".result", extents, resultLengths);
1364       return toExtendedValue(loc, temp, extents, lengths);
1365     }();
1366 
1367     if (mustPopSymMap)
1368       symMap.popScope();
1369 
1370     // Place allocated result or prepare the fir.save_result arguments.
1371     mlir::Value arrayResultShape;
1372     if (allocatedResult) {
1373       if (std::optional<Fortran::lower::CallInterface<
1374               Fortran::lower::CallerInterface>::PassedEntity>
1375               resultArg = caller.getPassedResult()) {
1376         if (resultArg->passBy == PassBy::AddressAndLength)
1377           caller.placeAddressAndLengthInput(*resultArg,
1378                                             fir::getBase(*allocatedResult),
1379                                             fir::getLen(*allocatedResult));
1380         else if (resultArg->passBy == PassBy::BaseAddress)
1381           caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
1382         else
1383           fir::emitFatalError(
1384               loc, "only expect character scalar result to be passed by ref");
1385       } else {
1386         assert(caller.mustSaveResult());
1387         arrayResultShape = allocatedResult->match(
1388             [&](const fir::CharArrayBoxValue &) {
1389               return builder.createShape(loc, *allocatedResult);
1390             },
1391             [&](const fir::ArrayBoxValue &) {
1392               return builder.createShape(loc, *allocatedResult);
1393             },
1394             [&](const auto &) { return mlir::Value{}; });
1395       }
1396     }
1397 
1398     // In older Fortran, procedure argument types are inferred. This may lead
1399     // different view of what the function signature is in different locations.
1400     // Casts are inserted as needed below to accommodate this.
1401 
1402     // The mlir::FuncOp type prevails, unless it has a different number of
1403     // arguments which can happen in legal program if it was passed as a dummy
1404     // procedure argument earlier with no further type information.
1405     mlir::SymbolRefAttr funcSymbolAttr;
1406     bool addHostAssociations = false;
1407     if (!funcPointer) {
1408       mlir::FunctionType funcOpType = caller.getFuncOp().getType();
1409       mlir::SymbolRefAttr symbolAttr =
1410           builder.getSymbolRefAttr(caller.getMangledName());
1411       if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
1412           callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
1413           fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
1414                                    fir::getHostAssocAttrName())) {
1415         // The number of arguments is off by one, and we're lowering a function
1416         // with host associations. Modify call to include host associations
1417         // argument by appending the value at the end of the operands.
1418         assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
1419                converter.hostAssocTupleValue().getType());
1420         addHostAssociations = true;
1421       }
1422       if (!addHostAssociations &&
1423           (callSiteType.getNumResults() != funcOpType.getNumResults() ||
1424            callSiteType.getNumInputs() != funcOpType.getNumInputs())) {
1425         // Deal with argument number mismatch by making a function pointer so
1426         // that function type cast can be inserted. Do not emit a warning here
1427         // because this can happen in legal program if the function is not
1428         // defined here and it was first passed as an argument without any more
1429         // information.
1430         funcPointer =
1431             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1432       } else if (callSiteType.getResults() != funcOpType.getResults()) {
1433         // Implicit interface result type mismatch are not standard Fortran, but
1434         // some compilers are not complaining about it.  The front end is not
1435         // protecting lowering from this currently. Support this with a
1436         // discouraging warning.
1437         LLVM_DEBUG(mlir::emitWarning(
1438             loc, "a return type mismatch is not standard compliant and may "
1439                  "lead to undefined behavior."));
1440         // Cast the actual function to the current caller implicit type because
1441         // that is the behavior we would get if we could not see the definition.
1442         funcPointer =
1443             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1444       } else {
1445         funcSymbolAttr = symbolAttr;
1446       }
1447     }
1448 
1449     mlir::FunctionType funcType =
1450         funcPointer ? callSiteType : caller.getFuncOp().getType();
1451     llvm::SmallVector<mlir::Value> operands;
1452     // First operand of indirect call is the function pointer. Cast it to
1453     // required function type for the call to handle procedures that have a
1454     // compatible interface in Fortran, but that have different signatures in
1455     // FIR.
1456     if (funcPointer) {
1457       operands.push_back(
1458           funcPointer.getType().isa<fir::BoxProcType>()
1459               ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
1460               : builder.createConvert(loc, funcType, funcPointer));
1461     }
1462 
1463     // Deal with potential mismatches in arguments types. Passing an array to a
1464     // scalar argument should for instance be tolerated here.
1465     bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
1466     for (auto [fst, snd] :
1467          llvm::zip(caller.getInputs(), funcType.getInputs())) {
1468       // When passing arguments to a procedure that can be called an implicit
1469       // interface, allow character actual arguments to be passed to dummy
1470       // arguments of any type and vice versa
1471       mlir::Value cast;
1472       auto *context = builder.getContext();
1473       if (snd.isa<fir::BoxProcType>() &&
1474           fst.getType().isa<mlir::FunctionType>()) {
1475         auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None);
1476         auto boxProcTy = builder.getBoxProcType(funcTy);
1477         if (mlir::Value host = argumentHostAssocs(converter, fst)) {
1478           cast = builder.create<fir::EmboxProcOp>(
1479               loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
1480         } else {
1481           cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
1482         }
1483       } else {
1484         cast = builder.convertWithSemantics(loc, snd, fst,
1485                                             callingImplicitInterface);
1486       }
1487       operands.push_back(cast);
1488     }
1489 
1490     // Add host associations as necessary.
1491     if (addHostAssociations)
1492       operands.push_back(converter.hostAssocTupleValue());
1493 
1494     auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
1495                                             funcSymbolAttr, operands);
1496 
1497     if (caller.mustSaveResult())
1498       builder.create<fir::SaveResultOp>(
1499           loc, call.getResult(0), fir::getBase(allocatedResult.getValue()),
1500           arrayResultShape, resultLengths);
1501 
1502     if (allocatedResult) {
1503       allocatedResult->match(
1504           [&](const fir::MutableBoxValue &box) {
1505             if (box.isAllocatable()) {
1506               // 9.7.3.2 point 4. Finalize allocatables.
1507               fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
1508               stmtCtx.attachCleanup([bldr, loc, box]() {
1509                 fir::factory::genFinalization(*bldr, loc, box);
1510               });
1511             }
1512           },
1513           [](const auto &) {});
1514       return *allocatedResult;
1515     }
1516 
1517     if (!resultType.hasValue())
1518       return mlir::Value{}; // subroutine call
1519     // For now, Fortran return values are implemented with a single MLIR
1520     // function return value.
1521     assert(call.getNumResults() == 1 &&
1522            "Expected exactly one result in FUNCTION call");
1523     return call.getResult(0);
1524   }
1525 
1526   /// Like genExtAddr, but ensure the address returned is a temporary even if \p
1527   /// expr is variable inside parentheses.
1528   ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
1529     // In general, genExtAddr might not create a temp for variable inside
1530     // parentheses to avoid creating array temporary in sub-expressions. It only
1531     // ensures the sub-expression is not re-associated with other parts of the
1532     // expression. In the call semantics, there is a difference between expr and
1533     // variable (see R1524). For expressions, a variable storage must not be
1534     // argument associated since it could be modified inside the call, or the
1535     // variable could also be modified by other means during the call.
1536     if (!isParenthesizedVariable(expr))
1537       return genExtAddr(expr);
1538     mlir::Location loc = getLoc();
1539     if (expr.Rank() > 0)
1540       TODO(loc, "genTempExtAddr array");
1541     return genExtValue(expr).match(
1542         [&](const fir::CharBoxValue &boxChar) -> ExtValue {
1543           TODO(loc, "genTempExtAddr CharBoxValue");
1544         },
1545         [&](const fir::UnboxedValue &v) -> ExtValue {
1546           mlir::Type type = v.getType();
1547           mlir::Value value = v;
1548           if (fir::isa_ref_type(type))
1549             value = builder.create<fir::LoadOp>(loc, value);
1550           mlir::Value temp = builder.createTemporary(loc, value.getType());
1551           builder.create<fir::StoreOp>(loc, value, temp);
1552           return temp;
1553         },
1554         [&](const fir::BoxValue &x) -> ExtValue {
1555           // Derived type scalar that may be polymorphic.
1556           assert(!x.hasRank() && x.isDerived());
1557           if (x.isDerivedWithLengthParameters())
1558             fir::emitFatalError(
1559                 loc, "making temps for derived type with length parameters");
1560           // TODO: polymorphic aspects should be kept but for now the temp
1561           // created always has the declared type.
1562           mlir::Value var =
1563               fir::getBase(fir::factory::readBoxValue(builder, loc, x));
1564           auto value = builder.create<fir::LoadOp>(loc, var);
1565           mlir::Value temp = builder.createTemporary(loc, value.getType());
1566           builder.create<fir::StoreOp>(loc, value, temp);
1567           return temp;
1568         },
1569         [&](const auto &) -> ExtValue {
1570           fir::emitFatalError(loc, "expr is not a scalar value");
1571         });
1572   }
1573 
1574   /// Helper structure to track potential copy-in of non contiguous variable
1575   /// argument into a contiguous temp. It is used to deallocate the temp that
1576   /// may have been created as well as to the copy-out from the temp to the
1577   /// variable after the call.
1578   struct CopyOutPair {
1579     ExtValue var;
1580     ExtValue temp;
1581     // Flag to indicate if the argument may have been modified by the
1582     // callee, in which case it must be copied-out to the variable.
1583     bool argMayBeModifiedByCall;
1584     // Optional boolean value that, if present and false, prevents
1585     // the copy-out and temp deallocation.
1586     llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime;
1587   };
1588   using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>;
1589 
1590   /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories
1591   /// not based on fir.box.
1592   /// This will lose any non contiguous stride information and dynamic type and
1593   /// should only be called if \p exv is known to be contiguous or if its base
1594   /// address will be replaced by a contiguous one. If \p exv is not a
1595   /// fir::BoxValue, this is a no-op.
1596   ExtValue readIfBoxValue(const ExtValue &exv) {
1597     if (const auto *box = exv.getBoxOf<fir::BoxValue>())
1598       return fir::factory::readBoxValue(builder, getLoc(), *box);
1599     return exv;
1600   }
1601 
1602   /// Generate a contiguous temp to pass \p actualArg as argument \p arg. The
1603   /// creation of the temp and copy-in can be made conditional at runtime by
1604   /// providing a runtime boolean flag \p restrictCopyAtRuntime (in which case
1605   /// the temp and copy will only be made if the value is true at runtime).
1606   ExtValue genCopyIn(const ExtValue &actualArg,
1607                      const Fortran::lower::CallerInterface::PassedEntity &arg,
1608                      CopyOutPairs &copyOutPairs,
1609                      llvm::Optional<mlir::Value> restrictCopyAtRuntime) {
1610     if (!restrictCopyAtRuntime) {
1611       ExtValue temp = genArrayTempFromMold(actualArg, ".copyinout");
1612       if (arg.mayBeReadByCall())
1613         genArrayCopy(temp, actualArg);
1614       copyOutPairs.emplace_back(CopyOutPair{
1615           actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime});
1616       return temp;
1617     }
1618     // Otherwise, need to be careful to only copy-in if allowed at runtime.
1619     mlir::Location loc = getLoc();
1620     auto addrType = fir::HeapType::get(
1621         fir::unwrapPassByRefType(fir::getBase(actualArg).getType()));
1622     mlir::Value addr =
1623         builder
1624             .genIfOp(loc, {addrType}, *restrictCopyAtRuntime,
1625                      /*withElseRegion=*/true)
1626             .genThen([&]() {
1627               auto temp = genArrayTempFromMold(actualArg, ".copyinout");
1628               if (arg.mayBeReadByCall())
1629                 genArrayCopy(temp, actualArg);
1630               builder.create<fir::ResultOp>(loc, fir::getBase(temp));
1631             })
1632             .genElse([&]() {
1633               auto nullPtr = builder.createNullConstant(loc, addrType);
1634               builder.create<fir::ResultOp>(loc, nullPtr);
1635             })
1636             .getResults()[0];
1637     // Associate the temp address with actualArg lengths and extents.
1638     fir::ExtendedValue temp = fir::substBase(readIfBoxValue(actualArg), addr);
1639     copyOutPairs.emplace_back(CopyOutPair{
1640         actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime});
1641     return temp;
1642   }
1643 
1644   /// Lower a non-elemental procedure reference.
1645   ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
1646                               llvm::Optional<mlir::Type> resultType) {
1647     mlir::Location loc = getLoc();
1648     if (isElementalProcWithArrayArgs(procRef))
1649       fir::emitFatalError(loc, "trying to lower elemental procedure with array "
1650                                "arguments as normal procedure");
1651     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
1652             procRef.proc().GetSpecificIntrinsic())
1653       return genIntrinsicRef(procRef, *intrinsic, resultType);
1654 
1655     if (isStatementFunctionCall(procRef))
1656       TODO(loc, "Lower statement function call");
1657 
1658     Fortran::lower::CallerInterface caller(procRef, converter);
1659     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1660 
1661     llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall;
1662     // List of <var, temp> where temp must be copied into var after the call.
1663     CopyOutPairs copyOutPairs;
1664 
1665     mlir::FunctionType callSiteType = caller.genFunctionType();
1666 
1667     // Lower the actual arguments and map the lowered values to the dummy
1668     // arguments.
1669     for (const Fortran::lower::CallInterface<
1670              Fortran::lower::CallerInterface>::PassedEntity &arg :
1671          caller.getPassedArguments()) {
1672       const auto *actual = arg.entity;
1673       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
1674       if (!actual) {
1675         // Optional dummy argument for which there is no actual argument.
1676         caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy));
1677         continue;
1678       }
1679       const auto *expr = actual->UnwrapExpr();
1680       if (!expr)
1681         TODO(loc, "assumed type actual argument lowering");
1682 
1683       if (arg.passBy == PassBy::Value) {
1684         ExtValue argVal = genval(*expr);
1685         if (!fir::isUnboxedValue(argVal))
1686           fir::emitFatalError(
1687               loc, "internal error: passing non trivial value by value");
1688         caller.placeInput(arg, fir::getBase(argVal));
1689         continue;
1690       }
1691 
1692       if (arg.passBy == PassBy::MutableBox) {
1693         if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
1694                 *expr)) {
1695           // If expr is NULL(), the mutableBox created must be a deallocated
1696           // pointer with the dummy argument characteristics (see table 16.5
1697           // in Fortran 2018 standard).
1698           // No length parameters are set for the created box because any non
1699           // deferred type parameters of the dummy will be evaluated on the
1700           // callee side, and it is illegal to use NULL without a MOLD if any
1701           // dummy length parameters are assumed.
1702           mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);
1703           assert(boxTy && boxTy.isa<fir::BoxType>() &&
1704                  "must be a fir.box type");
1705           mlir::Value boxStorage = builder.createTemporary(loc, boxTy);
1706           mlir::Value nullBox = fir::factory::createUnallocatedBox(
1707               builder, loc, boxTy, /*nonDeferredParams=*/{});
1708           builder.create<fir::StoreOp>(loc, nullBox, boxStorage);
1709           caller.placeInput(arg, boxStorage);
1710           continue;
1711         }
1712         fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
1713         mlir::Value irBox =
1714             fir::factory::getMutableIRBox(builder, loc, mutableBox);
1715         caller.placeInput(arg, irBox);
1716         if (arg.mayBeModifiedByCall())
1717           mutableModifiedByCall.emplace_back(std::move(mutableBox));
1718         continue;
1719       }
1720       const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr);
1721       if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) {
1722         const bool actualIsSimplyContiguous =
1723             !actualArgIsVariable || Fortran::evaluate::IsSimplyContiguous(
1724                                         *expr, converter.getFoldingContext());
1725         auto argAddr = [&]() -> ExtValue {
1726           ExtValue baseAddr;
1727           if (actualArgIsVariable && arg.isOptional()) {
1728             if (Fortran::evaluate::IsAllocatableOrPointerObject(
1729                     *expr, converter.getFoldingContext())) {
1730               TODO(loc, "Allocatable or pointer argument");
1731             }
1732             if (const Fortran::semantics::Symbol *wholeSymbol =
1733                     Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(
1734                         *expr))
1735               if (Fortran::semantics::IsOptional(*wholeSymbol)) {
1736                 TODO(loc, "procedureref optional arg");
1737               }
1738             // Fall through: The actual argument can safely be
1739             // copied-in/copied-out without any care if needed.
1740           }
1741           if (actualArgIsVariable && expr->Rank() > 0) {
1742             ExtValue box = genBoxArg(*expr);
1743             if (!actualIsSimplyContiguous)
1744               return genCopyIn(box, arg, copyOutPairs,
1745                                /*restrictCopyAtRuntime=*/llvm::None);
1746             // Contiguous: just use the box we created above!
1747             // This gets "unboxed" below, if needed.
1748             return box;
1749           }
1750           // Actual argument is a non optional/non pointer/non allocatable
1751           // scalar.
1752           if (actualArgIsVariable)
1753             return genExtAddr(*expr);
1754           // Actual argument is not a variable. Make sure a variable address is
1755           // not passed.
1756           return genTempExtAddr(*expr);
1757         }();
1758         // Scalar and contiguous expressions may be lowered to a fir.box,
1759         // either to account for potential polymorphism, or because lowering
1760         // did not account for some contiguity hints.
1761         // Here, polymorphism does not matter (an entity of the declared type
1762         // is passed, not one of the dynamic type), and the expr is known to
1763         // be simply contiguous, so it is safe to unbox it and pass the
1764         // address without making a copy.
1765         argAddr = readIfBoxValue(argAddr);
1766 
1767         if (arg.passBy == PassBy::BaseAddress) {
1768           caller.placeInput(arg, fir::getBase(argAddr));
1769         } else {
1770           assert(arg.passBy == PassBy::BoxChar);
1771           auto helper = fir::factory::CharacterExprHelper{builder, loc};
1772           auto boxChar = argAddr.match(
1773               [&](const fir::CharBoxValue &x) { return helper.createEmbox(x); },
1774               [&](const fir::CharArrayBoxValue &x) {
1775                 return helper.createEmbox(x);
1776               },
1777               [&](const auto &x) -> mlir::Value {
1778                 // Fortran allows an actual argument of a completely different
1779                 // type to be passed to a procedure expecting a CHARACTER in the
1780                 // dummy argument position. When this happens, the data pointer
1781                 // argument is simply assumed to point to CHARACTER data and the
1782                 // LEN argument used is garbage. Simulate this behavior by
1783                 // free-casting the base address to be a !fir.char reference and
1784                 // setting the LEN argument to undefined. What could go wrong?
1785                 auto dataPtr = fir::getBase(x);
1786                 assert(!dataPtr.getType().template isa<fir::BoxType>());
1787                 return builder.convertWithSemantics(
1788                     loc, argTy, dataPtr,
1789                     /*allowCharacterConversion=*/true);
1790               });
1791           caller.placeInput(arg, boxChar);
1792         }
1793       } else if (arg.passBy == PassBy::Box) {
1794         // Before lowering to an address, handle the allocatable/pointer actual
1795         // argument to optional fir.box dummy. It is legal to pass
1796         // unallocated/disassociated entity to an optional. In this case, an
1797         // absent fir.box must be created instead of a fir.box with a null value
1798         // (Fortran 2018 15.5.2.12 point 1).
1799         if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject(
1800                                     *expr, converter.getFoldingContext())) {
1801           TODO(loc, "optional allocatable or pointer argument");
1802         } else {
1803           // Make sure a variable address is only passed if the expression is
1804           // actually a variable.
1805           mlir::Value box =
1806               actualArgIsVariable
1807                   ? builder.createBox(loc, genBoxArg(*expr))
1808                   : builder.createBox(getLoc(), genTempExtAddr(*expr));
1809           caller.placeInput(arg, box);
1810         }
1811       } else if (arg.passBy == PassBy::AddressAndLength) {
1812         ExtValue argRef = genExtAddr(*expr);
1813         caller.placeAddressAndLengthInput(arg, fir::getBase(argRef),
1814                                           fir::getLen(argRef));
1815       } else if (arg.passBy == PassBy::CharProcTuple) {
1816         TODO(loc, "procedureref CharProcTuple");
1817       } else {
1818         TODO(loc, "pass by value in non elemental function call");
1819       }
1820     }
1821 
1822     ExtValue result = genCallOpAndResult(caller, callSiteType, resultType);
1823 
1824     // // Copy-out temps that were created for non contiguous variable arguments
1825     // if
1826     // // needed.
1827     // for (const auto &copyOutPair : copyOutPairs)
1828     //   genCopyOut(copyOutPair);
1829 
1830     return result;
1831   }
1832 
1833   template <typename A>
1834   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1835     ExtValue result = genFunctionRef(funcRef);
1836     if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
1837       return genLoad(result);
1838     return result;
1839   }
1840 
1841   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
1842     llvm::Optional<mlir::Type> resTy;
1843     if (procRef.hasAlternateReturns())
1844       resTy = builder.getIndexType();
1845     return genProcedureRef(procRef, resTy);
1846   }
1847 
1848   /// Helper to lower intrinsic arguments for inquiry intrinsic.
1849   ExtValue
1850   lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr &expr) {
1851     if (Fortran::evaluate::IsAllocatableOrPointerObject(
1852             expr, converter.getFoldingContext()))
1853       return genMutableBoxValue(expr);
1854     return gen(expr);
1855   }
1856 
1857   /// Helper to lower intrinsic arguments to a fir::BoxValue.
1858   /// It preserves all the non default lower bounds/non deferred length
1859   /// parameter information.
1860   ExtValue lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr &expr) {
1861     mlir::Location loc = getLoc();
1862     ExtValue exv = genBoxArg(expr);
1863     mlir::Value box = builder.createBox(loc, exv);
1864     return fir::BoxValue(
1865         box, fir::factory::getNonDefaultLowerBounds(builder, loc, exv),
1866         fir::factory::getNonDeferredLengthParams(exv));
1867   }
1868 
1869   /// Generate a call to an intrinsic function.
1870   ExtValue
1871   genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
1872                   const Fortran::evaluate::SpecificIntrinsic &intrinsic,
1873                   llvm::Optional<mlir::Type> resultType) {
1874     llvm::SmallVector<ExtValue> operands;
1875 
1876     llvm::StringRef name = intrinsic.name;
1877     mlir::Location loc = getLoc();
1878 
1879     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
1880         Fortran::lower::getIntrinsicArgumentLowering(name);
1881     for (const auto &[arg, dummy] :
1882          llvm::zip(procRef.arguments(),
1883                    intrinsic.characteristics.value().dummyArguments)) {
1884       auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
1885       if (!expr) {
1886         // Absent optional.
1887         operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
1888         continue;
1889       }
1890       if (!argLowering) {
1891         // No argument lowering instruction, lower by value.
1892         operands.emplace_back(genval(*expr));
1893         continue;
1894       }
1895       // Ad-hoc argument lowering handling.
1896       Fortran::lower::ArgLoweringRule argRules =
1897           Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering,
1898                                                    dummy.name);
1899       if (argRules.handleDynamicOptional &&
1900           Fortran::evaluate::MayBePassedAsAbsentOptional(
1901               *expr, converter.getFoldingContext())) {
1902         ExtValue optional = lowerIntrinsicArgumentAsInquired(*expr);
1903         mlir::Value isPresent = genActualIsPresentTest(builder, loc, optional);
1904         switch (argRules.lowerAs) {
1905         case Fortran::lower::LowerIntrinsicArgAs::Value:
1906           operands.emplace_back(
1907               genOptionalValue(builder, loc, optional, isPresent));
1908           continue;
1909         case Fortran::lower::LowerIntrinsicArgAs::Addr:
1910           operands.emplace_back(
1911               genOptionalAddr(builder, loc, optional, isPresent));
1912           continue;
1913         case Fortran::lower::LowerIntrinsicArgAs::Box:
1914           operands.emplace_back(
1915               genOptionalBox(builder, loc, optional, isPresent));
1916           continue;
1917         case Fortran::lower::LowerIntrinsicArgAs::Inquired:
1918           operands.emplace_back(optional);
1919           continue;
1920         }
1921         llvm_unreachable("bad switch");
1922       }
1923       switch (argRules.lowerAs) {
1924       case Fortran::lower::LowerIntrinsicArgAs::Value:
1925         operands.emplace_back(genval(*expr));
1926         continue;
1927       case Fortran::lower::LowerIntrinsicArgAs::Addr:
1928         operands.emplace_back(gen(*expr));
1929         continue;
1930       case Fortran::lower::LowerIntrinsicArgAs::Box:
1931         operands.emplace_back(lowerIntrinsicArgumentAsBox(*expr));
1932         continue;
1933       case Fortran::lower::LowerIntrinsicArgAs::Inquired:
1934         operands.emplace_back(lowerIntrinsicArgumentAsInquired(*expr));
1935         continue;
1936       }
1937       llvm_unreachable("bad switch");
1938     }
1939     // Let the intrinsic library lower the intrinsic procedure call
1940     return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
1941                                             operands, stmtCtx);
1942   }
1943 
1944   template <typename A>
1945   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
1946     if (isScalar(x) || Fortran::evaluate::UnwrapWholeSymbolDataRef(x) ||
1947         inInitializer)
1948       return std::visit([&](const auto &e) { return genval(e); }, x.u);
1949     return asArray(x);
1950   }
1951 
1952   /// Helper to detect Transformational function reference.
1953   template <typename T>
1954   bool isTransformationalRef(const T &) {
1955     return false;
1956   }
1957   template <typename T>
1958   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
1959     return !funcRef.IsElemental() && funcRef.Rank();
1960   }
1961   template <typename T>
1962   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
1963     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
1964                       expr.u);
1965   }
1966 
1967   template <typename A>
1968   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
1969     // Whole array symbols or components, and results of transformational
1970     // functions already have a storage and the scalar expression lowering path
1971     // is used to not create a new temporary storage.
1972     if (isScalar(x) ||
1973         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
1974         isTransformationalRef(x))
1975       return std::visit([&](const auto &e) { return genref(e); }, x.u);
1976     TODO(getLoc(), "gen Expr non-scalar");
1977   }
1978 
1979   template <typename A>
1980   bool isScalar(const A &x) {
1981     return x.Rank() == 0;
1982   }
1983 
1984   template <typename A>
1985   ExtValue asArray(const A &x) {
1986     return Fortran::lower::createSomeArrayTempValue(converter, toEvExpr(x),
1987                                                     symMap, stmtCtx);
1988   }
1989 
1990   template <int KIND>
1991   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
1992                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
1993     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
1994   }
1995 
1996   using RefSet =
1997       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
1998                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
1999                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
2000                  Fortran::semantics::SymbolRef>;
2001   template <typename A>
2002   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
2003 
2004   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
2005   ExtValue genref(const A &a) {
2006     return gen(a);
2007   }
2008   template <typename A>
2009   ExtValue genref(const A &a) {
2010     mlir::Type storageType = converter.genType(toEvExpr(a));
2011     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
2012   }
2013 
2014   template <typename A, template <typename> typename T,
2015             typename B = std::decay_t<T<A>>,
2016             std::enable_if_t<
2017                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
2018                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
2019                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
2020                 bool> = true>
2021   ExtValue genref(const T<A> &x) {
2022     return gen(x);
2023   }
2024 
2025 private:
2026   mlir::Location location;
2027   Fortran::lower::AbstractConverter &converter;
2028   fir::FirOpBuilder &builder;
2029   Fortran::lower::StatementContext &stmtCtx;
2030   Fortran::lower::SymMap &symMap;
2031   InitializerData *inInitializer = nullptr;
2032   bool useBoxArg = false; // expression lowered as argument
2033 };
2034 } // namespace
2035 
2036 // Helper for changing the semantics in a given context. Preserves the current
2037 // semantics which is resumed when the "push" goes out of scope.
2038 #define PushSemantics(PushVal)                                                 \
2039   [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ =                 \
2040       Fortran::common::ScopedSet(semant, PushVal);
2041 
2042 static bool isAdjustedArrayElementType(mlir::Type t) {
2043   return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>();
2044 }
2045 
2046 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting
2047 /// the actual extents and lengths. This is only to allow their propagation as
2048 /// ExtendedValue without triggering verifier failures when propagating
2049 /// character/arrays as unboxed values. Only the base of the resulting
2050 /// ExtendedValue should be used, it is undefined to use the length or extents
2051 /// of the extended value returned,
2052 inline static fir::ExtendedValue
2053 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder,
2054                        mlir::Value val, mlir::Value len) {
2055   mlir::Type ty = fir::unwrapRefType(val.getType());
2056   mlir::IndexType idxTy = builder.getIndexType();
2057   auto seqTy = ty.cast<fir::SequenceType>();
2058   auto undef = builder.create<fir::UndefOp>(loc, idxTy);
2059   llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef);
2060   if (fir::isa_char(seqTy.getEleTy()))
2061     return fir::CharArrayBoxValue(val, len ? len : undef, extents);
2062   return fir::ArrayBoxValue(val, extents);
2063 }
2064 
2065 //===----------------------------------------------------------------------===//
2066 //
2067 // Lowering of scalar expressions in an explicit iteration space context.
2068 //
2069 //===----------------------------------------------------------------------===//
2070 
2071 // Shared code for creating a copy of a derived type element. This function is
2072 // called from a continuation.
2073 inline static fir::ArrayAmendOp
2074 createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad,
2075                         fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc,
2076                         const fir::ExtendedValue &elementExv, mlir::Type eleTy,
2077                         mlir::Value innerArg) {
2078   if (destLoad.getTypeparams().empty()) {
2079     fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv);
2080   } else {
2081     auto boxTy = fir::BoxType::get(eleTy);
2082     auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(),
2083                                               mlir::Value{}, mlir::Value{},
2084                                               destLoad.getTypeparams());
2085     auto fromBox = builder.create<fir::EmboxOp>(
2086         loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{},
2087         destLoad.getTypeparams());
2088     fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox),
2089                                       fir::BoxValue(fromBox));
2090   }
2091   return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg,
2092                                            destAcc);
2093 }
2094 
2095 inline static fir::ArrayAmendOp
2096 createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder,
2097                      fir::ArrayAccessOp dstOp, mlir::Value &dstLen,
2098                      const fir::ExtendedValue &srcExv, mlir::Value innerArg,
2099                      llvm::ArrayRef<mlir::Value> bounds) {
2100   fir::CharBoxValue dstChar(dstOp, dstLen);
2101   fir::factory::CharacterExprHelper helper{builder, loc};
2102   if (!bounds.empty()) {
2103     dstChar = helper.createSubstring(dstChar, bounds);
2104     fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv),
2105                                    dstChar.getAddr(), dstChar.getLen(), builder,
2106                                    loc);
2107     // Update the LEN to the substring's LEN.
2108     dstLen = dstChar.getLen();
2109   }
2110   // For a CHARACTER, we generate the element assignment loops inline.
2111   helper.createAssign(fir::ExtendedValue{dstChar}, srcExv);
2112   // Mark this array element as amended.
2113   mlir::Type ty = innerArg.getType();
2114   auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp);
2115   return amend;
2116 }
2117 
2118 //===----------------------------------------------------------------------===//
2119 //
2120 // Lowering of array expressions.
2121 //
2122 //===----------------------------------------------------------------------===//
2123 
2124 namespace {
2125 class ArrayExprLowering {
2126   using ExtValue = fir::ExtendedValue;
2127 
2128   /// Structure to keep track of lowered array operands in the
2129   /// array expression. Useful to later deduce the shape of the
2130   /// array expression.
2131   struct ArrayOperand {
2132     /// Array base (can be a fir.box).
2133     mlir::Value memref;
2134     /// ShapeOp, ShapeShiftOp or ShiftOp
2135     mlir::Value shape;
2136     /// SliceOp
2137     mlir::Value slice;
2138     /// Can this operand be absent ?
2139     bool mayBeAbsent = false;
2140   };
2141 
2142   using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts;
2143   using PathComponent = Fortran::lower::PathComponent;
2144 
2145   /// Active iteration space.
2146   using IterationSpace = Fortran::lower::IterationSpace;
2147   using IterSpace = const Fortran::lower::IterationSpace &;
2148 
2149   /// Current continuation. Function that will generate IR for a single
2150   /// iteration of the pending iterative loop structure.
2151   using CC = Fortran::lower::GenerateElementalArrayFunc;
2152 
2153   /// Projection continuation. Function that will project one iteration space
2154   /// into another.
2155   using PC = std::function<IterationSpace(IterSpace)>;
2156   using ArrayBaseTy =
2157       std::variant<std::monostate, const Fortran::evaluate::ArrayRef *,
2158                    const Fortran::evaluate::DataRef *>;
2159   using ComponentPath = Fortran::lower::ComponentPath;
2160 
2161 public:
2162   //===--------------------------------------------------------------------===//
2163   // Regular array assignment
2164   //===--------------------------------------------------------------------===//
2165 
2166   /// Entry point for array assignments. Both the left-hand and right-hand sides
2167   /// can either be ExtendedValue or evaluate::Expr.
2168   template <typename TL, typename TR>
2169   static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter,
2170                                    Fortran::lower::SymMap &symMap,
2171                                    Fortran::lower::StatementContext &stmtCtx,
2172                                    const TL &lhs, const TR &rhs) {
2173     ArrayExprLowering ael{converter, stmtCtx, symMap,
2174                           ConstituentSemantics::CopyInCopyOut};
2175     ael.lowerArrayAssignment(lhs, rhs);
2176   }
2177 
2178   template <typename TL, typename TR>
2179   void lowerArrayAssignment(const TL &lhs, const TR &rhs) {
2180     mlir::Location loc = getLoc();
2181     /// Here the target subspace is not necessarily contiguous. The ArrayUpdate
2182     /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad
2183     /// in `destination`.
2184     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
2185     ccStoreToDest = genarr(lhs);
2186     determineShapeOfDest(lhs);
2187     semant = ConstituentSemantics::RefTransparent;
2188     ExtValue exv = lowerArrayExpression(rhs);
2189     if (explicitSpaceIsActive()) {
2190       explicitSpace->finalizeContext();
2191       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
2192     } else {
2193       builder.create<fir::ArrayMergeStoreOp>(
2194           loc, destination, fir::getBase(exv), destination.getMemref(),
2195           destination.getSlice(), destination.getTypeparams());
2196     }
2197   }
2198 
2199   //===--------------------------------------------------------------------===//
2200   // Array assignment to allocatable array
2201   //===--------------------------------------------------------------------===//
2202 
2203   /// Entry point for assignment to allocatable array.
2204   static void lowerAllocatableArrayAssignment(
2205       Fortran::lower::AbstractConverter &converter,
2206       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
2207       const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
2208       Fortran::lower::ExplicitIterSpace &explicitSpace,
2209       Fortran::lower::ImplicitIterSpace &implicitSpace) {
2210     ArrayExprLowering ael(converter, stmtCtx, symMap,
2211                           ConstituentSemantics::CopyInCopyOut, &explicitSpace,
2212                           &implicitSpace);
2213     ael.lowerAllocatableArrayAssignment(lhs, rhs);
2214   }
2215 
2216   /// Assignment to allocatable array.
2217   ///
2218   /// The semantics are reverse that of a "regular" array assignment. The rhs
2219   /// defines the iteration space of the computation and the lhs is
2220   /// resized/reallocated to fit if necessary.
2221   void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs,
2222                                        const Fortran::lower::SomeExpr &rhs) {
2223     // With assignment to allocatable, we want to lower the rhs first and use
2224     // its shape to determine if we need to reallocate, etc.
2225     mlir::Location loc = getLoc();
2226     // FIXME: If the lhs is in an explicit iteration space, the assignment may
2227     // be to an array of allocatable arrays rather than a single allocatable
2228     // array.
2229     fir::MutableBoxValue mutableBox =
2230         createMutableBox(loc, converter, lhs, symMap);
2231     mlir::Type resultTy = converter.genType(rhs);
2232     if (rhs.Rank() > 0)
2233       determineShapeOfDest(rhs);
2234     auto rhsCC = [&]() {
2235       PushSemantics(ConstituentSemantics::RefTransparent);
2236       return genarr(rhs);
2237     }();
2238 
2239     llvm::SmallVector<mlir::Value> lengthParams;
2240     // Currently no safe way to gather length from rhs (at least for
2241     // character, it cannot be taken from array_loads since it may be
2242     // changed by concatenations).
2243     if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) ||
2244         mutableBox.isDerivedWithLengthParameters())
2245       TODO(loc, "gather rhs length parameters in assignment to allocatable");
2246 
2247     // The allocatable must take lower bounds from the expr if it is
2248     // reallocated and the right hand side is not a scalar.
2249     const bool takeLboundsIfRealloc = rhs.Rank() > 0;
2250     llvm::SmallVector<mlir::Value> lbounds;
2251     // When the reallocated LHS takes its lower bounds from the RHS,
2252     // they will be non default only if the RHS is a whole array
2253     // variable. Otherwise, lbounds is left empty and default lower bounds
2254     // will be used.
2255     if (takeLboundsIfRealloc &&
2256         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) {
2257       assert(arrayOperands.size() == 1 &&
2258              "lbounds can only come from one array");
2259       std::vector<mlir::Value> lbs =
2260           fir::factory::getOrigins(arrayOperands[0].shape);
2261       lbounds.append(lbs.begin(), lbs.end());
2262     }
2263     fir::factory::MutableBoxReallocation realloc =
2264         fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape,
2265                                          lengthParams);
2266     // Create ArrayLoad for the mutable box and save it into `destination`.
2267     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
2268     ccStoreToDest = genarr(realloc.newValue);
2269     // If the rhs is scalar, get shape from the allocatable ArrayLoad.
2270     if (destShape.empty())
2271       destShape = getShape(destination);
2272     // Finish lowering the loop nest.
2273     assert(destination && "destination must have been set");
2274     ExtValue exv = lowerArrayExpression(rhsCC, resultTy);
2275     if (explicitSpaceIsActive()) {
2276       explicitSpace->finalizeContext();
2277       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
2278     } else {
2279       builder.create<fir::ArrayMergeStoreOp>(
2280           loc, destination, fir::getBase(exv), destination.getMemref(),
2281           destination.getSlice(), destination.getTypeparams());
2282     }
2283     fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds,
2284                                   takeLboundsIfRealloc, realloc);
2285   }
2286 
2287   /// Entry point for when an array expression appears in a context where the
2288   /// result must be boxed. (BoxValue semantics.)
2289   static ExtValue
2290   lowerBoxedArrayExpression(Fortran::lower::AbstractConverter &converter,
2291                             Fortran::lower::SymMap &symMap,
2292                             Fortran::lower::StatementContext &stmtCtx,
2293                             const Fortran::lower::SomeExpr &expr) {
2294     ArrayExprLowering ael{converter, stmtCtx, symMap,
2295                           ConstituentSemantics::BoxValue};
2296     return ael.lowerBoxedArrayExpr(expr);
2297   }
2298 
2299   ExtValue lowerBoxedArrayExpr(const Fortran::lower::SomeExpr &exp) {
2300     return std::visit(
2301         [&](const auto &e) {
2302           auto f = genarr(e);
2303           ExtValue exv = f(IterationSpace{});
2304           if (fir::getBase(exv).getType().template isa<fir::BoxType>())
2305             return exv;
2306           fir::emitFatalError(getLoc(), "array must be emboxed");
2307         },
2308         exp.u);
2309   }
2310 
2311   /// Entry point into lowering an expression with rank. This entry point is for
2312   /// lowering a rhs expression, for example. (RefTransparent semantics.)
2313   static ExtValue
2314   lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter,
2315                           Fortran::lower::SymMap &symMap,
2316                           Fortran::lower::StatementContext &stmtCtx,
2317                           const Fortran::lower::SomeExpr &expr) {
2318     ArrayExprLowering ael{converter, stmtCtx, symMap};
2319     ael.determineShapeOfDest(expr);
2320     ExtValue loopRes = ael.lowerArrayExpression(expr);
2321     fir::ArrayLoadOp dest = ael.destination;
2322     mlir::Value tempRes = dest.getMemref();
2323     fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2324     mlir::Location loc = converter.getCurrentLocation();
2325     builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes),
2326                                            tempRes, dest.getSlice(),
2327                                            dest.getTypeparams());
2328 
2329     auto arrTy =
2330         fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>();
2331     if (auto charTy =
2332             arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) {
2333       if (fir::characterWithDynamicLen(charTy))
2334         TODO(loc, "CHARACTER does not have constant LEN");
2335       mlir::Value len = builder.createIntegerConstant(
2336           loc, builder.getCharacterLengthType(), charTy.getLen());
2337       return fir::CharArrayBoxValue(tempRes, len, dest.getExtents());
2338     }
2339     return fir::ArrayBoxValue(tempRes, dest.getExtents());
2340   }
2341 
2342   // FIXME: should take multiple inner arguments.
2343   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
2344   genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) {
2345     mlir::Location loc = getLoc();
2346     mlir::IndexType idxTy = builder.getIndexType();
2347     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
2348     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
2349     llvm::SmallVector<mlir::Value> loopUppers;
2350 
2351     // Convert any implied shape to closed interval form. The fir.do_loop will
2352     // run from 0 to `extent - 1` inclusive.
2353     for (auto extent : shape)
2354       loopUppers.push_back(
2355           builder.create<mlir::arith::SubIOp>(loc, extent, one));
2356 
2357     // Iteration space is created with outermost columns, innermost rows
2358     llvm::SmallVector<fir::DoLoopOp> loops;
2359 
2360     const std::size_t loopDepth = loopUppers.size();
2361     llvm::SmallVector<mlir::Value> ivars;
2362 
2363     for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) {
2364       if (i.index() > 0) {
2365         assert(!loops.empty());
2366         builder.setInsertionPointToStart(loops.back().getBody());
2367       }
2368       fir::DoLoopOp loop;
2369       if (innerArg) {
2370         loop = builder.create<fir::DoLoopOp>(
2371             loc, zero, i.value(), one, isUnordered(),
2372             /*finalCount=*/false, mlir::ValueRange{innerArg});
2373         innerArg = loop.getRegionIterArgs().front();
2374         if (explicitSpaceIsActive())
2375           explicitSpace->setInnerArg(0, innerArg);
2376       } else {
2377         loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one,
2378                                              isUnordered(),
2379                                              /*finalCount=*/false);
2380       }
2381       ivars.push_back(loop.getInductionVar());
2382       loops.push_back(loop);
2383     }
2384 
2385     if (innerArg)
2386       for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth;
2387            ++i) {
2388         builder.setInsertionPointToEnd(loops[i].getBody());
2389         builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0));
2390       }
2391 
2392     // Move insertion point to the start of the innermost loop in the nest.
2393     builder.setInsertionPointToStart(loops.back().getBody());
2394     // Set `afterLoopNest` to just after the entire loop nest.
2395     auto currPt = builder.saveInsertionPoint();
2396     builder.setInsertionPointAfter(loops[0]);
2397     auto afterLoopNest = builder.saveInsertionPoint();
2398     builder.restoreInsertionPoint(currPt);
2399 
2400     // Put the implicit loop variables in row to column order to match FIR's
2401     // Ops. (The loops were constructed from outermost column to innermost
2402     // row.)
2403     mlir::Value outerRes = loops[0].getResult(0);
2404     return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)),
2405             afterLoopNest};
2406   }
2407 
2408   /// Build the iteration space into which the array expression will be
2409   /// lowered. The resultType is used to create a temporary, if needed.
2410   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
2411   genIterSpace(mlir::Type resultType) {
2412     mlir::Location loc = getLoc();
2413     llvm::SmallVector<mlir::Value> shape = genIterationShape();
2414     if (!destination) {
2415       // Allocate storage for the result if it is not already provided.
2416       destination = createAndLoadSomeArrayTemp(resultType, shape);
2417     }
2418 
2419     // Generate the lazy mask allocation, if one was given.
2420     if (ccPrelude.hasValue())
2421       ccPrelude.getValue()(shape);
2422 
2423     // Now handle the implicit loops.
2424     mlir::Value inner = explicitSpaceIsActive()
2425                             ? explicitSpace->getInnerArgs().front()
2426                             : destination.getResult();
2427     auto [iters, afterLoopNest] = genImplicitLoops(shape, inner);
2428     mlir::Value innerArg = iters.innerArgument();
2429 
2430     // Generate the mask conditional structure, if there are masks. Unlike the
2431     // explicit masks, which are interleaved, these mask expression appear in
2432     // the innermost loop.
2433     if (implicitSpaceHasMasks()) {
2434       // Recover the cached condition from the mask buffer.
2435       auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) {
2436         return implicitSpace->getBoundClosure(e)(iters);
2437       };
2438 
2439       // Handle the negated conditions in topological order of the WHERE
2440       // clauses. See 10.2.3.2p4 as to why this control structure is produced.
2441       for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs :
2442            implicitSpace->getMasks()) {
2443         const std::size_t size = maskExprs.size() - 1;
2444         auto genFalseBlock = [&](const auto *e, auto &&cond) {
2445           auto ifOp = builder.create<fir::IfOp>(
2446               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
2447               /*withElseRegion=*/true);
2448           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
2449           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2450           builder.create<fir::ResultOp>(loc, innerArg);
2451           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
2452         };
2453         auto genTrueBlock = [&](const auto *e, auto &&cond) {
2454           auto ifOp = builder.create<fir::IfOp>(
2455               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
2456               /*withElseRegion=*/true);
2457           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
2458           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
2459           builder.create<fir::ResultOp>(loc, innerArg);
2460           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2461         };
2462         for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i)
2463           if (const auto *e = maskExprs[i])
2464             genFalseBlock(e, genCond(e, iters));
2465 
2466         // The last condition is either non-negated or unconditionally negated.
2467         if (const auto *e = maskExprs[size])
2468           genTrueBlock(e, genCond(e, iters));
2469       }
2470     }
2471 
2472     // We're ready to lower the body (an assignment statement) for this context
2473     // of loop nests at this point.
2474     return {iters, afterLoopNest};
2475   }
2476 
2477   fir::ArrayLoadOp
2478   createAndLoadSomeArrayTemp(mlir::Type type,
2479                              llvm::ArrayRef<mlir::Value> shape) {
2480     if (ccLoadDest.hasValue())
2481       return ccLoadDest.getValue()(shape);
2482     auto seqTy = type.dyn_cast<fir::SequenceType>();
2483     assert(seqTy && "must be an array");
2484     mlir::Location loc = getLoc();
2485     // TODO: Need to thread the length parameters here. For character, they may
2486     // differ from the operands length (e.g concatenation). So the array loads
2487     // type parameters are not enough.
2488     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>())
2489       if (charTy.hasDynamicLen())
2490         TODO(loc, "character array expression temp with dynamic length");
2491     if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>())
2492       if (recTy.getNumLenParams() > 0)
2493         TODO(loc, "derived type array expression temp with length parameters");
2494     mlir::Value temp = seqTy.hasConstantShape()
2495                            ? builder.create<fir::AllocMemOp>(loc, type)
2496                            : builder.create<fir::AllocMemOp>(
2497                                  loc, type, ".array.expr", llvm::None, shape);
2498     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
2499     stmtCtx.attachCleanup(
2500         [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); });
2501     mlir::Value shapeOp = genShapeOp(shape);
2502     return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp,
2503                                             /*slice=*/mlir::Value{},
2504                                             llvm::None);
2505   }
2506 
2507   static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder,
2508                                  llvm::ArrayRef<mlir::Value> shape) {
2509     mlir::IndexType idxTy = builder.getIndexType();
2510     llvm::SmallVector<mlir::Value> idxShape;
2511     for (auto s : shape)
2512       idxShape.push_back(builder.createConvert(loc, idxTy, s));
2513     auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size());
2514     return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape);
2515   }
2516 
2517   fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) {
2518     return genShapeOp(getLoc(), builder, shape);
2519   }
2520 
2521   //===--------------------------------------------------------------------===//
2522   // Expression traversal and lowering.
2523   //===--------------------------------------------------------------------===//
2524 
2525   /// Lower the expression, \p x, in a scalar context.
2526   template <typename A>
2527   ExtValue asScalar(const A &x) {
2528     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x);
2529   }
2530 
2531   /// Lower the expression in a scalar context to a memory reference.
2532   template <typename A>
2533   ExtValue asScalarRef(const A &x) {
2534     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x);
2535   }
2536 
2537   /// Lower an expression without dereferencing any indirection that may be
2538   /// a nullptr (because this is an absent optional or unallocated/disassociated
2539   /// descriptor). The returned expression cannot be addressed directly, it is
2540   /// meant to inquire about its status before addressing the related entity.
2541   template <typename A>
2542   ExtValue asInquired(const A &x) {
2543     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}
2544         .lowerIntrinsicArgumentAsInquired(x);
2545   }
2546 
2547   // An expression with non-zero rank is an array expression.
2548   template <typename A>
2549   bool isArray(const A &x) const {
2550     return x.Rank() != 0;
2551   }
2552 
2553   /// Some temporaries are allocated on an element-by-element basis during the
2554   /// array expression evaluation. Collect the cleanups here so the resources
2555   /// can be freed before the next loop iteration, avoiding memory leaks. etc.
2556   Fortran::lower::StatementContext &getElementCtx() {
2557     if (!elementCtx) {
2558       stmtCtx.pushScope();
2559       elementCtx = true;
2560     }
2561     return stmtCtx;
2562   }
2563 
2564   /// If there were temporaries created for this element evaluation, finalize
2565   /// and deallocate the resources now. This should be done just prior the the
2566   /// fir::ResultOp at the end of the innermost loop.
2567   void finalizeElementCtx() {
2568     if (elementCtx) {
2569       stmtCtx.finalize(/*popScope=*/true);
2570       elementCtx = false;
2571     }
2572   }
2573 
2574   /// Lower an elemental function array argument. This ensures array
2575   /// sub-expressions that are not variables and must be passed by address
2576   /// are lowered by value and placed in memory.
2577   template <typename A>
2578   CC genElementalArgument(const A &x) {
2579     // Ensure the returned element is in memory if this is what was requested.
2580     if ((semant == ConstituentSemantics::RefOpaque ||
2581          semant == ConstituentSemantics::DataAddr ||
2582          semant == ConstituentSemantics::ByValueArg)) {
2583       if (!Fortran::evaluate::IsVariable(x)) {
2584         PushSemantics(ConstituentSemantics::DataValue);
2585         CC cc = genarr(x);
2586         mlir::Location loc = getLoc();
2587         if (isParenthesizedVariable(x)) {
2588           // Parenthesised variables are lowered to a reference to the variable
2589           // storage. When passing it as an argument, a copy must be passed.
2590           return [=](IterSpace iters) -> ExtValue {
2591             return createInMemoryScalarCopy(builder, loc, cc(iters));
2592           };
2593         }
2594         mlir::Type storageType =
2595             fir::unwrapSequenceType(converter.genType(toEvExpr(x)));
2596         return [=](IterSpace iters) -> ExtValue {
2597           return placeScalarValueInMemory(builder, loc, cc(iters), storageType);
2598         };
2599       }
2600     }
2601     return genarr(x);
2602   }
2603 
2604   // A procedure reference to a Fortran elemental intrinsic procedure.
2605   CC genElementalIntrinsicProcRef(
2606       const Fortran::evaluate::ProcedureRef &procRef,
2607       llvm::Optional<mlir::Type> retTy,
2608       const Fortran::evaluate::SpecificIntrinsic &intrinsic) {
2609     llvm::SmallVector<CC> operands;
2610     llvm::StringRef name = intrinsic.name;
2611     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
2612         Fortran::lower::getIntrinsicArgumentLowering(name);
2613     mlir::Location loc = getLoc();
2614     if (Fortran::lower::intrinsicRequiresCustomOptionalHandling(
2615             procRef, intrinsic, converter)) {
2616       using CcPairT = std::pair<CC, llvm::Optional<mlir::Value>>;
2617       llvm::SmallVector<CcPairT> operands;
2618       auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
2619         if (expr.Rank() == 0) {
2620           ExtValue optionalArg = this->asInquired(expr);
2621           mlir::Value isPresent =
2622               genActualIsPresentTest(builder, loc, optionalArg);
2623           operands.emplace_back(
2624               [=](IterSpace iters) -> ExtValue {
2625                 return genLoad(builder, loc, optionalArg);
2626               },
2627               isPresent);
2628         } else {
2629           auto [cc, isPresent, _] = this->genOptionalArrayFetch(expr);
2630           operands.emplace_back(cc, isPresent);
2631         }
2632       };
2633       auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) {
2634         PushSemantics(ConstituentSemantics::RefTransparent);
2635         operands.emplace_back(genElementalArgument(expr), llvm::None);
2636       };
2637       Fortran::lower::prepareCustomIntrinsicArgument(
2638           procRef, intrinsic, retTy, prepareOptionalArg, prepareOtherArg,
2639           converter);
2640 
2641       fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
2642       llvm::StringRef name = intrinsic.name;
2643       return [=](IterSpace iters) -> ExtValue {
2644         auto getArgument = [&](std::size_t i) -> ExtValue {
2645           return operands[i].first(iters);
2646         };
2647         auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> {
2648           return operands[i].second;
2649         };
2650         return Fortran::lower::lowerCustomIntrinsic(
2651             *bldr, loc, name, retTy, isPresent, getArgument, operands.size(),
2652             getElementCtx());
2653       };
2654     }
2655     /// Otherwise, pre-lower arguments and use intrinsic lowering utility.
2656     for (const auto &[arg, dummy] :
2657          llvm::zip(procRef.arguments(),
2658                    intrinsic.characteristics.value().dummyArguments)) {
2659       const auto *expr =
2660           Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
2661       if (!expr) {
2662         // Absent optional.
2663         operands.emplace_back([=](IterSpace) { return mlir::Value{}; });
2664       } else if (!argLowering) {
2665         // No argument lowering instruction, lower by value.
2666         PushSemantics(ConstituentSemantics::RefTransparent);
2667         operands.emplace_back(genElementalArgument(*expr));
2668       } else {
2669         // Ad-hoc argument lowering handling.
2670         Fortran::lower::ArgLoweringRule argRules =
2671             Fortran::lower::lowerIntrinsicArgumentAs(getLoc(), *argLowering,
2672                                                      dummy.name);
2673         if (argRules.handleDynamicOptional &&
2674             Fortran::evaluate::MayBePassedAsAbsentOptional(
2675                 *expr, converter.getFoldingContext())) {
2676           // Currently, there is not elemental intrinsic that requires lowering
2677           // a potentially absent argument to something else than a value (apart
2678           // from character MAX/MIN that are handled elsewhere.)
2679           if (argRules.lowerAs != Fortran::lower::LowerIntrinsicArgAs::Value)
2680             TODO(loc, "lowering non trivial optional elemental intrinsic array "
2681                       "argument");
2682           PushSemantics(ConstituentSemantics::RefTransparent);
2683           operands.emplace_back(genarrForwardOptionalArgumentToCall(*expr));
2684           continue;
2685         }
2686         switch (argRules.lowerAs) {
2687         case Fortran::lower::LowerIntrinsicArgAs::Value: {
2688           PushSemantics(ConstituentSemantics::RefTransparent);
2689           operands.emplace_back(genElementalArgument(*expr));
2690         } break;
2691         case Fortran::lower::LowerIntrinsicArgAs::Addr: {
2692           // Note: assume does not have Fortran VALUE attribute semantics.
2693           PushSemantics(ConstituentSemantics::RefOpaque);
2694           operands.emplace_back(genElementalArgument(*expr));
2695         } break;
2696         case Fortran::lower::LowerIntrinsicArgAs::Box: {
2697           PushSemantics(ConstituentSemantics::RefOpaque);
2698           auto lambda = genElementalArgument(*expr);
2699           operands.emplace_back([=](IterSpace iters) {
2700             return builder.createBox(loc, lambda(iters));
2701           });
2702         } break;
2703         case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2704           TODO(loc, "intrinsic function with inquired argument");
2705           break;
2706         }
2707       }
2708     }
2709 
2710     // Let the intrinsic library lower the intrinsic procedure call
2711     return [=](IterSpace iters) {
2712       llvm::SmallVector<ExtValue> args;
2713       for (const auto &cc : operands)
2714         args.push_back(cc(iters));
2715       return Fortran::lower::genIntrinsicCall(builder, loc, name, retTy, args,
2716                                               getElementCtx());
2717     };
2718   }
2719 
2720   /// Generate a procedure reference. This code is shared for both functions and
2721   /// subroutines, the difference being reflected by `retTy`.
2722   CC genProcRef(const Fortran::evaluate::ProcedureRef &procRef,
2723                 llvm::Optional<mlir::Type> retTy) {
2724     mlir::Location loc = getLoc();
2725     if (procRef.IsElemental()) {
2726       if (const Fortran::evaluate::SpecificIntrinsic *intrin =
2727               procRef.proc().GetSpecificIntrinsic()) {
2728         // All elemental intrinsic functions are pure and cannot modify their
2729         // arguments. The only elemental subroutine, MVBITS has an Intent(inout)
2730         // argument. So for this last one, loops must be in element order
2731         // according to 15.8.3 p1.
2732         if (!retTy)
2733           setUnordered(false);
2734 
2735         // Elemental intrinsic call.
2736         // The intrinsic procedure is called once per element of the array.
2737         return genElementalIntrinsicProcRef(procRef, retTy, *intrin);
2738       }
2739       if (ScalarExprLowering::isStatementFunctionCall(procRef))
2740         fir::emitFatalError(loc, "statement function cannot be elemental");
2741 
2742       TODO(loc, "elemental user defined proc ref");
2743     }
2744 
2745     // Transformational call.
2746     // The procedure is called once and produces a value of rank > 0.
2747     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
2748             procRef.proc().GetSpecificIntrinsic()) {
2749       if (explicitSpaceIsActive() && procRef.Rank() == 0) {
2750         // Elide any implicit loop iters.
2751         return [=, &procRef](IterSpace) {
2752           return ScalarExprLowering{loc, converter, symMap, stmtCtx}
2753               .genIntrinsicRef(procRef, *intrinsic, retTy);
2754         };
2755       }
2756       return genarr(
2757           ScalarExprLowering{loc, converter, symMap, stmtCtx}.genIntrinsicRef(
2758               procRef, *intrinsic, retTy));
2759     }
2760 
2761     if (explicitSpaceIsActive() && procRef.Rank() == 0) {
2762       // Elide any implicit loop iters.
2763       return [=, &procRef](IterSpace) {
2764         return ScalarExprLowering{loc, converter, symMap, stmtCtx}
2765             .genProcedureRef(procRef, retTy);
2766       };
2767     }
2768     // In the default case, the call can be hoisted out of the loop nest. Apply
2769     // the iterations to the result, which may be an array value.
2770     return genarr(
2771         ScalarExprLowering{loc, converter, symMap, stmtCtx}.genProcedureRef(
2772             procRef, retTy));
2773   }
2774 
2775   template <typename A>
2776   CC genScalarAndForwardValue(const A &x) {
2777     ExtValue result = asScalar(x);
2778     return [=](IterSpace) { return result; };
2779   }
2780 
2781   template <typename A, typename = std::enable_if_t<Fortran::common::HasMember<
2782                             A, Fortran::evaluate::TypelessExpression>>>
2783   CC genarr(const A &x) {
2784     return genScalarAndForwardValue(x);
2785   }
2786 
2787   template <typename A>
2788   CC genarr(const Fortran::evaluate::Expr<A> &x) {
2789     LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x));
2790     if (isArray(x) || explicitSpaceIsActive() ||
2791         isElementalProcWithArrayArgs(x))
2792       return std::visit([&](const auto &e) { return genarr(e); }, x.u);
2793     return genScalarAndForwardValue(x);
2794   }
2795 
2796   template <Fortran::common::TypeCategory TC1, int KIND,
2797             Fortran::common::TypeCategory TC2>
2798   CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
2799                                              TC2> &x) {
2800     TODO(getLoc(), "");
2801   }
2802 
2803   template <int KIND>
2804   CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) {
2805     TODO(getLoc(), "");
2806   }
2807 
2808   template <typename T>
2809   CC genarr(const Fortran::evaluate::Parentheses<T> &x) {
2810     TODO(getLoc(), "");
2811   }
2812 
2813   template <int KIND>
2814   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
2815                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
2816     TODO(getLoc(), "");
2817   }
2818 
2819   template <int KIND>
2820   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
2821                 Fortran::common::TypeCategory::Real, KIND>> &x) {
2822     TODO(getLoc(), "");
2823   }
2824   template <int KIND>
2825   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
2826                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
2827     TODO(getLoc(), "");
2828   }
2829 
2830   //===--------------------------------------------------------------------===//
2831   // Binary elemental ops
2832   //===--------------------------------------------------------------------===//
2833 
2834   template <typename OP, typename A>
2835   CC createBinaryOp(const A &evEx) {
2836     mlir::Location loc = getLoc();
2837     auto lambda = genarr(evEx.left());
2838     auto rf = genarr(evEx.right());
2839     return [=](IterSpace iters) -> ExtValue {
2840       mlir::Value left = fir::getBase(lambda(iters));
2841       mlir::Value right = fir::getBase(rf(iters));
2842       return builder.create<OP>(loc, left, right);
2843     };
2844   }
2845 
2846 #undef GENBIN
2847 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
2848   template <int KIND>                                                          \
2849   CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type<       \
2850                 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) {       \
2851     return createBinaryOp<GenBinFirOp>(x);                                     \
2852   }
2853 
2854   GENBIN(Add, Integer, mlir::arith::AddIOp)
2855   GENBIN(Add, Real, mlir::arith::AddFOp)
2856   GENBIN(Add, Complex, fir::AddcOp)
2857   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
2858   GENBIN(Subtract, Real, mlir::arith::SubFOp)
2859   GENBIN(Subtract, Complex, fir::SubcOp)
2860   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
2861   GENBIN(Multiply, Real, mlir::arith::MulFOp)
2862   GENBIN(Multiply, Complex, fir::MulcOp)
2863   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
2864   GENBIN(Divide, Real, mlir::arith::DivFOp)
2865   GENBIN(Divide, Complex, fir::DivcOp)
2866 
2867   template <Fortran::common::TypeCategory TC, int KIND>
2868   CC genarr(
2869       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
2870     TODO(getLoc(), "genarr ");
2871   }
2872   template <Fortran::common::TypeCategory TC, int KIND>
2873   CC genarr(
2874       const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
2875     TODO(getLoc(), "genarr ");
2876   }
2877   template <Fortran::common::TypeCategory TC, int KIND>
2878   CC genarr(
2879       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
2880           &x) {
2881     TODO(getLoc(), "genarr ");
2882   }
2883   template <int KIND>
2884   CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
2885     TODO(getLoc(), "genarr ");
2886   }
2887 
2888   template <int KIND>
2889   CC genarr(const Fortran::evaluate::Concat<KIND> &x) {
2890     TODO(getLoc(), "genarr ");
2891   }
2892 
2893   template <int KIND>
2894   CC genarr(const Fortran::evaluate::SetLength<KIND> &x) {
2895     TODO(getLoc(), "genarr ");
2896   }
2897 
2898   template <typename A>
2899   CC genarr(const Fortran::evaluate::Constant<A> &x) {
2900     TODO(getLoc(), "genarr ");
2901   }
2902 
2903   CC genarr(const Fortran::semantics::SymbolRef &sym,
2904             ComponentPath &components) {
2905     return genarr(sym.get(), components);
2906   }
2907 
2908   ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) {
2909     return convertToArrayBoxValue(getLoc(), builder, val, len);
2910   }
2911 
2912   CC genarr(const ExtValue &extMemref) {
2913     ComponentPath dummy(/*isImplicit=*/true);
2914     return genarr(extMemref, dummy);
2915   }
2916 
2917   //===--------------------------------------------------------------------===//
2918   // Array construction
2919   //===--------------------------------------------------------------------===//
2920 
2921   /// Target agnostic computation of the size of an element in the array.
2922   /// Returns the size in bytes with type `index` or a null Value if the element
2923   /// size is not constant.
2924   mlir::Value computeElementSize(const ExtValue &exv, mlir::Type eleTy,
2925                                  mlir::Type resTy) {
2926     mlir::Location loc = getLoc();
2927     mlir::IndexType idxTy = builder.getIndexType();
2928     mlir::Value multiplier = builder.createIntegerConstant(loc, idxTy, 1);
2929     if (fir::hasDynamicSize(eleTy)) {
2930       if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
2931         // Array of char with dynamic length parameter. Downcast to an array
2932         // of singleton char, and scale by the len type parameter from
2933         // `exv`.
2934         exv.match(
2935             [&](const fir::CharBoxValue &cb) { multiplier = cb.getLen(); },
2936             [&](const fir::CharArrayBoxValue &cb) { multiplier = cb.getLen(); },
2937             [&](const fir::BoxValue &box) {
2938               multiplier = fir::factory::CharacterExprHelper(builder, loc)
2939                                .readLengthFromBox(box.getAddr());
2940             },
2941             [&](const fir::MutableBoxValue &box) {
2942               multiplier = fir::factory::CharacterExprHelper(builder, loc)
2943                                .readLengthFromBox(box.getAddr());
2944             },
2945             [&](const auto &) {
2946               fir::emitFatalError(loc,
2947                                   "array constructor element has unknown size");
2948             });
2949         fir::CharacterType newEleTy = fir::CharacterType::getSingleton(
2950             eleTy.getContext(), charTy.getFKind());
2951         if (auto seqTy = resTy.dyn_cast<fir::SequenceType>()) {
2952           assert(eleTy == seqTy.getEleTy());
2953           resTy = fir::SequenceType::get(seqTy.getShape(), newEleTy);
2954         }
2955         eleTy = newEleTy;
2956       } else {
2957         TODO(loc, "dynamic sized type");
2958       }
2959     }
2960     mlir::Type eleRefTy = builder.getRefType(eleTy);
2961     mlir::Type resRefTy = builder.getRefType(resTy);
2962     mlir::Value nullPtr = builder.createNullConstant(loc, resRefTy);
2963     auto offset = builder.create<fir::CoordinateOp>(
2964         loc, eleRefTy, nullPtr, mlir::ValueRange{multiplier});
2965     return builder.createConvert(loc, idxTy, offset);
2966   }
2967 
2968   /// Get the function signature of the LLVM memcpy intrinsic.
2969   mlir::FunctionType memcpyType() {
2970     return fir::factory::getLlvmMemcpy(builder).getType();
2971   }
2972 
2973   /// Create a call to the LLVM memcpy intrinsic.
2974   void createCallMemcpy(llvm::ArrayRef<mlir::Value> args) {
2975     mlir::Location loc = getLoc();
2976     mlir::FuncOp memcpyFunc = fir::factory::getLlvmMemcpy(builder);
2977     mlir::SymbolRefAttr funcSymAttr =
2978         builder.getSymbolRefAttr(memcpyFunc.getName());
2979     mlir::FunctionType funcTy = memcpyFunc.getType();
2980     builder.create<fir::CallOp>(loc, funcTy.getResults(), funcSymAttr, args);
2981   }
2982 
2983   // Construct code to check for a buffer overrun and realloc the buffer when
2984   // space is depleted. This is done between each item in the ac-value-list.
2985   mlir::Value growBuffer(mlir::Value mem, mlir::Value needed,
2986                          mlir::Value bufferSize, mlir::Value buffSize,
2987                          mlir::Value eleSz) {
2988     mlir::Location loc = getLoc();
2989     mlir::FuncOp reallocFunc = fir::factory::getRealloc(builder);
2990     auto cond = builder.create<mlir::arith::CmpIOp>(
2991         loc, mlir::arith::CmpIPredicate::sle, bufferSize, needed);
2992     auto ifOp = builder.create<fir::IfOp>(loc, mem.getType(), cond,
2993                                           /*withElseRegion=*/true);
2994     auto insPt = builder.saveInsertionPoint();
2995     builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2996     // Not enough space, resize the buffer.
2997     mlir::IndexType idxTy = builder.getIndexType();
2998     mlir::Value two = builder.createIntegerConstant(loc, idxTy, 2);
2999     auto newSz = builder.create<mlir::arith::MulIOp>(loc, needed, two);
3000     builder.create<fir::StoreOp>(loc, newSz, buffSize);
3001     mlir::Value byteSz = builder.create<mlir::arith::MulIOp>(loc, newSz, eleSz);
3002     mlir::SymbolRefAttr funcSymAttr =
3003         builder.getSymbolRefAttr(reallocFunc.getName());
3004     mlir::FunctionType funcTy = reallocFunc.getType();
3005     auto newMem = builder.create<fir::CallOp>(
3006         loc, funcTy.getResults(), funcSymAttr,
3007         llvm::ArrayRef<mlir::Value>{
3008             builder.createConvert(loc, funcTy.getInputs()[0], mem),
3009             builder.createConvert(loc, funcTy.getInputs()[1], byteSz)});
3010     mlir::Value castNewMem =
3011         builder.createConvert(loc, mem.getType(), newMem.getResult(0));
3012     builder.create<fir::ResultOp>(loc, castNewMem);
3013     builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
3014     // Otherwise, just forward the buffer.
3015     builder.create<fir::ResultOp>(loc, mem);
3016     builder.restoreInsertionPoint(insPt);
3017     return ifOp.getResult(0);
3018   }
3019 
3020   /// Copy the next value (or vector of values) into the array being
3021   /// constructed.
3022   mlir::Value copyNextArrayCtorSection(const ExtValue &exv, mlir::Value buffPos,
3023                                        mlir::Value buffSize, mlir::Value mem,
3024                                        mlir::Value eleSz, mlir::Type eleTy,
3025                                        mlir::Type eleRefTy, mlir::Type resTy) {
3026     mlir::Location loc = getLoc();
3027     auto off = builder.create<fir::LoadOp>(loc, buffPos);
3028     auto limit = builder.create<fir::LoadOp>(loc, buffSize);
3029     mlir::IndexType idxTy = builder.getIndexType();
3030     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
3031 
3032     if (fir::isRecordWithAllocatableMember(eleTy))
3033       TODO(loc, "deep copy on allocatable members");
3034 
3035     if (!eleSz) {
3036       // Compute the element size at runtime.
3037       assert(fir::hasDynamicSize(eleTy));
3038       if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
3039         auto charBytes =
3040             builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8;
3041         mlir::Value bytes =
3042             builder.createIntegerConstant(loc, idxTy, charBytes);
3043         mlir::Value length = fir::getLen(exv);
3044         if (!length)
3045           fir::emitFatalError(loc, "result is not boxed character");
3046         eleSz = builder.create<mlir::arith::MulIOp>(loc, bytes, length);
3047       } else {
3048         TODO(loc, "PDT size");
3049         // Will call the PDT's size function with the type parameters.
3050       }
3051     }
3052 
3053     // Compute the coordinate using `fir.coordinate_of`, or, if the type has
3054     // dynamic size, generating the pointer arithmetic.
3055     auto computeCoordinate = [&](mlir::Value buff, mlir::Value off) {
3056       mlir::Type refTy = eleRefTy;
3057       if (fir::hasDynamicSize(eleTy)) {
3058         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
3059           // Scale a simple pointer using dynamic length and offset values.
3060           auto chTy = fir::CharacterType::getSingleton(charTy.getContext(),
3061                                                        charTy.getFKind());
3062           refTy = builder.getRefType(chTy);
3063           mlir::Type toTy = builder.getRefType(builder.getVarLenSeqTy(chTy));
3064           buff = builder.createConvert(loc, toTy, buff);
3065           off = builder.create<mlir::arith::MulIOp>(loc, off, eleSz);
3066         } else {
3067           TODO(loc, "PDT offset");
3068         }
3069       }
3070       auto coor = builder.create<fir::CoordinateOp>(loc, refTy, buff,
3071                                                     mlir::ValueRange{off});
3072       return builder.createConvert(loc, eleRefTy, coor);
3073     };
3074 
3075     // Lambda to lower an abstract array box value.
3076     auto doAbstractArray = [&](const auto &v) {
3077       // Compute the array size.
3078       mlir::Value arrSz = one;
3079       for (auto ext : v.getExtents())
3080         arrSz = builder.create<mlir::arith::MulIOp>(loc, arrSz, ext);
3081 
3082       // Grow the buffer as needed.
3083       auto endOff = builder.create<mlir::arith::AddIOp>(loc, off, arrSz);
3084       mem = growBuffer(mem, endOff, limit, buffSize, eleSz);
3085 
3086       // Copy the elements to the buffer.
3087       mlir::Value byteSz =
3088           builder.create<mlir::arith::MulIOp>(loc, arrSz, eleSz);
3089       auto buff = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
3090       mlir::Value buffi = computeCoordinate(buff, off);
3091       llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
3092           builder, loc, memcpyType(), buffi, v.getAddr(), byteSz,
3093           /*volatile=*/builder.createBool(loc, false));
3094       createCallMemcpy(args);
3095 
3096       // Save the incremented buffer position.
3097       builder.create<fir::StoreOp>(loc, endOff, buffPos);
3098     };
3099 
3100     // Copy a trivial scalar value into the buffer.
3101     auto doTrivialScalar = [&](const ExtValue &v, mlir::Value len = {}) {
3102       // Increment the buffer position.
3103       auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
3104 
3105       // Grow the buffer as needed.
3106       mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
3107 
3108       // Store the element in the buffer.
3109       mlir::Value buff =
3110           builder.createConvert(loc, fir::HeapType::get(resTy), mem);
3111       auto buffi = builder.create<fir::CoordinateOp>(loc, eleRefTy, buff,
3112                                                      mlir::ValueRange{off});
3113       fir::factory::genScalarAssignment(
3114           builder, loc,
3115           [&]() -> ExtValue {
3116             if (len)
3117               return fir::CharBoxValue(buffi, len);
3118             return buffi;
3119           }(),
3120           v);
3121       builder.create<fir::StoreOp>(loc, plusOne, buffPos);
3122     };
3123 
3124     // Copy the value.
3125     exv.match(
3126         [&](mlir::Value) { doTrivialScalar(exv); },
3127         [&](const fir::CharBoxValue &v) {
3128           auto buffer = v.getBuffer();
3129           if (fir::isa_char(buffer.getType())) {
3130             doTrivialScalar(exv, eleSz);
3131           } else {
3132             // Increment the buffer position.
3133             auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
3134 
3135             // Grow the buffer as needed.
3136             mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
3137 
3138             // Store the element in the buffer.
3139             mlir::Value buff =
3140                 builder.createConvert(loc, fir::HeapType::get(resTy), mem);
3141             mlir::Value buffi = computeCoordinate(buff, off);
3142             llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
3143                 builder, loc, memcpyType(), buffi, v.getAddr(), eleSz,
3144                 /*volatile=*/builder.createBool(loc, false));
3145             createCallMemcpy(args);
3146 
3147             builder.create<fir::StoreOp>(loc, plusOne, buffPos);
3148           }
3149         },
3150         [&](const fir::ArrayBoxValue &v) { doAbstractArray(v); },
3151         [&](const fir::CharArrayBoxValue &v) { doAbstractArray(v); },
3152         [&](const auto &) {
3153           TODO(loc, "unhandled array constructor expression");
3154         });
3155     return mem;
3156   }
3157 
3158   // Lower the expr cases in an ac-value-list.
3159   template <typename A>
3160   std::pair<ExtValue, bool>
3161   genArrayCtorInitializer(const Fortran::evaluate::Expr<A> &x, mlir::Type,
3162                           mlir::Value, mlir::Value, mlir::Value,
3163                           Fortran::lower::StatementContext &stmtCtx) {
3164     if (isArray(x))
3165       return {lowerNewArrayExpression(converter, symMap, stmtCtx, toEvExpr(x)),
3166               /*needCopy=*/true};
3167     return {asScalar(x), /*needCopy=*/true};
3168   }
3169 
3170   // Lower an ac-implied-do in an ac-value-list.
3171   template <typename A>
3172   std::pair<ExtValue, bool>
3173   genArrayCtorInitializer(const Fortran::evaluate::ImpliedDo<A> &x,
3174                           mlir::Type resTy, mlir::Value mem,
3175                           mlir::Value buffPos, mlir::Value buffSize,
3176                           Fortran::lower::StatementContext &) {
3177     mlir::Location loc = getLoc();
3178     mlir::IndexType idxTy = builder.getIndexType();
3179     mlir::Value lo =
3180         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.lower())));
3181     mlir::Value up =
3182         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.upper())));
3183     mlir::Value step =
3184         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.stride())));
3185     auto seqTy = resTy.template cast<fir::SequenceType>();
3186     mlir::Type eleTy = fir::unwrapSequenceType(seqTy);
3187     auto loop =
3188         builder.create<fir::DoLoopOp>(loc, lo, up, step, /*unordered=*/false,
3189                                       /*finalCount=*/false, mem);
3190     // create a new binding for x.name(), to ac-do-variable, to the iteration
3191     // value.
3192     symMap.pushImpliedDoBinding(toStringRef(x.name()), loop.getInductionVar());
3193     auto insPt = builder.saveInsertionPoint();
3194     builder.setInsertionPointToStart(loop.getBody());
3195     // Thread mem inside the loop via loop argument.
3196     mem = loop.getRegionIterArgs()[0];
3197 
3198     mlir::Type eleRefTy = builder.getRefType(eleTy);
3199 
3200     // Any temps created in the loop body must be freed inside the loop body.
3201     stmtCtx.pushScope();
3202     llvm::Optional<mlir::Value> charLen;
3203     for (const Fortran::evaluate::ArrayConstructorValue<A> &acv : x.values()) {
3204       auto [exv, copyNeeded] = std::visit(
3205           [&](const auto &v) {
3206             return genArrayCtorInitializer(v, resTy, mem, buffPos, buffSize,
3207                                            stmtCtx);
3208           },
3209           acv.u);
3210       mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
3211       mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
3212                                                   eleSz, eleTy, eleRefTy, resTy)
3213                        : fir::getBase(exv);
3214       if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) {
3215         charLen = builder.createTemporary(loc, builder.getI64Type());
3216         mlir::Value castLen =
3217             builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
3218         builder.create<fir::StoreOp>(loc, castLen, charLen.getValue());
3219       }
3220     }
3221     stmtCtx.finalize(/*popScope=*/true);
3222 
3223     builder.create<fir::ResultOp>(loc, mem);
3224     builder.restoreInsertionPoint(insPt);
3225     mem = loop.getResult(0);
3226     symMap.popImpliedDoBinding();
3227     llvm::SmallVector<mlir::Value> extents = {
3228         builder.create<fir::LoadOp>(loc, buffPos).getResult()};
3229 
3230     // Convert to extended value.
3231     if (fir::isa_char(seqTy.getEleTy())) {
3232       auto len = builder.create<fir::LoadOp>(loc, charLen.getValue());
3233       return {fir::CharArrayBoxValue{mem, len, extents}, /*needCopy=*/false};
3234     }
3235     return {fir::ArrayBoxValue{mem, extents}, /*needCopy=*/false};
3236   }
3237 
3238   // To simplify the handling and interaction between the various cases, array
3239   // constructors are always lowered to the incremental construction code
3240   // pattern, even if the extent of the array value is constant. After the
3241   // MemToReg pass and constant folding, the optimizer should be able to
3242   // determine that all the buffer overrun tests are false when the
3243   // incremental construction wasn't actually required.
3244   template <typename A>
3245   CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) {
3246     mlir::Location loc = getLoc();
3247     auto evExpr = toEvExpr(x);
3248     mlir::Type resTy = translateSomeExprToFIRType(converter, evExpr);
3249     mlir::IndexType idxTy = builder.getIndexType();
3250     auto seqTy = resTy.template cast<fir::SequenceType>();
3251     mlir::Type eleTy = fir::unwrapSequenceType(resTy);
3252     mlir::Value buffSize = builder.createTemporary(loc, idxTy, ".buff.size");
3253     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
3254     mlir::Value buffPos = builder.createTemporary(loc, idxTy, ".buff.pos");
3255     builder.create<fir::StoreOp>(loc, zero, buffPos);
3256     // Allocate space for the array to be constructed.
3257     mlir::Value mem;
3258     if (fir::hasDynamicSize(resTy)) {
3259       if (fir::hasDynamicSize(eleTy)) {
3260         // The size of each element may depend on a general expression. Defer
3261         // creating the buffer until after the expression is evaluated.
3262         mem = builder.createNullConstant(loc, builder.getRefType(eleTy));
3263         builder.create<fir::StoreOp>(loc, zero, buffSize);
3264       } else {
3265         mlir::Value initBuffSz =
3266             builder.createIntegerConstant(loc, idxTy, clInitialBufferSize);
3267         mem = builder.create<fir::AllocMemOp>(
3268             loc, eleTy, /*typeparams=*/llvm::None, initBuffSz);
3269         builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
3270       }
3271     } else {
3272       mem = builder.create<fir::AllocMemOp>(loc, resTy);
3273       int64_t buffSz = 1;
3274       for (auto extent : seqTy.getShape())
3275         buffSz *= extent;
3276       mlir::Value initBuffSz =
3277           builder.createIntegerConstant(loc, idxTy, buffSz);
3278       builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
3279     }
3280     // Compute size of element
3281     mlir::Type eleRefTy = builder.getRefType(eleTy);
3282 
3283     // Populate the buffer with the elements, growing as necessary.
3284     llvm::Optional<mlir::Value> charLen;
3285     for (const auto &expr : x) {
3286       auto [exv, copyNeeded] = std::visit(
3287           [&](const auto &e) {
3288             return genArrayCtorInitializer(e, resTy, mem, buffPos, buffSize,
3289                                            stmtCtx);
3290           },
3291           expr.u);
3292       mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
3293       mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
3294                                                   eleSz, eleTy, eleRefTy, resTy)
3295                        : fir::getBase(exv);
3296       if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) {
3297         charLen = builder.createTemporary(loc, builder.getI64Type());
3298         mlir::Value castLen =
3299             builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
3300         builder.create<fir::StoreOp>(loc, castLen, charLen.getValue());
3301       }
3302     }
3303     mem = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
3304     llvm::SmallVector<mlir::Value> extents = {
3305         builder.create<fir::LoadOp>(loc, buffPos)};
3306 
3307     // Cleanup the temporary.
3308     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
3309     stmtCtx.attachCleanup(
3310         [bldr, loc, mem]() { bldr->create<fir::FreeMemOp>(loc, mem); });
3311 
3312     // Return the continuation.
3313     if (fir::isa_char(seqTy.getEleTy())) {
3314       if (charLen.hasValue()) {
3315         auto len = builder.create<fir::LoadOp>(loc, charLen.getValue());
3316         return genarr(fir::CharArrayBoxValue{mem, len, extents});
3317       }
3318       return genarr(fir::CharArrayBoxValue{mem, zero, extents});
3319     }
3320     return genarr(fir::ArrayBoxValue{mem, extents});
3321   }
3322 
3323   CC genarr(const Fortran::evaluate::ImpliedDoIndex &) {
3324     TODO(getLoc(), "genarr ImpliedDoIndex");
3325   }
3326 
3327   CC genarr(const Fortran::evaluate::TypeParamInquiry &x) {
3328     TODO(getLoc(), "genarr TypeParamInquiry");
3329   }
3330 
3331   CC genarr(const Fortran::evaluate::DescriptorInquiry &x) {
3332     TODO(getLoc(), "genarr DescriptorInquiry");
3333   }
3334 
3335   CC genarr(const Fortran::evaluate::StructureConstructor &x) {
3336     TODO(getLoc(), "genarr StructureConstructor");
3337   }
3338 
3339   template <int KIND>
3340   CC genarr(const Fortran::evaluate::Not<KIND> &x) {
3341     TODO(getLoc(), "genarr Not");
3342   }
3343 
3344   template <int KIND>
3345   CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) {
3346     TODO(getLoc(), "genarr LogicalOperation");
3347   }
3348 
3349   template <int KIND>
3350   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
3351                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
3352     TODO(getLoc(), "genarr Relational Integer");
3353   }
3354   template <int KIND>
3355   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
3356                 Fortran::common::TypeCategory::Character, KIND>> &x) {
3357     TODO(getLoc(), "genarr Relational Character");
3358   }
3359   template <int KIND>
3360   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
3361                 Fortran::common::TypeCategory::Real, KIND>> &x) {
3362     TODO(getLoc(), "genarr Relational Real");
3363   }
3364   template <int KIND>
3365   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
3366                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
3367     TODO(getLoc(), "genarr Relational Complex");
3368   }
3369   CC genarr(
3370       const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) {
3371     TODO(getLoc(), "genarr Relational SomeType");
3372   }
3373 
3374   template <typename A>
3375   CC genarr(const Fortran::evaluate::Designator<A> &des) {
3376     ComponentPath components(des.Rank() > 0);
3377     return std::visit([&](const auto &x) { return genarr(x, components); },
3378                       des.u);
3379   }
3380 
3381   template <typename T>
3382   CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) {
3383     // Note that it's possible that the function being called returns either an
3384     // array or a scalar.  In the first case, use the element type of the array.
3385     return genProcRef(
3386         funRef, fir::unwrapSequenceType(converter.genType(toEvExpr(funRef))));
3387   }
3388 
3389   template <typename A>
3390   CC genImplicitArrayAccess(const A &x, ComponentPath &components) {
3391     components.reversePath.push_back(ImplicitSubscripts{});
3392     ExtValue exv = asScalarRef(x);
3393     // lowerPath(exv, components);
3394     auto lambda = genarr(exv, components);
3395     return [=](IterSpace iters) { return lambda(components.pc(iters)); };
3396   }
3397   CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x,
3398                             ComponentPath &components) {
3399     if (x.IsSymbol())
3400       return genImplicitArrayAccess(x.GetFirstSymbol(), components);
3401     return genImplicitArrayAccess(x.GetComponent(), components);
3402   }
3403 
3404   template <typename A>
3405   CC genAsScalar(const A &x) {
3406     mlir::Location loc = getLoc();
3407     if (isProjectedCopyInCopyOut()) {
3408       return [=, &x, builder = &converter.getFirOpBuilder()](
3409                  IterSpace iters) -> ExtValue {
3410         ExtValue exv = asScalarRef(x);
3411         mlir::Value val = fir::getBase(exv);
3412         mlir::Type eleTy = fir::unwrapRefType(val.getType());
3413         if (isAdjustedArrayElementType(eleTy)) {
3414           if (fir::isa_char(eleTy)) {
3415             TODO(getLoc(), "assignment of character type");
3416           } else if (fir::isa_derived(eleTy)) {
3417             TODO(loc, "assignment of derived type");
3418           } else {
3419             fir::emitFatalError(loc, "array type not expected in scalar");
3420           }
3421         } else {
3422           builder->create<fir::StoreOp>(loc, iters.getElement(), val);
3423         }
3424         return exv;
3425       };
3426     }
3427     return [=, &x](IterSpace) { return asScalar(x); };
3428   }
3429 
3430   CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) {
3431     if (explicitSpaceIsActive()) {
3432       TODO(getLoc(), "genarr Symbol explicitSpace");
3433     } else {
3434       return genImplicitArrayAccess(x, components);
3435     }
3436   }
3437 
3438   CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) {
3439     TODO(getLoc(), "genarr Component");
3440   }
3441 
3442   /// Array reference with subscripts. If this has rank > 0, this is a form
3443   /// of an array section (slice).
3444   ///
3445   /// There are two "slicing" primitives that may be applied on a dimension by
3446   /// dimension basis: (1) triple notation and (2) vector addressing. Since
3447   /// dimensions can be selectively sliced, some dimensions may contain
3448   /// regular scalar expressions and those dimensions do not participate in
3449   /// the array expression evaluation.
3450   CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) {
3451     if (explicitSpaceIsActive()) {
3452       TODO(getLoc(), "genarr ArrayRef explicitSpace");
3453     } else {
3454       if (Fortran::lower::isRankedArrayAccess(x)) {
3455         components.reversePath.push_back(&x);
3456         return genImplicitArrayAccess(x.base(), components);
3457       }
3458     }
3459     bool atEnd = pathIsEmpty(components);
3460     components.reversePath.push_back(&x);
3461     auto result = genarr(x.base(), components);
3462     if (components.applied)
3463       return result;
3464     mlir::Location loc = getLoc();
3465     if (atEnd) {
3466       if (x.Rank() == 0)
3467         return genAsScalar(x);
3468       fir::emitFatalError(loc, "expected scalar");
3469     }
3470     return [=](IterSpace) -> ExtValue {
3471       fir::emitFatalError(loc, "reached arrayref with path");
3472     };
3473   }
3474 
3475   CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) {
3476     TODO(getLoc(), "coarray reference");
3477   }
3478 
3479   CC genarr(const Fortran::evaluate::NamedEntity &x,
3480             ComponentPath &components) {
3481     return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components)
3482                         : genarr(x.GetComponent(), components);
3483   }
3484 
3485   CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) {
3486     return std::visit([&](const auto &v) { return genarr(v, components); },
3487                       x.u);
3488   }
3489 
3490   bool pathIsEmpty(const ComponentPath &components) {
3491     return components.reversePath.empty();
3492   }
3493 
3494   /// Given an optional fir.box, returns an fir.box that is the original one if
3495   /// it is present and it otherwise an unallocated box.
3496   /// Absent fir.box are implemented as a null pointer descriptor. Generated
3497   /// code may need to unconditionally read a fir.box that can be absent.
3498   /// This helper allows creating a fir.box that can be read in all cases
3499   /// outside of a fir.if (isPresent) region. However, the usages of the value
3500   /// read from such box should still only be done in a fir.if(isPresent).
3501   static fir::ExtendedValue
3502   absentBoxToUnalllocatedBox(fir::FirOpBuilder &builder, mlir::Location loc,
3503                              const fir::ExtendedValue &exv,
3504                              mlir::Value isPresent) {
3505     mlir::Value box = fir::getBase(exv);
3506     mlir::Type boxType = box.getType();
3507     assert(boxType.isa<fir::BoxType>() && "argument must be a fir.box");
3508     mlir::Value emptyBox =
3509         fir::factory::createUnallocatedBox(builder, loc, boxType, llvm::None);
3510     auto safeToReadBox =
3511         builder.create<mlir::arith::SelectOp>(loc, isPresent, box, emptyBox);
3512     return fir::substBase(exv, safeToReadBox);
3513   }
3514 
3515   std::tuple<CC, mlir::Value, mlir::Type>
3516   genOptionalArrayFetch(const Fortran::lower::SomeExpr &expr) {
3517     assert(expr.Rank() > 0 && "expr must be an array");
3518     mlir::Location loc = getLoc();
3519     ExtValue optionalArg = asInquired(expr);
3520     mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
3521     // Generate an array load and access to an array that may be an absent
3522     // optional or an unallocated optional.
3523     mlir::Value base = getBase(optionalArg);
3524     const bool hasOptionalAttr =
3525         fir::valueHasFirAttribute(base, fir::getOptionalAttrName());
3526     mlir::Type baseType = fir::unwrapRefType(base.getType());
3527     const bool isBox = baseType.isa<fir::BoxType>();
3528     const bool isAllocOrPtr = Fortran::evaluate::IsAllocatableOrPointerObject(
3529         expr, converter.getFoldingContext());
3530     mlir::Type arrType = fir::unwrapPassByRefType(baseType);
3531     mlir::Type eleType = fir::unwrapSequenceType(arrType);
3532     ExtValue exv = optionalArg;
3533     if (hasOptionalAttr && isBox && !isAllocOrPtr) {
3534       // Elemental argument cannot be allocatable or pointers (C15100).
3535       // Hence, per 15.5.2.12 3 (8) and (9), the provided Allocatable and
3536       // Pointer optional arrays cannot be absent. The only kind of entities
3537       // that can get here are optional assumed shape and polymorphic entities.
3538       exv = absentBoxToUnalllocatedBox(builder, loc, exv, isPresent);
3539     }
3540     // All the properties can be read from any fir.box but the read values may
3541     // be undefined and should only be used inside a fir.if (canBeRead) region.
3542     if (const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>())
3543       exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox);
3544 
3545     mlir::Value memref = fir::getBase(exv);
3546     mlir::Value shape = builder.createShape(loc, exv);
3547     mlir::Value noSlice;
3548     auto arrLoad = builder.create<fir::ArrayLoadOp>(
3549         loc, arrType, memref, shape, noSlice, fir::getTypeParams(exv));
3550     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
3551     mlir::Value arrLd = arrLoad.getResult();
3552     // Mark the load to tell later passes it is unsafe to use this array_load
3553     // shape unconditionally.
3554     arrLoad->setAttr(fir::getOptionalAttrName(), builder.getUnitAttr());
3555 
3556     // Place the array as optional on the arrayOperands stack so that its
3557     // shape will only be used as a fallback to induce the implicit loop nest
3558     // (that is if there is no non optional array arguments).
3559     arrayOperands.push_back(
3560         ArrayOperand{memref, shape, noSlice, /*mayBeAbsent=*/true});
3561 
3562     // By value semantics.
3563     auto cc = [=](IterSpace iters) -> ExtValue {
3564       auto arrFetch = builder.create<fir::ArrayFetchOp>(
3565           loc, eleType, arrLd, iters.iterVec(), arrLdTypeParams);
3566       return fir::factory::arraySectionElementToExtendedValue(
3567           builder, loc, exv, arrFetch, noSlice);
3568     };
3569     return {cc, isPresent, eleType};
3570   }
3571 
3572   /// Generate a continuation to pass \p expr to an OPTIONAL argument of an
3573   /// elemental procedure. This is meant to handle the cases where \p expr might
3574   /// be dynamically absent (i.e. when it is a POINTER, an ALLOCATABLE or an
3575   /// OPTIONAL variable). If p\ expr is guaranteed to be present genarr() can
3576   /// directly be called instead.
3577   CC genarrForwardOptionalArgumentToCall(const Fortran::lower::SomeExpr &expr) {
3578     mlir::Location loc = getLoc();
3579     // Only by-value numerical and logical so far.
3580     if (semant != ConstituentSemantics::RefTransparent)
3581       TODO(loc, "optional arguments in user defined elemental procedures");
3582 
3583     // Handle scalar argument case (the if-then-else is generated outside of the
3584     // implicit loop nest).
3585     if (expr.Rank() == 0) {
3586       ExtValue optionalArg = asInquired(expr);
3587       mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
3588       mlir::Value elementValue =
3589           fir::getBase(genOptionalValue(builder, loc, optionalArg, isPresent));
3590       return [=](IterSpace iters) -> ExtValue { return elementValue; };
3591     }
3592 
3593     CC cc;
3594     mlir::Value isPresent;
3595     mlir::Type eleType;
3596     std::tie(cc, isPresent, eleType) = genOptionalArrayFetch(expr);
3597     return [=](IterSpace iters) -> ExtValue {
3598       mlir::Value elementValue =
3599           builder
3600               .genIfOp(loc, {eleType}, isPresent,
3601                        /*withElseRegion=*/true)
3602               .genThen([&]() {
3603                 builder.create<fir::ResultOp>(loc, fir::getBase(cc(iters)));
3604               })
3605               .genElse([&]() {
3606                 mlir::Value zero =
3607                     fir::factory::createZeroValue(builder, loc, eleType);
3608                 builder.create<fir::ResultOp>(loc, zero);
3609               })
3610               .getResults()[0];
3611       return elementValue;
3612     };
3613   }
3614 
3615   CC genarr(const Fortran::evaluate::ComplexPart &x,
3616             ComponentPath &components) {
3617     TODO(getLoc(), "genarr ComplexPart");
3618   }
3619 
3620   CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &,
3621             ComponentPath &components) {
3622     TODO(getLoc(), "genarr StaticDataObject::Pointer");
3623   }
3624 
3625   /// Substrings (see 9.4.1)
3626   CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) {
3627     TODO(getLoc(), "genarr Substring");
3628   }
3629 
3630   /// Base case of generating an array reference,
3631   CC genarr(const ExtValue &extMemref, ComponentPath &components) {
3632     mlir::Location loc = getLoc();
3633     mlir::Value memref = fir::getBase(extMemref);
3634     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType());
3635     assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array");
3636     mlir::Value shape = builder.createShape(loc, extMemref);
3637     mlir::Value slice;
3638     if (components.isSlice()) {
3639       TODO(loc, "genarr with Slices");
3640     }
3641     arrayOperands.push_back(ArrayOperand{memref, shape, slice});
3642     if (destShape.empty())
3643       destShape = getShape(arrayOperands.back());
3644     if (isBoxValue()) {
3645       // Semantics are a reference to a boxed array.
3646       // This case just requires that an embox operation be created to box the
3647       // value. The value of the box is forwarded in the continuation.
3648       mlir::Type reduceTy = reduceRank(arrTy, slice);
3649       auto boxTy = fir::BoxType::get(reduceTy);
3650       if (components.substring) {
3651         // Adjust char length to substring size.
3652         fir::CharacterType charTy =
3653             fir::factory::CharacterExprHelper::getCharType(reduceTy);
3654         auto seqTy = reduceTy.cast<fir::SequenceType>();
3655         // TODO: Use a constant for fir.char LEN if we can compute it.
3656         boxTy = fir::BoxType::get(
3657             fir::SequenceType::get(fir::CharacterType::getUnknownLen(
3658                                        builder.getContext(), charTy.getFKind()),
3659                                    seqTy.getDimension()));
3660       }
3661       mlir::Value embox =
3662           memref.getType().isa<fir::BoxType>()
3663               ? builder.create<fir::ReboxOp>(loc, boxTy, memref, shape, slice)
3664                     .getResult()
3665               : builder
3666                     .create<fir::EmboxOp>(loc, boxTy, memref, shape, slice,
3667                                           fir::getTypeParams(extMemref))
3668                     .getResult();
3669       return [=](IterSpace) -> ExtValue { return fir::BoxValue(embox); };
3670     }
3671     if (isReferentiallyOpaque()) {
3672       TODO(loc, "genarr isReferentiallyOpaque");
3673     }
3674     auto arrLoad = builder.create<fir::ArrayLoadOp>(
3675         loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref));
3676     mlir::Value arrLd = arrLoad.getResult();
3677     if (isProjectedCopyInCopyOut()) {
3678       // Semantics are projected copy-in copy-out.
3679       // The backing store of the destination of an array expression may be
3680       // partially modified. These updates are recorded in FIR by forwarding a
3681       // continuation that generates an `array_update` Op. The destination is
3682       // always loaded at the beginning of the statement and merged at the
3683       // end.
3684       destination = arrLoad;
3685       auto lambda = ccStoreToDest.hasValue()
3686                         ? ccStoreToDest.getValue()
3687                         : defaultStoreToDestination(components.substring);
3688       return [=](IterSpace iters) -> ExtValue { return lambda(iters); };
3689     }
3690     if (isCustomCopyInCopyOut()) {
3691       TODO(loc, "isCustomCopyInCopyOut");
3692     }
3693     if (isCopyInCopyOut()) {
3694       // Semantics are copy-in copy-out.
3695       // The continuation simply forwards the result of the `array_load` Op,
3696       // which is the value of the array as it was when loaded. All data
3697       // references with rank > 0 in an array expression typically have
3698       // copy-in copy-out semantics.
3699       return [=](IterSpace) -> ExtValue { return arrLd; };
3700     }
3701     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
3702     if (isValueAttribute()) {
3703       // Semantics are value attribute.
3704       // Here the continuation will `array_fetch` a value from an array and
3705       // then store that value in a temporary. One can thus imitate pass by
3706       // value even when the call is pass by reference.
3707       return [=](IterSpace iters) -> ExtValue {
3708         mlir::Value base;
3709         mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
3710         if (isAdjustedArrayElementType(eleTy)) {
3711           mlir::Type eleRefTy = builder.getRefType(eleTy);
3712           base = builder.create<fir::ArrayAccessOp>(
3713               loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
3714         } else {
3715           base = builder.create<fir::ArrayFetchOp>(
3716               loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
3717         }
3718         mlir::Value temp = builder.createTemporary(
3719             loc, base.getType(),
3720             llvm::ArrayRef<mlir::NamedAttribute>{
3721                 Fortran::lower::getAdaptToByRefAttr(builder)});
3722         builder.create<fir::StoreOp>(loc, base, temp);
3723         return fir::factory::arraySectionElementToExtendedValue(
3724             builder, loc, extMemref, temp, slice);
3725       };
3726     }
3727     // In the default case, the array reference forwards an `array_fetch` or
3728     // `array_access` Op in the continuation.
3729     return [=](IterSpace iters) -> ExtValue {
3730       mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
3731       if (isAdjustedArrayElementType(eleTy)) {
3732         mlir::Type eleRefTy = builder.getRefType(eleTy);
3733         mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>(
3734             loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
3735         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
3736           llvm::SmallVector<mlir::Value> substringBounds;
3737           populateBounds(substringBounds, components.substring);
3738           if (!substringBounds.empty()) {
3739             // mlir::Value dstLen = fir::factory::genLenOfCharacter(
3740             //     builder, loc, arrLoad, iters.iterVec(), substringBounds);
3741             // fir::CharBoxValue dstChar(arrayOp, dstLen);
3742             // return fir::factory::CharacterExprHelper{builder, loc}
3743             //     .createSubstring(dstChar, substringBounds);
3744           }
3745         }
3746         return fir::factory::arraySectionElementToExtendedValue(
3747             builder, loc, extMemref, arrayOp, slice);
3748       }
3749       auto arrFetch = builder.create<fir::ArrayFetchOp>(
3750           loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
3751       return fir::factory::arraySectionElementToExtendedValue(
3752           builder, loc, extMemref, arrFetch, slice);
3753     };
3754   }
3755 
3756   /// Reduce the rank of a array to be boxed based on the slice's operands.
3757   static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) {
3758     if (slice) {
3759       auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp());
3760       assert(slOp && "expected slice op");
3761       auto seqTy = arrTy.dyn_cast<fir::SequenceType>();
3762       assert(seqTy && "expected array type");
3763       mlir::Operation::operand_range triples = slOp.getTriples();
3764       fir::SequenceType::Shape shape;
3765       // reduce the rank for each invariant dimension
3766       for (unsigned i = 1, end = triples.size(); i < end; i += 3)
3767         if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp()))
3768           shape.push_back(fir::SequenceType::getUnknownExtent());
3769       return fir::SequenceType::get(shape, seqTy.getEleTy());
3770     }
3771     // not sliced, so no change in rank
3772     return arrTy;
3773   }
3774 
3775 private:
3776   void determineShapeOfDest(const fir::ExtendedValue &lhs) {
3777     destShape = fir::factory::getExtents(builder, getLoc(), lhs);
3778   }
3779 
3780   void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) {
3781     if (!destShape.empty())
3782       return;
3783     // if (explicitSpaceIsActive() && determineShapeWithSlice(lhs))
3784     //   return;
3785     mlir::Type idxTy = builder.getIndexType();
3786     mlir::Location loc = getLoc();
3787     if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape =
3788             Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(),
3789                                                   lhs))
3790       for (Fortran::common::ConstantSubscript extent : *constantShape)
3791         destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent));
3792   }
3793 
3794   ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) {
3795     mlir::Type resTy = converter.genType(exp);
3796     return std::visit(
3797         [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); },
3798         exp.u);
3799   }
3800   ExtValue lowerArrayExpression(const ExtValue &exv) {
3801     assert(!explicitSpace);
3802     mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType());
3803     return lowerArrayExpression(genarr(exv), resTy);
3804   }
3805 
3806   void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds,
3807                       const Fortran::evaluate::Substring *substring) {
3808     if (!substring)
3809       return;
3810     bounds.push_back(fir::getBase(asScalar(substring->lower())));
3811     if (auto upper = substring->upper())
3812       bounds.push_back(fir::getBase(asScalar(*upper)));
3813   }
3814 
3815   /// Default store to destination implementation.
3816   /// This implements the default case, which is to assign the value in
3817   /// `iters.element` into the destination array, `iters.innerArgument`. Handles
3818   /// by value and by reference assignment.
3819   CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) {
3820     return [=](IterSpace iterSpace) -> ExtValue {
3821       mlir::Location loc = getLoc();
3822       mlir::Value innerArg = iterSpace.innerArgument();
3823       fir::ExtendedValue exv = iterSpace.elementExv();
3824       mlir::Type arrTy = innerArg.getType();
3825       mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec());
3826       if (isAdjustedArrayElementType(eleTy)) {
3827         // The elemental update is in the memref domain. Under this semantics,
3828         // we must always copy the computed new element from its location in
3829         // memory into the destination array.
3830         mlir::Type resRefTy = builder.getRefType(eleTy);
3831         // Get a reference to the array element to be amended.
3832         auto arrayOp = builder.create<fir::ArrayAccessOp>(
3833             loc, resRefTy, innerArg, iterSpace.iterVec(),
3834             destination.getTypeparams());
3835         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
3836           llvm::SmallVector<mlir::Value> substringBounds;
3837           populateBounds(substringBounds, substring);
3838           mlir::Value dstLen = fir::factory::genLenOfCharacter(
3839               builder, loc, destination, iterSpace.iterVec(), substringBounds);
3840           fir::ArrayAmendOp amend = createCharArrayAmend(
3841               loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds);
3842           return abstractArrayExtValue(amend, dstLen);
3843         }
3844         if (fir::isa_derived(eleTy)) {
3845           fir::ArrayAmendOp amend = createDerivedArrayAmend(
3846               loc, destination, builder, arrayOp, exv, eleTy, innerArg);
3847           return abstractArrayExtValue(amend /*FIXME: typeparams?*/);
3848         }
3849         assert(eleTy.isa<fir::SequenceType>() && "must be an array");
3850         TODO(loc, "array (as element) assignment");
3851       }
3852       // By value semantics. The element is being assigned by value.
3853       mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv));
3854       auto update = builder.create<fir::ArrayUpdateOp>(
3855           loc, arrTy, innerArg, ele, iterSpace.iterVec(),
3856           destination.getTypeparams());
3857       return abstractArrayExtValue(update);
3858     };
3859   }
3860 
3861   /// For an elemental array expression.
3862   ///   1. Lower the scalars and array loads.
3863   ///   2. Create the iteration space.
3864   ///   3. Create the element-by-element computation in the loop.
3865   ///   4. Return the resulting array value.
3866   /// If no destination was set in the array context, a temporary of
3867   /// \p resultTy will be created to hold the evaluated expression.
3868   /// Otherwise, \p resultTy is ignored and the expression is evaluated
3869   /// in the destination. \p f is a continuation built from an
3870   /// evaluate::Expr or an ExtendedValue.
3871   ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) {
3872     mlir::Location loc = getLoc();
3873     auto [iterSpace, insPt] = genIterSpace(resultTy);
3874     auto exv = f(iterSpace);
3875     iterSpace.setElement(std::move(exv));
3876     auto lambda = ccStoreToDest.hasValue()
3877                       ? ccStoreToDest.getValue()
3878                       : defaultStoreToDestination(/*substring=*/nullptr);
3879     mlir::Value updVal = fir::getBase(lambda(iterSpace));
3880     finalizeElementCtx();
3881     builder.create<fir::ResultOp>(loc, updVal);
3882     builder.restoreInsertionPoint(insPt);
3883     return abstractArrayExtValue(iterSpace.outerResult());
3884   }
3885 
3886   /// Get the shape from an ArrayOperand. The shape of the array is adjusted if
3887   /// the array was sliced.
3888   llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) {
3889     // if (array.slice)
3890     //   return computeSliceShape(array.slice);
3891     if (array.memref.getType().isa<fir::BoxType>())
3892       return fir::factory::readExtents(builder, getLoc(),
3893                                        fir::BoxValue{array.memref});
3894     std::vector<mlir::Value, std::allocator<mlir::Value>> extents =
3895         fir::factory::getExtents(array.shape);
3896     return {extents.begin(), extents.end()};
3897   }
3898 
3899   /// Get the shape from an ArrayLoad.
3900   llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) {
3901     return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(),
3902                                  arrayLoad.getSlice()});
3903   }
3904 
3905   /// Returns the first array operand that may not be absent. If all
3906   /// array operands may be absent, return the first one.
3907   const ArrayOperand &getInducingShapeArrayOperand() const {
3908     assert(!arrayOperands.empty());
3909     for (const ArrayOperand &op : arrayOperands)
3910       if (!op.mayBeAbsent)
3911         return op;
3912     // If all arrays operand appears in optional position, then none of them
3913     // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the
3914     // first operands.
3915     // TODO: There is an opportunity to add a runtime check here that
3916     // this array is present as required.
3917     return arrayOperands[0];
3918   }
3919 
3920   /// Generate the shape of the iteration space over the array expression. The
3921   /// iteration space may be implicit, explicit, or both. If it is implied it is
3922   /// based on the destination and operand array loads, or an optional
3923   /// Fortran::evaluate::Shape from the front end. If the shape is explicit,
3924   /// this returns any implicit shape component, if it exists.
3925   llvm::SmallVector<mlir::Value> genIterationShape() {
3926     // Use the precomputed destination shape.
3927     if (!destShape.empty())
3928       return destShape;
3929     // Otherwise, use the destination's shape.
3930     if (destination)
3931       return getShape(destination);
3932     // Otherwise, use the first ArrayLoad operand shape.
3933     if (!arrayOperands.empty())
3934       return getShape(getInducingShapeArrayOperand());
3935     fir::emitFatalError(getLoc(),
3936                         "failed to compute the array expression shape");
3937   }
3938 
3939   bool explicitSpaceIsActive() const {
3940     return explicitSpace && explicitSpace->isActive();
3941   }
3942 
3943   bool implicitSpaceHasMasks() const {
3944     return implicitSpace && !implicitSpace->empty();
3945   }
3946 
3947   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
3948                              Fortran::lower::StatementContext &stmtCtx,
3949                              Fortran::lower::SymMap &symMap)
3950       : converter{converter}, builder{converter.getFirOpBuilder()},
3951         stmtCtx{stmtCtx}, symMap{symMap} {}
3952 
3953   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
3954                              Fortran::lower::StatementContext &stmtCtx,
3955                              Fortran::lower::SymMap &symMap,
3956                              ConstituentSemantics sem)
3957       : converter{converter}, builder{converter.getFirOpBuilder()},
3958         stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {}
3959 
3960   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
3961                              Fortran::lower::StatementContext &stmtCtx,
3962                              Fortran::lower::SymMap &symMap,
3963                              ConstituentSemantics sem,
3964                              Fortran::lower::ExplicitIterSpace *expSpace,
3965                              Fortran::lower::ImplicitIterSpace *impSpace)
3966       : converter{converter}, builder{converter.getFirOpBuilder()},
3967         stmtCtx{stmtCtx}, symMap{symMap},
3968         explicitSpace(expSpace->isActive() ? expSpace : nullptr),
3969         implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} {
3970     // Generate any mask expressions, as necessary. This is the compute step
3971     // that creates the effective masks. See 10.2.3.2 in particular.
3972     // genMasks();
3973   }
3974 
3975   mlir::Location getLoc() { return converter.getCurrentLocation(); }
3976 
3977   /// Array appears in a lhs context such that it is assigned after the rhs is
3978   /// fully evaluated.
3979   inline bool isCopyInCopyOut() {
3980     return semant == ConstituentSemantics::CopyInCopyOut;
3981   }
3982 
3983   /// Array appears in a lhs (or temp) context such that a projected,
3984   /// discontiguous subspace of the array is assigned after the rhs is fully
3985   /// evaluated. That is, the rhs array value is merged into a section of the
3986   /// lhs array.
3987   inline bool isProjectedCopyInCopyOut() {
3988     return semant == ConstituentSemantics::ProjectedCopyInCopyOut;
3989   }
3990 
3991   inline bool isCustomCopyInCopyOut() {
3992     return semant == ConstituentSemantics::CustomCopyInCopyOut;
3993   }
3994 
3995   /// Array appears in a context where it must be boxed.
3996   inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; }
3997 
3998   /// Array appears in a context where differences in the memory reference can
3999   /// be observable in the computational results. For example, an array
4000   /// element is passed to an impure procedure.
4001   inline bool isReferentiallyOpaque() {
4002     return semant == ConstituentSemantics::RefOpaque;
4003   }
4004 
4005   /// Array appears in a context where it is passed as a VALUE argument.
4006   inline bool isValueAttribute() {
4007     return semant == ConstituentSemantics::ByValueArg;
4008   }
4009 
4010   /// Can the loops over the expression be unordered?
4011   inline bool isUnordered() const { return unordered; }
4012 
4013   void setUnordered(bool b) { unordered = b; }
4014 
4015   Fortran::lower::AbstractConverter &converter;
4016   fir::FirOpBuilder &builder;
4017   Fortran::lower::StatementContext &stmtCtx;
4018   bool elementCtx = false;
4019   Fortran::lower::SymMap &symMap;
4020   /// The continuation to generate code to update the destination.
4021   llvm::Optional<CC> ccStoreToDest;
4022   llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude;
4023   llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>>
4024       ccLoadDest;
4025   /// The destination is the loaded array into which the results will be
4026   /// merged.
4027   fir::ArrayLoadOp destination;
4028   /// The shape of the destination.
4029   llvm::SmallVector<mlir::Value> destShape;
4030   /// List of arrays in the expression that have been loaded.
4031   llvm::SmallVector<ArrayOperand> arrayOperands;
4032   /// If there is a user-defined iteration space, explicitShape will hold the
4033   /// information from the front end.
4034   Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr;
4035   Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr;
4036   ConstituentSemantics semant = ConstituentSemantics::RefTransparent;
4037   // Can the array expression be evaluated in any order?
4038   // Will be set to false if any of the expression parts prevent this.
4039   bool unordered = true;
4040 };
4041 } // namespace
4042 
4043 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
4044     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4045     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4046     Fortran::lower::StatementContext &stmtCtx) {
4047   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
4048   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr);
4049 }
4050 
4051 fir::GlobalOp Fortran::lower::createDenseGlobal(
4052     mlir::Location loc, mlir::Type symTy, llvm::StringRef globalName,
4053     mlir::StringAttr linkage, bool isConst,
4054     const Fortran::lower::SomeExpr &expr,
4055     Fortran::lower::AbstractConverter &converter) {
4056 
4057   Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true);
4058   Fortran::lower::SymMap emptyMap;
4059   InitializerData initData(/*genRawVals=*/true);
4060   ScalarExprLowering sel(loc, converter, emptyMap, stmtCtx,
4061                          /*initializer=*/&initData);
4062   sel.genval(expr);
4063 
4064   size_t sz = initData.rawVals.size();
4065   llvm::ArrayRef<mlir::Attribute> ar = {initData.rawVals.data(), sz};
4066 
4067   mlir::RankedTensorType tensorTy;
4068   auto &builder = converter.getFirOpBuilder();
4069   mlir::Type iTy = initData.rawType;
4070   if (!iTy)
4071     return 0; // array extent is probably 0 in this case, so just return 0.
4072   tensorTy = mlir::RankedTensorType::get(sz, iTy);
4073   auto init = mlir::DenseElementsAttr::get(tensorTy, ar);
4074   return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst);
4075 }
4076 
4077 fir::ExtendedValue Fortran::lower::createSomeInitializerExpression(
4078     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4079     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4080     Fortran::lower::StatementContext &stmtCtx) {
4081   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
4082   InitializerData initData; // needed for initializations
4083   return ScalarExprLowering{loc, converter, symMap, stmtCtx,
4084                             /*initializer=*/&initData}
4085       .genval(expr);
4086 }
4087 
4088 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
4089     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4090     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4091     Fortran::lower::StatementContext &stmtCtx) {
4092   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
4093   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr);
4094 }
4095 
4096 fir::ExtendedValue Fortran::lower::createInitializerAddress(
4097     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4098     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4099     Fortran::lower::StatementContext &stmtCtx) {
4100   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
4101   InitializerData init;
4102   return ScalarExprLowering(loc, converter, symMap, stmtCtx, &init).gen(expr);
4103 }
4104 
4105 fir::ExtendedValue
4106 Fortran::lower::createSomeArrayBox(Fortran::lower::AbstractConverter &converter,
4107                                    const Fortran::lower::SomeExpr &expr,
4108                                    Fortran::lower::SymMap &symMap,
4109                                    Fortran::lower::StatementContext &stmtCtx) {
4110   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "box designator: ") << '\n');
4111   return ArrayExprLowering::lowerBoxedArrayExpression(converter, symMap,
4112                                                       stmtCtx, expr);
4113 }
4114 
4115 fir::MutableBoxValue Fortran::lower::createMutableBox(
4116     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4117     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
4118   // MutableBox lowering StatementContext does not need to be propagated
4119   // to the caller because the result value is a variable, not a temporary
4120   // expression. The StatementContext clean-up can occur before using the
4121   // resulting MutableBoxValue. Variables of all other types are handled in the
4122   // bridge.
4123   Fortran::lower::StatementContext dummyStmtCtx;
4124   return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx}
4125       .genMutableBoxValue(expr);
4126 }
4127 
4128 mlir::Value Fortran::lower::createSubroutineCall(
4129     AbstractConverter &converter, const evaluate::ProcedureRef &call,
4130     SymMap &symMap, StatementContext &stmtCtx) {
4131   mlir::Location loc = converter.getCurrentLocation();
4132 
4133   // Simple subroutine call, with potential alternate return.
4134   auto res = Fortran::lower::createSomeExtendedExpression(
4135       loc, converter, toEvExpr(call), symMap, stmtCtx);
4136   return fir::getBase(res);
4137 }
4138 
4139 void Fortran::lower::createSomeArrayAssignment(
4140     Fortran::lower::AbstractConverter &converter,
4141     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
4142     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
4143   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
4144              rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
4145   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
4146 }
4147 
4148 void Fortran::lower::createSomeArrayAssignment(
4149     Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
4150     const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap,
4151     Fortran::lower::StatementContext &stmtCtx) {
4152   LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
4153              llvm::dbgs() << "assign expression: " << rhs << '\n';);
4154   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
4155 }
4156 
4157 void Fortran::lower::createAllocatableArrayAssignment(
4158     Fortran::lower::AbstractConverter &converter,
4159     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
4160     Fortran::lower::ExplicitIterSpace &explicitSpace,
4161     Fortran::lower::ImplicitIterSpace &implicitSpace,
4162     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
4163   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n';
4164              rhs.AsFortran(llvm::dbgs() << "assign expression: ")
4165              << " given the explicit iteration space:\n"
4166              << explicitSpace << "\n and implied mask conditions:\n"
4167              << implicitSpace << '\n';);
4168   ArrayExprLowering::lowerAllocatableArrayAssignment(
4169       converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
4170 }
4171 
4172 fir::ExtendedValue Fortran::lower::createSomeArrayTempValue(
4173     Fortran::lower::AbstractConverter &converter,
4174     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4175     Fortran::lower::StatementContext &stmtCtx) {
4176   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
4177   return ArrayExprLowering::lowerNewArrayExpression(converter, symMap, stmtCtx,
4178                                                     expr);
4179 }
4180 
4181 mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder,
4182                                            mlir::Location loc,
4183                                            mlir::Value value) {
4184   mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
4185   if (mlir::Operation *definingOp = value.getDefiningOp())
4186     if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
4187       if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>())
4188         return intAttr.getInt() < 0 ? zero : value;
4189   return Fortran::lower::genMax(builder, loc,
4190                                 llvm::SmallVector<mlir::Value>{value, zero});
4191 }
4192