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/Allocatable.h"
18 #include "flang/Lower/BuiltinModules.h"
19 #include "flang/Lower/CallInterface.h"
20 #include "flang/Lower/ComponentPath.h"
21 #include "flang/Lower/ConvertType.h"
22 #include "flang/Lower/ConvertVariable.h"
23 #include "flang/Lower/CustomIntrinsicCall.h"
24 #include "flang/Lower/DumpEvaluateExpr.h"
25 #include "flang/Lower/IntrinsicCall.h"
26 #include "flang/Lower/Mangler.h"
27 #include "flang/Lower/StatementContext.h"
28 #include "flang/Lower/SymbolMap.h"
29 #include "flang/Lower/Todo.h"
30 #include "flang/Optimizer/Builder/Character.h"
31 #include "flang/Optimizer/Builder/Complex.h"
32 #include "flang/Optimizer/Builder/Factory.h"
33 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h"
34 #include "flang/Optimizer/Builder/MutableBox.h"
35 #include "flang/Optimizer/Builder/Runtime/Character.h"
36 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
37 #include "flang/Optimizer/Builder/Runtime/Ragged.h"
38 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
39 #include "flang/Optimizer/Support/Matcher.h"
40 #include "flang/Semantics/expression.h"
41 #include "flang/Semantics/symbol.h"
42 #include "flang/Semantics/tools.h"
43 #include "flang/Semantics/type.h"
44 #include "mlir/Dialect/Func/IR/FuncOps.h"
45 #include "llvm/Support/CommandLine.h"
46 #include "llvm/Support/Debug.h"
47 
48 #define DEBUG_TYPE "flang-lower-expr"
49 
50 //===----------------------------------------------------------------------===//
51 // The composition and structure of Fortran::evaluate::Expr is defined in
52 // the various header files in include/flang/Evaluate. You are referred
53 // there for more information on these data structures. Generally speaking,
54 // these data structures are a strongly typed family of abstract data types
55 // that, composed as trees, describe the syntax of Fortran expressions.
56 //
57 // This part of the bridge can traverse these tree structures and lower them
58 // to the correct FIR representation in SSA form.
59 //===----------------------------------------------------------------------===//
60 
61 // The default attempts to balance a modest allocation size with expected user
62 // input to minimize bounds checks and reallocations during dynamic array
63 // construction. Some user codes may have very large array constructors for
64 // which the default can be increased.
65 static llvm::cl::opt<unsigned> clInitialBufferSize(
66     "array-constructor-initial-buffer-size",
67     llvm::cl::desc(
68         "set the incremental array construction buffer size (default=32)"),
69     llvm::cl::init(32u));
70 
71 /// The various semantics of a program constituent (or a part thereof) as it may
72 /// appear in an expression.
73 ///
74 /// Given the following Fortran declarations.
75 /// ```fortran
76 ///   REAL :: v1, v2, v3
77 ///   REAL, POINTER :: vp1
78 ///   REAL :: a1(c), a2(c)
79 ///   REAL ELEMENTAL FUNCTION f1(arg) ! array -> array
80 ///   FUNCTION f2(arg)                ! array -> array
81 ///   vp1 => v3       ! 1
82 ///   v1 = v2 * vp1   ! 2
83 ///   a1 = a1 + a2    ! 3
84 ///   a1 = f1(a2)     ! 4
85 ///   a1 = f2(a2)     ! 5
86 /// ```
87 ///
88 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is
89 /// constructed from the DataAddr of `v3`.
90 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed
91 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double
92 /// dereference in the `vp1` case.
93 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs
94 /// is CopyInCopyOut as `a1` is replaced elementally by the additions.
95 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if
96 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/
97 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut.
98 ///  In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational.
99 ///  `a1` on the lhs is again CopyInCopyOut.
100 enum class ConstituentSemantics {
101   // Scalar data reference semantics.
102   //
103   // For these let `v` be the location in memory of a variable with value `x`
104   DataValue, // refers to the value `x`
105   DataAddr,  // refers to the address `v`
106   BoxValue,  // refers to a box value containing `v`
107   BoxAddr,   // refers to the address of a box value containing `v`
108 
109   // Array data reference semantics.
110   //
111   // For these let `a` be the location in memory of a sequence of value `[xs]`.
112   // Let `x_i` be the `i`-th value in the sequence `[xs]`.
113 
114   // Referentially transparent. Refers to the array's value, `[xs]`.
115   RefTransparent,
116   // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7
117   // note 2). (Passing a copy by reference to simulate pass-by-value.)
118   ByValueArg,
119   // Refers to the merge of array value `[xs]` with another array value `[ys]`.
120   // This merged array value will be written into memory location `a`.
121   CopyInCopyOut,
122   // Similar to CopyInCopyOut but `a` may be a transient projection (rather than
123   // a whole array).
124   ProjectedCopyInCopyOut,
125   // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned
126   // automatically by the framework. Instead, and address for `[xs]` is made
127   // accessible so that custom assignments to `[xs]` can be implemented.
128   CustomCopyInCopyOut,
129   // Referentially opaque. Refers to the address of `x_i`.
130   RefOpaque
131 };
132 
133 /// Convert parser's INTEGER relational operators to MLIR.  TODO: using
134 /// unordered, but we may want to cons ordered in certain situation.
135 static mlir::arith::CmpIPredicate
136 translateRelational(Fortran::common::RelationalOperator rop) {
137   switch (rop) {
138   case Fortran::common::RelationalOperator::LT:
139     return mlir::arith::CmpIPredicate::slt;
140   case Fortran::common::RelationalOperator::LE:
141     return mlir::arith::CmpIPredicate::sle;
142   case Fortran::common::RelationalOperator::EQ:
143     return mlir::arith::CmpIPredicate::eq;
144   case Fortran::common::RelationalOperator::NE:
145     return mlir::arith::CmpIPredicate::ne;
146   case Fortran::common::RelationalOperator::GT:
147     return mlir::arith::CmpIPredicate::sgt;
148   case Fortran::common::RelationalOperator::GE:
149     return mlir::arith::CmpIPredicate::sge;
150   }
151   llvm_unreachable("unhandled INTEGER relational operator");
152 }
153 
154 /// Convert parser's REAL relational operators to MLIR.
155 /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018
156 /// requirements in the IEEE context (table 17.1 of F2018). This choice is
157 /// also applied in other contexts because it is easier and in line with
158 /// other Fortran compilers.
159 /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not
160 /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee
161 /// whether the comparison will signal or not in case of quiet NaN argument.
162 static mlir::arith::CmpFPredicate
163 translateFloatRelational(Fortran::common::RelationalOperator rop) {
164   switch (rop) {
165   case Fortran::common::RelationalOperator::LT:
166     return mlir::arith::CmpFPredicate::OLT;
167   case Fortran::common::RelationalOperator::LE:
168     return mlir::arith::CmpFPredicate::OLE;
169   case Fortran::common::RelationalOperator::EQ:
170     return mlir::arith::CmpFPredicate::OEQ;
171   case Fortran::common::RelationalOperator::NE:
172     return mlir::arith::CmpFPredicate::UNE;
173   case Fortran::common::RelationalOperator::GT:
174     return mlir::arith::CmpFPredicate::OGT;
175   case Fortran::common::RelationalOperator::GE:
176     return mlir::arith::CmpFPredicate::OGE;
177   }
178   llvm_unreachable("unhandled REAL relational operator");
179 }
180 
181 static mlir::Value genActualIsPresentTest(fir::FirOpBuilder &builder,
182                                           mlir::Location loc,
183                                           fir::ExtendedValue actual) {
184   if (const auto *ptrOrAlloc = actual.getBoxOf<fir::MutableBoxValue>())
185     return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc,
186                                                         *ptrOrAlloc);
187   // Optional case (not that optional allocatable/pointer cannot be absent
188   // when passed to CMPLX as per 15.5.2.12 point 3 (7) and (8)). It is
189   // therefore possible to catch them in the `then` case above.
190   return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
191                                           fir::getBase(actual));
192 }
193 
194 /// Convert the array_load, `load`, to an extended value. If `path` is not
195 /// empty, then traverse through the components designated. The base value is
196 /// `newBase`. This does not accept an array_load with a slice operand.
197 static fir::ExtendedValue
198 arrayLoadExtValue(fir::FirOpBuilder &builder, mlir::Location loc,
199                   fir::ArrayLoadOp load, llvm::ArrayRef<mlir::Value> path,
200                   mlir::Value newBase, mlir::Value newLen = {}) {
201   // Recover the extended value from the load.
202   assert(!load.getSlice() && "slice is not allowed");
203   mlir::Type arrTy = load.getType();
204   if (!path.empty()) {
205     mlir::Type ty = fir::applyPathToType(arrTy, path);
206     if (!ty)
207       fir::emitFatalError(loc, "path does not apply to type");
208     if (!ty.isa<fir::SequenceType>()) {
209       if (fir::isa_char(ty)) {
210         mlir::Value len = newLen;
211         if (!len)
212           len = fir::factory::CharacterExprHelper{builder, loc}.getLength(
213               load.getMemref());
214         if (!len) {
215           assert(load.getTypeparams().size() == 1 &&
216                  "length must be in array_load");
217           len = load.getTypeparams()[0];
218         }
219         return fir::CharBoxValue{newBase, len};
220       }
221       return newBase;
222     }
223     arrTy = ty.cast<fir::SequenceType>();
224   }
225 
226   // Use the shape op, if there is one.
227   mlir::Value shapeVal = load.getShape();
228   if (shapeVal) {
229     if (!mlir::isa<fir::ShiftOp>(shapeVal.getDefiningOp())) {
230       mlir::Type eleTy = fir::unwrapSequenceType(arrTy);
231       std::vector<mlir::Value> extents = fir::factory::getExtents(shapeVal);
232       std::vector<mlir::Value> origins = fir::factory::getOrigins(shapeVal);
233       if (fir::isa_char(eleTy)) {
234         mlir::Value len = newLen;
235         if (!len)
236           len = fir::factory::CharacterExprHelper{builder, loc}.getLength(
237               load.getMemref());
238         if (!len) {
239           assert(load.getTypeparams().size() == 1 &&
240                  "length must be in array_load");
241           len = load.getTypeparams()[0];
242         }
243         return fir::CharArrayBoxValue(newBase, len, extents, origins);
244       }
245       return fir::ArrayBoxValue(newBase, extents, origins);
246     }
247     if (!fir::isa_box_type(load.getMemref().getType()))
248       fir::emitFatalError(loc, "shift op is invalid in this context");
249   }
250 
251   // There is no shape or the array is in a box. Extents and lower bounds must
252   // be read at runtime.
253   if (path.empty() && !shapeVal) {
254     fir::ExtendedValue exv =
255         fir::factory::readBoxValue(builder, loc, load.getMemref());
256     return fir::substBase(exv, newBase);
257   }
258   TODO(loc, "component is boxed, retreive its type parameters");
259 }
260 
261 /// Place \p exv in memory if it is not already a memory reference. If
262 /// \p forceValueType is provided, the value is first casted to the provided
263 /// type before being stored (this is mainly intended for logicals whose value
264 /// may be `i1` but needed to be stored as Fortran logicals).
265 static fir::ExtendedValue
266 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
267                          const fir::ExtendedValue &exv,
268                          mlir::Type storageType) {
269   mlir::Value valBase = fir::getBase(exv);
270   if (fir::conformsWithPassByRef(valBase.getType()))
271     return exv;
272 
273   assert(!fir::hasDynamicSize(storageType) &&
274          "only expect statically sized scalars to be by value");
275 
276   // Since `a` is not itself a valid referent, determine its value and
277   // create a temporary location at the beginning of the function for
278   // referencing.
279   mlir::Value val = builder.createConvert(loc, storageType, valBase);
280   mlir::Value temp = builder.createTemporary(
281       loc, storageType,
282       llvm::ArrayRef<mlir::NamedAttribute>{
283           Fortran::lower::getAdaptToByRefAttr(builder)});
284   builder.create<fir::StoreOp>(loc, val, temp);
285   return fir::substBase(exv, temp);
286 }
287 
288 // Copy a copy of scalar \p exv in a new temporary.
289 static fir::ExtendedValue
290 createInMemoryScalarCopy(fir::FirOpBuilder &builder, mlir::Location loc,
291                          const fir::ExtendedValue &exv) {
292   assert(exv.rank() == 0 && "input to scalar memory copy must be a scalar");
293   if (exv.getCharBox() != nullptr)
294     return fir::factory::CharacterExprHelper{builder, loc}.createTempFrom(exv);
295   if (fir::isDerivedWithLengthParameters(exv))
296     TODO(loc, "copy derived type with length parameters");
297   mlir::Type type = fir::unwrapPassByRefType(fir::getBase(exv).getType());
298   fir::ExtendedValue temp = builder.createTemporary(loc, type);
299   fir::factory::genScalarAssignment(builder, loc, temp, exv);
300   return temp;
301 }
302 
303 /// Is this a variable wrapped in parentheses?
304 template <typename A>
305 static bool isParenthesizedVariable(const A &) {
306   return false;
307 }
308 template <typename T>
309 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) {
310   using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
311   using Parentheses = Fortran::evaluate::Parentheses<T>;
312   if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
313     if (const auto *parentheses = std::get_if<Parentheses>(&expr.u))
314       return Fortran::evaluate::IsVariable(parentheses->left());
315     return false;
316   } else {
317     return std::visit([&](const auto &x) { return isParenthesizedVariable(x); },
318                       expr.u);
319   }
320 }
321 
322 /// Generate a load of a value from an address. Beware that this will lose
323 /// any dynamic type information for polymorphic entities (note that unlimited
324 /// polymorphic cannot be loaded and must not be provided here).
325 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
326                                   mlir::Location loc,
327                                   const fir::ExtendedValue &addr) {
328   return addr.match(
329       [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
330       [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
331         if (fir::unwrapRefType(fir::getBase(v).getType())
332                 .isa<fir::RecordType>())
333           return v;
334         return builder.create<fir::LoadOp>(loc, fir::getBase(v));
335       },
336       [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
337         TODO(loc, "genLoad for MutableBoxValue");
338       },
339       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
340         TODO(loc, "genLoad for BoxValue");
341       },
342       [&](const auto &) -> fir::ExtendedValue {
343         fir::emitFatalError(
344             loc, "attempting to load whole array or procedure address");
345       });
346 }
347 
348 /// Create an optional dummy argument value from entity \p exv that may be
349 /// absent. This can only be called with numerical or logical scalar \p exv.
350 /// If \p exv is considered absent according to 15.5.2.12 point 1., the returned
351 /// value is zero (or false), otherwise it is the value of \p exv.
352 static fir::ExtendedValue genOptionalValue(fir::FirOpBuilder &builder,
353                                            mlir::Location loc,
354                                            const fir::ExtendedValue &exv,
355                                            mlir::Value isPresent) {
356   mlir::Type eleType = fir::getBaseTypeOf(exv);
357   assert(exv.rank() == 0 && fir::isa_trivial(eleType) &&
358          "must be a numerical or logical scalar");
359   return builder
360       .genIfOp(loc, {eleType}, isPresent,
361                /*withElseRegion=*/true)
362       .genThen([&]() {
363         mlir::Value val = fir::getBase(genLoad(builder, loc, exv));
364         builder.create<fir::ResultOp>(loc, val);
365       })
366       .genElse([&]() {
367         mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType);
368         builder.create<fir::ResultOp>(loc, zero);
369       })
370       .getResults()[0];
371 }
372 
373 /// Create an optional dummy argument address from entity \p exv that may be
374 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the
375 /// returned value is a null pointer, otherwise it is the address of \p exv.
376 static fir::ExtendedValue genOptionalAddr(fir::FirOpBuilder &builder,
377                                           mlir::Location loc,
378                                           const fir::ExtendedValue &exv,
379                                           mlir::Value isPresent) {
380   // If it is an exv pointer/allocatable, then it cannot be absent
381   // because it is passed to a non-pointer/non-allocatable.
382   if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
383     return fir::factory::genMutableBoxRead(builder, loc, *box);
384   // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL
385   // address and can be passed directly.
386   return exv;
387 }
388 
389 /// Create an optional dummy argument address from entity \p exv that may be
390 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the
391 /// returned value is an absent fir.box, otherwise it is a fir.box describing \p
392 /// exv.
393 static fir::ExtendedValue genOptionalBox(fir::FirOpBuilder &builder,
394                                          mlir::Location loc,
395                                          const fir::ExtendedValue &exv,
396                                          mlir::Value isPresent) {
397   // Non allocatable/pointer optional box -> simply forward
398   if (exv.getBoxOf<fir::BoxValue>())
399     return exv;
400 
401   fir::ExtendedValue newExv = exv;
402   // Optional allocatable/pointer -> Cannot be absent, but need to translate
403   // unallocated/diassociated into absent fir.box.
404   if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
405     newExv = fir::factory::genMutableBoxRead(builder, loc, *box);
406 
407   // createBox will not do create any invalid memory dereferences if exv is
408   // absent. The created fir.box will not be usable, but the SelectOp below
409   // ensures it won't be.
410   mlir::Value box = builder.createBox(loc, newExv);
411   mlir::Type boxType = box.getType();
412   auto absent = builder.create<fir::AbsentOp>(loc, boxType);
413   auto boxOrAbsent = builder.create<mlir::arith::SelectOp>(
414       loc, boxType, isPresent, box, absent);
415   return fir::BoxValue(boxOrAbsent);
416 }
417 
418 /// Is this a call to an elemental procedure with at least one array argument?
419 static bool
420 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
421   if (procRef.IsElemental())
422     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
423          procRef.arguments())
424       if (arg && arg->Rank() != 0)
425         return true;
426   return false;
427 }
428 template <typename T>
429 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) {
430   return false;
431 }
432 template <>
433 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) {
434   if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u))
435     return isElementalProcWithArrayArgs(*procRef);
436   return false;
437 }
438 
439 /// Some auxiliary data for processing initialization in ScalarExprLowering
440 /// below. This is currently used for generating dense attributed global
441 /// arrays.
442 struct InitializerData {
443   explicit InitializerData(bool getRawVals = false) : genRawVals{getRawVals} {}
444   llvm::SmallVector<mlir::Attribute> rawVals; // initialization raw values
445   mlir::Type rawType; // Type of elements processed for rawVals vector.
446   bool genRawVals;    // generate the rawVals vector if set.
447 };
448 
449 /// If \p arg is the address of a function with a denoted host-association tuple
450 /// argument, then return the host-associations tuple value of the current
451 /// procedure. Otherwise, return nullptr.
452 static mlir::Value
453 argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
454                    mlir::Value arg) {
455   if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
456     auto &builder = converter.getFirOpBuilder();
457     if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
458       if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
459         return converter.hostAssocTupleValue();
460   }
461   return {};
462 }
463 
464 /// \p argTy must be a tuple (pair) of boxproc and integral types. Convert the
465 /// \p funcAddr argument to a boxproc value, with the host-association as
466 /// required. Call the factory function to finish creating the tuple value.
467 static mlir::Value
468 createBoxProcCharTuple(Fortran::lower::AbstractConverter &converter,
469                        mlir::Type argTy, mlir::Value funcAddr,
470                        mlir::Value charLen) {
471   auto boxTy =
472       argTy.cast<mlir::TupleType>().getType(0).cast<fir::BoxProcType>();
473   mlir::Location loc = converter.getCurrentLocation();
474   auto &builder = converter.getFirOpBuilder();
475   auto boxProc = [&]() -> mlir::Value {
476     if (auto host = argumentHostAssocs(converter, funcAddr))
477       return builder.create<fir::EmboxProcOp>(
478           loc, boxTy, llvm::ArrayRef<mlir::Value>{funcAddr, host});
479     return builder.create<fir::EmboxProcOp>(loc, boxTy, funcAddr);
480   }();
481   return fir::factory::createCharacterProcedureTuple(builder, loc, argTy,
482                                                      boxProc, charLen);
483 }
484 
485 namespace {
486 
487 /// Lowering of Fortran::evaluate::Expr<T> expressions
488 class ScalarExprLowering {
489 public:
490   using ExtValue = fir::ExtendedValue;
491 
492   explicit ScalarExprLowering(mlir::Location loc,
493                               Fortran::lower::AbstractConverter &converter,
494                               Fortran::lower::SymMap &symMap,
495                               Fortran::lower::StatementContext &stmtCtx,
496                               InitializerData *initializer = nullptr)
497       : location{loc}, converter{converter},
498         builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap},
499         inInitializer{initializer} {}
500 
501   ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) {
502     return gen(expr);
503   }
504 
505   /// Lower `expr` to be passed as a fir.box argument. Do not create a temp
506   /// for the expr if it is a variable that can be described as a fir.box.
507   ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) {
508     bool saveUseBoxArg = useBoxArg;
509     useBoxArg = true;
510     ExtValue result = gen(expr);
511     useBoxArg = saveUseBoxArg;
512     return result;
513   }
514 
515   ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) {
516     return genval(expr);
517   }
518 
519   /// Lower an expression that is a pointer or an allocatable to a
520   /// MutableBoxValue.
521   fir::MutableBoxValue
522   genMutableBoxValue(const Fortran::lower::SomeExpr &expr) {
523     // Pointers and allocatables can only be:
524     //    - a simple designator "x"
525     //    - a component designator "a%b(i,j)%x"
526     //    - a function reference "foo()"
527     //    - result of NULL() or NULL(MOLD) intrinsic.
528     //    NULL() requires some context to be lowered, so it is not handled
529     //    here and must be lowered according to the context where it appears.
530     ExtValue exv = std::visit(
531         [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u);
532     const fir::MutableBoxValue *mutableBox =
533         exv.getBoxOf<fir::MutableBoxValue>();
534     if (!mutableBox)
535       fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue");
536     return *mutableBox;
537   }
538 
539   template <typename T>
540   ExtValue genMutableBoxValueImpl(const T &) {
541     // NULL() case should not be handled here.
542     fir::emitFatalError(getLoc(), "NULL() must be lowered in its context");
543   }
544 
545   template <typename T>
546   ExtValue
547   genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) {
548     return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef)));
549   }
550 
551   template <typename T>
552   ExtValue
553   genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) {
554     return std::visit(
555         Fortran::common::visitors{
556             [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue {
557               return symMap.lookupSymbol(*sym).toExtendedValue();
558             },
559             [&](const Fortran::evaluate::Component &comp) -> ExtValue {
560               return genComponent(comp);
561             },
562             [&](const auto &) -> ExtValue {
563               fir::emitFatalError(getLoc(),
564                                   "not an allocatable or pointer designator");
565             }},
566         designator.u);
567   }
568 
569   template <typename T>
570   ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) {
571     return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); },
572                       expr.u);
573   }
574 
575   mlir::Location getLoc() { return location; }
576 
577   template <typename A>
578   mlir::Value genunbox(const A &expr) {
579     ExtValue e = genval(expr);
580     if (const fir::UnboxedValue *r = e.getUnboxed())
581       return *r;
582     fir::emitFatalError(getLoc(), "unboxed expression expected");
583   }
584 
585   /// Generate an integral constant of `value`
586   template <int KIND>
587   mlir::Value genIntegerConstant(mlir::MLIRContext *context,
588                                  std::int64_t value) {
589     mlir::Type type =
590         converter.genType(Fortran::common::TypeCategory::Integer, KIND);
591     return builder.createIntegerConstant(getLoc(), type, value);
592   }
593 
594   /// Generate a logical/boolean constant of `value`
595   mlir::Value genBoolConstant(bool value) {
596     return builder.createBool(getLoc(), value);
597   }
598 
599   /// Generate a real constant with a value `value`.
600   template <int KIND>
601   mlir::Value genRealConstant(mlir::MLIRContext *context,
602                               const llvm::APFloat &value) {
603     mlir::Type fltTy = Fortran::lower::convertReal(context, KIND);
604     return builder.createRealConstant(getLoc(), fltTy, value);
605   }
606 
607   template <typename OpTy>
608   mlir::Value createCompareOp(mlir::arith::CmpIPredicate pred,
609                               const ExtValue &left, const ExtValue &right) {
610     if (const fir::UnboxedValue *lhs = left.getUnboxed())
611       if (const fir::UnboxedValue *rhs = right.getUnboxed())
612         return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
613     fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
614   }
615   template <typename OpTy, typename A>
616   mlir::Value createCompareOp(const A &ex, mlir::arith::CmpIPredicate pred) {
617     ExtValue left = genval(ex.left());
618     return createCompareOp<OpTy>(pred, left, genval(ex.right()));
619   }
620 
621   template <typename OpTy>
622   mlir::Value createFltCmpOp(mlir::arith::CmpFPredicate pred,
623                              const ExtValue &left, const ExtValue &right) {
624     if (const fir::UnboxedValue *lhs = left.getUnboxed())
625       if (const fir::UnboxedValue *rhs = right.getUnboxed())
626         return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
627     fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
628   }
629   template <typename OpTy, typename A>
630   mlir::Value createFltCmpOp(const A &ex, mlir::arith::CmpFPredicate pred) {
631     ExtValue left = genval(ex.left());
632     return createFltCmpOp<OpTy>(pred, left, genval(ex.right()));
633   }
634 
635   /// Returns a reference to a symbol or its box/boxChar descriptor if it has
636   /// one.
637   ExtValue gen(Fortran::semantics::SymbolRef sym) {
638     if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
639       return val.match(
640           [&](const Fortran::lower::SymbolBox::PointerOrAllocatable &boxAddr) {
641             return fir::factory::genMutableBoxRead(builder, getLoc(), boxAddr);
642           },
643           [&val](auto &) { return val.toExtendedValue(); });
644     LLVM_DEBUG(llvm::dbgs()
645                << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
646     llvm::errs() << "SYM: " << sym << "\n";
647     fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
648   }
649 
650   ExtValue genLoad(const ExtValue &exv) {
651     return ::genLoad(builder, getLoc(), exv);
652   }
653 
654   ExtValue genval(Fortran::semantics::SymbolRef sym) {
655     ExtValue var = gen(sym);
656     if (const fir::UnboxedValue *s = var.getUnboxed())
657       if (fir::isReferenceLike(s->getType()))
658         return genLoad(*s);
659     return var;
660   }
661 
662   ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
663     TODO(getLoc(), "genval BOZ");
664   }
665 
666   /// Return indirection to function designated in ProcedureDesignator.
667   /// The type of the function indirection is not guaranteed to match the one
668   /// of the ProcedureDesignator due to Fortran implicit typing rules.
669   ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
670     TODO(getLoc(), "genval ProcedureDesignator");
671   }
672 
673   ExtValue genval(const Fortran::evaluate::NullPointer &) {
674     TODO(getLoc(), "genval NullPointer");
675   }
676 
677   static bool
678   isDerivedTypeWithLengthParameters(const Fortran::semantics::Symbol &sym) {
679     if (const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType())
680       if (const Fortran::semantics::DerivedTypeSpec *derived =
681               declTy->AsDerived())
682         return Fortran::semantics::CountLenParameters(*derived) > 0;
683     return false;
684   }
685 
686   static bool isBuiltinCPtr(const Fortran::semantics::Symbol &sym) {
687     if (const Fortran::semantics::DeclTypeSpec *declType = sym.GetType())
688       if (const Fortran::semantics::DerivedTypeSpec *derived =
689               declType->AsDerived())
690         return Fortran::semantics::IsIsoCType(derived);
691     return false;
692   }
693 
694   /// Lower structure constructor without a temporary. This can be used in
695   /// fir::GloablOp, and assumes that the structure component is a constant.
696   ExtValue genStructComponentInInitializer(
697       const Fortran::evaluate::StructureConstructor &ctor) {
698     mlir::Location loc = getLoc();
699     mlir::Type ty = translateSomeExprToFIRType(converter, toEvExpr(ctor));
700     auto recTy = ty.cast<fir::RecordType>();
701     auto fieldTy = fir::FieldType::get(ty.getContext());
702     mlir::Value res = builder.create<fir::UndefOp>(loc, recTy);
703 
704     for (const auto &[sym, expr] : ctor.values()) {
705       // Parent components need more work because they do not appear in the
706       // fir.rec type.
707       if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp))
708         TODO(loc, "parent component in structure constructor");
709 
710       llvm::StringRef name = toStringRef(sym->name());
711       mlir::Type componentTy = recTy.getType(name);
712       // FIXME: type parameters must come from the derived-type-spec
713       auto field = builder.create<fir::FieldIndexOp>(
714           loc, fieldTy, name, ty,
715           /*typeParams=*/mlir::ValueRange{} /*TODO*/);
716 
717       if (Fortran::semantics::IsAllocatable(sym))
718         TODO(loc, "allocatable component in structure constructor");
719 
720       if (Fortran::semantics::IsPointer(sym)) {
721         mlir::Value initialTarget = Fortran::lower::genInitialDataTarget(
722             converter, loc, componentTy, expr.value());
723         res = builder.create<fir::InsertValueOp>(
724             loc, recTy, res, initialTarget,
725             builder.getArrayAttr(field.getAttributes()));
726         continue;
727       }
728 
729       if (isDerivedTypeWithLengthParameters(sym))
730         TODO(loc, "component with length parameters in structure constructor");
731 
732       if (isBuiltinCPtr(sym)) {
733         // Builtin c_ptr and c_funptr have special handling because initial
734         // value are handled for them as an extension.
735         mlir::Value addr = fir::getBase(Fortran::lower::genExtAddrInInitializer(
736             converter, loc, expr.value()));
737         if (addr.getType() == componentTy) {
738           // Do nothing. The Ev::Expr was returned as a value that can be
739           // inserted directly to the component without an intermediary.
740         } else {
741           // The Ev::Expr returned is an initializer that is a pointer (e.g.,
742           // null) that must be inserted into an intermediate cptr record
743           // value's address field, which ought to be an intptr_t on the target.
744           assert((fir::isa_ref_type(addr.getType()) ||
745                   addr.getType().isa<mlir::FunctionType>()) &&
746                  "expect reference type for address field");
747           assert(fir::isa_derived(componentTy) &&
748                  "expect C_PTR, C_FUNPTR to be a record");
749           auto cPtrRecTy = componentTy.cast<fir::RecordType>();
750           llvm::StringRef addrFieldName =
751               Fortran::lower::builtin::cptrFieldName;
752           mlir::Type addrFieldTy = cPtrRecTy.getType(addrFieldName);
753           auto addrField = builder.create<fir::FieldIndexOp>(
754               loc, fieldTy, addrFieldName, componentTy,
755               /*typeParams=*/mlir::ValueRange{});
756           mlir::Value castAddr = builder.createConvert(loc, addrFieldTy, addr);
757           auto undef = builder.create<fir::UndefOp>(loc, componentTy);
758           addr = builder.create<fir::InsertValueOp>(
759               loc, componentTy, undef, castAddr,
760               builder.getArrayAttr(addrField.getAttributes()));
761         }
762         res = builder.create<fir::InsertValueOp>(
763             loc, recTy, res, addr, builder.getArrayAttr(field.getAttributes()));
764         continue;
765       }
766 
767       mlir::Value val = fir::getBase(genval(expr.value()));
768       assert(!fir::isa_ref_type(val.getType()) && "expecting a constant value");
769       mlir::Value castVal = builder.createConvert(loc, componentTy, val);
770       res = builder.create<fir::InsertValueOp>(
771           loc, recTy, res, castVal,
772           builder.getArrayAttr(field.getAttributes()));
773     }
774     return res;
775   }
776 
777   /// A structure constructor is lowered two ways. In an initializer context,
778   /// the entire structure must be constant, so the aggregate value is
779   /// constructed inline. This allows it to be the body of a GlobalOp.
780   /// Otherwise, the structure constructor is in an expression. In that case, a
781   /// temporary object is constructed in the stack frame of the procedure.
782   ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
783     if (inInitializer)
784       return genStructComponentInInitializer(ctor);
785     mlir::Location loc = getLoc();
786     mlir::Type ty = translateSomeExprToFIRType(converter, toEvExpr(ctor));
787     auto recTy = ty.cast<fir::RecordType>();
788     auto fieldTy = fir::FieldType::get(ty.getContext());
789     mlir::Value res = builder.createTemporary(loc, recTy);
790 
791     for (const auto &value : ctor.values()) {
792       const Fortran::semantics::Symbol &sym = *value.first;
793       const Fortran::lower::SomeExpr &expr = value.second.value();
794       // Parent components need more work because they do not appear in the
795       // fir.rec type.
796       if (sym.test(Fortran::semantics::Symbol::Flag::ParentComp))
797         TODO(loc, "parent component in structure constructor");
798 
799       if (isDerivedTypeWithLengthParameters(sym))
800         TODO(loc, "component with length parameters in structure constructor");
801 
802       llvm::StringRef name = toStringRef(sym.name());
803       // FIXME: type parameters must come from the derived-type-spec
804       mlir::Value field = builder.create<fir::FieldIndexOp>(
805           loc, fieldTy, name, ty,
806           /*typeParams=*/mlir::ValueRange{} /*TODO*/);
807       mlir::Type coorTy = builder.getRefType(recTy.getType(name));
808       auto coor = builder.create<fir::CoordinateOp>(loc, coorTy,
809                                                     fir::getBase(res), field);
810       ExtValue to = fir::factory::componentToExtendedValue(builder, loc, coor);
811       to.match(
812           [&](const fir::UnboxedValue &toPtr) {
813             ExtValue value = genval(expr);
814             fir::factory::genScalarAssignment(builder, loc, to, value);
815           },
816           [&](const fir::CharBoxValue &) {
817             ExtValue value = genval(expr);
818             fir::factory::genScalarAssignment(builder, loc, to, value);
819           },
820           [&](const fir::ArrayBoxValue &) {
821             Fortran::lower::createSomeArrayAssignment(converter, to, expr,
822                                                       symMap, stmtCtx);
823           },
824           [&](const fir::CharArrayBoxValue &) {
825             Fortran::lower::createSomeArrayAssignment(converter, to, expr,
826                                                       symMap, stmtCtx);
827           },
828           [&](const fir::BoxValue &toBox) {
829             fir::emitFatalError(loc, "derived type components must not be "
830                                      "represented by fir::BoxValue");
831           },
832           [&](const fir::MutableBoxValue &toBox) {
833             if (toBox.isPointer()) {
834               Fortran::lower::associateMutableBox(
835                   converter, loc, toBox, expr, /*lbounds=*/llvm::None, stmtCtx);
836               return;
837             }
838             // For allocatable components, a deep copy is needed.
839             TODO(loc, "allocatable components in derived type assignment");
840           },
841           [&](const fir::ProcBoxValue &toBox) {
842             TODO(loc, "procedure pointer component in derived type assignment");
843           });
844     }
845     return res;
846   }
847 
848   /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
849   ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
850     return converter.impliedDoBinding(toStringRef(var.name));
851   }
852 
853   ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
854     ExtValue exv = desc.base().IsSymbol() ? gen(desc.base().GetLastSymbol())
855                                           : gen(desc.base().GetComponent());
856     mlir::IndexType idxTy = builder.getIndexType();
857     mlir::Location loc = getLoc();
858     auto castResult = [&](mlir::Value v) {
859       using ResTy = Fortran::evaluate::DescriptorInquiry::Result;
860       return builder.createConvert(
861           loc, converter.genType(ResTy::category, ResTy::kind), v);
862     };
863     switch (desc.field()) {
864     case Fortran::evaluate::DescriptorInquiry::Field::Len:
865       return castResult(fir::factory::readCharLen(builder, loc, exv));
866     case Fortran::evaluate::DescriptorInquiry::Field::LowerBound:
867       return castResult(fir::factory::readLowerBound(
868           builder, loc, exv, desc.dimension(),
869           builder.createIntegerConstant(loc, idxTy, 1)));
870     case Fortran::evaluate::DescriptorInquiry::Field::Extent:
871       return castResult(
872           fir::factory::readExtent(builder, loc, exv, desc.dimension()));
873     case Fortran::evaluate::DescriptorInquiry::Field::Rank:
874       TODO(loc, "rank inquiry on assumed rank");
875     case Fortran::evaluate::DescriptorInquiry::Field::Stride:
876       // So far the front end does not generate this inquiry.
877       TODO(loc, "Stride inquiry");
878     }
879     llvm_unreachable("unknown descriptor inquiry");
880   }
881 
882   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
883     TODO(getLoc(), "genval TypeParamInquiry");
884   }
885 
886   template <int KIND>
887   ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
888     TODO(getLoc(), "genval ComplexComponent");
889   }
890 
891   template <int KIND>
892   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
893                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
894     mlir::Value input = genunbox(op.left());
895     // Like LLVM, integer negation is the binary op "0 - value"
896     mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
897     return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
898   }
899 
900   template <int KIND>
901   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
902                       Fortran::common::TypeCategory::Real, KIND>> &op) {
903     return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
904   }
905   template <int KIND>
906   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
907                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
908     return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
909   }
910 
911   template <typename OpTy>
912   mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
913     assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
914     mlir::Value lhs = fir::getBase(left);
915     mlir::Value rhs = fir::getBase(right);
916     assert(lhs.getType() == rhs.getType() && "types must be the same");
917     return builder.create<OpTy>(getLoc(), lhs, rhs);
918   }
919 
920   template <typename OpTy, typename A>
921   mlir::Value createBinaryOp(const A &ex) {
922     ExtValue left = genval(ex.left());
923     return createBinaryOp<OpTy>(left, genval(ex.right()));
924   }
925 
926 #undef GENBIN
927 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
928   template <int KIND>                                                          \
929   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
930                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
931     return createBinaryOp<GenBinFirOp>(x);                                     \
932   }
933 
934   GENBIN(Add, Integer, mlir::arith::AddIOp)
935   GENBIN(Add, Real, mlir::arith::AddFOp)
936   GENBIN(Add, Complex, fir::AddcOp)
937   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
938   GENBIN(Subtract, Real, mlir::arith::SubFOp)
939   GENBIN(Subtract, Complex, fir::SubcOp)
940   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
941   GENBIN(Multiply, Real, mlir::arith::MulFOp)
942   GENBIN(Multiply, Complex, fir::MulcOp)
943   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
944   GENBIN(Divide, Real, mlir::arith::DivFOp)
945   GENBIN(Divide, Complex, fir::DivcOp)
946 
947   template <Fortran::common::TypeCategory TC, int KIND>
948   ExtValue genval(
949       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
950     mlir::Type ty = converter.genType(TC, KIND);
951     mlir::Value lhs = genunbox(op.left());
952     mlir::Value rhs = genunbox(op.right());
953     return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
954   }
955 
956   template <Fortran::common::TypeCategory TC, int KIND>
957   ExtValue genval(
958       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
959           &op) {
960     mlir::Type ty = converter.genType(TC, KIND);
961     mlir::Value lhs = genunbox(op.left());
962     mlir::Value rhs = genunbox(op.right());
963     return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
964   }
965 
966   template <int KIND>
967   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
968     mlir::Value realPartValue = genunbox(op.left());
969     return fir::factory::Complex{builder, getLoc()}.createComplex(
970         KIND, realPartValue, genunbox(op.right()));
971   }
972 
973   template <int KIND>
974   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
975     ExtValue lhs = genval(op.left());
976     ExtValue rhs = genval(op.right());
977     const fir::CharBoxValue *lhsChar = lhs.getCharBox();
978     const fir::CharBoxValue *rhsChar = rhs.getCharBox();
979     if (lhsChar && rhsChar)
980       return fir::factory::CharacterExprHelper{builder, getLoc()}
981           .createConcatenate(*lhsChar, *rhsChar);
982     TODO(getLoc(), "character array concatenate");
983   }
984 
985   /// MIN and MAX operations
986   template <Fortran::common::TypeCategory TC, int KIND>
987   ExtValue
988   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
989              &op) {
990     TODO(getLoc(), "genval Extremum<TC, KIND>");
991   }
992 
993   template <int KIND>
994   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
995     TODO(getLoc(), "genval SetLength<KIND>");
996   }
997 
998   template <int KIND>
999   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
1000                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
1001     return createCompareOp<mlir::arith::CmpIOp>(op,
1002                                                 translateRelational(op.opr));
1003   }
1004   template <int KIND>
1005   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
1006                       Fortran::common::TypeCategory::Real, KIND>> &op) {
1007     return createFltCmpOp<mlir::arith::CmpFOp>(
1008         op, translateFloatRelational(op.opr));
1009   }
1010   template <int KIND>
1011   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
1012                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
1013     TODO(getLoc(), "genval complex comparison");
1014   }
1015   template <int KIND>
1016   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
1017                       Fortran::common::TypeCategory::Character, KIND>> &op) {
1018     TODO(getLoc(), "genval char comparison");
1019   }
1020 
1021   ExtValue
1022   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
1023     return std::visit([&](const auto &x) { return genval(x); }, op.u);
1024   }
1025 
1026   template <Fortran::common::TypeCategory TC1, int KIND,
1027             Fortran::common::TypeCategory TC2>
1028   ExtValue
1029   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
1030                                           TC2> &convert) {
1031     mlir::Type ty = converter.genType(TC1, KIND);
1032     mlir::Value operand = genunbox(convert.left());
1033     return builder.convertWithSemantics(getLoc(), ty, operand);
1034   }
1035 
1036   template <typename A>
1037   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
1038     TODO(getLoc(), "genval parentheses<A>");
1039   }
1040 
1041   template <int KIND>
1042   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
1043     mlir::Value logical = genunbox(op.left());
1044     mlir::Value one = genBoolConstant(true);
1045     mlir::Value val =
1046         builder.createConvert(getLoc(), builder.getI1Type(), logical);
1047     return builder.create<mlir::arith::XOrIOp>(getLoc(), val, one);
1048   }
1049 
1050   template <int KIND>
1051   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
1052     mlir::IntegerType i1Type = builder.getI1Type();
1053     mlir::Value slhs = genunbox(op.left());
1054     mlir::Value srhs = genunbox(op.right());
1055     mlir::Value lhs = builder.createConvert(getLoc(), i1Type, slhs);
1056     mlir::Value rhs = builder.createConvert(getLoc(), i1Type, srhs);
1057     switch (op.logicalOperator) {
1058     case Fortran::evaluate::LogicalOperator::And:
1059       return createBinaryOp<mlir::arith::AndIOp>(lhs, rhs);
1060     case Fortran::evaluate::LogicalOperator::Or:
1061       return createBinaryOp<mlir::arith::OrIOp>(lhs, rhs);
1062     case Fortran::evaluate::LogicalOperator::Eqv:
1063       return createCompareOp<mlir::arith::CmpIOp>(
1064           mlir::arith::CmpIPredicate::eq, lhs, rhs);
1065     case Fortran::evaluate::LogicalOperator::Neqv:
1066       return createCompareOp<mlir::arith::CmpIOp>(
1067           mlir::arith::CmpIPredicate::ne, lhs, rhs);
1068     case Fortran::evaluate::LogicalOperator::Not:
1069       // lib/evaluate expression for .NOT. is Fortran::evaluate::Not<KIND>.
1070       llvm_unreachable(".NOT. is not a binary operator");
1071     }
1072     llvm_unreachable("unhandled logical operation");
1073   }
1074 
1075   /// Convert a scalar literal constant to IR.
1076   template <Fortran::common::TypeCategory TC, int KIND>
1077   ExtValue genScalarLit(
1078       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
1079           &value) {
1080     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
1081       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
1082     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
1083       return genBoolConstant(value.IsTrue());
1084     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
1085       std::string str = value.DumpHexadecimal();
1086       if constexpr (KIND == 2) {
1087         llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str};
1088         return genRealConstant<KIND>(builder.getContext(), floatVal);
1089       } else if constexpr (KIND == 3) {
1090         llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str};
1091         return genRealConstant<KIND>(builder.getContext(), floatVal);
1092       } else if constexpr (KIND == 4) {
1093         llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str};
1094         return genRealConstant<KIND>(builder.getContext(), floatVal);
1095       } else if constexpr (KIND == 10) {
1096         llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str};
1097         return genRealConstant<KIND>(builder.getContext(), floatVal);
1098       } else if constexpr (KIND == 16) {
1099         llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str};
1100         return genRealConstant<KIND>(builder.getContext(), floatVal);
1101       } else {
1102         // convert everything else to double
1103         llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str};
1104         return genRealConstant<KIND>(builder.getContext(), floatVal);
1105       }
1106     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
1107       using TR =
1108           Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>;
1109       Fortran::evaluate::ComplexConstructor<KIND> ctor(
1110           Fortran::evaluate::Expr<TR>{
1111               Fortran::evaluate::Constant<TR>{value.REAL()}},
1112           Fortran::evaluate::Expr<TR>{
1113               Fortran::evaluate::Constant<TR>{value.AIMAG()}});
1114       return genunbox(ctor);
1115     } else /*constexpr*/ {
1116       llvm_unreachable("unhandled constant");
1117     }
1118   }
1119 
1120   /// Generate a raw literal value and store it in the rawVals vector.
1121   template <Fortran::common::TypeCategory TC, int KIND>
1122   void
1123   genRawLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
1124                 &value) {
1125     mlir::Attribute val;
1126     assert(inInitializer != nullptr);
1127     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
1128       inInitializer->rawType = converter.genType(TC, KIND);
1129       val = builder.getIntegerAttr(inInitializer->rawType, value.ToInt64());
1130     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
1131       inInitializer->rawType =
1132           converter.genType(Fortran::common::TypeCategory::Integer, KIND);
1133       val = builder.getIntegerAttr(inInitializer->rawType, value.IsTrue());
1134     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
1135       std::string str = value.DumpHexadecimal();
1136       inInitializer->rawType = converter.genType(TC, KIND);
1137       llvm::APFloat floatVal{builder.getKindMap().getFloatSemantics(KIND), str};
1138       val = builder.getFloatAttr(inInitializer->rawType, floatVal);
1139     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
1140       std::string strReal = value.REAL().DumpHexadecimal();
1141       std::string strImg = value.AIMAG().DumpHexadecimal();
1142       inInitializer->rawType = converter.genType(TC, KIND);
1143       llvm::APFloat realVal{builder.getKindMap().getFloatSemantics(KIND),
1144                             strReal};
1145       val = builder.getFloatAttr(inInitializer->rawType, realVal);
1146       inInitializer->rawVals.push_back(val);
1147       llvm::APFloat imgVal{builder.getKindMap().getFloatSemantics(KIND),
1148                            strImg};
1149       val = builder.getFloatAttr(inInitializer->rawType, imgVal);
1150     }
1151     inInitializer->rawVals.push_back(val);
1152   }
1153 
1154   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
1155   ExtValue
1156   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
1157                         Fortran::common::TypeCategory::Character, 1>> &value,
1158                     int64_t len) {
1159     assert(value.size() == static_cast<std::uint64_t>(len));
1160     // Outline character constant in ro data if it is not in an initializer.
1161     if (!inInitializer)
1162       return fir::factory::createStringLiteral(builder, getLoc(), value);
1163     // When in an initializer context, construct the literal op itself and do
1164     // not construct another constant object in rodata.
1165     fir::StringLitOp stringLit = builder.createStringLitOp(getLoc(), value);
1166     mlir::Value lenp = builder.createIntegerConstant(
1167         getLoc(), builder.getCharacterLengthType(), len);
1168     return fir::CharBoxValue{stringLit.getResult(), lenp};
1169   }
1170   /// Convert a non ascii scalar literal CHARACTER to IR. (specialization)
1171   template <int KIND>
1172   ExtValue
1173   genScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
1174                    Fortran::common::TypeCategory::Character, KIND>> &value,
1175                int64_t len) {
1176     using ET = typename std::decay_t<decltype(value)>::value_type;
1177     if constexpr (KIND == 1) {
1178       return genAsciiScalarLit(value, len);
1179     }
1180     fir::CharacterType type =
1181         fir::CharacterType::get(builder.getContext(), KIND, len);
1182     auto consLit = [&]() -> fir::StringLitOp {
1183       mlir::MLIRContext *context = builder.getContext();
1184       std::int64_t size = static_cast<std::int64_t>(value.size());
1185       mlir::ShapedType shape = mlir::VectorType::get(
1186           llvm::ArrayRef<std::int64_t>{size},
1187           mlir::IntegerType::get(builder.getContext(), sizeof(ET) * 8));
1188       auto strAttr = mlir::DenseElementsAttr::get(
1189           shape, llvm::ArrayRef<ET>{value.data(), value.size()});
1190       auto valTag = mlir::StringAttr::get(context, fir::StringLitOp::value());
1191       mlir::NamedAttribute dataAttr(valTag, strAttr);
1192       auto sizeTag = mlir::StringAttr::get(context, fir::StringLitOp::size());
1193       mlir::NamedAttribute sizeAttr(sizeTag, builder.getI64IntegerAttr(len));
1194       llvm::SmallVector<mlir::NamedAttribute> attrs = {dataAttr, sizeAttr};
1195       return builder.create<fir::StringLitOp>(
1196           getLoc(), llvm::ArrayRef<mlir::Type>{type}, llvm::None, attrs);
1197     };
1198 
1199     mlir::Value lenp = builder.createIntegerConstant(
1200         getLoc(), builder.getCharacterLengthType(), len);
1201     // When in an initializer context, construct the literal op itself and do
1202     // not construct another constant object in rodata.
1203     if (inInitializer)
1204       return fir::CharBoxValue{consLit().getResult(), lenp};
1205 
1206     // Otherwise, the string is in a plain old expression so "outline" the value
1207     // by hashconsing it to a constant literal object.
1208 
1209     // FIXME: For wider char types, lowering ought to use an array of i16 or
1210     // i32. But for now, lowering just fakes that the string value is a range of
1211     // i8 to get it past the C++ compiler.
1212     std::string globalName =
1213         fir::factory::uniqueCGIdent("cl", (const char *)value.c_str());
1214     fir::GlobalOp global = builder.getNamedGlobal(globalName);
1215     if (!global)
1216       global = builder.createGlobalConstant(
1217           getLoc(), type, globalName,
1218           [&](fir::FirOpBuilder &builder) {
1219             fir::StringLitOp str = consLit();
1220             builder.create<fir::HasValueOp>(getLoc(), str);
1221           },
1222           builder.createLinkOnceLinkage());
1223     auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(),
1224                                               global.getSymbol());
1225     return fir::CharBoxValue{addr, lenp};
1226   }
1227 
1228   template <Fortran::common::TypeCategory TC, int KIND>
1229   ExtValue genArrayLit(
1230       const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
1231           &con) {
1232     mlir::Location loc = getLoc();
1233     mlir::IndexType idxTy = builder.getIndexType();
1234     Fortran::evaluate::ConstantSubscript size =
1235         Fortran::evaluate::GetSize(con.shape());
1236     fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end());
1237     mlir::Type eleTy;
1238     if constexpr (TC == Fortran::common::TypeCategory::Character)
1239       eleTy = converter.genType(TC, KIND, {con.LEN()});
1240     else
1241       eleTy = converter.genType(TC, KIND);
1242     auto arrayTy = fir::SequenceType::get(shape, eleTy);
1243     mlir::Value array;
1244     llvm::SmallVector<mlir::Value> lbounds;
1245     llvm::SmallVector<mlir::Value> extents;
1246     if (!inInitializer || !inInitializer->genRawVals) {
1247       array = builder.create<fir::UndefOp>(loc, arrayTy);
1248       for (auto [lb, extent] : llvm::zip(con.lbounds(), shape)) {
1249         lbounds.push_back(builder.createIntegerConstant(loc, idxTy, lb - 1));
1250         extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
1251       }
1252     }
1253     if (size == 0) {
1254       if constexpr (TC == Fortran::common::TypeCategory::Character) {
1255         mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN());
1256         return fir::CharArrayBoxValue{array, len, extents, lbounds};
1257       } else {
1258         return fir::ArrayBoxValue{array, extents, lbounds};
1259       }
1260     }
1261     Fortran::evaluate::ConstantSubscripts subscripts = con.lbounds();
1262     auto createIdx = [&]() {
1263       llvm::SmallVector<mlir::Attribute> idx;
1264       for (size_t i = 0; i < subscripts.size(); ++i)
1265         idx.push_back(
1266             builder.getIntegerAttr(idxTy, subscripts[i] - con.lbounds()[i]));
1267       return idx;
1268     };
1269     if constexpr (TC == Fortran::common::TypeCategory::Character) {
1270       assert(array && "array must not be nullptr");
1271       do {
1272         mlir::Value elementVal =
1273             fir::getBase(genScalarLit<KIND>(con.At(subscripts), con.LEN()));
1274         array = builder.create<fir::InsertValueOp>(
1275             loc, arrayTy, array, elementVal, builder.getArrayAttr(createIdx()));
1276       } while (con.IncrementSubscripts(subscripts));
1277       mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN());
1278       return fir::CharArrayBoxValue{array, len, extents, lbounds};
1279     } else {
1280       llvm::SmallVector<mlir::Attribute> rangeStartIdx;
1281       uint64_t rangeSize = 0;
1282       do {
1283         if (inInitializer && inInitializer->genRawVals) {
1284           genRawLit<TC, KIND>(con.At(subscripts));
1285           continue;
1286         }
1287         auto getElementVal = [&]() {
1288           return builder.createConvert(
1289               loc, eleTy,
1290               fir::getBase(genScalarLit<TC, KIND>(con.At(subscripts))));
1291         };
1292         Fortran::evaluate::ConstantSubscripts nextSubscripts = subscripts;
1293         bool nextIsSame = con.IncrementSubscripts(nextSubscripts) &&
1294                           con.At(subscripts) == con.At(nextSubscripts);
1295         if (!rangeSize && !nextIsSame) { // single (non-range) value
1296           array = builder.create<fir::InsertValueOp>(
1297               loc, arrayTy, array, getElementVal(),
1298               builder.getArrayAttr(createIdx()));
1299         } else if (!rangeSize) { // start a range
1300           rangeStartIdx = createIdx();
1301           rangeSize = 1;
1302         } else if (nextIsSame) { // expand a range
1303           ++rangeSize;
1304         } else { // end a range
1305           llvm::SmallVector<int64_t> rangeBounds;
1306           llvm::SmallVector<mlir::Attribute> idx = createIdx();
1307           for (size_t i = 0; i < idx.size(); ++i) {
1308             rangeBounds.push_back(rangeStartIdx[i]
1309                                       .cast<mlir::IntegerAttr>()
1310                                       .getValue()
1311                                       .getSExtValue());
1312             rangeBounds.push_back(
1313                 idx[i].cast<mlir::IntegerAttr>().getValue().getSExtValue());
1314           }
1315           array = builder.create<fir::InsertOnRangeOp>(
1316               loc, arrayTy, array, getElementVal(),
1317               builder.getIndexVectorAttr(rangeBounds));
1318           rangeSize = 0;
1319         }
1320       } while (con.IncrementSubscripts(subscripts));
1321       return fir::ArrayBoxValue{array, extents, lbounds};
1322     }
1323   }
1324 
1325   fir::ExtendedValue genArrayLit(
1326       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
1327     mlir::Location loc = getLoc();
1328     mlir::IndexType idxTy = builder.getIndexType();
1329     Fortran::evaluate::ConstantSubscript size =
1330         Fortran::evaluate::GetSize(con.shape());
1331     fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end());
1332     mlir::Type eleTy = converter.genType(con.GetType().GetDerivedTypeSpec());
1333     auto arrayTy = fir::SequenceType::get(shape, eleTy);
1334     mlir::Value array = builder.create<fir::UndefOp>(loc, arrayTy);
1335     llvm::SmallVector<mlir::Value> lbounds;
1336     llvm::SmallVector<mlir::Value> extents;
1337     for (auto [lb, extent] : llvm::zip(con.lbounds(), con.shape())) {
1338       lbounds.push_back(builder.createIntegerConstant(loc, idxTy, lb - 1));
1339       extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
1340     }
1341     if (size == 0)
1342       return fir::ArrayBoxValue{array, extents, lbounds};
1343     Fortran::evaluate::ConstantSubscripts subscripts = con.lbounds();
1344     do {
1345       mlir::Value derivedVal = fir::getBase(genval(con.At(subscripts)));
1346       llvm::SmallVector<mlir::Attribute> idx;
1347       for (auto [dim, lb] : llvm::zip(subscripts, con.lbounds()))
1348         idx.push_back(builder.getIntegerAttr(idxTy, dim - lb));
1349       array = builder.create<fir::InsertValueOp>(
1350           loc, arrayTy, array, derivedVal, builder.getArrayAttr(idx));
1351     } while (con.IncrementSubscripts(subscripts));
1352     return fir::ArrayBoxValue{array, extents, lbounds};
1353   }
1354 
1355   template <Fortran::common::TypeCategory TC, int KIND>
1356   ExtValue
1357   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
1358              &con) {
1359     if (con.Rank() > 0)
1360       return genArrayLit(con);
1361     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
1362         opt = con.GetScalarValue();
1363     assert(opt.has_value() && "constant has no value");
1364     if constexpr (TC == Fortran::common::TypeCategory::Character) {
1365       return genScalarLit<KIND>(opt.value(), con.LEN());
1366     } else {
1367       return genScalarLit<TC, KIND>(opt.value());
1368     }
1369   }
1370 
1371   fir::ExtendedValue genval(
1372       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
1373     if (con.Rank() > 0)
1374       return genArrayLit(con);
1375     if (auto ctor = con.GetScalarValue())
1376       return genval(ctor.value());
1377     fir::emitFatalError(getLoc(),
1378                         "constant of derived type has no constructor");
1379   }
1380 
1381   template <typename A>
1382   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
1383     TODO(getLoc(), "genval ArrayConstructor<A>");
1384   }
1385 
1386   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
1387     TODO(getLoc(), "gen ComplexPart");
1388   }
1389   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
1390     TODO(getLoc(), "genval ComplexPart");
1391   }
1392 
1393   ExtValue gen(const Fortran::evaluate::Substring &s) {
1394     TODO(getLoc(), "gen Substring");
1395   }
1396   ExtValue genval(const Fortran::evaluate::Substring &ss) {
1397     TODO(getLoc(), "genval Substring");
1398   }
1399 
1400   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
1401     if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>(
1402             &subs.u)) {
1403       if (s->value().Rank() > 0)
1404         fir::emitFatalError(getLoc(), "vector subscript is not scalar");
1405       return {genval(s->value())};
1406     }
1407     fir::emitFatalError(getLoc(), "subscript triple notation is not scalar");
1408   }
1409 
1410   ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) {
1411     return genval(subs);
1412   }
1413 
1414   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
1415     return std::visit([&](const auto &x) { return gen(x); }, dref.u);
1416   }
1417   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
1418     return std::visit([&](const auto &x) { return genval(x); }, dref.u);
1419   }
1420 
1421   // Helper function to turn the Component structure into a list of nested
1422   // components, ordered from largest/leftmost to smallest/rightmost:
1423   //  - where only the smallest/rightmost item may be allocatable or a pointer
1424   //    (nested allocatable/pointer components require nested coordinate_of ops)
1425   //  - that does not contain any parent components
1426   //    (the front end places parent components directly in the object)
1427   // Return the object used as the base coordinate for the component chain.
1428   static Fortran::evaluate::DataRef const *
1429   reverseComponents(const Fortran::evaluate::Component &cmpt,
1430                     std::list<const Fortran::evaluate::Component *> &list) {
1431     if (!cmpt.GetLastSymbol().test(
1432             Fortran::semantics::Symbol::Flag::ParentComp))
1433       list.push_front(&cmpt);
1434     return std::visit(
1435         Fortran::common::visitors{
1436             [&](const Fortran::evaluate::Component &x) {
1437               if (Fortran::semantics::IsAllocatableOrPointer(x.GetLastSymbol()))
1438                 return &cmpt.base();
1439               return reverseComponents(x, list);
1440             },
1441             [&](auto &) { return &cmpt.base(); },
1442         },
1443         cmpt.base().u);
1444   }
1445 
1446   // Return the coordinate of the component reference
1447   ExtValue genComponent(const Fortran::evaluate::Component &cmpt) {
1448     std::list<const Fortran::evaluate::Component *> list;
1449     const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list);
1450     llvm::SmallVector<mlir::Value> coorArgs;
1451     ExtValue obj = gen(*base);
1452     mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType());
1453     mlir::Location loc = getLoc();
1454     auto fldTy = fir::FieldType::get(&converter.getMLIRContext());
1455     // FIXME: need to thread the LEN type parameters here.
1456     for (const Fortran::evaluate::Component *field : list) {
1457       auto recTy = ty.cast<fir::RecordType>();
1458       const Fortran::semantics::Symbol &sym = field->GetLastSymbol();
1459       llvm::StringRef name = toStringRef(sym.name());
1460       coorArgs.push_back(builder.create<fir::FieldIndexOp>(
1461           loc, fldTy, name, recTy, fir::getTypeParams(obj)));
1462       ty = recTy.getType(name);
1463     }
1464     ty = builder.getRefType(ty);
1465     return fir::factory::componentToExtendedValue(
1466         builder, loc,
1467         builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj),
1468                                           coorArgs));
1469   }
1470 
1471   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
1472     // Components may be pointer or allocatable. In the gen() path, the mutable
1473     // aspect is lost to simplify handling on the client side. To retain the
1474     // mutable aspect, genMutableBoxValue should be used.
1475     return genComponent(cmpt).match(
1476         [&](const fir::MutableBoxValue &mutableBox) {
1477           return fir::factory::genMutableBoxRead(builder, getLoc(), mutableBox);
1478         },
1479         [](auto &box) -> ExtValue { return box; });
1480   }
1481 
1482   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
1483     return genLoad(gen(cmpt));
1484   }
1485 
1486   ExtValue genval(const Fortran::semantics::Bound &bound) {
1487     TODO(getLoc(), "genval Bound");
1488   }
1489 
1490   /// Return lower bounds of \p box in dimension \p dim. The returned value
1491   /// has type \ty.
1492   mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) {
1493     assert(box.rank() > 0 && "must be an array");
1494     mlir::Location loc = getLoc();
1495     mlir::Value one = builder.createIntegerConstant(loc, ty, 1);
1496     mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one);
1497     return builder.createConvert(loc, ty, lb);
1498   }
1499 
1500   static bool isSlice(const Fortran::evaluate::ArrayRef &aref) {
1501     for (const Fortran::evaluate::Subscript &sub : aref.subscript())
1502       if (std::holds_alternative<Fortran::evaluate::Triplet>(sub.u))
1503         return true;
1504     return false;
1505   }
1506 
1507   /// Lower an ArrayRef to a fir.coordinate_of given its lowered base.
1508   ExtValue genCoordinateOp(const ExtValue &array,
1509                            const Fortran::evaluate::ArrayRef &aref) {
1510     mlir::Location loc = getLoc();
1511     // References to array of rank > 1 with non constant shape that are not
1512     // fir.box must be collapsed into an offset computation in lowering already.
1513     // The same is needed with dynamic length character arrays of all ranks.
1514     mlir::Type baseType =
1515         fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType());
1516     if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) ||
1517         fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType)))
1518       if (!array.getBoxOf<fir::BoxValue>())
1519         return genOffsetAndCoordinateOp(array, aref);
1520     // Generate a fir.coordinate_of with zero based array indexes.
1521     llvm::SmallVector<mlir::Value> args;
1522     for (const auto &subsc : llvm::enumerate(aref.subscript())) {
1523       ExtValue subVal = genSubscript(subsc.value());
1524       assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar");
1525       mlir::Value val = fir::getBase(subVal);
1526       mlir::Type ty = val.getType();
1527       mlir::Value lb = getLBound(array, subsc.index(), ty);
1528       args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb));
1529     }
1530 
1531     mlir::Value base = fir::getBase(array);
1532     auto seqTy =
1533         fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>();
1534     assert(args.size() == seqTy.getDimension());
1535     mlir::Type ty = builder.getRefType(seqTy.getEleTy());
1536     auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args);
1537     return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr);
1538   }
1539 
1540   /// Lower an ArrayRef to a fir.coordinate_of using an element offset instead
1541   /// of array indexes.
1542   /// This generates offset computation from the indexes and length parameters,
1543   /// and use the offset to access the element with a fir.coordinate_of. This
1544   /// must only be used if it is not possible to generate a normal
1545   /// fir.coordinate_of using array indexes (i.e. when the shape information is
1546   /// unavailable in the IR).
1547   ExtValue genOffsetAndCoordinateOp(const ExtValue &array,
1548                                     const Fortran::evaluate::ArrayRef &aref) {
1549     mlir::Location loc = getLoc();
1550     mlir::Value addr = fir::getBase(array);
1551     mlir::Type arrTy = fir::dyn_cast_ptrEleTy(addr.getType());
1552     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
1553     mlir::Type seqTy = builder.getRefType(builder.getVarLenSeqTy(eleTy));
1554     mlir::Type refTy = builder.getRefType(eleTy);
1555     mlir::Value base = builder.createConvert(loc, seqTy, addr);
1556     mlir::IndexType idxTy = builder.getIndexType();
1557     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
1558     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
1559     auto getLB = [&](const auto &arr, unsigned dim) -> mlir::Value {
1560       return arr.getLBounds().empty() ? one : arr.getLBounds()[dim];
1561     };
1562     auto genFullDim = [&](const auto &arr, mlir::Value delta) -> mlir::Value {
1563       mlir::Value total = zero;
1564       assert(arr.getExtents().size() == aref.subscript().size());
1565       delta = builder.createConvert(loc, idxTy, delta);
1566       unsigned dim = 0;
1567       for (auto [ext, sub] : llvm::zip(arr.getExtents(), aref.subscript())) {
1568         ExtValue subVal = genSubscript(sub);
1569         assert(fir::isUnboxedValue(subVal));
1570         mlir::Value val =
1571             builder.createConvert(loc, idxTy, fir::getBase(subVal));
1572         mlir::Value lb = builder.createConvert(loc, idxTy, getLB(arr, dim));
1573         mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, val, lb);
1574         mlir::Value prod =
1575             builder.create<mlir::arith::MulIOp>(loc, delta, diff);
1576         total = builder.create<mlir::arith::AddIOp>(loc, prod, total);
1577         if (ext)
1578           delta = builder.create<mlir::arith::MulIOp>(loc, delta, ext);
1579         ++dim;
1580       }
1581       mlir::Type origRefTy = refTy;
1582       if (fir::factory::CharacterExprHelper::isCharacterScalar(refTy)) {
1583         fir::CharacterType chTy =
1584             fir::factory::CharacterExprHelper::getCharacterType(refTy);
1585         if (fir::characterWithDynamicLen(chTy)) {
1586           mlir::MLIRContext *ctx = builder.getContext();
1587           fir::KindTy kind =
1588               fir::factory::CharacterExprHelper::getCharacterKind(chTy);
1589           fir::CharacterType singleTy =
1590               fir::CharacterType::getSingleton(ctx, kind);
1591           refTy = builder.getRefType(singleTy);
1592           mlir::Type seqRefTy =
1593               builder.getRefType(builder.getVarLenSeqTy(singleTy));
1594           base = builder.createConvert(loc, seqRefTy, base);
1595         }
1596       }
1597       auto coor = builder.create<fir::CoordinateOp>(
1598           loc, refTy, base, llvm::ArrayRef<mlir::Value>{total});
1599       // Convert to expected, original type after address arithmetic.
1600       return builder.createConvert(loc, origRefTy, coor);
1601     };
1602     return array.match(
1603         [&](const fir::ArrayBoxValue &arr) -> ExtValue {
1604           // FIXME: this check can be removed when slicing is implemented
1605           if (isSlice(aref))
1606             fir::emitFatalError(
1607                 getLoc(),
1608                 "slice should be handled in array expression context");
1609           return genFullDim(arr, one);
1610         },
1611         [&](const fir::CharArrayBoxValue &arr) -> ExtValue {
1612           mlir::Value delta = arr.getLen();
1613           // If the length is known in the type, fir.coordinate_of will
1614           // already take the length into account.
1615           if (fir::factory::CharacterExprHelper::hasConstantLengthInType(arr))
1616             delta = one;
1617           return fir::CharBoxValue(genFullDim(arr, delta), arr.getLen());
1618         },
1619         [&](const fir::BoxValue &arr) -> ExtValue {
1620           // CoordinateOp for BoxValue is not generated here. The dimensions
1621           // must be kept in the fir.coordinate_op so that potential fir.box
1622           // strides can be applied by codegen.
1623           fir::emitFatalError(
1624               loc, "internal: BoxValue in dim-collapsed fir.coordinate_of");
1625         },
1626         [&](const auto &) -> ExtValue {
1627           fir::emitFatalError(loc, "internal: array lowering failed");
1628         });
1629   }
1630 
1631   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
1632     ExtValue base = aref.base().IsSymbol() ? gen(aref.base().GetFirstSymbol())
1633                                            : gen(aref.base().GetComponent());
1634     return genCoordinateOp(base, aref);
1635   }
1636   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
1637     return genLoad(gen(aref));
1638   }
1639 
1640   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
1641     TODO(getLoc(), "gen CoarrayRef");
1642   }
1643   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
1644     TODO(getLoc(), "genval CoarrayRef");
1645   }
1646 
1647   template <typename A>
1648   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
1649     return std::visit([&](const auto &x) { return gen(x); }, des.u);
1650   }
1651   template <typename A>
1652   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
1653     return std::visit([&](const auto &x) { return genval(x); }, des.u);
1654   }
1655 
1656   mlir::Type genType(const Fortran::evaluate::DynamicType &dt) {
1657     if (dt.category() != Fortran::common::TypeCategory::Derived)
1658       return converter.genType(dt.category(), dt.kind());
1659     return converter.genType(dt.GetDerivedTypeSpec());
1660   }
1661 
1662   /// Lower a function reference
1663   template <typename A>
1664   ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1665     if (!funcRef.GetType().has_value())
1666       fir::emitFatalError(getLoc(), "internal: a function must have a type");
1667     mlir::Type resTy = genType(*funcRef.GetType());
1668     return genProcedureRef(funcRef, {resTy});
1669   }
1670 
1671   /// Lower function call `funcRef` and return a reference to the resultant
1672   /// value. This is required for lowering expressions such as `f1(f2(v))`.
1673   template <typename A>
1674   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1675     ExtValue retVal = genFunctionRef(funcRef);
1676     mlir::Value retValBase = fir::getBase(retVal);
1677     if (fir::conformsWithPassByRef(retValBase.getType()))
1678       return retVal;
1679     auto mem = builder.create<fir::AllocaOp>(getLoc(), retValBase.getType());
1680     builder.create<fir::StoreOp>(getLoc(), retValBase, mem);
1681     return fir::substBase(retVal, mem.getResult());
1682   }
1683 
1684   /// helper to detect statement functions
1685   static bool
1686   isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) {
1687     if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
1688       if (const auto *details =
1689               symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
1690         return details->stmtFunction().has_value();
1691     return false;
1692   }
1693 
1694   /// Helper to package a Value and its properties into an ExtendedValue.
1695   static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base,
1696                                   llvm::ArrayRef<mlir::Value> extents,
1697                                   llvm::ArrayRef<mlir::Value> lengths) {
1698     mlir::Type type = base.getType();
1699     if (type.isa<fir::BoxType>())
1700       return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);
1701     type = fir::unwrapRefType(type);
1702     if (type.isa<fir::BoxType>())
1703       return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});
1704     if (auto seqTy = type.dyn_cast<fir::SequenceType>()) {
1705       if (seqTy.getDimension() != extents.size())
1706         fir::emitFatalError(loc, "incorrect number of extents for array");
1707       if (seqTy.getEleTy().isa<fir::CharacterType>()) {
1708         if (lengths.empty())
1709           fir::emitFatalError(loc, "missing length for character");
1710         assert(lengths.size() == 1);
1711         return fir::CharArrayBoxValue(base, lengths[0], extents);
1712       }
1713       return fir::ArrayBoxValue(base, extents);
1714     }
1715     if (type.isa<fir::CharacterType>()) {
1716       if (lengths.empty())
1717         fir::emitFatalError(loc, "missing length for character");
1718       assert(lengths.size() == 1);
1719       return fir::CharBoxValue(base, lengths[0]);
1720     }
1721     return base;
1722   }
1723 
1724   // Find the argument that corresponds to the host associations.
1725   // Verify some assumptions about how the signature was built here.
1726   [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) {
1727     // Scan the argument list from last to first as the host associations are
1728     // appended for now.
1729     for (unsigned i = fn.getNumArguments(); i > 0; --i)
1730       if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
1731         // Host assoc tuple must be last argument (for now).
1732         assert(i == fn.getNumArguments() && "tuple must be last");
1733         return i - 1;
1734       }
1735     llvm_unreachable("anyFuncArgsHaveAttr failed");
1736   }
1737 
1738   /// Create a contiguous temporary array with the same shape,
1739   /// length parameters and type as mold. It is up to the caller to deallocate
1740   /// the temporary.
1741   ExtValue genArrayTempFromMold(const ExtValue &mold,
1742                                 llvm::StringRef tempName) {
1743     mlir::Type type = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(mold).getType());
1744     assert(type && "expected descriptor or memory type");
1745     mlir::Location loc = getLoc();
1746     llvm::SmallVector<mlir::Value> extents =
1747         fir::factory::getExtents(builder, loc, mold);
1748     llvm::SmallVector<mlir::Value> allocMemTypeParams =
1749         fir::getTypeParams(mold);
1750     mlir::Value charLen;
1751     mlir::Type elementType = fir::unwrapSequenceType(type);
1752     if (auto charType = elementType.dyn_cast<fir::CharacterType>()) {
1753       charLen = allocMemTypeParams.empty()
1754                     ? fir::factory::readCharLen(builder, loc, mold)
1755                     : allocMemTypeParams[0];
1756       if (charType.hasDynamicLen() && allocMemTypeParams.empty())
1757         allocMemTypeParams.push_back(charLen);
1758     } else if (fir::hasDynamicSize(elementType)) {
1759       TODO(loc, "Creating temporary for derived type with length parameters");
1760     }
1761 
1762     mlir::Value temp = builder.create<fir::AllocMemOp>(
1763         loc, type, tempName, allocMemTypeParams, extents);
1764     if (fir::unwrapSequenceType(type).isa<fir::CharacterType>())
1765       return fir::CharArrayBoxValue{temp, charLen, extents};
1766     return fir::ArrayBoxValue{temp, extents};
1767   }
1768 
1769   /// Copy \p source array into \p dest array. Both arrays must be
1770   /// conforming, but neither array must be contiguous.
1771   void genArrayCopy(ExtValue dest, ExtValue source) {
1772     return createSomeArrayAssignment(converter, dest, source, symMap, stmtCtx);
1773   }
1774 
1775   /// Lower a non-elemental procedure reference and read allocatable and pointer
1776   /// results into normal values.
1777   ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
1778                            llvm::Optional<mlir::Type> resultType) {
1779     ExtValue res = genRawProcedureRef(procRef, resultType);
1780     // In most contexts, pointers and allocatable do not appear as allocatable
1781     // or pointer variable on the caller side (see 8.5.3 note 1 for
1782     // allocatables). The few context where this can happen must call
1783     // genRawProcedureRef directly.
1784     if (const auto *box = res.getBoxOf<fir::MutableBoxValue>())
1785       return fir::factory::genMutableBoxRead(builder, getLoc(), *box);
1786     return res;
1787   }
1788 
1789   /// Given a call site for which the arguments were already lowered, generate
1790   /// the call and return the result. This function deals with explicit result
1791   /// allocation and lowering if needed. It also deals with passing the host
1792   /// link to internal procedures.
1793   ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller,
1794                               mlir::FunctionType callSiteType,
1795                               llvm::Optional<mlir::Type> resultType) {
1796     mlir::Location loc = getLoc();
1797     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1798     // Handle cases where caller must allocate the result or a fir.box for it.
1799     bool mustPopSymMap = false;
1800     if (caller.mustMapInterfaceSymbols()) {
1801       symMap.pushScope();
1802       mustPopSymMap = true;
1803       Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap);
1804     }
1805     // If this is an indirect call, retrieve the function address. Also retrieve
1806     // the result length if this is a character function (note that this length
1807     // will be used only if there is no explicit length in the local interface).
1808     mlir::Value funcPointer;
1809     mlir::Value charFuncPointerLength;
1810     if (const Fortran::semantics::Symbol *sym =
1811             caller.getIfIndirectCallSymbol()) {
1812       funcPointer = symMap.lookupSymbol(*sym).getAddr();
1813       if (!funcPointer)
1814         fir::emitFatalError(loc, "failed to find indirect call symbol address");
1815       if (fir::isCharacterProcedureTuple(funcPointer.getType(),
1816                                          /*acceptRawFunc=*/false))
1817         std::tie(funcPointer, charFuncPointerLength) =
1818             fir::factory::extractCharacterProcedureTuple(builder, loc,
1819                                                          funcPointer);
1820     }
1821 
1822     mlir::IndexType idxTy = builder.getIndexType();
1823     auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
1824       return builder.createConvert(
1825           loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
1826     };
1827     llvm::SmallVector<mlir::Value> resultLengths;
1828     auto allocatedResult = [&]() -> llvm::Optional<ExtValue> {
1829       llvm::SmallVector<mlir::Value> extents;
1830       llvm::SmallVector<mlir::Value> lengths;
1831       if (!caller.callerAllocateResult())
1832         return {};
1833       mlir::Type type = caller.getResultStorageType();
1834       if (type.isa<fir::SequenceType>())
1835         caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) {
1836           extents.emplace_back(lowerSpecExpr(e));
1837         });
1838       caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) {
1839         lengths.emplace_back(lowerSpecExpr(e));
1840       });
1841 
1842       // Result length parameters should not be provided to box storage
1843       // allocation and save_results, but they are still useful information to
1844       // keep in the ExtendedValue if non-deferred.
1845       if (!type.isa<fir::BoxType>()) {
1846         if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
1847           // Calling an assumed length function. This is only possible if this
1848           // is a call to a character dummy procedure.
1849           if (!charFuncPointerLength)
1850             fir::emitFatalError(loc, "failed to retrieve character function "
1851                                      "length while calling it");
1852           lengths.push_back(charFuncPointerLength);
1853         }
1854         resultLengths = lengths;
1855       }
1856 
1857       if (!extents.empty() || !lengths.empty()) {
1858         auto *bldr = &converter.getFirOpBuilder();
1859         auto stackSaveFn = fir::factory::getLlvmStackSave(builder);
1860         auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName());
1861         mlir::Value sp =
1862             bldr->create<fir::CallOp>(loc, stackSaveFn.getType().getResults(),
1863                                       stackSaveSymbol, mlir::ValueRange{})
1864                 .getResult(0);
1865         stmtCtx.attachCleanup([bldr, loc, sp]() {
1866           auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr);
1867           auto stackRestoreSymbol =
1868               bldr->getSymbolRefAttr(stackRestoreFn.getName());
1869           bldr->create<fir::CallOp>(loc, stackRestoreFn.getType().getResults(),
1870                                     stackRestoreSymbol, mlir::ValueRange{sp});
1871         });
1872       }
1873       mlir::Value temp =
1874           builder.createTemporary(loc, type, ".result", extents, resultLengths);
1875       return toExtendedValue(loc, temp, extents, lengths);
1876     }();
1877 
1878     if (mustPopSymMap)
1879       symMap.popScope();
1880 
1881     // Place allocated result or prepare the fir.save_result arguments.
1882     mlir::Value arrayResultShape;
1883     if (allocatedResult) {
1884       if (std::optional<Fortran::lower::CallInterface<
1885               Fortran::lower::CallerInterface>::PassedEntity>
1886               resultArg = caller.getPassedResult()) {
1887         if (resultArg->passBy == PassBy::AddressAndLength)
1888           caller.placeAddressAndLengthInput(*resultArg,
1889                                             fir::getBase(*allocatedResult),
1890                                             fir::getLen(*allocatedResult));
1891         else if (resultArg->passBy == PassBy::BaseAddress)
1892           caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
1893         else
1894           fir::emitFatalError(
1895               loc, "only expect character scalar result to be passed by ref");
1896       } else {
1897         assert(caller.mustSaveResult());
1898         arrayResultShape = allocatedResult->match(
1899             [&](const fir::CharArrayBoxValue &) {
1900               return builder.createShape(loc, *allocatedResult);
1901             },
1902             [&](const fir::ArrayBoxValue &) {
1903               return builder.createShape(loc, *allocatedResult);
1904             },
1905             [&](const auto &) { return mlir::Value{}; });
1906       }
1907     }
1908 
1909     // In older Fortran, procedure argument types are inferred. This may lead
1910     // different view of what the function signature is in different locations.
1911     // Casts are inserted as needed below to accommodate this.
1912 
1913     // The mlir::FuncOp type prevails, unless it has a different number of
1914     // arguments which can happen in legal program if it was passed as a dummy
1915     // procedure argument earlier with no further type information.
1916     mlir::SymbolRefAttr funcSymbolAttr;
1917     bool addHostAssociations = false;
1918     if (!funcPointer) {
1919       mlir::FunctionType funcOpType = caller.getFuncOp().getType();
1920       mlir::SymbolRefAttr symbolAttr =
1921           builder.getSymbolRefAttr(caller.getMangledName());
1922       if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
1923           callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
1924           fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
1925                                    fir::getHostAssocAttrName())) {
1926         // The number of arguments is off by one, and we're lowering a function
1927         // with host associations. Modify call to include host associations
1928         // argument by appending the value at the end of the operands.
1929         assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
1930                converter.hostAssocTupleValue().getType());
1931         addHostAssociations = true;
1932       }
1933       if (!addHostAssociations &&
1934           (callSiteType.getNumResults() != funcOpType.getNumResults() ||
1935            callSiteType.getNumInputs() != funcOpType.getNumInputs())) {
1936         // Deal with argument number mismatch by making a function pointer so
1937         // that function type cast can be inserted. Do not emit a warning here
1938         // because this can happen in legal program if the function is not
1939         // defined here and it was first passed as an argument without any more
1940         // information.
1941         funcPointer =
1942             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1943       } else if (callSiteType.getResults() != funcOpType.getResults()) {
1944         // Implicit interface result type mismatch are not standard Fortran, but
1945         // some compilers are not complaining about it.  The front end is not
1946         // protecting lowering from this currently. Support this with a
1947         // discouraging warning.
1948         LLVM_DEBUG(mlir::emitWarning(
1949             loc, "a return type mismatch is not standard compliant and may "
1950                  "lead to undefined behavior."));
1951         // Cast the actual function to the current caller implicit type because
1952         // that is the behavior we would get if we could not see the definition.
1953         funcPointer =
1954             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1955       } else {
1956         funcSymbolAttr = symbolAttr;
1957       }
1958     }
1959 
1960     mlir::FunctionType funcType =
1961         funcPointer ? callSiteType : caller.getFuncOp().getType();
1962     llvm::SmallVector<mlir::Value> operands;
1963     // First operand of indirect call is the function pointer. Cast it to
1964     // required function type for the call to handle procedures that have a
1965     // compatible interface in Fortran, but that have different signatures in
1966     // FIR.
1967     if (funcPointer) {
1968       operands.push_back(
1969           funcPointer.getType().isa<fir::BoxProcType>()
1970               ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
1971               : builder.createConvert(loc, funcType, funcPointer));
1972     }
1973 
1974     // Deal with potential mismatches in arguments types. Passing an array to a
1975     // scalar argument should for instance be tolerated here.
1976     bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
1977     for (auto [fst, snd] :
1978          llvm::zip(caller.getInputs(), funcType.getInputs())) {
1979       // When passing arguments to a procedure that can be called an implicit
1980       // interface, allow character actual arguments to be passed to dummy
1981       // arguments of any type and vice versa
1982       mlir::Value cast;
1983       auto *context = builder.getContext();
1984       if (snd.isa<fir::BoxProcType>() &&
1985           fst.getType().isa<mlir::FunctionType>()) {
1986         auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None);
1987         auto boxProcTy = builder.getBoxProcType(funcTy);
1988         if (mlir::Value host = argumentHostAssocs(converter, fst)) {
1989           cast = builder.create<fir::EmboxProcOp>(
1990               loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
1991         } else {
1992           cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
1993         }
1994       } else {
1995         cast = builder.convertWithSemantics(loc, snd, fst,
1996                                             callingImplicitInterface);
1997       }
1998       operands.push_back(cast);
1999     }
2000 
2001     // Add host associations as necessary.
2002     if (addHostAssociations)
2003       operands.push_back(converter.hostAssocTupleValue());
2004 
2005     auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
2006                                             funcSymbolAttr, operands);
2007 
2008     if (caller.mustSaveResult())
2009       builder.create<fir::SaveResultOp>(
2010           loc, call.getResult(0), fir::getBase(allocatedResult.getValue()),
2011           arrayResultShape, resultLengths);
2012 
2013     if (allocatedResult) {
2014       allocatedResult->match(
2015           [&](const fir::MutableBoxValue &box) {
2016             if (box.isAllocatable()) {
2017               // 9.7.3.2 point 4. Finalize allocatables.
2018               fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
2019               stmtCtx.attachCleanup([bldr, loc, box]() {
2020                 fir::factory::genFinalization(*bldr, loc, box);
2021               });
2022             }
2023           },
2024           [](const auto &) {});
2025       return *allocatedResult;
2026     }
2027 
2028     if (!resultType.hasValue())
2029       return mlir::Value{}; // subroutine call
2030     // For now, Fortran return values are implemented with a single MLIR
2031     // function return value.
2032     assert(call.getNumResults() == 1 &&
2033            "Expected exactly one result in FUNCTION call");
2034     return call.getResult(0);
2035   }
2036 
2037   /// Like genExtAddr, but ensure the address returned is a temporary even if \p
2038   /// expr is variable inside parentheses.
2039   ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
2040     // In general, genExtAddr might not create a temp for variable inside
2041     // parentheses to avoid creating array temporary in sub-expressions. It only
2042     // ensures the sub-expression is not re-associated with other parts of the
2043     // expression. In the call semantics, there is a difference between expr and
2044     // variable (see R1524). For expressions, a variable storage must not be
2045     // argument associated since it could be modified inside the call, or the
2046     // variable could also be modified by other means during the call.
2047     if (!isParenthesizedVariable(expr))
2048       return genExtAddr(expr);
2049     mlir::Location loc = getLoc();
2050     if (expr.Rank() > 0)
2051       TODO(loc, "genTempExtAddr array");
2052     return genExtValue(expr).match(
2053         [&](const fir::CharBoxValue &boxChar) -> ExtValue {
2054           TODO(loc, "genTempExtAddr CharBoxValue");
2055         },
2056         [&](const fir::UnboxedValue &v) -> ExtValue {
2057           mlir::Type type = v.getType();
2058           mlir::Value value = v;
2059           if (fir::isa_ref_type(type))
2060             value = builder.create<fir::LoadOp>(loc, value);
2061           mlir::Value temp = builder.createTemporary(loc, value.getType());
2062           builder.create<fir::StoreOp>(loc, value, temp);
2063           return temp;
2064         },
2065         [&](const fir::BoxValue &x) -> ExtValue {
2066           // Derived type scalar that may be polymorphic.
2067           assert(!x.hasRank() && x.isDerived());
2068           if (x.isDerivedWithLengthParameters())
2069             fir::emitFatalError(
2070                 loc, "making temps for derived type with length parameters");
2071           // TODO: polymorphic aspects should be kept but for now the temp
2072           // created always has the declared type.
2073           mlir::Value var =
2074               fir::getBase(fir::factory::readBoxValue(builder, loc, x));
2075           auto value = builder.create<fir::LoadOp>(loc, var);
2076           mlir::Value temp = builder.createTemporary(loc, value.getType());
2077           builder.create<fir::StoreOp>(loc, value, temp);
2078           return temp;
2079         },
2080         [&](const auto &) -> ExtValue {
2081           fir::emitFatalError(loc, "expr is not a scalar value");
2082         });
2083   }
2084 
2085   /// Helper structure to track potential copy-in of non contiguous variable
2086   /// argument into a contiguous temp. It is used to deallocate the temp that
2087   /// may have been created as well as to the copy-out from the temp to the
2088   /// variable after the call.
2089   struct CopyOutPair {
2090     ExtValue var;
2091     ExtValue temp;
2092     // Flag to indicate if the argument may have been modified by the
2093     // callee, in which case it must be copied-out to the variable.
2094     bool argMayBeModifiedByCall;
2095     // Optional boolean value that, if present and false, prevents
2096     // the copy-out and temp deallocation.
2097     llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime;
2098   };
2099   using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>;
2100 
2101   /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories
2102   /// not based on fir.box.
2103   /// This will lose any non contiguous stride information and dynamic type and
2104   /// should only be called if \p exv is known to be contiguous or if its base
2105   /// address will be replaced by a contiguous one. If \p exv is not a
2106   /// fir::BoxValue, this is a no-op.
2107   ExtValue readIfBoxValue(const ExtValue &exv) {
2108     if (const auto *box = exv.getBoxOf<fir::BoxValue>())
2109       return fir::factory::readBoxValue(builder, getLoc(), *box);
2110     return exv;
2111   }
2112 
2113   /// Generate a contiguous temp to pass \p actualArg as argument \p arg. The
2114   /// creation of the temp and copy-in can be made conditional at runtime by
2115   /// providing a runtime boolean flag \p restrictCopyAtRuntime (in which case
2116   /// the temp and copy will only be made if the value is true at runtime).
2117   ExtValue genCopyIn(const ExtValue &actualArg,
2118                      const Fortran::lower::CallerInterface::PassedEntity &arg,
2119                      CopyOutPairs &copyOutPairs,
2120                      llvm::Optional<mlir::Value> restrictCopyAtRuntime) {
2121     if (!restrictCopyAtRuntime) {
2122       ExtValue temp = genArrayTempFromMold(actualArg, ".copyinout");
2123       if (arg.mayBeReadByCall())
2124         genArrayCopy(temp, actualArg);
2125       copyOutPairs.emplace_back(CopyOutPair{
2126           actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime});
2127       return temp;
2128     }
2129     // Otherwise, need to be careful to only copy-in if allowed at runtime.
2130     mlir::Location loc = getLoc();
2131     auto addrType = fir::HeapType::get(
2132         fir::unwrapPassByRefType(fir::getBase(actualArg).getType()));
2133     mlir::Value addr =
2134         builder
2135             .genIfOp(loc, {addrType}, *restrictCopyAtRuntime,
2136                      /*withElseRegion=*/true)
2137             .genThen([&]() {
2138               auto temp = genArrayTempFromMold(actualArg, ".copyinout");
2139               if (arg.mayBeReadByCall())
2140                 genArrayCopy(temp, actualArg);
2141               builder.create<fir::ResultOp>(loc, fir::getBase(temp));
2142             })
2143             .genElse([&]() {
2144               auto nullPtr = builder.createNullConstant(loc, addrType);
2145               builder.create<fir::ResultOp>(loc, nullPtr);
2146             })
2147             .getResults()[0];
2148     // Associate the temp address with actualArg lengths and extents.
2149     fir::ExtendedValue temp = fir::substBase(readIfBoxValue(actualArg), addr);
2150     copyOutPairs.emplace_back(CopyOutPair{
2151         actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime});
2152     return temp;
2153   }
2154 
2155   /// Lower a non-elemental procedure reference.
2156   ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
2157                               llvm::Optional<mlir::Type> resultType) {
2158     mlir::Location loc = getLoc();
2159     if (isElementalProcWithArrayArgs(procRef))
2160       fir::emitFatalError(loc, "trying to lower elemental procedure with array "
2161                                "arguments as normal procedure");
2162     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
2163             procRef.proc().GetSpecificIntrinsic())
2164       return genIntrinsicRef(procRef, *intrinsic, resultType);
2165 
2166     if (isStatementFunctionCall(procRef))
2167       TODO(loc, "Lower statement function call");
2168 
2169     Fortran::lower::CallerInterface caller(procRef, converter);
2170     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
2171 
2172     llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall;
2173     // List of <var, temp> where temp must be copied into var after the call.
2174     CopyOutPairs copyOutPairs;
2175 
2176     mlir::FunctionType callSiteType = caller.genFunctionType();
2177 
2178     // Lower the actual arguments and map the lowered values to the dummy
2179     // arguments.
2180     for (const Fortran::lower::CallInterface<
2181              Fortran::lower::CallerInterface>::PassedEntity &arg :
2182          caller.getPassedArguments()) {
2183       const auto *actual = arg.entity;
2184       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
2185       if (!actual) {
2186         // Optional dummy argument for which there is no actual argument.
2187         caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy));
2188         continue;
2189       }
2190       const auto *expr = actual->UnwrapExpr();
2191       if (!expr)
2192         TODO(loc, "assumed type actual argument lowering");
2193 
2194       if (arg.passBy == PassBy::Value) {
2195         ExtValue argVal = genval(*expr);
2196         if (!fir::isUnboxedValue(argVal))
2197           fir::emitFatalError(
2198               loc, "internal error: passing non trivial value by value");
2199         caller.placeInput(arg, fir::getBase(argVal));
2200         continue;
2201       }
2202 
2203       if (arg.passBy == PassBy::MutableBox) {
2204         if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
2205                 *expr)) {
2206           // If expr is NULL(), the mutableBox created must be a deallocated
2207           // pointer with the dummy argument characteristics (see table 16.5
2208           // in Fortran 2018 standard).
2209           // No length parameters are set for the created box because any non
2210           // deferred type parameters of the dummy will be evaluated on the
2211           // callee side, and it is illegal to use NULL without a MOLD if any
2212           // dummy length parameters are assumed.
2213           mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);
2214           assert(boxTy && boxTy.isa<fir::BoxType>() &&
2215                  "must be a fir.box type");
2216           mlir::Value boxStorage = builder.createTemporary(loc, boxTy);
2217           mlir::Value nullBox = fir::factory::createUnallocatedBox(
2218               builder, loc, boxTy, /*nonDeferredParams=*/{});
2219           builder.create<fir::StoreOp>(loc, nullBox, boxStorage);
2220           caller.placeInput(arg, boxStorage);
2221           continue;
2222         }
2223         fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
2224         mlir::Value irBox =
2225             fir::factory::getMutableIRBox(builder, loc, mutableBox);
2226         caller.placeInput(arg, irBox);
2227         if (arg.mayBeModifiedByCall())
2228           mutableModifiedByCall.emplace_back(std::move(mutableBox));
2229         continue;
2230       }
2231       const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr);
2232       if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) {
2233         const bool actualIsSimplyContiguous =
2234             !actualArgIsVariable || Fortran::evaluate::IsSimplyContiguous(
2235                                         *expr, converter.getFoldingContext());
2236         auto argAddr = [&]() -> ExtValue {
2237           ExtValue baseAddr;
2238           if (actualArgIsVariable && arg.isOptional()) {
2239             if (Fortran::evaluate::IsAllocatableOrPointerObject(
2240                     *expr, converter.getFoldingContext())) {
2241               TODO(loc, "Allocatable or pointer argument");
2242             }
2243             if (const Fortran::semantics::Symbol *wholeSymbol =
2244                     Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(
2245                         *expr))
2246               if (Fortran::semantics::IsOptional(*wholeSymbol)) {
2247                 TODO(loc, "procedureref optional arg");
2248               }
2249             // Fall through: The actual argument can safely be
2250             // copied-in/copied-out without any care if needed.
2251           }
2252           if (actualArgIsVariable && expr->Rank() > 0) {
2253             ExtValue box = genBoxArg(*expr);
2254             if (!actualIsSimplyContiguous)
2255               return genCopyIn(box, arg, copyOutPairs,
2256                                /*restrictCopyAtRuntime=*/llvm::None);
2257             // Contiguous: just use the box we created above!
2258             // This gets "unboxed" below, if needed.
2259             return box;
2260           }
2261           // Actual argument is a non optional/non pointer/non allocatable
2262           // scalar.
2263           if (actualArgIsVariable)
2264             return genExtAddr(*expr);
2265           // Actual argument is not a variable. Make sure a variable address is
2266           // not passed.
2267           return genTempExtAddr(*expr);
2268         }();
2269         // Scalar and contiguous expressions may be lowered to a fir.box,
2270         // either to account for potential polymorphism, or because lowering
2271         // did not account for some contiguity hints.
2272         // Here, polymorphism does not matter (an entity of the declared type
2273         // is passed, not one of the dynamic type), and the expr is known to
2274         // be simply contiguous, so it is safe to unbox it and pass the
2275         // address without making a copy.
2276         argAddr = readIfBoxValue(argAddr);
2277 
2278         if (arg.passBy == PassBy::BaseAddress) {
2279           caller.placeInput(arg, fir::getBase(argAddr));
2280         } else {
2281           assert(arg.passBy == PassBy::BoxChar);
2282           auto helper = fir::factory::CharacterExprHelper{builder, loc};
2283           auto boxChar = argAddr.match(
2284               [&](const fir::CharBoxValue &x) { return helper.createEmbox(x); },
2285               [&](const fir::CharArrayBoxValue &x) {
2286                 return helper.createEmbox(x);
2287               },
2288               [&](const auto &x) -> mlir::Value {
2289                 // Fortran allows an actual argument of a completely different
2290                 // type to be passed to a procedure expecting a CHARACTER in the
2291                 // dummy argument position. When this happens, the data pointer
2292                 // argument is simply assumed to point to CHARACTER data and the
2293                 // LEN argument used is garbage. Simulate this behavior by
2294                 // free-casting the base address to be a !fir.char reference and
2295                 // setting the LEN argument to undefined. What could go wrong?
2296                 auto dataPtr = fir::getBase(x);
2297                 assert(!dataPtr.getType().template isa<fir::BoxType>());
2298                 return builder.convertWithSemantics(
2299                     loc, argTy, dataPtr,
2300                     /*allowCharacterConversion=*/true);
2301               });
2302           caller.placeInput(arg, boxChar);
2303         }
2304       } else if (arg.passBy == PassBy::Box) {
2305         // Before lowering to an address, handle the allocatable/pointer actual
2306         // argument to optional fir.box dummy. It is legal to pass
2307         // unallocated/disassociated entity to an optional. In this case, an
2308         // absent fir.box must be created instead of a fir.box with a null value
2309         // (Fortran 2018 15.5.2.12 point 1).
2310         if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject(
2311                                     *expr, converter.getFoldingContext())) {
2312           TODO(loc, "optional allocatable or pointer argument");
2313         } else {
2314           // Make sure a variable address is only passed if the expression is
2315           // actually a variable.
2316           mlir::Value box =
2317               actualArgIsVariable
2318                   ? builder.createBox(loc, genBoxArg(*expr))
2319                   : builder.createBox(getLoc(), genTempExtAddr(*expr));
2320           caller.placeInput(arg, box);
2321         }
2322       } else if (arg.passBy == PassBy::AddressAndLength) {
2323         ExtValue argRef = genExtAddr(*expr);
2324         caller.placeAddressAndLengthInput(arg, fir::getBase(argRef),
2325                                           fir::getLen(argRef));
2326       } else if (arg.passBy == PassBy::CharProcTuple) {
2327         TODO(loc, "procedureref CharProcTuple");
2328       } else {
2329         TODO(loc, "pass by value in non elemental function call");
2330       }
2331     }
2332 
2333     ExtValue result = genCallOpAndResult(caller, callSiteType, resultType);
2334 
2335     // // Copy-out temps that were created for non contiguous variable arguments
2336     // if
2337     // // needed.
2338     // for (const auto &copyOutPair : copyOutPairs)
2339     //   genCopyOut(copyOutPair);
2340 
2341     return result;
2342   }
2343 
2344   template <typename A>
2345   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
2346     ExtValue result = genFunctionRef(funcRef);
2347     if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
2348       return genLoad(result);
2349     return result;
2350   }
2351 
2352   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
2353     llvm::Optional<mlir::Type> resTy;
2354     if (procRef.hasAlternateReturns())
2355       resTy = builder.getIndexType();
2356     return genProcedureRef(procRef, resTy);
2357   }
2358 
2359   /// Helper to lower intrinsic arguments for inquiry intrinsic.
2360   ExtValue
2361   lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr &expr) {
2362     if (Fortran::evaluate::IsAllocatableOrPointerObject(
2363             expr, converter.getFoldingContext()))
2364       return genMutableBoxValue(expr);
2365     return gen(expr);
2366   }
2367 
2368   /// Helper to lower intrinsic arguments to a fir::BoxValue.
2369   /// It preserves all the non default lower bounds/non deferred length
2370   /// parameter information.
2371   ExtValue lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr &expr) {
2372     mlir::Location loc = getLoc();
2373     ExtValue exv = genBoxArg(expr);
2374     mlir::Value box = builder.createBox(loc, exv);
2375     return fir::BoxValue(
2376         box, fir::factory::getNonDefaultLowerBounds(builder, loc, exv),
2377         fir::factory::getNonDeferredLengthParams(exv));
2378   }
2379 
2380   /// Generate a call to an intrinsic function.
2381   ExtValue
2382   genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
2383                   const Fortran::evaluate::SpecificIntrinsic &intrinsic,
2384                   llvm::Optional<mlir::Type> resultType) {
2385     llvm::SmallVector<ExtValue> operands;
2386 
2387     llvm::StringRef name = intrinsic.name;
2388     mlir::Location loc = getLoc();
2389 
2390     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
2391         Fortran::lower::getIntrinsicArgumentLowering(name);
2392     for (const auto &[arg, dummy] :
2393          llvm::zip(procRef.arguments(),
2394                    intrinsic.characteristics.value().dummyArguments)) {
2395       auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
2396       if (!expr) {
2397         // Absent optional.
2398         operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
2399         continue;
2400       }
2401       if (!argLowering) {
2402         // No argument lowering instruction, lower by value.
2403         operands.emplace_back(genval(*expr));
2404         continue;
2405       }
2406       // Ad-hoc argument lowering handling.
2407       Fortran::lower::ArgLoweringRule argRules =
2408           Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering,
2409                                                    dummy.name);
2410       if (argRules.handleDynamicOptional &&
2411           Fortran::evaluate::MayBePassedAsAbsentOptional(
2412               *expr, converter.getFoldingContext())) {
2413         ExtValue optional = lowerIntrinsicArgumentAsInquired(*expr);
2414         mlir::Value isPresent = genActualIsPresentTest(builder, loc, optional);
2415         switch (argRules.lowerAs) {
2416         case Fortran::lower::LowerIntrinsicArgAs::Value:
2417           operands.emplace_back(
2418               genOptionalValue(builder, loc, optional, isPresent));
2419           continue;
2420         case Fortran::lower::LowerIntrinsicArgAs::Addr:
2421           operands.emplace_back(
2422               genOptionalAddr(builder, loc, optional, isPresent));
2423           continue;
2424         case Fortran::lower::LowerIntrinsicArgAs::Box:
2425           operands.emplace_back(
2426               genOptionalBox(builder, loc, optional, isPresent));
2427           continue;
2428         case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2429           operands.emplace_back(optional);
2430           continue;
2431         }
2432         llvm_unreachable("bad switch");
2433       }
2434       switch (argRules.lowerAs) {
2435       case Fortran::lower::LowerIntrinsicArgAs::Value:
2436         operands.emplace_back(genval(*expr));
2437         continue;
2438       case Fortran::lower::LowerIntrinsicArgAs::Addr:
2439         operands.emplace_back(gen(*expr));
2440         continue;
2441       case Fortran::lower::LowerIntrinsicArgAs::Box:
2442         operands.emplace_back(lowerIntrinsicArgumentAsBox(*expr));
2443         continue;
2444       case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2445         operands.emplace_back(lowerIntrinsicArgumentAsInquired(*expr));
2446         continue;
2447       }
2448       llvm_unreachable("bad switch");
2449     }
2450     // Let the intrinsic library lower the intrinsic procedure call
2451     return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
2452                                             operands, stmtCtx);
2453   }
2454 
2455   template <typename A>
2456   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
2457     if (isScalar(x) || Fortran::evaluate::UnwrapWholeSymbolDataRef(x) ||
2458         inInitializer)
2459       return std::visit([&](const auto &e) { return genval(e); }, x.u);
2460     return asArray(x);
2461   }
2462 
2463   /// Helper to detect Transformational function reference.
2464   template <typename T>
2465   bool isTransformationalRef(const T &) {
2466     return false;
2467   }
2468   template <typename T>
2469   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
2470     return !funcRef.IsElemental() && funcRef.Rank();
2471   }
2472   template <typename T>
2473   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
2474     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
2475                       expr.u);
2476   }
2477 
2478   template <typename A>
2479   ExtValue asArray(const A &x) {
2480     return Fortran::lower::createSomeArrayTempValue(converter, toEvExpr(x),
2481                                                     symMap, stmtCtx);
2482   }
2483 
2484   /// Lower an array value as an argument. This argument can be passed as a box
2485   /// value, so it may be possible to avoid making a temporary.
2486   template <typename A>
2487   ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x) {
2488     return std::visit([&](const auto &e) { return asArrayArg(e, x); }, x.u);
2489   }
2490   template <typename A, typename B>
2491   ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x, const B &y) {
2492     return std::visit([&](const auto &e) { return asArrayArg(e, y); }, x.u);
2493   }
2494   template <typename A, typename B>
2495   ExtValue asArrayArg(const Fortran::evaluate::Designator<A> &, const B &x) {
2496     // Designator is being passed as an argument to a procedure. Lower the
2497     // expression to a boxed value.
2498     auto someExpr = toEvExpr(x);
2499     return Fortran::lower::createBoxValue(getLoc(), converter, someExpr, symMap,
2500                                           stmtCtx);
2501   }
2502   template <typename A, typename B>
2503   ExtValue asArrayArg(const A &, const B &x) {
2504     // If the expression to pass as an argument is not a designator, then create
2505     // an array temp.
2506     return asArray(x);
2507   }
2508 
2509   template <typename A>
2510   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
2511     // Whole array symbols or components, and results of transformational
2512     // functions already have a storage and the scalar expression lowering path
2513     // is used to not create a new temporary storage.
2514     if (isScalar(x) ||
2515         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
2516         isTransformationalRef(x))
2517       return std::visit([&](const auto &e) { return genref(e); }, x.u);
2518     if (useBoxArg)
2519       return asArrayArg(x);
2520     return asArray(x);
2521   }
2522 
2523   template <typename A>
2524   bool isScalar(const A &x) {
2525     return x.Rank() == 0;
2526   }
2527 
2528   template <int KIND>
2529   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
2530                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
2531     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
2532   }
2533 
2534   using RefSet =
2535       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
2536                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
2537                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
2538                  Fortran::semantics::SymbolRef>;
2539   template <typename A>
2540   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
2541 
2542   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
2543   ExtValue genref(const A &a) {
2544     return gen(a);
2545   }
2546   template <typename A>
2547   ExtValue genref(const A &a) {
2548     mlir::Type storageType = converter.genType(toEvExpr(a));
2549     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
2550   }
2551 
2552   template <typename A, template <typename> typename T,
2553             typename B = std::decay_t<T<A>>,
2554             std::enable_if_t<
2555                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
2556                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
2557                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
2558                 bool> = true>
2559   ExtValue genref(const T<A> &x) {
2560     return gen(x);
2561   }
2562 
2563 private:
2564   mlir::Location location;
2565   Fortran::lower::AbstractConverter &converter;
2566   fir::FirOpBuilder &builder;
2567   Fortran::lower::StatementContext &stmtCtx;
2568   Fortran::lower::SymMap &symMap;
2569   InitializerData *inInitializer = nullptr;
2570   bool useBoxArg = false; // expression lowered as argument
2571 };
2572 } // namespace
2573 
2574 // Helper for changing the semantics in a given context. Preserves the current
2575 // semantics which is resumed when the "push" goes out of scope.
2576 #define PushSemantics(PushVal)                                                 \
2577   [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ =                 \
2578       Fortran::common::ScopedSet(semant, PushVal);
2579 
2580 static bool isAdjustedArrayElementType(mlir::Type t) {
2581   return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>();
2582 }
2583 static bool elementTypeWasAdjusted(mlir::Type t) {
2584   if (auto ty = t.dyn_cast<fir::ReferenceType>())
2585     return isAdjustedArrayElementType(ty.getEleTy());
2586   return false;
2587 }
2588 
2589 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting
2590 /// the actual extents and lengths. This is only to allow their propagation as
2591 /// ExtendedValue without triggering verifier failures when propagating
2592 /// character/arrays as unboxed values. Only the base of the resulting
2593 /// ExtendedValue should be used, it is undefined to use the length or extents
2594 /// of the extended value returned,
2595 inline static fir::ExtendedValue
2596 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder,
2597                        mlir::Value val, mlir::Value len) {
2598   mlir::Type ty = fir::unwrapRefType(val.getType());
2599   mlir::IndexType idxTy = builder.getIndexType();
2600   auto seqTy = ty.cast<fir::SequenceType>();
2601   auto undef = builder.create<fir::UndefOp>(loc, idxTy);
2602   llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef);
2603   if (fir::isa_char(seqTy.getEleTy()))
2604     return fir::CharArrayBoxValue(val, len ? len : undef, extents);
2605   return fir::ArrayBoxValue(val, extents);
2606 }
2607 
2608 /// Helper to generate calls to scalar user defined assignment procedures.
2609 static void genScalarUserDefinedAssignmentCall(fir::FirOpBuilder &builder,
2610                                                mlir::Location loc,
2611                                                mlir::FuncOp func,
2612                                                const fir::ExtendedValue &lhs,
2613                                                const fir::ExtendedValue &rhs) {
2614   auto prepareUserDefinedArg =
2615       [](fir::FirOpBuilder &builder, mlir::Location loc,
2616          const fir::ExtendedValue &value, mlir::Type argType) -> mlir::Value {
2617     if (argType.isa<fir::BoxCharType>()) {
2618       const fir::CharBoxValue *charBox = value.getCharBox();
2619       assert(charBox && "argument type mismatch in elemental user assignment");
2620       return fir::factory::CharacterExprHelper{builder, loc}.createEmbox(
2621           *charBox);
2622     }
2623     if (argType.isa<fir::BoxType>()) {
2624       mlir::Value box = builder.createBox(loc, value);
2625       return builder.createConvert(loc, argType, box);
2626     }
2627     // Simple pass by address.
2628     mlir::Type argBaseType = fir::unwrapRefType(argType);
2629     assert(!fir::hasDynamicSize(argBaseType));
2630     mlir::Value from = fir::getBase(value);
2631     if (argBaseType != fir::unwrapRefType(from.getType())) {
2632       // With logicals, it is possible that from is i1 here.
2633       if (fir::isa_ref_type(from.getType()))
2634         from = builder.create<fir::LoadOp>(loc, from);
2635       from = builder.createConvert(loc, argBaseType, from);
2636     }
2637     if (!fir::isa_ref_type(from.getType())) {
2638       mlir::Value temp = builder.createTemporary(loc, argBaseType);
2639       builder.create<fir::StoreOp>(loc, from, temp);
2640       from = temp;
2641     }
2642     return builder.createConvert(loc, argType, from);
2643   };
2644   assert(func.getNumArguments() == 2);
2645   mlir::Type lhsType = func.getType().getInput(0);
2646   mlir::Type rhsType = func.getType().getInput(1);
2647   mlir::Value lhsArg = prepareUserDefinedArg(builder, loc, lhs, lhsType);
2648   mlir::Value rhsArg = prepareUserDefinedArg(builder, loc, rhs, rhsType);
2649   builder.create<fir::CallOp>(loc, func, mlir::ValueRange{lhsArg, rhsArg});
2650 }
2651 
2652 /// Convert the result of a fir.array_modify to an ExtendedValue given the
2653 /// related fir.array_load.
2654 static fir::ExtendedValue arrayModifyToExv(fir::FirOpBuilder &builder,
2655                                            mlir::Location loc,
2656                                            fir::ArrayLoadOp load,
2657                                            mlir::Value elementAddr) {
2658   mlir::Type eleTy = fir::unwrapPassByRefType(elementAddr.getType());
2659   if (fir::isa_char(eleTy)) {
2660     auto len = fir::factory::CharacterExprHelper{builder, loc}.getLength(
2661         load.getMemref());
2662     if (!len) {
2663       assert(load.getTypeparams().size() == 1 &&
2664              "length must be in array_load");
2665       len = load.getTypeparams()[0];
2666     }
2667     return fir::CharBoxValue{elementAddr, len};
2668   }
2669   return elementAddr;
2670 }
2671 
2672 //===----------------------------------------------------------------------===//
2673 //
2674 // Lowering of scalar expressions in an explicit iteration space context.
2675 //
2676 //===----------------------------------------------------------------------===//
2677 
2678 // Shared code for creating a copy of a derived type element. This function is
2679 // called from a continuation.
2680 inline static fir::ArrayAmendOp
2681 createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad,
2682                         fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc,
2683                         const fir::ExtendedValue &elementExv, mlir::Type eleTy,
2684                         mlir::Value innerArg) {
2685   if (destLoad.getTypeparams().empty()) {
2686     fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv);
2687   } else {
2688     auto boxTy = fir::BoxType::get(eleTy);
2689     auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(),
2690                                               mlir::Value{}, mlir::Value{},
2691                                               destLoad.getTypeparams());
2692     auto fromBox = builder.create<fir::EmboxOp>(
2693         loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{},
2694         destLoad.getTypeparams());
2695     fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox),
2696                                       fir::BoxValue(fromBox));
2697   }
2698   return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg,
2699                                            destAcc);
2700 }
2701 
2702 inline static fir::ArrayAmendOp
2703 createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder,
2704                      fir::ArrayAccessOp dstOp, mlir::Value &dstLen,
2705                      const fir::ExtendedValue &srcExv, mlir::Value innerArg,
2706                      llvm::ArrayRef<mlir::Value> bounds) {
2707   fir::CharBoxValue dstChar(dstOp, dstLen);
2708   fir::factory::CharacterExprHelper helper{builder, loc};
2709   if (!bounds.empty()) {
2710     dstChar = helper.createSubstring(dstChar, bounds);
2711     fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv),
2712                                    dstChar.getAddr(), dstChar.getLen(), builder,
2713                                    loc);
2714     // Update the LEN to the substring's LEN.
2715     dstLen = dstChar.getLen();
2716   }
2717   // For a CHARACTER, we generate the element assignment loops inline.
2718   helper.createAssign(fir::ExtendedValue{dstChar}, srcExv);
2719   // Mark this array element as amended.
2720   mlir::Type ty = innerArg.getType();
2721   auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp);
2722   return amend;
2723 }
2724 
2725 //===----------------------------------------------------------------------===//
2726 //
2727 // Lowering of array expressions.
2728 //
2729 //===----------------------------------------------------------------------===//
2730 
2731 namespace {
2732 class ArrayExprLowering {
2733   using ExtValue = fir::ExtendedValue;
2734 
2735   /// Structure to keep track of lowered array operands in the
2736   /// array expression. Useful to later deduce the shape of the
2737   /// array expression.
2738   struct ArrayOperand {
2739     /// Array base (can be a fir.box).
2740     mlir::Value memref;
2741     /// ShapeOp, ShapeShiftOp or ShiftOp
2742     mlir::Value shape;
2743     /// SliceOp
2744     mlir::Value slice;
2745     /// Can this operand be absent ?
2746     bool mayBeAbsent = false;
2747   };
2748 
2749   using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts;
2750   using PathComponent = Fortran::lower::PathComponent;
2751 
2752   /// Active iteration space.
2753   using IterationSpace = Fortran::lower::IterationSpace;
2754   using IterSpace = const Fortran::lower::IterationSpace &;
2755 
2756   /// Current continuation. Function that will generate IR for a single
2757   /// iteration of the pending iterative loop structure.
2758   using CC = Fortran::lower::GenerateElementalArrayFunc;
2759 
2760   /// Projection continuation. Function that will project one iteration space
2761   /// into another.
2762   using PC = std::function<IterationSpace(IterSpace)>;
2763   using ArrayBaseTy =
2764       std::variant<std::monostate, const Fortran::evaluate::ArrayRef *,
2765                    const Fortran::evaluate::DataRef *>;
2766   using ComponentPath = Fortran::lower::ComponentPath;
2767 
2768 public:
2769   //===--------------------------------------------------------------------===//
2770   // Regular array assignment
2771   //===--------------------------------------------------------------------===//
2772 
2773   /// Entry point for array assignments. Both the left-hand and right-hand sides
2774   /// can either be ExtendedValue or evaluate::Expr.
2775   template <typename TL, typename TR>
2776   static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter,
2777                                    Fortran::lower::SymMap &symMap,
2778                                    Fortran::lower::StatementContext &stmtCtx,
2779                                    const TL &lhs, const TR &rhs) {
2780     ArrayExprLowering ael{converter, stmtCtx, symMap,
2781                           ConstituentSemantics::CopyInCopyOut};
2782     ael.lowerArrayAssignment(lhs, rhs);
2783   }
2784 
2785   template <typename TL, typename TR>
2786   void lowerArrayAssignment(const TL &lhs, const TR &rhs) {
2787     mlir::Location loc = getLoc();
2788     /// Here the target subspace is not necessarily contiguous. The ArrayUpdate
2789     /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad
2790     /// in `destination`.
2791     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
2792     ccStoreToDest = genarr(lhs);
2793     determineShapeOfDest(lhs);
2794     semant = ConstituentSemantics::RefTransparent;
2795     ExtValue exv = lowerArrayExpression(rhs);
2796     if (explicitSpaceIsActive()) {
2797       explicitSpace->finalizeContext();
2798       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
2799     } else {
2800       builder.create<fir::ArrayMergeStoreOp>(
2801           loc, destination, fir::getBase(exv), destination.getMemref(),
2802           destination.getSlice(), destination.getTypeparams());
2803     }
2804   }
2805 
2806   //===--------------------------------------------------------------------===//
2807   // WHERE array assignment, FORALL assignment, and FORALL+WHERE array
2808   // assignment
2809   //===--------------------------------------------------------------------===//
2810 
2811   /// Entry point for array assignment when the iteration space is explicitly
2812   /// defined (Fortran's FORALL) with or without masks, and/or the implied
2813   /// iteration space involves masks (Fortran's WHERE). Both contexts (explicit
2814   /// space and implicit space with masks) may be present.
2815   static void lowerAnyMaskedArrayAssignment(
2816       Fortran::lower::AbstractConverter &converter,
2817       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
2818       const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
2819       Fortran::lower::ExplicitIterSpace &explicitSpace,
2820       Fortran::lower::ImplicitIterSpace &implicitSpace) {
2821     if (explicitSpace.isActive() && lhs.Rank() == 0) {
2822       // Scalar assignment expression in a FORALL context.
2823       ArrayExprLowering ael(converter, stmtCtx, symMap,
2824                             ConstituentSemantics::RefTransparent,
2825                             &explicitSpace, &implicitSpace);
2826       ael.lowerScalarAssignment(lhs, rhs);
2827       return;
2828     }
2829     // Array assignment expression in a FORALL and/or WHERE context.
2830     ArrayExprLowering ael(converter, stmtCtx, symMap,
2831                           ConstituentSemantics::CopyInCopyOut, &explicitSpace,
2832                           &implicitSpace);
2833     ael.lowerArrayAssignment(lhs, rhs);
2834   }
2835 
2836   //===--------------------------------------------------------------------===//
2837   // Array assignment to allocatable array
2838   //===--------------------------------------------------------------------===//
2839 
2840   /// Entry point for assignment to allocatable array.
2841   static void lowerAllocatableArrayAssignment(
2842       Fortran::lower::AbstractConverter &converter,
2843       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
2844       const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
2845       Fortran::lower::ExplicitIterSpace &explicitSpace,
2846       Fortran::lower::ImplicitIterSpace &implicitSpace) {
2847     ArrayExprLowering ael(converter, stmtCtx, symMap,
2848                           ConstituentSemantics::CopyInCopyOut, &explicitSpace,
2849                           &implicitSpace);
2850     ael.lowerAllocatableArrayAssignment(lhs, rhs);
2851   }
2852 
2853   /// Assignment to allocatable array.
2854   ///
2855   /// The semantics are reverse that of a "regular" array assignment. The rhs
2856   /// defines the iteration space of the computation and the lhs is
2857   /// resized/reallocated to fit if necessary.
2858   void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs,
2859                                        const Fortran::lower::SomeExpr &rhs) {
2860     // With assignment to allocatable, we want to lower the rhs first and use
2861     // its shape to determine if we need to reallocate, etc.
2862     mlir::Location loc = getLoc();
2863     // FIXME: If the lhs is in an explicit iteration space, the assignment may
2864     // be to an array of allocatable arrays rather than a single allocatable
2865     // array.
2866     fir::MutableBoxValue mutableBox =
2867         createMutableBox(loc, converter, lhs, symMap);
2868     mlir::Type resultTy = converter.genType(rhs);
2869     if (rhs.Rank() > 0)
2870       determineShapeOfDest(rhs);
2871     auto rhsCC = [&]() {
2872       PushSemantics(ConstituentSemantics::RefTransparent);
2873       return genarr(rhs);
2874     }();
2875 
2876     llvm::SmallVector<mlir::Value> lengthParams;
2877     // Currently no safe way to gather length from rhs (at least for
2878     // character, it cannot be taken from array_loads since it may be
2879     // changed by concatenations).
2880     if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) ||
2881         mutableBox.isDerivedWithLengthParameters())
2882       TODO(loc, "gather rhs length parameters in assignment to allocatable");
2883 
2884     // The allocatable must take lower bounds from the expr if it is
2885     // reallocated and the right hand side is not a scalar.
2886     const bool takeLboundsIfRealloc = rhs.Rank() > 0;
2887     llvm::SmallVector<mlir::Value> lbounds;
2888     // When the reallocated LHS takes its lower bounds from the RHS,
2889     // they will be non default only if the RHS is a whole array
2890     // variable. Otherwise, lbounds is left empty and default lower bounds
2891     // will be used.
2892     if (takeLboundsIfRealloc &&
2893         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) {
2894       assert(arrayOperands.size() == 1 &&
2895              "lbounds can only come from one array");
2896       std::vector<mlir::Value> lbs =
2897           fir::factory::getOrigins(arrayOperands[0].shape);
2898       lbounds.append(lbs.begin(), lbs.end());
2899     }
2900     fir::factory::MutableBoxReallocation realloc =
2901         fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape,
2902                                          lengthParams);
2903     // Create ArrayLoad for the mutable box and save it into `destination`.
2904     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
2905     ccStoreToDest = genarr(realloc.newValue);
2906     // If the rhs is scalar, get shape from the allocatable ArrayLoad.
2907     if (destShape.empty())
2908       destShape = getShape(destination);
2909     // Finish lowering the loop nest.
2910     assert(destination && "destination must have been set");
2911     ExtValue exv = lowerArrayExpression(rhsCC, resultTy);
2912     if (explicitSpaceIsActive()) {
2913       explicitSpace->finalizeContext();
2914       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
2915     } else {
2916       builder.create<fir::ArrayMergeStoreOp>(
2917           loc, destination, fir::getBase(exv), destination.getMemref(),
2918           destination.getSlice(), destination.getTypeparams());
2919     }
2920     fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds,
2921                                   takeLboundsIfRealloc, realloc);
2922   }
2923 
2924   /// Entry point for when an array expression appears in a context where the
2925   /// result must be boxed. (BoxValue semantics.)
2926   static ExtValue
2927   lowerBoxedArrayExpression(Fortran::lower::AbstractConverter &converter,
2928                             Fortran::lower::SymMap &symMap,
2929                             Fortran::lower::StatementContext &stmtCtx,
2930                             const Fortran::lower::SomeExpr &expr) {
2931     ArrayExprLowering ael{converter, stmtCtx, symMap,
2932                           ConstituentSemantics::BoxValue};
2933     return ael.lowerBoxedArrayExpr(expr);
2934   }
2935 
2936   ExtValue lowerBoxedArrayExpr(const Fortran::lower::SomeExpr &exp) {
2937     return std::visit(
2938         [&](const auto &e) {
2939           auto f = genarr(e);
2940           ExtValue exv = f(IterationSpace{});
2941           if (fir::getBase(exv).getType().template isa<fir::BoxType>())
2942             return exv;
2943           fir::emitFatalError(getLoc(), "array must be emboxed");
2944         },
2945         exp.u);
2946   }
2947 
2948   /// Entry point into lowering an expression with rank. This entry point is for
2949   /// lowering a rhs expression, for example. (RefTransparent semantics.)
2950   static ExtValue
2951   lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter,
2952                           Fortran::lower::SymMap &symMap,
2953                           Fortran::lower::StatementContext &stmtCtx,
2954                           const Fortran::lower::SomeExpr &expr) {
2955     ArrayExprLowering ael{converter, stmtCtx, symMap};
2956     ael.determineShapeOfDest(expr);
2957     ExtValue loopRes = ael.lowerArrayExpression(expr);
2958     fir::ArrayLoadOp dest = ael.destination;
2959     mlir::Value tempRes = dest.getMemref();
2960     fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2961     mlir::Location loc = converter.getCurrentLocation();
2962     builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes),
2963                                            tempRes, dest.getSlice(),
2964                                            dest.getTypeparams());
2965 
2966     auto arrTy =
2967         fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>();
2968     if (auto charTy =
2969             arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) {
2970       if (fir::characterWithDynamicLen(charTy))
2971         TODO(loc, "CHARACTER does not have constant LEN");
2972       mlir::Value len = builder.createIntegerConstant(
2973           loc, builder.getCharacterLengthType(), charTy.getLen());
2974       return fir::CharArrayBoxValue(tempRes, len, dest.getExtents());
2975     }
2976     return fir::ArrayBoxValue(tempRes, dest.getExtents());
2977   }
2978 
2979   static void lowerLazyArrayExpression(
2980       Fortran::lower::AbstractConverter &converter,
2981       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
2982       const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader) {
2983     ArrayExprLowering ael(converter, stmtCtx, symMap);
2984     ael.lowerLazyArrayExpression(expr, raggedHeader);
2985   }
2986 
2987   /// Lower the expression \p expr into a buffer that is created on demand. The
2988   /// variable containing the pointer to the buffer is \p var and the variable
2989   /// containing the shape of the buffer is \p shapeBuffer.
2990   void lowerLazyArrayExpression(const Fortran::lower::SomeExpr &expr,
2991                                 mlir::Value header) {
2992     mlir::Location loc = getLoc();
2993     mlir::TupleType hdrTy = fir::factory::getRaggedArrayHeaderType(builder);
2994     mlir::IntegerType i32Ty = builder.getIntegerType(32);
2995 
2996     // Once the loop extents have been computed, which may require being inside
2997     // some explicit loops, lazily allocate the expression on the heap. The
2998     // following continuation creates the buffer as needed.
2999     ccPrelude = [=](llvm::ArrayRef<mlir::Value> shape) {
3000       mlir::IntegerType i64Ty = builder.getIntegerType(64);
3001       mlir::Value byteSize = builder.createIntegerConstant(loc, i64Ty, 1);
3002       fir::runtime::genRaggedArrayAllocate(
3003           loc, builder, header, /*asHeaders=*/false, byteSize, shape);
3004     };
3005 
3006     // Create a dummy array_load before the loop. We're storing to a lazy
3007     // temporary, so there will be no conflict and no copy-in. TODO: skip this
3008     // as there isn't any necessity for it.
3009     ccLoadDest = [=](llvm::ArrayRef<mlir::Value> shape) -> fir::ArrayLoadOp {
3010       mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
3011       auto var = builder.create<fir::CoordinateOp>(
3012           loc, builder.getRefType(hdrTy.getType(1)), header, one);
3013       auto load = builder.create<fir::LoadOp>(loc, var);
3014       mlir::Type eleTy =
3015           fir::unwrapSequenceType(fir::unwrapRefType(load.getType()));
3016       auto seqTy = fir::SequenceType::get(eleTy, shape.size());
3017       mlir::Value castTo =
3018           builder.createConvert(loc, fir::HeapType::get(seqTy), load);
3019       mlir::Value shapeOp = builder.genShape(loc, shape);
3020       return builder.create<fir::ArrayLoadOp>(
3021           loc, seqTy, castTo, shapeOp, /*slice=*/mlir::Value{}, llvm::None);
3022     };
3023     // Custom lowering of the element store to deal with the extra indirection
3024     // to the lazy allocated buffer.
3025     ccStoreToDest = [=](IterSpace iters) {
3026       mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
3027       auto var = builder.create<fir::CoordinateOp>(
3028           loc, builder.getRefType(hdrTy.getType(1)), header, one);
3029       auto load = builder.create<fir::LoadOp>(loc, var);
3030       mlir::Type eleTy =
3031           fir::unwrapSequenceType(fir::unwrapRefType(load.getType()));
3032       auto seqTy = fir::SequenceType::get(eleTy, iters.iterVec().size());
3033       auto toTy = fir::HeapType::get(seqTy);
3034       mlir::Value castTo = builder.createConvert(loc, toTy, load);
3035       mlir::Value shape = builder.genShape(loc, genIterationShape());
3036       llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
3037           loc, builder, castTo.getType(), shape, iters.iterVec());
3038       auto eleAddr = builder.create<fir::ArrayCoorOp>(
3039           loc, builder.getRefType(eleTy), castTo, shape,
3040           /*slice=*/mlir::Value{}, indices, destination.getTypeparams());
3041       mlir::Value eleVal =
3042           builder.createConvert(loc, eleTy, iters.getElement());
3043       builder.create<fir::StoreOp>(loc, eleVal, eleAddr);
3044       return iters.innerArgument();
3045     };
3046 
3047     // Lower the array expression now. Clean-up any temps that may have
3048     // been generated when lowering `expr` right after the lowered value
3049     // was stored to the ragged array temporary. The local temps will not
3050     // be needed afterwards.
3051     stmtCtx.pushScope();
3052     [[maybe_unused]] ExtValue loopRes = lowerArrayExpression(expr);
3053     stmtCtx.finalize(/*popScope=*/true);
3054     assert(fir::getBase(loopRes));
3055   }
3056 
3057   static void
3058   lowerElementalUserAssignment(Fortran::lower::AbstractConverter &converter,
3059                                Fortran::lower::SymMap &symMap,
3060                                Fortran::lower::StatementContext &stmtCtx,
3061                                Fortran::lower::ExplicitIterSpace &explicitSpace,
3062                                Fortran::lower::ImplicitIterSpace &implicitSpace,
3063                                const Fortran::evaluate::ProcedureRef &procRef) {
3064     ArrayExprLowering ael(converter, stmtCtx, symMap,
3065                           ConstituentSemantics::CustomCopyInCopyOut,
3066                           &explicitSpace, &implicitSpace);
3067     assert(procRef.arguments().size() == 2);
3068     const auto *lhs = procRef.arguments()[0].value().UnwrapExpr();
3069     const auto *rhs = procRef.arguments()[1].value().UnwrapExpr();
3070     assert(lhs && rhs &&
3071            "user defined assignment arguments must be expressions");
3072     mlir::FuncOp func =
3073         Fortran::lower::CallerInterface(procRef, converter).getFuncOp();
3074     ael.lowerElementalUserAssignment(func, *lhs, *rhs);
3075   }
3076 
3077   void lowerElementalUserAssignment(mlir::FuncOp userAssignment,
3078                                     const Fortran::lower::SomeExpr &lhs,
3079                                     const Fortran::lower::SomeExpr &rhs) {
3080     mlir::Location loc = getLoc();
3081     PushSemantics(ConstituentSemantics::CustomCopyInCopyOut);
3082     auto genArrayModify = genarr(lhs);
3083     ccStoreToDest = [=](IterSpace iters) -> ExtValue {
3084       auto modifiedArray = genArrayModify(iters);
3085       auto arrayModify = mlir::dyn_cast_or_null<fir::ArrayModifyOp>(
3086           fir::getBase(modifiedArray).getDefiningOp());
3087       assert(arrayModify && "must be created by ArrayModifyOp");
3088       fir::ExtendedValue lhs =
3089           arrayModifyToExv(builder, loc, destination, arrayModify.getResult(0));
3090       genScalarUserDefinedAssignmentCall(builder, loc, userAssignment, lhs,
3091                                          iters.elementExv());
3092       return modifiedArray;
3093     };
3094     determineShapeOfDest(lhs);
3095     semant = ConstituentSemantics::RefTransparent;
3096     auto exv = lowerArrayExpression(rhs);
3097     if (explicitSpaceIsActive()) {
3098       explicitSpace->finalizeContext();
3099       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
3100     } else {
3101       builder.create<fir::ArrayMergeStoreOp>(
3102           loc, destination, fir::getBase(exv), destination.getMemref(),
3103           destination.getSlice(), destination.getTypeparams());
3104     }
3105   }
3106 
3107   /// Lower an elemental subroutine call with at least one array argument.
3108   /// An elemental subroutine is an exception and does not have copy-in/copy-out
3109   /// semantics. See 15.8.3.
3110   /// Do NOT use this for user defined assignments.
3111   static void
3112   lowerElementalSubroutine(Fortran::lower::AbstractConverter &converter,
3113                            Fortran::lower::SymMap &symMap,
3114                            Fortran::lower::StatementContext &stmtCtx,
3115                            const Fortran::lower::SomeExpr &call) {
3116     ArrayExprLowering ael(converter, stmtCtx, symMap,
3117                           ConstituentSemantics::RefTransparent);
3118     ael.lowerElementalSubroutine(call);
3119   }
3120 
3121   // TODO: See the comment in genarr(const Fortran::lower::Parentheses<T>&).
3122   // This is skipping generation of copy-in/copy-out code for analysis that is
3123   // required when arguments are in parentheses.
3124   void lowerElementalSubroutine(const Fortran::lower::SomeExpr &call) {
3125     auto f = genarr(call);
3126     llvm::SmallVector<mlir::Value> shape = genIterationShape();
3127     auto [iterSpace, insPt] = genImplicitLoops(shape, /*innerArg=*/{});
3128     f(iterSpace);
3129     finalizeElementCtx();
3130     builder.restoreInsertionPoint(insPt);
3131   }
3132 
3133   template <typename A, typename B>
3134   ExtValue lowerScalarAssignment(const A &lhs, const B &rhs) {
3135     // 1) Lower the rhs expression with array_fetch op(s).
3136     IterationSpace iters;
3137     iters.setElement(genarr(rhs)(iters));
3138     fir::ExtendedValue elementalExv = iters.elementExv();
3139     // 2) Lower the lhs expression to an array_update.
3140     semant = ConstituentSemantics::ProjectedCopyInCopyOut;
3141     auto lexv = genarr(lhs)(iters);
3142     // 3) Finalize the inner context.
3143     explicitSpace->finalizeContext();
3144     // 4) Thread the array value updated forward. Note: the lhs might be
3145     // ill-formed (performing scalar assignment in an array context),
3146     // in which case there is no array to thread.
3147     auto createResult = [&](auto op) {
3148       mlir::Value oldInnerArg = op.getSequence();
3149       std::size_t offset = explicitSpace->argPosition(oldInnerArg);
3150       explicitSpace->setInnerArg(offset, fir::getBase(lexv));
3151       builder.create<fir::ResultOp>(getLoc(), fir::getBase(lexv));
3152     };
3153     if (auto updateOp = mlir::dyn_cast<fir::ArrayUpdateOp>(
3154             fir::getBase(lexv).getDefiningOp()))
3155       createResult(updateOp);
3156     else if (auto amend = mlir::dyn_cast<fir::ArrayAmendOp>(
3157                  fir::getBase(lexv).getDefiningOp()))
3158       createResult(amend);
3159     else if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>(
3160                  fir::getBase(lexv).getDefiningOp()))
3161       createResult(modifyOp);
3162     return lexv;
3163   }
3164 
3165   static ExtValue lowerScalarUserAssignment(
3166       Fortran::lower::AbstractConverter &converter,
3167       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3168       Fortran::lower::ExplicitIterSpace &explicitIterSpace,
3169       mlir::FuncOp userAssignmentFunction, const Fortran::lower::SomeExpr &lhs,
3170       const Fortran::lower::SomeExpr &rhs) {
3171     Fortran::lower::ImplicitIterSpace implicit;
3172     ArrayExprLowering ael(converter, stmtCtx, symMap,
3173                           ConstituentSemantics::RefTransparent,
3174                           &explicitIterSpace, &implicit);
3175     return ael.lowerScalarUserAssignment(userAssignmentFunction, lhs, rhs);
3176   }
3177 
3178   ExtValue lowerScalarUserAssignment(mlir::FuncOp userAssignment,
3179                                      const Fortran::lower::SomeExpr &lhs,
3180                                      const Fortran::lower::SomeExpr &rhs) {
3181     mlir::Location loc = getLoc();
3182     if (rhs.Rank() > 0)
3183       TODO(loc, "user-defined elemental assigment from expression with rank");
3184     // 1) Lower the rhs expression with array_fetch op(s).
3185     IterationSpace iters;
3186     iters.setElement(genarr(rhs)(iters));
3187     fir::ExtendedValue elementalExv = iters.elementExv();
3188     // 2) Lower the lhs expression to an array_modify.
3189     semant = ConstituentSemantics::CustomCopyInCopyOut;
3190     auto lexv = genarr(lhs)(iters);
3191     bool isIllFormedLHS = false;
3192     // 3) Insert the call
3193     if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>(
3194             fir::getBase(lexv).getDefiningOp())) {
3195       mlir::Value oldInnerArg = modifyOp.getSequence();
3196       std::size_t offset = explicitSpace->argPosition(oldInnerArg);
3197       explicitSpace->setInnerArg(offset, fir::getBase(lexv));
3198       fir::ExtendedValue exv = arrayModifyToExv(
3199           builder, loc, explicitSpace->getLhsLoad(0).getValue(),
3200           modifyOp.getResult(0));
3201       genScalarUserDefinedAssignmentCall(builder, loc, userAssignment, exv,
3202                                          elementalExv);
3203     } else {
3204       // LHS is ill formed, it is a scalar with no references to FORALL
3205       // subscripts, so there is actually no array assignment here. The user
3206       // code is probably bad, but still insert user assignment call since it
3207       // was not rejected by semantics (a warning was emitted).
3208       isIllFormedLHS = true;
3209       genScalarUserDefinedAssignmentCall(builder, getLoc(), userAssignment,
3210                                          lexv, elementalExv);
3211     }
3212     // 4) Finalize the inner context.
3213     explicitSpace->finalizeContext();
3214     // 5). Thread the array value updated forward.
3215     if (!isIllFormedLHS)
3216       builder.create<fir::ResultOp>(getLoc(), fir::getBase(lexv));
3217     return lexv;
3218   }
3219 
3220   bool explicitSpaceIsActive() const {
3221     return explicitSpace && explicitSpace->isActive();
3222   }
3223 
3224   bool implicitSpaceHasMasks() const {
3225     return implicitSpace && !implicitSpace->empty();
3226   }
3227 
3228   CC genMaskAccess(mlir::Value tmp, mlir::Value shape) {
3229     mlir::Location loc = getLoc();
3230     return [=, builder = &converter.getFirOpBuilder()](IterSpace iters) {
3231       mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(tmp.getType());
3232       auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
3233       mlir::Type eleRefTy = builder->getRefType(eleTy);
3234       mlir::IntegerType i1Ty = builder->getI1Type();
3235       // Adjust indices for any shift of the origin of the array.
3236       llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
3237           loc, *builder, tmp.getType(), shape, iters.iterVec());
3238       auto addr = builder->create<fir::ArrayCoorOp>(
3239           loc, eleRefTy, tmp, shape, /*slice=*/mlir::Value{}, indices,
3240           /*typeParams=*/llvm::None);
3241       auto load = builder->create<fir::LoadOp>(loc, addr);
3242       return builder->createConvert(loc, i1Ty, load);
3243     };
3244   }
3245 
3246   /// Construct the incremental instantiations of the ragged array structure.
3247   /// Rebind the lazy buffer variable, etc. as we go.
3248   template <bool withAllocation = false>
3249   mlir::Value prepareRaggedArrays(Fortran::lower::FrontEndExpr expr) {
3250     assert(explicitSpaceIsActive());
3251     mlir::Location loc = getLoc();
3252     mlir::TupleType raggedTy = fir::factory::getRaggedArrayHeaderType(builder);
3253     llvm::SmallVector<llvm::SmallVector<fir::DoLoopOp>> loopStack =
3254         explicitSpace->getLoopStack();
3255     const std::size_t depth = loopStack.size();
3256     mlir::IntegerType i64Ty = builder.getIntegerType(64);
3257     [[maybe_unused]] mlir::Value byteSize =
3258         builder.createIntegerConstant(loc, i64Ty, 1);
3259     mlir::Value header = implicitSpace->lookupMaskHeader(expr);
3260     for (std::remove_const_t<decltype(depth)> i = 0; i < depth; ++i) {
3261       auto insPt = builder.saveInsertionPoint();
3262       if (i < depth - 1)
3263         builder.setInsertionPoint(loopStack[i + 1][0]);
3264 
3265       // Compute and gather the extents.
3266       llvm::SmallVector<mlir::Value> extents;
3267       for (auto doLoop : loopStack[i])
3268         extents.push_back(builder.genExtentFromTriplet(
3269             loc, doLoop.getLowerBound(), doLoop.getUpperBound(),
3270             doLoop.getStep(), i64Ty));
3271       if constexpr (withAllocation) {
3272         fir::runtime::genRaggedArrayAllocate(
3273             loc, builder, header, /*asHeader=*/true, byteSize, extents);
3274       }
3275 
3276       // Compute the dynamic position into the header.
3277       llvm::SmallVector<mlir::Value> offsets;
3278       for (auto doLoop : loopStack[i]) {
3279         auto m = builder.create<mlir::arith::SubIOp>(
3280             loc, doLoop.getInductionVar(), doLoop.getLowerBound());
3281         auto n = builder.create<mlir::arith::DivSIOp>(loc, m, doLoop.getStep());
3282         mlir::Value one = builder.createIntegerConstant(loc, n.getType(), 1);
3283         offsets.push_back(builder.create<mlir::arith::AddIOp>(loc, n, one));
3284       }
3285       mlir::IntegerType i32Ty = builder.getIntegerType(32);
3286       mlir::Value uno = builder.createIntegerConstant(loc, i32Ty, 1);
3287       mlir::Type coorTy = builder.getRefType(raggedTy.getType(1));
3288       auto hdOff = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno);
3289       auto toTy = fir::SequenceType::get(raggedTy, offsets.size());
3290       mlir::Type toRefTy = builder.getRefType(toTy);
3291       auto ldHdr = builder.create<fir::LoadOp>(loc, hdOff);
3292       mlir::Value hdArr = builder.createConvert(loc, toRefTy, ldHdr);
3293       auto shapeOp = builder.genShape(loc, extents);
3294       header = builder.create<fir::ArrayCoorOp>(
3295           loc, builder.getRefType(raggedTy), hdArr, shapeOp,
3296           /*slice=*/mlir::Value{}, offsets,
3297           /*typeparams=*/mlir::ValueRange{});
3298       auto hdrVar = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno);
3299       auto inVar = builder.create<fir::LoadOp>(loc, hdrVar);
3300       mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2);
3301       mlir::Type coorTy2 = builder.getRefType(raggedTy.getType(2));
3302       auto hdrSh = builder.create<fir::CoordinateOp>(loc, coorTy2, header, two);
3303       auto shapePtr = builder.create<fir::LoadOp>(loc, hdrSh);
3304       // Replace the binding.
3305       implicitSpace->rebind(expr, genMaskAccess(inVar, shapePtr));
3306       if (i < depth - 1)
3307         builder.restoreInsertionPoint(insPt);
3308     }
3309     return header;
3310   }
3311 
3312   /// Lower mask expressions with implied iteration spaces from the variants of
3313   /// WHERE syntax. Since it is legal for mask expressions to have side-effects
3314   /// and modify values that will be used for the lhs, rhs, or both of
3315   /// subsequent assignments, the mask must be evaluated before the assignment
3316   /// is processed.
3317   /// Mask expressions are array expressions too.
3318   void genMasks() {
3319     // Lower the mask expressions, if any.
3320     if (implicitSpaceHasMasks()) {
3321       mlir::Location loc = getLoc();
3322       // Mask expressions are array expressions too.
3323       for (const auto *e : implicitSpace->getExprs())
3324         if (e && !implicitSpace->isLowered(e)) {
3325           if (mlir::Value var = implicitSpace->lookupMaskVariable(e)) {
3326             // Allocate the mask buffer lazily.
3327             assert(explicitSpaceIsActive());
3328             mlir::Value header =
3329                 prepareRaggedArrays</*withAllocations=*/true>(e);
3330             Fortran::lower::createLazyArrayTempValue(converter, *e, header,
3331                                                      symMap, stmtCtx);
3332             // Close the explicit loops.
3333             builder.create<fir::ResultOp>(loc, explicitSpace->getInnerArgs());
3334             builder.setInsertionPointAfter(explicitSpace->getOuterLoop());
3335             // Open a new copy of the explicit loop nest.
3336             explicitSpace->genLoopNest();
3337             continue;
3338           }
3339           fir::ExtendedValue tmp = Fortran::lower::createSomeArrayTempValue(
3340               converter, *e, symMap, stmtCtx);
3341           mlir::Value shape = builder.createShape(loc, tmp);
3342           implicitSpace->bind(e, genMaskAccess(fir::getBase(tmp), shape));
3343         }
3344 
3345       // Set buffer from the header.
3346       for (const auto *e : implicitSpace->getExprs()) {
3347         if (!e)
3348           continue;
3349         if (implicitSpace->lookupMaskVariable(e)) {
3350           // Index into the ragged buffer to retrieve cached results.
3351           const int rank = e->Rank();
3352           assert(destShape.empty() ||
3353                  static_cast<std::size_t>(rank) == destShape.size());
3354           mlir::Value header = prepareRaggedArrays(e);
3355           mlir::TupleType raggedTy =
3356               fir::factory::getRaggedArrayHeaderType(builder);
3357           mlir::IntegerType i32Ty = builder.getIntegerType(32);
3358           mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
3359           auto coor1 = builder.create<fir::CoordinateOp>(
3360               loc, builder.getRefType(raggedTy.getType(1)), header, one);
3361           auto db = builder.create<fir::LoadOp>(loc, coor1);
3362           mlir::Type eleTy =
3363               fir::unwrapSequenceType(fir::unwrapRefType(db.getType()));
3364           mlir::Type buffTy =
3365               builder.getRefType(fir::SequenceType::get(eleTy, rank));
3366           // Address of ragged buffer data.
3367           mlir::Value buff = builder.createConvert(loc, buffTy, db);
3368 
3369           mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2);
3370           auto coor2 = builder.create<fir::CoordinateOp>(
3371               loc, builder.getRefType(raggedTy.getType(2)), header, two);
3372           auto shBuff = builder.create<fir::LoadOp>(loc, coor2);
3373           mlir::IntegerType i64Ty = builder.getIntegerType(64);
3374           mlir::IndexType idxTy = builder.getIndexType();
3375           llvm::SmallVector<mlir::Value> extents;
3376           for (std::remove_const_t<decltype(rank)> i = 0; i < rank; ++i) {
3377             mlir::Value off = builder.createIntegerConstant(loc, i32Ty, i);
3378             auto coor = builder.create<fir::CoordinateOp>(
3379                 loc, builder.getRefType(i64Ty), shBuff, off);
3380             auto ldExt = builder.create<fir::LoadOp>(loc, coor);
3381             extents.push_back(builder.createConvert(loc, idxTy, ldExt));
3382           }
3383           if (destShape.empty())
3384             destShape = extents;
3385           // Construct shape of buffer.
3386           mlir::Value shapeOp = builder.genShape(loc, extents);
3387 
3388           // Replace binding with the local result.
3389           implicitSpace->rebind(e, genMaskAccess(buff, shapeOp));
3390         }
3391       }
3392     }
3393   }
3394 
3395   // FIXME: should take multiple inner arguments.
3396   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
3397   genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) {
3398     mlir::Location loc = getLoc();
3399     mlir::IndexType idxTy = builder.getIndexType();
3400     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
3401     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
3402     llvm::SmallVector<mlir::Value> loopUppers;
3403 
3404     // Convert any implied shape to closed interval form. The fir.do_loop will
3405     // run from 0 to `extent - 1` inclusive.
3406     for (auto extent : shape)
3407       loopUppers.push_back(
3408           builder.create<mlir::arith::SubIOp>(loc, extent, one));
3409 
3410     // Iteration space is created with outermost columns, innermost rows
3411     llvm::SmallVector<fir::DoLoopOp> loops;
3412 
3413     const std::size_t loopDepth = loopUppers.size();
3414     llvm::SmallVector<mlir::Value> ivars;
3415 
3416     for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) {
3417       if (i.index() > 0) {
3418         assert(!loops.empty());
3419         builder.setInsertionPointToStart(loops.back().getBody());
3420       }
3421       fir::DoLoopOp loop;
3422       if (innerArg) {
3423         loop = builder.create<fir::DoLoopOp>(
3424             loc, zero, i.value(), one, isUnordered(),
3425             /*finalCount=*/false, mlir::ValueRange{innerArg});
3426         innerArg = loop.getRegionIterArgs().front();
3427         if (explicitSpaceIsActive())
3428           explicitSpace->setInnerArg(0, innerArg);
3429       } else {
3430         loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one,
3431                                              isUnordered(),
3432                                              /*finalCount=*/false);
3433       }
3434       ivars.push_back(loop.getInductionVar());
3435       loops.push_back(loop);
3436     }
3437 
3438     if (innerArg)
3439       for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth;
3440            ++i) {
3441         builder.setInsertionPointToEnd(loops[i].getBody());
3442         builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0));
3443       }
3444 
3445     // Move insertion point to the start of the innermost loop in the nest.
3446     builder.setInsertionPointToStart(loops.back().getBody());
3447     // Set `afterLoopNest` to just after the entire loop nest.
3448     auto currPt = builder.saveInsertionPoint();
3449     builder.setInsertionPointAfter(loops[0]);
3450     auto afterLoopNest = builder.saveInsertionPoint();
3451     builder.restoreInsertionPoint(currPt);
3452 
3453     // Put the implicit loop variables in row to column order to match FIR's
3454     // Ops. (The loops were constructed from outermost column to innermost
3455     // row.)
3456     mlir::Value outerRes = loops[0].getResult(0);
3457     return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)),
3458             afterLoopNest};
3459   }
3460 
3461   /// Build the iteration space into which the array expression will be
3462   /// lowered. The resultType is used to create a temporary, if needed.
3463   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
3464   genIterSpace(mlir::Type resultType) {
3465     mlir::Location loc = getLoc();
3466     llvm::SmallVector<mlir::Value> shape = genIterationShape();
3467     if (!destination) {
3468       // Allocate storage for the result if it is not already provided.
3469       destination = createAndLoadSomeArrayTemp(resultType, shape);
3470     }
3471 
3472     // Generate the lazy mask allocation, if one was given.
3473     if (ccPrelude.hasValue())
3474       ccPrelude.getValue()(shape);
3475 
3476     // Now handle the implicit loops.
3477     mlir::Value inner = explicitSpaceIsActive()
3478                             ? explicitSpace->getInnerArgs().front()
3479                             : destination.getResult();
3480     auto [iters, afterLoopNest] = genImplicitLoops(shape, inner);
3481     mlir::Value innerArg = iters.innerArgument();
3482 
3483     // Generate the mask conditional structure, if there are masks. Unlike the
3484     // explicit masks, which are interleaved, these mask expression appear in
3485     // the innermost loop.
3486     if (implicitSpaceHasMasks()) {
3487       // Recover the cached condition from the mask buffer.
3488       auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) {
3489         return implicitSpace->getBoundClosure(e)(iters);
3490       };
3491 
3492       // Handle the negated conditions in topological order of the WHERE
3493       // clauses. See 10.2.3.2p4 as to why this control structure is produced.
3494       for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs :
3495            implicitSpace->getMasks()) {
3496         const std::size_t size = maskExprs.size() - 1;
3497         auto genFalseBlock = [&](const auto *e, auto &&cond) {
3498           auto ifOp = builder.create<fir::IfOp>(
3499               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
3500               /*withElseRegion=*/true);
3501           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
3502           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
3503           builder.create<fir::ResultOp>(loc, innerArg);
3504           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
3505         };
3506         auto genTrueBlock = [&](const auto *e, auto &&cond) {
3507           auto ifOp = builder.create<fir::IfOp>(
3508               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
3509               /*withElseRegion=*/true);
3510           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
3511           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
3512           builder.create<fir::ResultOp>(loc, innerArg);
3513           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
3514         };
3515         for (std::size_t i = 0; i < size; ++i)
3516           if (const auto *e = maskExprs[i])
3517             genFalseBlock(e, genCond(e, iters));
3518 
3519         // The last condition is either non-negated or unconditionally negated.
3520         if (const auto *e = maskExprs[size])
3521           genTrueBlock(e, genCond(e, iters));
3522       }
3523     }
3524 
3525     // We're ready to lower the body (an assignment statement) for this context
3526     // of loop nests at this point.
3527     return {iters, afterLoopNest};
3528   }
3529 
3530   fir::ArrayLoadOp
3531   createAndLoadSomeArrayTemp(mlir::Type type,
3532                              llvm::ArrayRef<mlir::Value> shape) {
3533     if (ccLoadDest.hasValue())
3534       return ccLoadDest.getValue()(shape);
3535     auto seqTy = type.dyn_cast<fir::SequenceType>();
3536     assert(seqTy && "must be an array");
3537     mlir::Location loc = getLoc();
3538     // TODO: Need to thread the length parameters here. For character, they may
3539     // differ from the operands length (e.g concatenation). So the array loads
3540     // type parameters are not enough.
3541     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>())
3542       if (charTy.hasDynamicLen())
3543         TODO(loc, "character array expression temp with dynamic length");
3544     if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>())
3545       if (recTy.getNumLenParams() > 0)
3546         TODO(loc, "derived type array expression temp with length parameters");
3547     mlir::Value temp = seqTy.hasConstantShape()
3548                            ? builder.create<fir::AllocMemOp>(loc, type)
3549                            : builder.create<fir::AllocMemOp>(
3550                                  loc, type, ".array.expr", llvm::None, shape);
3551     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
3552     stmtCtx.attachCleanup(
3553         [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); });
3554     mlir::Value shapeOp = genShapeOp(shape);
3555     return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp,
3556                                             /*slice=*/mlir::Value{},
3557                                             llvm::None);
3558   }
3559 
3560   static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder,
3561                                  llvm::ArrayRef<mlir::Value> shape) {
3562     mlir::IndexType idxTy = builder.getIndexType();
3563     llvm::SmallVector<mlir::Value> idxShape;
3564     for (auto s : shape)
3565       idxShape.push_back(builder.createConvert(loc, idxTy, s));
3566     auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size());
3567     return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape);
3568   }
3569 
3570   fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) {
3571     return genShapeOp(getLoc(), builder, shape);
3572   }
3573 
3574   //===--------------------------------------------------------------------===//
3575   // Expression traversal and lowering.
3576   //===--------------------------------------------------------------------===//
3577 
3578   /// Lower the expression, \p x, in a scalar context.
3579   template <typename A>
3580   ExtValue asScalar(const A &x) {
3581     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x);
3582   }
3583 
3584   /// Lower the expression, \p x, in a scalar context. If this is an explicit
3585   /// space, the expression may be scalar and refer to an array. We want to
3586   /// raise the array access to array operations in FIR to analyze potential
3587   /// conflicts even when the result is a scalar element.
3588   template <typename A>
3589   ExtValue asScalarArray(const A &x) {
3590     return explicitSpaceIsActive() ? genarr(x)(IterationSpace{}) : asScalar(x);
3591   }
3592 
3593   /// Lower the expression in a scalar context to a memory reference.
3594   template <typename A>
3595   ExtValue asScalarRef(const A &x) {
3596     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x);
3597   }
3598 
3599   /// Lower an expression without dereferencing any indirection that may be
3600   /// a nullptr (because this is an absent optional or unallocated/disassociated
3601   /// descriptor). The returned expression cannot be addressed directly, it is
3602   /// meant to inquire about its status before addressing the related entity.
3603   template <typename A>
3604   ExtValue asInquired(const A &x) {
3605     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}
3606         .lowerIntrinsicArgumentAsInquired(x);
3607   }
3608 
3609   // An expression with non-zero rank is an array expression.
3610   template <typename A>
3611   bool isArray(const A &x) const {
3612     return x.Rank() != 0;
3613   }
3614 
3615   /// Some temporaries are allocated on an element-by-element basis during the
3616   /// array expression evaluation. Collect the cleanups here so the resources
3617   /// can be freed before the next loop iteration, avoiding memory leaks. etc.
3618   Fortran::lower::StatementContext &getElementCtx() {
3619     if (!elementCtx) {
3620       stmtCtx.pushScope();
3621       elementCtx = true;
3622     }
3623     return stmtCtx;
3624   }
3625 
3626   /// If there were temporaries created for this element evaluation, finalize
3627   /// and deallocate the resources now. This should be done just prior the the
3628   /// fir::ResultOp at the end of the innermost loop.
3629   void finalizeElementCtx() {
3630     if (elementCtx) {
3631       stmtCtx.finalize(/*popScope=*/true);
3632       elementCtx = false;
3633     }
3634   }
3635 
3636   /// Lower an elemental function array argument. This ensures array
3637   /// sub-expressions that are not variables and must be passed by address
3638   /// are lowered by value and placed in memory.
3639   template <typename A>
3640   CC genElementalArgument(const A &x) {
3641     // Ensure the returned element is in memory if this is what was requested.
3642     if ((semant == ConstituentSemantics::RefOpaque ||
3643          semant == ConstituentSemantics::DataAddr ||
3644          semant == ConstituentSemantics::ByValueArg)) {
3645       if (!Fortran::evaluate::IsVariable(x)) {
3646         PushSemantics(ConstituentSemantics::DataValue);
3647         CC cc = genarr(x);
3648         mlir::Location loc = getLoc();
3649         if (isParenthesizedVariable(x)) {
3650           // Parenthesised variables are lowered to a reference to the variable
3651           // storage. When passing it as an argument, a copy must be passed.
3652           return [=](IterSpace iters) -> ExtValue {
3653             return createInMemoryScalarCopy(builder, loc, cc(iters));
3654           };
3655         }
3656         mlir::Type storageType =
3657             fir::unwrapSequenceType(converter.genType(toEvExpr(x)));
3658         return [=](IterSpace iters) -> ExtValue {
3659           return placeScalarValueInMemory(builder, loc, cc(iters), storageType);
3660         };
3661       }
3662     }
3663     return genarr(x);
3664   }
3665 
3666   // A procedure reference to a Fortran elemental intrinsic procedure.
3667   CC genElementalIntrinsicProcRef(
3668       const Fortran::evaluate::ProcedureRef &procRef,
3669       llvm::Optional<mlir::Type> retTy,
3670       const Fortran::evaluate::SpecificIntrinsic &intrinsic) {
3671     llvm::SmallVector<CC> operands;
3672     llvm::StringRef name = intrinsic.name;
3673     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
3674         Fortran::lower::getIntrinsicArgumentLowering(name);
3675     mlir::Location loc = getLoc();
3676     if (Fortran::lower::intrinsicRequiresCustomOptionalHandling(
3677             procRef, intrinsic, converter)) {
3678       using CcPairT = std::pair<CC, llvm::Optional<mlir::Value>>;
3679       llvm::SmallVector<CcPairT> operands;
3680       auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
3681         if (expr.Rank() == 0) {
3682           ExtValue optionalArg = this->asInquired(expr);
3683           mlir::Value isPresent =
3684               genActualIsPresentTest(builder, loc, optionalArg);
3685           operands.emplace_back(
3686               [=](IterSpace iters) -> ExtValue {
3687                 return genLoad(builder, loc, optionalArg);
3688               },
3689               isPresent);
3690         } else {
3691           auto [cc, isPresent, _] = this->genOptionalArrayFetch(expr);
3692           operands.emplace_back(cc, isPresent);
3693         }
3694       };
3695       auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) {
3696         PushSemantics(ConstituentSemantics::RefTransparent);
3697         operands.emplace_back(genElementalArgument(expr), llvm::None);
3698       };
3699       Fortran::lower::prepareCustomIntrinsicArgument(
3700           procRef, intrinsic, retTy, prepareOptionalArg, prepareOtherArg,
3701           converter);
3702 
3703       fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
3704       llvm::StringRef name = intrinsic.name;
3705       return [=](IterSpace iters) -> ExtValue {
3706         auto getArgument = [&](std::size_t i) -> ExtValue {
3707           return operands[i].first(iters);
3708         };
3709         auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> {
3710           return operands[i].second;
3711         };
3712         return Fortran::lower::lowerCustomIntrinsic(
3713             *bldr, loc, name, retTy, isPresent, getArgument, operands.size(),
3714             getElementCtx());
3715       };
3716     }
3717     /// Otherwise, pre-lower arguments and use intrinsic lowering utility.
3718     for (const auto &[arg, dummy] :
3719          llvm::zip(procRef.arguments(),
3720                    intrinsic.characteristics.value().dummyArguments)) {
3721       const auto *expr =
3722           Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
3723       if (!expr) {
3724         // Absent optional.
3725         operands.emplace_back([=](IterSpace) { return mlir::Value{}; });
3726       } else if (!argLowering) {
3727         // No argument lowering instruction, lower by value.
3728         PushSemantics(ConstituentSemantics::RefTransparent);
3729         operands.emplace_back(genElementalArgument(*expr));
3730       } else {
3731         // Ad-hoc argument lowering handling.
3732         Fortran::lower::ArgLoweringRule argRules =
3733             Fortran::lower::lowerIntrinsicArgumentAs(getLoc(), *argLowering,
3734                                                      dummy.name);
3735         if (argRules.handleDynamicOptional &&
3736             Fortran::evaluate::MayBePassedAsAbsentOptional(
3737                 *expr, converter.getFoldingContext())) {
3738           // Currently, there is not elemental intrinsic that requires lowering
3739           // a potentially absent argument to something else than a value (apart
3740           // from character MAX/MIN that are handled elsewhere.)
3741           if (argRules.lowerAs != Fortran::lower::LowerIntrinsicArgAs::Value)
3742             TODO(loc, "lowering non trivial optional elemental intrinsic array "
3743                       "argument");
3744           PushSemantics(ConstituentSemantics::RefTransparent);
3745           operands.emplace_back(genarrForwardOptionalArgumentToCall(*expr));
3746           continue;
3747         }
3748         switch (argRules.lowerAs) {
3749         case Fortran::lower::LowerIntrinsicArgAs::Value: {
3750           PushSemantics(ConstituentSemantics::RefTransparent);
3751           operands.emplace_back(genElementalArgument(*expr));
3752         } break;
3753         case Fortran::lower::LowerIntrinsicArgAs::Addr: {
3754           // Note: assume does not have Fortran VALUE attribute semantics.
3755           PushSemantics(ConstituentSemantics::RefOpaque);
3756           operands.emplace_back(genElementalArgument(*expr));
3757         } break;
3758         case Fortran::lower::LowerIntrinsicArgAs::Box: {
3759           PushSemantics(ConstituentSemantics::RefOpaque);
3760           auto lambda = genElementalArgument(*expr);
3761           operands.emplace_back([=](IterSpace iters) {
3762             return builder.createBox(loc, lambda(iters));
3763           });
3764         } break;
3765         case Fortran::lower::LowerIntrinsicArgAs::Inquired:
3766           TODO(loc, "intrinsic function with inquired argument");
3767           break;
3768         }
3769       }
3770     }
3771 
3772     // Let the intrinsic library lower the intrinsic procedure call
3773     return [=](IterSpace iters) {
3774       llvm::SmallVector<ExtValue> args;
3775       for (const auto &cc : operands)
3776         args.push_back(cc(iters));
3777       return Fortran::lower::genIntrinsicCall(builder, loc, name, retTy, args,
3778                                               getElementCtx());
3779     };
3780   }
3781 
3782   /// Lower a procedure reference to a user-defined elemental procedure.
3783   CC genElementalUserDefinedProcRef(
3784       const Fortran::evaluate::ProcedureRef &procRef,
3785       llvm::Optional<mlir::Type> retTy) {
3786     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
3787 
3788     // 10.1.4 p5. Impure elemental procedures must be called in element order.
3789     if (const Fortran::semantics::Symbol *procSym = procRef.proc().GetSymbol())
3790       if (!Fortran::semantics::IsPureProcedure(*procSym))
3791         setUnordered(false);
3792 
3793     Fortran::lower::CallerInterface caller(procRef, converter);
3794     llvm::SmallVector<CC> operands;
3795     operands.reserve(caller.getPassedArguments().size());
3796     mlir::Location loc = getLoc();
3797     mlir::FunctionType callSiteType = caller.genFunctionType();
3798     for (const Fortran::lower::CallInterface<
3799              Fortran::lower::CallerInterface>::PassedEntity &arg :
3800          caller.getPassedArguments()) {
3801       // 15.8.3 p1. Elemental procedure with intent(out)/intent(inout)
3802       // arguments must be called in element order.
3803       if (arg.mayBeModifiedByCall())
3804         setUnordered(false);
3805       const auto *actual = arg.entity;
3806       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
3807       if (!actual) {
3808         // Optional dummy argument for which there is no actual argument.
3809         auto absent = builder.create<fir::AbsentOp>(loc, argTy);
3810         operands.emplace_back([=](IterSpace) { return absent; });
3811         continue;
3812       }
3813       const auto *expr = actual->UnwrapExpr();
3814       if (!expr)
3815         TODO(loc, "assumed type actual argument lowering");
3816 
3817       LLVM_DEBUG(expr->AsFortran(llvm::dbgs()
3818                                  << "argument: " << arg.firArgument << " = [")
3819                  << "]\n");
3820       if (arg.isOptional() && Fortran::evaluate::MayBePassedAsAbsentOptional(
3821                                   *expr, converter.getFoldingContext()))
3822         TODO(loc,
3823              "passing dynamically optional argument to elemental procedures");
3824       switch (arg.passBy) {
3825       case PassBy::Value: {
3826         // True pass-by-value semantics.
3827         PushSemantics(ConstituentSemantics::RefTransparent);
3828         operands.emplace_back(genElementalArgument(*expr));
3829       } break;
3830       case PassBy::BaseAddressValueAttribute: {
3831         // VALUE attribute or pass-by-reference to a copy semantics. (byval*)
3832         if (isArray(*expr)) {
3833           PushSemantics(ConstituentSemantics::ByValueArg);
3834           operands.emplace_back(genElementalArgument(*expr));
3835         } else {
3836           // Store scalar value in a temp to fulfill VALUE attribute.
3837           mlir::Value val = fir::getBase(asScalar(*expr));
3838           mlir::Value temp = builder.createTemporary(
3839               loc, val.getType(),
3840               llvm::ArrayRef<mlir::NamedAttribute>{
3841                   Fortran::lower::getAdaptToByRefAttr(builder)});
3842           builder.create<fir::StoreOp>(loc, val, temp);
3843           operands.emplace_back(
3844               [=](IterSpace iters) -> ExtValue { return temp; });
3845         }
3846       } break;
3847       case PassBy::BaseAddress: {
3848         if (isArray(*expr)) {
3849           PushSemantics(ConstituentSemantics::RefOpaque);
3850           operands.emplace_back(genElementalArgument(*expr));
3851         } else {
3852           ExtValue exv = asScalarRef(*expr);
3853           operands.emplace_back([=](IterSpace iters) { return exv; });
3854         }
3855       } break;
3856       case PassBy::CharBoxValueAttribute: {
3857         if (isArray(*expr)) {
3858           PushSemantics(ConstituentSemantics::DataValue);
3859           auto lambda = genElementalArgument(*expr);
3860           operands.emplace_back([=](IterSpace iters) {
3861             return fir::factory::CharacterExprHelper{builder, loc}
3862                 .createTempFrom(lambda(iters));
3863           });
3864         } else {
3865           fir::factory::CharacterExprHelper helper(builder, loc);
3866           fir::CharBoxValue argVal = helper.createTempFrom(asScalarRef(*expr));
3867           operands.emplace_back(
3868               [=](IterSpace iters) -> ExtValue { return argVal; });
3869         }
3870       } break;
3871       case PassBy::BoxChar: {
3872         PushSemantics(ConstituentSemantics::RefOpaque);
3873         operands.emplace_back(genElementalArgument(*expr));
3874       } break;
3875       case PassBy::AddressAndLength:
3876         // PassBy::AddressAndLength is only used for character results. Results
3877         // are not handled here.
3878         fir::emitFatalError(
3879             loc, "unexpected PassBy::AddressAndLength in elemental call");
3880         break;
3881       case PassBy::CharProcTuple: {
3882         ExtValue argRef = asScalarRef(*expr);
3883         mlir::Value tuple = createBoxProcCharTuple(
3884             converter, argTy, fir::getBase(argRef), fir::getLen(argRef));
3885         operands.emplace_back(
3886             [=](IterSpace iters) -> ExtValue { return tuple; });
3887       } break;
3888       case PassBy::Box:
3889       case PassBy::MutableBox:
3890         // See C15100 and C15101
3891         fir::emitFatalError(loc, "cannot be POINTER, ALLOCATABLE");
3892       }
3893     }
3894 
3895     if (caller.getIfIndirectCallSymbol())
3896       fir::emitFatalError(loc, "cannot be indirect call");
3897 
3898     // The lambda is mutable so that `caller` copy can be modified inside it.
3899     return
3900         [=, caller = std::move(caller)](IterSpace iters) mutable -> ExtValue {
3901           for (const auto &[cc, argIface] :
3902                llvm::zip(operands, caller.getPassedArguments())) {
3903             auto exv = cc(iters);
3904             auto arg = exv.match(
3905                 [&](const fir::CharBoxValue &cb) -> mlir::Value {
3906                   return fir::factory::CharacterExprHelper{builder, loc}
3907                       .createEmbox(cb);
3908                 },
3909                 [&](const auto &) { return fir::getBase(exv); });
3910             caller.placeInput(argIface, arg);
3911           }
3912           return ScalarExprLowering{loc, converter, symMap, getElementCtx()}
3913               .genCallOpAndResult(caller, callSiteType, retTy);
3914         };
3915   }
3916 
3917   /// Generate a procedure reference. This code is shared for both functions and
3918   /// subroutines, the difference being reflected by `retTy`.
3919   CC genProcRef(const Fortran::evaluate::ProcedureRef &procRef,
3920                 llvm::Optional<mlir::Type> retTy) {
3921     mlir::Location loc = getLoc();
3922     if (procRef.IsElemental()) {
3923       if (const Fortran::evaluate::SpecificIntrinsic *intrin =
3924               procRef.proc().GetSpecificIntrinsic()) {
3925         // All elemental intrinsic functions are pure and cannot modify their
3926         // arguments. The only elemental subroutine, MVBITS has an Intent(inout)
3927         // argument. So for this last one, loops must be in element order
3928         // according to 15.8.3 p1.
3929         if (!retTy)
3930           setUnordered(false);
3931 
3932         // Elemental intrinsic call.
3933         // The intrinsic procedure is called once per element of the array.
3934         return genElementalIntrinsicProcRef(procRef, retTy, *intrin);
3935       }
3936       if (ScalarExprLowering::isStatementFunctionCall(procRef))
3937         fir::emitFatalError(loc, "statement function cannot be elemental");
3938 
3939       // Elemental call.
3940       // The procedure is called once per element of the array argument(s).
3941       return genElementalUserDefinedProcRef(procRef, retTy);
3942     }
3943 
3944     // Transformational call.
3945     // The procedure is called once and produces a value of rank > 0.
3946     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
3947             procRef.proc().GetSpecificIntrinsic()) {
3948       if (explicitSpaceIsActive() && procRef.Rank() == 0) {
3949         // Elide any implicit loop iters.
3950         return [=, &procRef](IterSpace) {
3951           return ScalarExprLowering{loc, converter, symMap, stmtCtx}
3952               .genIntrinsicRef(procRef, *intrinsic, retTy);
3953         };
3954       }
3955       return genarr(
3956           ScalarExprLowering{loc, converter, symMap, stmtCtx}.genIntrinsicRef(
3957               procRef, *intrinsic, retTy));
3958     }
3959 
3960     if (explicitSpaceIsActive() && procRef.Rank() == 0) {
3961       // Elide any implicit loop iters.
3962       return [=, &procRef](IterSpace) {
3963         return ScalarExprLowering{loc, converter, symMap, stmtCtx}
3964             .genProcedureRef(procRef, retTy);
3965       };
3966     }
3967     // In the default case, the call can be hoisted out of the loop nest. Apply
3968     // the iterations to the result, which may be an array value.
3969     return genarr(
3970         ScalarExprLowering{loc, converter, symMap, stmtCtx}.genProcedureRef(
3971             procRef, retTy));
3972   }
3973 
3974   template <typename A>
3975   CC genScalarAndForwardValue(const A &x) {
3976     ExtValue result = asScalar(x);
3977     return [=](IterSpace) { return result; };
3978   }
3979 
3980   template <typename A, typename = std::enable_if_t<Fortran::common::HasMember<
3981                             A, Fortran::evaluate::TypelessExpression>>>
3982   CC genarr(const A &x) {
3983     return genScalarAndForwardValue(x);
3984   }
3985 
3986   template <typename A>
3987   CC genarr(const Fortran::evaluate::Expr<A> &x) {
3988     LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x));
3989     if (isArray(x) || explicitSpaceIsActive() ||
3990         isElementalProcWithArrayArgs(x))
3991       return std::visit([&](const auto &e) { return genarr(e); }, x.u);
3992     return genScalarAndForwardValue(x);
3993   }
3994 
3995   // Converting a value of memory bound type requires creating a temp and
3996   // copying the value.
3997   static ExtValue convertAdjustedType(fir::FirOpBuilder &builder,
3998                                       mlir::Location loc, mlir::Type toType,
3999                                       const ExtValue &exv) {
4000     return exv.match(
4001         [&](const fir::CharBoxValue &cb) -> ExtValue {
4002           mlir::Value len = cb.getLen();
4003           auto mem =
4004               builder.create<fir::AllocaOp>(loc, toType, mlir::ValueRange{len});
4005           fir::CharBoxValue result(mem, len);
4006           fir::factory::CharacterExprHelper{builder, loc}.createAssign(
4007               ExtValue{result}, exv);
4008           return result;
4009         },
4010         [&](const auto &) -> ExtValue {
4011           fir::emitFatalError(loc, "convert on adjusted extended value");
4012         });
4013   }
4014   template <Fortran::common::TypeCategory TC1, int KIND,
4015             Fortran::common::TypeCategory TC2>
4016   CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
4017                                              TC2> &x) {
4018     mlir::Location loc = getLoc();
4019     auto lambda = genarr(x.left());
4020     mlir::Type ty = converter.genType(TC1, KIND);
4021     return [=](IterSpace iters) -> ExtValue {
4022       auto exv = lambda(iters);
4023       mlir::Value val = fir::getBase(exv);
4024       auto valTy = val.getType();
4025       if (elementTypeWasAdjusted(valTy) &&
4026           !(fir::isa_ref_type(valTy) && fir::isa_integer(ty)))
4027         return convertAdjustedType(builder, loc, ty, exv);
4028       return builder.createConvert(loc, ty, val);
4029     };
4030   }
4031 
4032   template <int KIND>
4033   CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) {
4034     TODO(getLoc(), "ComplexComponent<KIND>");
4035   }
4036 
4037   template <typename T>
4038   CC genarr(const Fortran::evaluate::Parentheses<T> &x) {
4039     mlir::Location loc = getLoc();
4040     if (isReferentiallyOpaque()) {
4041       // Context is a call argument in, for example, an elemental procedure
4042       // call. TODO: all array arguments should use array_load, array_access,
4043       // array_amend, and INTENT(OUT), INTENT(INOUT) arguments should have
4044       // array_merge_store ops.
4045       TODO(loc, "parentheses on argument in elemental call");
4046     }
4047     auto f = genarr(x.left());
4048     return [=](IterSpace iters) -> ExtValue {
4049       auto val = f(iters);
4050       mlir::Value base = fir::getBase(val);
4051       auto newBase =
4052           builder.create<fir::NoReassocOp>(loc, base.getType(), base);
4053       return fir::substBase(val, newBase);
4054     };
4055   }
4056   template <int KIND>
4057   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
4058                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
4059     mlir::Location loc = getLoc();
4060     auto f = genarr(x.left());
4061     return [=](IterSpace iters) -> ExtValue {
4062       mlir::Value val = fir::getBase(f(iters));
4063       mlir::Type ty =
4064           converter.genType(Fortran::common::TypeCategory::Integer, KIND);
4065       mlir::Value zero = builder.createIntegerConstant(loc, ty, 0);
4066       return builder.create<mlir::arith::SubIOp>(loc, zero, val);
4067     };
4068   }
4069   template <int KIND>
4070   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
4071                 Fortran::common::TypeCategory::Real, KIND>> &x) {
4072     mlir::Location loc = getLoc();
4073     auto f = genarr(x.left());
4074     return [=](IterSpace iters) -> ExtValue {
4075       return builder.create<mlir::arith::NegFOp>(loc, fir::getBase(f(iters)));
4076     };
4077   }
4078   template <int KIND>
4079   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
4080                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
4081     mlir::Location loc = getLoc();
4082     auto f = genarr(x.left());
4083     return [=](IterSpace iters) -> ExtValue {
4084       return builder.create<fir::NegcOp>(loc, fir::getBase(f(iters)));
4085     };
4086   }
4087 
4088   //===--------------------------------------------------------------------===//
4089   // Binary elemental ops
4090   //===--------------------------------------------------------------------===//
4091 
4092   template <typename OP, typename A>
4093   CC createBinaryOp(const A &evEx) {
4094     mlir::Location loc = getLoc();
4095     auto lambda = genarr(evEx.left());
4096     auto rf = genarr(evEx.right());
4097     return [=](IterSpace iters) -> ExtValue {
4098       mlir::Value left = fir::getBase(lambda(iters));
4099       mlir::Value right = fir::getBase(rf(iters));
4100       return builder.create<OP>(loc, left, right);
4101     };
4102   }
4103 
4104 #undef GENBIN
4105 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
4106   template <int KIND>                                                          \
4107   CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type<       \
4108                 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) {       \
4109     return createBinaryOp<GenBinFirOp>(x);                                     \
4110   }
4111 
4112   GENBIN(Add, Integer, mlir::arith::AddIOp)
4113   GENBIN(Add, Real, mlir::arith::AddFOp)
4114   GENBIN(Add, Complex, fir::AddcOp)
4115   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
4116   GENBIN(Subtract, Real, mlir::arith::SubFOp)
4117   GENBIN(Subtract, Complex, fir::SubcOp)
4118   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
4119   GENBIN(Multiply, Real, mlir::arith::MulFOp)
4120   GENBIN(Multiply, Complex, fir::MulcOp)
4121   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
4122   GENBIN(Divide, Real, mlir::arith::DivFOp)
4123   GENBIN(Divide, Complex, fir::DivcOp)
4124 
4125   template <Fortran::common::TypeCategory TC, int KIND>
4126   CC genarr(
4127       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
4128     mlir::Location loc = getLoc();
4129     mlir::Type ty = converter.genType(TC, KIND);
4130     auto lf = genarr(x.left());
4131     auto rf = genarr(x.right());
4132     return [=](IterSpace iters) -> ExtValue {
4133       mlir::Value lhs = fir::getBase(lf(iters));
4134       mlir::Value rhs = fir::getBase(rf(iters));
4135       return Fortran::lower::genPow(builder, loc, ty, lhs, rhs);
4136     };
4137   }
4138   template <Fortran::common::TypeCategory TC, int KIND>
4139   CC genarr(
4140       const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
4141     TODO(getLoc(), "genarr Extremum<Fortran::evaluate::Type<TC, KIND>>");
4142   }
4143   template <Fortran::common::TypeCategory TC, int KIND>
4144   CC genarr(
4145       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
4146           &x) {
4147     TODO(getLoc(), "genarr RealToIntPower<Fortran::evaluate::Type<TC, KIND>>");
4148   }
4149   template <int KIND>
4150   CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
4151     TODO(getLoc(), "genarr ComplexConstructor<KIND>");
4152   }
4153 
4154   template <int KIND>
4155   CC genarr(const Fortran::evaluate::Concat<KIND> &x) {
4156     TODO(getLoc(), "genarr Concat<KIND>");
4157   }
4158 
4159   template <int KIND>
4160   CC genarr(const Fortran::evaluate::SetLength<KIND> &x) {
4161     TODO(getLoc(), "genarr SetLength<KIND>");
4162   }
4163 
4164   template <typename A>
4165   CC genarr(const Fortran::evaluate::Constant<A> &x) {
4166     if (/*explicitSpaceIsActive() &&*/ x.Rank() == 0)
4167       return genScalarAndForwardValue(x);
4168     mlir::Location loc = getLoc();
4169     mlir::IndexType idxTy = builder.getIndexType();
4170     mlir::Type arrTy = converter.genType(toEvExpr(x));
4171     std::string globalName = Fortran::lower::mangle::mangleArrayLiteral(x);
4172     fir::GlobalOp global = builder.getNamedGlobal(globalName);
4173     if (!global) {
4174       mlir::Type symTy = arrTy;
4175       mlir::Type eleTy = symTy.cast<fir::SequenceType>().getEleTy();
4176       // If we have a rank-1 array of integer, real, or logical, then we can
4177       // create a global array with the dense attribute.
4178       //
4179       // The mlir tensor type can only handle integer, real, or logical. It
4180       // does not currently support nested structures which is required for
4181       // complex.
4182       //
4183       // Also, we currently handle just rank-1 since tensor type assumes
4184       // row major array ordering. We will need to reorder the dimensions
4185       // in the tensor type to support Fortran's column major array ordering.
4186       // How to create this tensor type is to be determined.
4187       if (x.Rank() == 1 &&
4188           eleTy.isa<fir::LogicalType, mlir::IntegerType, mlir::FloatType>())
4189         global = Fortran::lower::createDenseGlobal(
4190             loc, arrTy, globalName, builder.createInternalLinkage(), true,
4191             toEvExpr(x), converter);
4192       // Note: If call to createDenseGlobal() returns 0, then call
4193       // createGlobalConstant() below.
4194       if (!global)
4195         global = builder.createGlobalConstant(
4196             loc, arrTy, globalName,
4197             [&](fir::FirOpBuilder &builder) {
4198               Fortran::lower::StatementContext stmtCtx(
4199                   /*cleanupProhibited=*/true);
4200               fir::ExtendedValue result =
4201                   Fortran::lower::createSomeInitializerExpression(
4202                       loc, converter, toEvExpr(x), symMap, stmtCtx);
4203               mlir::Value castTo =
4204                   builder.createConvert(loc, arrTy, fir::getBase(result));
4205               builder.create<fir::HasValueOp>(loc, castTo);
4206             },
4207             builder.createInternalLinkage());
4208     }
4209     auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(),
4210                                               global.getSymbol());
4211     auto seqTy = global.getType().cast<fir::SequenceType>();
4212     llvm::SmallVector<mlir::Value> extents;
4213     for (auto extent : seqTy.getShape())
4214       extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
4215     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>()) {
4216       mlir::Value len = builder.createIntegerConstant(loc, builder.getI64Type(),
4217                                                       charTy.getLen());
4218       return genarr(fir::CharArrayBoxValue{addr, len, extents});
4219     }
4220     return genarr(fir::ArrayBoxValue{addr, extents});
4221   }
4222 
4223   //===--------------------------------------------------------------------===//
4224   // A vector subscript expression may be wrapped with a cast to INTEGER*8.
4225   // Get rid of it here so the vector can be loaded. Add it back when
4226   // generating the elemental evaluation (inside the loop nest).
4227 
4228   static Fortran::lower::SomeExpr
4229   ignoreEvConvert(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
4230                       Fortran::common::TypeCategory::Integer, 8>> &x) {
4231     return std::visit([&](const auto &v) { return ignoreEvConvert(v); }, x.u);
4232   }
4233   template <Fortran::common::TypeCategory FROM>
4234   static Fortran::lower::SomeExpr ignoreEvConvert(
4235       const Fortran::evaluate::Convert<
4236           Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 8>,
4237           FROM> &x) {
4238     return toEvExpr(x.left());
4239   }
4240   template <typename A>
4241   static Fortran::lower::SomeExpr ignoreEvConvert(const A &x) {
4242     return toEvExpr(x);
4243   }
4244 
4245   //===--------------------------------------------------------------------===//
4246   // Get the `Se::Symbol*` for the subscript expression, `x`. This symbol can
4247   // be used to determine the lbound, ubound of the vector.
4248 
4249   template <typename A>
4250   static const Fortran::semantics::Symbol *
4251   extractSubscriptSymbol(const Fortran::evaluate::Expr<A> &x) {
4252     return std::visit([&](const auto &v) { return extractSubscriptSymbol(v); },
4253                       x.u);
4254   }
4255   template <typename A>
4256   static const Fortran::semantics::Symbol *
4257   extractSubscriptSymbol(const Fortran::evaluate::Designator<A> &x) {
4258     return Fortran::evaluate::UnwrapWholeSymbolDataRef(x);
4259   }
4260   template <typename A>
4261   static const Fortran::semantics::Symbol *extractSubscriptSymbol(const A &x) {
4262     return nullptr;
4263   }
4264 
4265   //===--------------------------------------------------------------------===//
4266 
4267   /// Get the declared lower bound value of the array `x` in dimension `dim`.
4268   /// The argument `one` must be an ssa-value for the constant 1.
4269   mlir::Value getLBound(const ExtValue &x, unsigned dim, mlir::Value one) {
4270     return fir::factory::readLowerBound(builder, getLoc(), x, dim, one);
4271   }
4272 
4273   /// Get the declared upper bound value of the array `x` in dimension `dim`.
4274   /// The argument `one` must be an ssa-value for the constant 1.
4275   mlir::Value getUBound(const ExtValue &x, unsigned dim, mlir::Value one) {
4276     mlir::Location loc = getLoc();
4277     mlir::Value lb = getLBound(x, dim, one);
4278     mlir::Value extent = fir::factory::readExtent(builder, loc, x, dim);
4279     auto add = builder.create<mlir::arith::AddIOp>(loc, lb, extent);
4280     return builder.create<mlir::arith::SubIOp>(loc, add, one);
4281   }
4282 
4283   /// Return the extent of the boxed array `x` in dimesion `dim`.
4284   mlir::Value getExtent(const ExtValue &x, unsigned dim) {
4285     return fir::factory::readExtent(builder, getLoc(), x, dim);
4286   }
4287 
4288   template <typename A>
4289   ExtValue genArrayBase(const A &base) {
4290     ScalarExprLowering sel{getLoc(), converter, symMap, stmtCtx};
4291     return base.IsSymbol() ? sel.gen(base.GetFirstSymbol())
4292                            : sel.gen(base.GetComponent());
4293   }
4294 
4295   template <typename A>
4296   bool hasEvArrayRef(const A &x) {
4297     struct HasEvArrayRefHelper
4298         : public Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper> {
4299       HasEvArrayRefHelper()
4300           : Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper>(*this) {}
4301       using Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper>::operator();
4302       bool operator()(const Fortran::evaluate::ArrayRef &) const {
4303         return true;
4304       }
4305     } helper;
4306     return helper(x);
4307   }
4308 
4309   CC genVectorSubscriptArrayFetch(const Fortran::lower::SomeExpr &expr,
4310                                   std::size_t dim) {
4311     PushSemantics(ConstituentSemantics::RefTransparent);
4312     auto saved = Fortran::common::ScopedSet(explicitSpace, nullptr);
4313     llvm::SmallVector<mlir::Value> savedDestShape = destShape;
4314     destShape.clear();
4315     auto result = genarr(expr);
4316     if (destShape.empty())
4317       TODO(getLoc(), "expected vector to have an extent");
4318     assert(destShape.size() == 1 && "vector has rank > 1");
4319     if (destShape[0] != savedDestShape[dim]) {
4320       // Not the same, so choose the smaller value.
4321       mlir::Location loc = getLoc();
4322       auto cmp = builder.create<mlir::arith::CmpIOp>(
4323           loc, mlir::arith::CmpIPredicate::sgt, destShape[0],
4324           savedDestShape[dim]);
4325       auto sel = builder.create<mlir::arith::SelectOp>(
4326           loc, cmp, savedDestShape[dim], destShape[0]);
4327       savedDestShape[dim] = sel;
4328       destShape = savedDestShape;
4329     }
4330     return result;
4331   }
4332 
4333   /// Generate an access by vector subscript using the index in the iteration
4334   /// vector at `dim`.
4335   mlir::Value genAccessByVector(mlir::Location loc, CC genArrFetch,
4336                                 IterSpace iters, std::size_t dim) {
4337     IterationSpace vecIters(iters,
4338                             llvm::ArrayRef<mlir::Value>{iters.iterValue(dim)});
4339     fir::ExtendedValue fetch = genArrFetch(vecIters);
4340     mlir::IndexType idxTy = builder.getIndexType();
4341     return builder.createConvert(loc, idxTy, fir::getBase(fetch));
4342   }
4343 
4344   /// When we have an array reference, the expressions specified in each
4345   /// dimension may be slice operations (e.g. `i:j:k`), vectors, or simple
4346   /// (loop-invarianet) scalar expressions. This returns the base entity, the
4347   /// resulting type, and a continuation to adjust the default iteration space.
4348   void genSliceIndices(ComponentPath &cmptData, const ExtValue &arrayExv,
4349                        const Fortran::evaluate::ArrayRef &x, bool atBase) {
4350     mlir::Location loc = getLoc();
4351     mlir::IndexType idxTy = builder.getIndexType();
4352     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
4353     llvm::SmallVector<mlir::Value> &trips = cmptData.trips;
4354     LLVM_DEBUG(llvm::dbgs() << "array: " << arrayExv << '\n');
4355     auto &pc = cmptData.pc;
4356     const bool useTripsForSlice = !explicitSpaceIsActive();
4357     const bool createDestShape = destShape.empty();
4358     bool useSlice = false;
4359     std::size_t shapeIndex = 0;
4360     for (auto sub : llvm::enumerate(x.subscript())) {
4361       const std::size_t subsIndex = sub.index();
4362       std::visit(
4363           Fortran::common::visitors{
4364               [&](const Fortran::evaluate::Triplet &t) {
4365                 mlir::Value lowerBound;
4366                 if (auto optLo = t.lower())
4367                   lowerBound = fir::getBase(asScalar(*optLo));
4368                 else
4369                   lowerBound = getLBound(arrayExv, subsIndex, one);
4370                 lowerBound = builder.createConvert(loc, idxTy, lowerBound);
4371                 mlir::Value stride = fir::getBase(asScalar(t.stride()));
4372                 stride = builder.createConvert(loc, idxTy, stride);
4373                 if (useTripsForSlice || createDestShape) {
4374                   // Generate a slice operation for the triplet. The first and
4375                   // second position of the triplet may be omitted, and the
4376                   // declared lbound and/or ubound expression values,
4377                   // respectively, should be used instead.
4378                   trips.push_back(lowerBound);
4379                   mlir::Value upperBound;
4380                   if (auto optUp = t.upper())
4381                     upperBound = fir::getBase(asScalar(*optUp));
4382                   else
4383                     upperBound = getUBound(arrayExv, subsIndex, one);
4384                   upperBound = builder.createConvert(loc, idxTy, upperBound);
4385                   trips.push_back(upperBound);
4386                   trips.push_back(stride);
4387                   if (createDestShape) {
4388                     auto extent = builder.genExtentFromTriplet(
4389                         loc, lowerBound, upperBound, stride, idxTy);
4390                     destShape.push_back(extent);
4391                   }
4392                   useSlice = true;
4393                 }
4394                 if (!useTripsForSlice) {
4395                   auto currentPC = pc;
4396                   pc = [=](IterSpace iters) {
4397                     IterationSpace newIters = currentPC(iters);
4398                     mlir::Value impliedIter = newIters.iterValue(subsIndex);
4399                     // FIXME: must use the lower bound of this component.
4400                     auto arrLowerBound =
4401                         atBase ? getLBound(arrayExv, subsIndex, one) : one;
4402                     auto initial = builder.create<mlir::arith::SubIOp>(
4403                         loc, lowerBound, arrLowerBound);
4404                     auto prod = builder.create<mlir::arith::MulIOp>(
4405                         loc, impliedIter, stride);
4406                     auto result =
4407                         builder.create<mlir::arith::AddIOp>(loc, initial, prod);
4408                     newIters.setIndexValue(subsIndex, result);
4409                     return newIters;
4410                   };
4411                 }
4412                 shapeIndex++;
4413               },
4414               [&](const Fortran::evaluate::IndirectSubscriptIntegerExpr &ie) {
4415                 const auto &e = ie.value(); // dereference
4416                 if (isArray(e)) {
4417                   // This is a vector subscript. Use the index values as read
4418                   // from a vector to determine the temporary array value.
4419                   // Note: 9.5.3.3.3(3) specifies undefined behavior for
4420                   // multiple updates to any specific array element through a
4421                   // vector subscript with replicated values.
4422                   assert(!isBoxValue() &&
4423                          "fir.box cannot be created with vector subscripts");
4424                   auto arrExpr = ignoreEvConvert(e);
4425                   if (createDestShape) {
4426                     destShape.push_back(fir::getExtentAtDimension(
4427                         arrayExv, builder, loc, subsIndex));
4428                   }
4429                   auto genArrFetch =
4430                       genVectorSubscriptArrayFetch(arrExpr, shapeIndex);
4431                   auto currentPC = pc;
4432                   pc = [=](IterSpace iters) {
4433                     IterationSpace newIters = currentPC(iters);
4434                     auto val = genAccessByVector(loc, genArrFetch, newIters,
4435                                                  subsIndex);
4436                     // Value read from vector subscript array and normalized
4437                     // using the base array's lower bound value.
4438                     mlir::Value lb = fir::factory::readLowerBound(
4439                         builder, loc, arrayExv, subsIndex, one);
4440                     auto origin = builder.create<mlir::arith::SubIOp>(
4441                         loc, idxTy, val, lb);
4442                     newIters.setIndexValue(subsIndex, origin);
4443                     return newIters;
4444                   };
4445                   if (useTripsForSlice) {
4446                     LLVM_ATTRIBUTE_UNUSED auto vectorSubscriptShape =
4447                         getShape(arrayOperands.back());
4448                     auto undef = builder.create<fir::UndefOp>(loc, idxTy);
4449                     trips.push_back(undef);
4450                     trips.push_back(undef);
4451                     trips.push_back(undef);
4452                   }
4453                   shapeIndex++;
4454                 } else {
4455                   // This is a regular scalar subscript.
4456                   if (useTripsForSlice) {
4457                     // A regular scalar index, which does not yield an array
4458                     // section. Use a degenerate slice operation
4459                     // `(e:undef:undef)` in this dimension as a placeholder.
4460                     // This does not necessarily change the rank of the original
4461                     // array, so the iteration space must also be extended to
4462                     // include this expression in this dimension to adjust to
4463                     // the array's declared rank.
4464                     mlir::Value v = fir::getBase(asScalar(e));
4465                     trips.push_back(v);
4466                     auto undef = builder.create<fir::UndefOp>(loc, idxTy);
4467                     trips.push_back(undef);
4468                     trips.push_back(undef);
4469                     auto currentPC = pc;
4470                     // Cast `e` to index type.
4471                     mlir::Value iv = builder.createConvert(loc, idxTy, v);
4472                     // Normalize `e` by subtracting the declared lbound.
4473                     mlir::Value lb = fir::factory::readLowerBound(
4474                         builder, loc, arrayExv, subsIndex, one);
4475                     mlir::Value ivAdj =
4476                         builder.create<mlir::arith::SubIOp>(loc, idxTy, iv, lb);
4477                     // Add lbound adjusted value of `e` to the iteration vector
4478                     // (except when creating a box because the iteration vector
4479                     // is empty).
4480                     if (!isBoxValue())
4481                       pc = [=](IterSpace iters) {
4482                         IterationSpace newIters = currentPC(iters);
4483                         newIters.insertIndexValue(subsIndex, ivAdj);
4484                         return newIters;
4485                       };
4486                   } else {
4487                     auto currentPC = pc;
4488                     mlir::Value newValue = fir::getBase(asScalarArray(e));
4489                     mlir::Value result =
4490                         builder.createConvert(loc, idxTy, newValue);
4491                     mlir::Value lb = fir::factory::readLowerBound(
4492                         builder, loc, arrayExv, subsIndex, one);
4493                     result = builder.create<mlir::arith::SubIOp>(loc, idxTy,
4494                                                                  result, lb);
4495                     pc = [=](IterSpace iters) {
4496                       IterationSpace newIters = currentPC(iters);
4497                       newIters.insertIndexValue(subsIndex, result);
4498                       return newIters;
4499                     };
4500                   }
4501                 }
4502               }},
4503           sub.value().u);
4504     }
4505     if (!useSlice)
4506       trips.clear();
4507   }
4508 
4509   CC genarr(const Fortran::semantics::SymbolRef &sym,
4510             ComponentPath &components) {
4511     return genarr(sym.get(), components);
4512   }
4513 
4514   ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) {
4515     return convertToArrayBoxValue(getLoc(), builder, val, len);
4516   }
4517 
4518   CC genarr(const ExtValue &extMemref) {
4519     ComponentPath dummy(/*isImplicit=*/true);
4520     return genarr(extMemref, dummy);
4521   }
4522 
4523   //===--------------------------------------------------------------------===//
4524   // Array construction
4525   //===--------------------------------------------------------------------===//
4526 
4527   /// Target agnostic computation of the size of an element in the array.
4528   /// Returns the size in bytes with type `index` or a null Value if the element
4529   /// size is not constant.
4530   mlir::Value computeElementSize(const ExtValue &exv, mlir::Type eleTy,
4531                                  mlir::Type resTy) {
4532     mlir::Location loc = getLoc();
4533     mlir::IndexType idxTy = builder.getIndexType();
4534     mlir::Value multiplier = builder.createIntegerConstant(loc, idxTy, 1);
4535     if (fir::hasDynamicSize(eleTy)) {
4536       if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4537         // Array of char with dynamic length parameter. Downcast to an array
4538         // of singleton char, and scale by the len type parameter from
4539         // `exv`.
4540         exv.match(
4541             [&](const fir::CharBoxValue &cb) { multiplier = cb.getLen(); },
4542             [&](const fir::CharArrayBoxValue &cb) { multiplier = cb.getLen(); },
4543             [&](const fir::BoxValue &box) {
4544               multiplier = fir::factory::CharacterExprHelper(builder, loc)
4545                                .readLengthFromBox(box.getAddr());
4546             },
4547             [&](const fir::MutableBoxValue &box) {
4548               multiplier = fir::factory::CharacterExprHelper(builder, loc)
4549                                .readLengthFromBox(box.getAddr());
4550             },
4551             [&](const auto &) {
4552               fir::emitFatalError(loc,
4553                                   "array constructor element has unknown size");
4554             });
4555         fir::CharacterType newEleTy = fir::CharacterType::getSingleton(
4556             eleTy.getContext(), charTy.getFKind());
4557         if (auto seqTy = resTy.dyn_cast<fir::SequenceType>()) {
4558           assert(eleTy == seqTy.getEleTy());
4559           resTy = fir::SequenceType::get(seqTy.getShape(), newEleTy);
4560         }
4561         eleTy = newEleTy;
4562       } else {
4563         TODO(loc, "dynamic sized type");
4564       }
4565     }
4566     mlir::Type eleRefTy = builder.getRefType(eleTy);
4567     mlir::Type resRefTy = builder.getRefType(resTy);
4568     mlir::Value nullPtr = builder.createNullConstant(loc, resRefTy);
4569     auto offset = builder.create<fir::CoordinateOp>(
4570         loc, eleRefTy, nullPtr, mlir::ValueRange{multiplier});
4571     return builder.createConvert(loc, idxTy, offset);
4572   }
4573 
4574   /// Get the function signature of the LLVM memcpy intrinsic.
4575   mlir::FunctionType memcpyType() {
4576     return fir::factory::getLlvmMemcpy(builder).getType();
4577   }
4578 
4579   /// Create a call to the LLVM memcpy intrinsic.
4580   void createCallMemcpy(llvm::ArrayRef<mlir::Value> args) {
4581     mlir::Location loc = getLoc();
4582     mlir::FuncOp memcpyFunc = fir::factory::getLlvmMemcpy(builder);
4583     mlir::SymbolRefAttr funcSymAttr =
4584         builder.getSymbolRefAttr(memcpyFunc.getName());
4585     mlir::FunctionType funcTy = memcpyFunc.getType();
4586     builder.create<fir::CallOp>(loc, funcTy.getResults(), funcSymAttr, args);
4587   }
4588 
4589   // Construct code to check for a buffer overrun and realloc the buffer when
4590   // space is depleted. This is done between each item in the ac-value-list.
4591   mlir::Value growBuffer(mlir::Value mem, mlir::Value needed,
4592                          mlir::Value bufferSize, mlir::Value buffSize,
4593                          mlir::Value eleSz) {
4594     mlir::Location loc = getLoc();
4595     mlir::FuncOp reallocFunc = fir::factory::getRealloc(builder);
4596     auto cond = builder.create<mlir::arith::CmpIOp>(
4597         loc, mlir::arith::CmpIPredicate::sle, bufferSize, needed);
4598     auto ifOp = builder.create<fir::IfOp>(loc, mem.getType(), cond,
4599                                           /*withElseRegion=*/true);
4600     auto insPt = builder.saveInsertionPoint();
4601     builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
4602     // Not enough space, resize the buffer.
4603     mlir::IndexType idxTy = builder.getIndexType();
4604     mlir::Value two = builder.createIntegerConstant(loc, idxTy, 2);
4605     auto newSz = builder.create<mlir::arith::MulIOp>(loc, needed, two);
4606     builder.create<fir::StoreOp>(loc, newSz, buffSize);
4607     mlir::Value byteSz = builder.create<mlir::arith::MulIOp>(loc, newSz, eleSz);
4608     mlir::SymbolRefAttr funcSymAttr =
4609         builder.getSymbolRefAttr(reallocFunc.getName());
4610     mlir::FunctionType funcTy = reallocFunc.getType();
4611     auto newMem = builder.create<fir::CallOp>(
4612         loc, funcTy.getResults(), funcSymAttr,
4613         llvm::ArrayRef<mlir::Value>{
4614             builder.createConvert(loc, funcTy.getInputs()[0], mem),
4615             builder.createConvert(loc, funcTy.getInputs()[1], byteSz)});
4616     mlir::Value castNewMem =
4617         builder.createConvert(loc, mem.getType(), newMem.getResult(0));
4618     builder.create<fir::ResultOp>(loc, castNewMem);
4619     builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
4620     // Otherwise, just forward the buffer.
4621     builder.create<fir::ResultOp>(loc, mem);
4622     builder.restoreInsertionPoint(insPt);
4623     return ifOp.getResult(0);
4624   }
4625 
4626   /// Copy the next value (or vector of values) into the array being
4627   /// constructed.
4628   mlir::Value copyNextArrayCtorSection(const ExtValue &exv, mlir::Value buffPos,
4629                                        mlir::Value buffSize, mlir::Value mem,
4630                                        mlir::Value eleSz, mlir::Type eleTy,
4631                                        mlir::Type eleRefTy, mlir::Type resTy) {
4632     mlir::Location loc = getLoc();
4633     auto off = builder.create<fir::LoadOp>(loc, buffPos);
4634     auto limit = builder.create<fir::LoadOp>(loc, buffSize);
4635     mlir::IndexType idxTy = builder.getIndexType();
4636     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
4637 
4638     if (fir::isRecordWithAllocatableMember(eleTy))
4639       TODO(loc, "deep copy on allocatable members");
4640 
4641     if (!eleSz) {
4642       // Compute the element size at runtime.
4643       assert(fir::hasDynamicSize(eleTy));
4644       if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4645         auto charBytes =
4646             builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8;
4647         mlir::Value bytes =
4648             builder.createIntegerConstant(loc, idxTy, charBytes);
4649         mlir::Value length = fir::getLen(exv);
4650         if (!length)
4651           fir::emitFatalError(loc, "result is not boxed character");
4652         eleSz = builder.create<mlir::arith::MulIOp>(loc, bytes, length);
4653       } else {
4654         TODO(loc, "PDT size");
4655         // Will call the PDT's size function with the type parameters.
4656       }
4657     }
4658 
4659     // Compute the coordinate using `fir.coordinate_of`, or, if the type has
4660     // dynamic size, generating the pointer arithmetic.
4661     auto computeCoordinate = [&](mlir::Value buff, mlir::Value off) {
4662       mlir::Type refTy = eleRefTy;
4663       if (fir::hasDynamicSize(eleTy)) {
4664         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4665           // Scale a simple pointer using dynamic length and offset values.
4666           auto chTy = fir::CharacterType::getSingleton(charTy.getContext(),
4667                                                        charTy.getFKind());
4668           refTy = builder.getRefType(chTy);
4669           mlir::Type toTy = builder.getRefType(builder.getVarLenSeqTy(chTy));
4670           buff = builder.createConvert(loc, toTy, buff);
4671           off = builder.create<mlir::arith::MulIOp>(loc, off, eleSz);
4672         } else {
4673           TODO(loc, "PDT offset");
4674         }
4675       }
4676       auto coor = builder.create<fir::CoordinateOp>(loc, refTy, buff,
4677                                                     mlir::ValueRange{off});
4678       return builder.createConvert(loc, eleRefTy, coor);
4679     };
4680 
4681     // Lambda to lower an abstract array box value.
4682     auto doAbstractArray = [&](const auto &v) {
4683       // Compute the array size.
4684       mlir::Value arrSz = one;
4685       for (auto ext : v.getExtents())
4686         arrSz = builder.create<mlir::arith::MulIOp>(loc, arrSz, ext);
4687 
4688       // Grow the buffer as needed.
4689       auto endOff = builder.create<mlir::arith::AddIOp>(loc, off, arrSz);
4690       mem = growBuffer(mem, endOff, limit, buffSize, eleSz);
4691 
4692       // Copy the elements to the buffer.
4693       mlir::Value byteSz =
4694           builder.create<mlir::arith::MulIOp>(loc, arrSz, eleSz);
4695       auto buff = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
4696       mlir::Value buffi = computeCoordinate(buff, off);
4697       llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
4698           builder, loc, memcpyType(), buffi, v.getAddr(), byteSz,
4699           /*volatile=*/builder.createBool(loc, false));
4700       createCallMemcpy(args);
4701 
4702       // Save the incremented buffer position.
4703       builder.create<fir::StoreOp>(loc, endOff, buffPos);
4704     };
4705 
4706     // Copy a trivial scalar value into the buffer.
4707     auto doTrivialScalar = [&](const ExtValue &v, mlir::Value len = {}) {
4708       // Increment the buffer position.
4709       auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
4710 
4711       // Grow the buffer as needed.
4712       mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
4713 
4714       // Store the element in the buffer.
4715       mlir::Value buff =
4716           builder.createConvert(loc, fir::HeapType::get(resTy), mem);
4717       auto buffi = builder.create<fir::CoordinateOp>(loc, eleRefTy, buff,
4718                                                      mlir::ValueRange{off});
4719       fir::factory::genScalarAssignment(
4720           builder, loc,
4721           [&]() -> ExtValue {
4722             if (len)
4723               return fir::CharBoxValue(buffi, len);
4724             return buffi;
4725           }(),
4726           v);
4727       builder.create<fir::StoreOp>(loc, plusOne, buffPos);
4728     };
4729 
4730     // Copy the value.
4731     exv.match(
4732         [&](mlir::Value) { doTrivialScalar(exv); },
4733         [&](const fir::CharBoxValue &v) {
4734           auto buffer = v.getBuffer();
4735           if (fir::isa_char(buffer.getType())) {
4736             doTrivialScalar(exv, eleSz);
4737           } else {
4738             // Increment the buffer position.
4739             auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
4740 
4741             // Grow the buffer as needed.
4742             mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
4743 
4744             // Store the element in the buffer.
4745             mlir::Value buff =
4746                 builder.createConvert(loc, fir::HeapType::get(resTy), mem);
4747             mlir::Value buffi = computeCoordinate(buff, off);
4748             llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
4749                 builder, loc, memcpyType(), buffi, v.getAddr(), eleSz,
4750                 /*volatile=*/builder.createBool(loc, false));
4751             createCallMemcpy(args);
4752 
4753             builder.create<fir::StoreOp>(loc, plusOne, buffPos);
4754           }
4755         },
4756         [&](const fir::ArrayBoxValue &v) { doAbstractArray(v); },
4757         [&](const fir::CharArrayBoxValue &v) { doAbstractArray(v); },
4758         [&](const auto &) {
4759           TODO(loc, "unhandled array constructor expression");
4760         });
4761     return mem;
4762   }
4763 
4764   // Lower the expr cases in an ac-value-list.
4765   template <typename A>
4766   std::pair<ExtValue, bool>
4767   genArrayCtorInitializer(const Fortran::evaluate::Expr<A> &x, mlir::Type,
4768                           mlir::Value, mlir::Value, mlir::Value,
4769                           Fortran::lower::StatementContext &stmtCtx) {
4770     if (isArray(x))
4771       return {lowerNewArrayExpression(converter, symMap, stmtCtx, toEvExpr(x)),
4772               /*needCopy=*/true};
4773     return {asScalar(x), /*needCopy=*/true};
4774   }
4775 
4776   // Lower an ac-implied-do in an ac-value-list.
4777   template <typename A>
4778   std::pair<ExtValue, bool>
4779   genArrayCtorInitializer(const Fortran::evaluate::ImpliedDo<A> &x,
4780                           mlir::Type resTy, mlir::Value mem,
4781                           mlir::Value buffPos, mlir::Value buffSize,
4782                           Fortran::lower::StatementContext &) {
4783     mlir::Location loc = getLoc();
4784     mlir::IndexType idxTy = builder.getIndexType();
4785     mlir::Value lo =
4786         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.lower())));
4787     mlir::Value up =
4788         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.upper())));
4789     mlir::Value step =
4790         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.stride())));
4791     auto seqTy = resTy.template cast<fir::SequenceType>();
4792     mlir::Type eleTy = fir::unwrapSequenceType(seqTy);
4793     auto loop =
4794         builder.create<fir::DoLoopOp>(loc, lo, up, step, /*unordered=*/false,
4795                                       /*finalCount=*/false, mem);
4796     // create a new binding for x.name(), to ac-do-variable, to the iteration
4797     // value.
4798     symMap.pushImpliedDoBinding(toStringRef(x.name()), loop.getInductionVar());
4799     auto insPt = builder.saveInsertionPoint();
4800     builder.setInsertionPointToStart(loop.getBody());
4801     // Thread mem inside the loop via loop argument.
4802     mem = loop.getRegionIterArgs()[0];
4803 
4804     mlir::Type eleRefTy = builder.getRefType(eleTy);
4805 
4806     // Any temps created in the loop body must be freed inside the loop body.
4807     stmtCtx.pushScope();
4808     llvm::Optional<mlir::Value> charLen;
4809     for (const Fortran::evaluate::ArrayConstructorValue<A> &acv : x.values()) {
4810       auto [exv, copyNeeded] = std::visit(
4811           [&](const auto &v) {
4812             return genArrayCtorInitializer(v, resTy, mem, buffPos, buffSize,
4813                                            stmtCtx);
4814           },
4815           acv.u);
4816       mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
4817       mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
4818                                                   eleSz, eleTy, eleRefTy, resTy)
4819                        : fir::getBase(exv);
4820       if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) {
4821         charLen = builder.createTemporary(loc, builder.getI64Type());
4822         mlir::Value castLen =
4823             builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
4824         builder.create<fir::StoreOp>(loc, castLen, charLen.getValue());
4825       }
4826     }
4827     stmtCtx.finalize(/*popScope=*/true);
4828 
4829     builder.create<fir::ResultOp>(loc, mem);
4830     builder.restoreInsertionPoint(insPt);
4831     mem = loop.getResult(0);
4832     symMap.popImpliedDoBinding();
4833     llvm::SmallVector<mlir::Value> extents = {
4834         builder.create<fir::LoadOp>(loc, buffPos).getResult()};
4835 
4836     // Convert to extended value.
4837     if (fir::isa_char(seqTy.getEleTy())) {
4838       auto len = builder.create<fir::LoadOp>(loc, charLen.getValue());
4839       return {fir::CharArrayBoxValue{mem, len, extents}, /*needCopy=*/false};
4840     }
4841     return {fir::ArrayBoxValue{mem, extents}, /*needCopy=*/false};
4842   }
4843 
4844   // To simplify the handling and interaction between the various cases, array
4845   // constructors are always lowered to the incremental construction code
4846   // pattern, even if the extent of the array value is constant. After the
4847   // MemToReg pass and constant folding, the optimizer should be able to
4848   // determine that all the buffer overrun tests are false when the
4849   // incremental construction wasn't actually required.
4850   template <typename A>
4851   CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) {
4852     mlir::Location loc = getLoc();
4853     auto evExpr = toEvExpr(x);
4854     mlir::Type resTy = translateSomeExprToFIRType(converter, evExpr);
4855     mlir::IndexType idxTy = builder.getIndexType();
4856     auto seqTy = resTy.template cast<fir::SequenceType>();
4857     mlir::Type eleTy = fir::unwrapSequenceType(resTy);
4858     mlir::Value buffSize = builder.createTemporary(loc, idxTy, ".buff.size");
4859     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
4860     mlir::Value buffPos = builder.createTemporary(loc, idxTy, ".buff.pos");
4861     builder.create<fir::StoreOp>(loc, zero, buffPos);
4862     // Allocate space for the array to be constructed.
4863     mlir::Value mem;
4864     if (fir::hasDynamicSize(resTy)) {
4865       if (fir::hasDynamicSize(eleTy)) {
4866         // The size of each element may depend on a general expression. Defer
4867         // creating the buffer until after the expression is evaluated.
4868         mem = builder.createNullConstant(loc, builder.getRefType(eleTy));
4869         builder.create<fir::StoreOp>(loc, zero, buffSize);
4870       } else {
4871         mlir::Value initBuffSz =
4872             builder.createIntegerConstant(loc, idxTy, clInitialBufferSize);
4873         mem = builder.create<fir::AllocMemOp>(
4874             loc, eleTy, /*typeparams=*/llvm::None, initBuffSz);
4875         builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
4876       }
4877     } else {
4878       mem = builder.create<fir::AllocMemOp>(loc, resTy);
4879       int64_t buffSz = 1;
4880       for (auto extent : seqTy.getShape())
4881         buffSz *= extent;
4882       mlir::Value initBuffSz =
4883           builder.createIntegerConstant(loc, idxTy, buffSz);
4884       builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
4885     }
4886     // Compute size of element
4887     mlir::Type eleRefTy = builder.getRefType(eleTy);
4888 
4889     // Populate the buffer with the elements, growing as necessary.
4890     llvm::Optional<mlir::Value> charLen;
4891     for (const auto &expr : x) {
4892       auto [exv, copyNeeded] = std::visit(
4893           [&](const auto &e) {
4894             return genArrayCtorInitializer(e, resTy, mem, buffPos, buffSize,
4895                                            stmtCtx);
4896           },
4897           expr.u);
4898       mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
4899       mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
4900                                                   eleSz, eleTy, eleRefTy, resTy)
4901                        : fir::getBase(exv);
4902       if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) {
4903         charLen = builder.createTemporary(loc, builder.getI64Type());
4904         mlir::Value castLen =
4905             builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
4906         builder.create<fir::StoreOp>(loc, castLen, charLen.getValue());
4907       }
4908     }
4909     mem = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
4910     llvm::SmallVector<mlir::Value> extents = {
4911         builder.create<fir::LoadOp>(loc, buffPos)};
4912 
4913     // Cleanup the temporary.
4914     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
4915     stmtCtx.attachCleanup(
4916         [bldr, loc, mem]() { bldr->create<fir::FreeMemOp>(loc, mem); });
4917 
4918     // Return the continuation.
4919     if (fir::isa_char(seqTy.getEleTy())) {
4920       if (charLen.hasValue()) {
4921         auto len = builder.create<fir::LoadOp>(loc, charLen.getValue());
4922         return genarr(fir::CharArrayBoxValue{mem, len, extents});
4923       }
4924       return genarr(fir::CharArrayBoxValue{mem, zero, extents});
4925     }
4926     return genarr(fir::ArrayBoxValue{mem, extents});
4927   }
4928 
4929   CC genarr(const Fortran::evaluate::ImpliedDoIndex &) {
4930     TODO(getLoc(), "genarr ImpliedDoIndex");
4931   }
4932 
4933   CC genarr(const Fortran::evaluate::TypeParamInquiry &x) {
4934     TODO(getLoc(), "genarr TypeParamInquiry");
4935   }
4936 
4937   CC genarr(const Fortran::evaluate::DescriptorInquiry &x) {
4938     TODO(getLoc(), "genarr DescriptorInquiry");
4939   }
4940 
4941   CC genarr(const Fortran::evaluate::StructureConstructor &x) {
4942     TODO(getLoc(), "genarr StructureConstructor");
4943   }
4944 
4945   //===--------------------------------------------------------------------===//
4946   // LOCICAL operators (.NOT., .AND., .EQV., etc.)
4947   //===--------------------------------------------------------------------===//
4948 
4949   template <int KIND>
4950   CC genarr(const Fortran::evaluate::Not<KIND> &x) {
4951     mlir::Location loc = getLoc();
4952     mlir::IntegerType i1Ty = builder.getI1Type();
4953     auto lambda = genarr(x.left());
4954     mlir::Value truth = builder.createBool(loc, true);
4955     return [=](IterSpace iters) -> ExtValue {
4956       mlir::Value logical = fir::getBase(lambda(iters));
4957       mlir::Value val = builder.createConvert(loc, i1Ty, logical);
4958       return builder.create<mlir::arith::XOrIOp>(loc, val, truth);
4959     };
4960   }
4961   template <typename OP, typename A>
4962   CC createBinaryBoolOp(const A &x) {
4963     mlir::Location loc = getLoc();
4964     mlir::IntegerType i1Ty = builder.getI1Type();
4965     auto lf = genarr(x.left());
4966     auto rf = genarr(x.right());
4967     return [=](IterSpace iters) -> ExtValue {
4968       mlir::Value left = fir::getBase(lf(iters));
4969       mlir::Value right = fir::getBase(rf(iters));
4970       mlir::Value lhs = builder.createConvert(loc, i1Ty, left);
4971       mlir::Value rhs = builder.createConvert(loc, i1Ty, right);
4972       return builder.create<OP>(loc, lhs, rhs);
4973     };
4974   }
4975   template <typename OP, typename A>
4976   CC createCompareBoolOp(mlir::arith::CmpIPredicate pred, const A &x) {
4977     mlir::Location loc = getLoc();
4978     mlir::IntegerType i1Ty = builder.getI1Type();
4979     auto lf = genarr(x.left());
4980     auto rf = genarr(x.right());
4981     return [=](IterSpace iters) -> ExtValue {
4982       mlir::Value left = fir::getBase(lf(iters));
4983       mlir::Value right = fir::getBase(rf(iters));
4984       mlir::Value lhs = builder.createConvert(loc, i1Ty, left);
4985       mlir::Value rhs = builder.createConvert(loc, i1Ty, right);
4986       return builder.create<OP>(loc, pred, lhs, rhs);
4987     };
4988   }
4989   template <int KIND>
4990   CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) {
4991     switch (x.logicalOperator) {
4992     case Fortran::evaluate::LogicalOperator::And:
4993       return createBinaryBoolOp<mlir::arith::AndIOp>(x);
4994     case Fortran::evaluate::LogicalOperator::Or:
4995       return createBinaryBoolOp<mlir::arith::OrIOp>(x);
4996     case Fortran::evaluate::LogicalOperator::Eqv:
4997       return createCompareBoolOp<mlir::arith::CmpIOp>(
4998           mlir::arith::CmpIPredicate::eq, x);
4999     case Fortran::evaluate::LogicalOperator::Neqv:
5000       return createCompareBoolOp<mlir::arith::CmpIOp>(
5001           mlir::arith::CmpIPredicate::ne, x);
5002     case Fortran::evaluate::LogicalOperator::Not:
5003       llvm_unreachable(".NOT. handled elsewhere");
5004     }
5005     llvm_unreachable("unhandled case");
5006   }
5007 
5008   //===--------------------------------------------------------------------===//
5009   // Relational operators (<, <=, ==, etc.)
5010   //===--------------------------------------------------------------------===//
5011 
5012   template <typename OP, typename PRED, typename A>
5013   CC createCompareOp(PRED pred, const A &x) {
5014     mlir::Location loc = getLoc();
5015     auto lf = genarr(x.left());
5016     auto rf = genarr(x.right());
5017     return [=](IterSpace iters) -> ExtValue {
5018       mlir::Value lhs = fir::getBase(lf(iters));
5019       mlir::Value rhs = fir::getBase(rf(iters));
5020       return builder.create<OP>(loc, pred, lhs, rhs);
5021     };
5022   }
5023   template <typename A>
5024   CC createCompareCharOp(mlir::arith::CmpIPredicate pred, const A &x) {
5025     mlir::Location loc = getLoc();
5026     auto lf = genarr(x.left());
5027     auto rf = genarr(x.right());
5028     return [=](IterSpace iters) -> ExtValue {
5029       auto lhs = lf(iters);
5030       auto rhs = rf(iters);
5031       return fir::runtime::genCharCompare(builder, loc, pred, lhs, rhs);
5032     };
5033   }
5034   template <int KIND>
5035   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
5036                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
5037     return createCompareOp<mlir::arith::CmpIOp>(translateRelational(x.opr), x);
5038   }
5039   template <int KIND>
5040   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
5041                 Fortran::common::TypeCategory::Character, KIND>> &x) {
5042     return createCompareCharOp(translateRelational(x.opr), x);
5043   }
5044   template <int KIND>
5045   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
5046                 Fortran::common::TypeCategory::Real, KIND>> &x) {
5047     return createCompareOp<mlir::arith::CmpFOp>(translateFloatRelational(x.opr),
5048                                                 x);
5049   }
5050   template <int KIND>
5051   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
5052                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
5053     return createCompareOp<fir::CmpcOp>(translateFloatRelational(x.opr), x);
5054   }
5055   CC genarr(
5056       const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) {
5057     return std::visit([&](const auto &x) { return genarr(x); }, r.u);
5058   }
5059 
5060   template <typename A>
5061   CC genarr(const Fortran::evaluate::Designator<A> &des) {
5062     ComponentPath components(des.Rank() > 0);
5063     return std::visit([&](const auto &x) { return genarr(x, components); },
5064                       des.u);
5065   }
5066 
5067   template <typename T>
5068   CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) {
5069     // Note that it's possible that the function being called returns either an
5070     // array or a scalar.  In the first case, use the element type of the array.
5071     return genProcRef(
5072         funRef, fir::unwrapSequenceType(converter.genType(toEvExpr(funRef))));
5073   }
5074 
5075   //===-------------------------------------------------------------------===//
5076   // Array data references in an explicit iteration space.
5077   //
5078   // Use the base array that was loaded before the loop nest.
5079   //===-------------------------------------------------------------------===//
5080 
5081   /// Lower the path (`revPath`, in reverse) to be appended to an array_fetch or
5082   /// array_update op. \p ty is the initial type of the array
5083   /// (reference). Returns the type of the element after application of the
5084   /// path in \p components.
5085   ///
5086   /// TODO: This needs to deal with array's with initial bounds other than 1.
5087   /// TODO: Thread type parameters correctly.
5088   mlir::Type lowerPath(const ExtValue &arrayExv, ComponentPath &components) {
5089     mlir::Location loc = getLoc();
5090     mlir::Type ty = fir::getBase(arrayExv).getType();
5091     auto &revPath = components.reversePath;
5092     ty = fir::unwrapPassByRefType(ty);
5093     bool prefix = true;
5094     auto addComponent = [&](mlir::Value v) {
5095       if (prefix)
5096         components.prefixComponents.push_back(v);
5097       else
5098         components.suffixComponents.push_back(v);
5099     };
5100     mlir::IndexType idxTy = builder.getIndexType();
5101     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
5102     bool atBase = true;
5103     auto saveSemant = semant;
5104     if (isProjectedCopyInCopyOut())
5105       semant = ConstituentSemantics::RefTransparent;
5106     for (const auto &v : llvm::reverse(revPath)) {
5107       std::visit(
5108           Fortran::common::visitors{
5109               [&](const ImplicitSubscripts &) {
5110                 prefix = false;
5111                 ty = fir::unwrapSequenceType(ty);
5112               },
5113               [&](const Fortran::evaluate::ComplexPart *x) {
5114                 assert(!prefix && "complex part must be at end");
5115                 mlir::Value offset = builder.createIntegerConstant(
5116                     loc, builder.getI32Type(),
5117                     x->part() == Fortran::evaluate::ComplexPart::Part::RE ? 0
5118                                                                           : 1);
5119                 components.suffixComponents.push_back(offset);
5120                 ty = fir::applyPathToType(ty, mlir::ValueRange{offset});
5121               },
5122               [&](const Fortran::evaluate::ArrayRef *x) {
5123                 if (Fortran::lower::isRankedArrayAccess(*x)) {
5124                   genSliceIndices(components, arrayExv, *x, atBase);
5125                 } else {
5126                   // Array access where the expressions are scalar and cannot
5127                   // depend upon the implied iteration space.
5128                   unsigned ssIndex = 0u;
5129                   for (const auto &ss : x->subscript()) {
5130                     std::visit(
5131                         Fortran::common::visitors{
5132                             [&](const Fortran::evaluate::
5133                                     IndirectSubscriptIntegerExpr &ie) {
5134                               const auto &e = ie.value();
5135                               if (isArray(e))
5136                                 fir::emitFatalError(
5137                                     loc,
5138                                     "multiple components along single path "
5139                                     "generating array subexpressions");
5140                               // Lower scalar index expression, append it to
5141                               // subs.
5142                               mlir::Value subscriptVal =
5143                                   fir::getBase(asScalarArray(e));
5144                               // arrayExv is the base array. It needs to reflect
5145                               // the current array component instead.
5146                               // FIXME: must use lower bound of this component,
5147                               // not just the constant 1.
5148                               mlir::Value lb =
5149                                   atBase ? fir::factory::readLowerBound(
5150                                                builder, loc, arrayExv, ssIndex,
5151                                                one)
5152                                          : one;
5153                               mlir::Value val = builder.createConvert(
5154                                   loc, idxTy, subscriptVal);
5155                               mlir::Value ivAdj =
5156                                   builder.create<mlir::arith::SubIOp>(
5157                                       loc, idxTy, val, lb);
5158                               addComponent(
5159                                   builder.createConvert(loc, idxTy, ivAdj));
5160                             },
5161                             [&](const auto &) {
5162                               fir::emitFatalError(
5163                                   loc, "multiple components along single path "
5164                                        "generating array subexpressions");
5165                             }},
5166                         ss.u);
5167                     ssIndex++;
5168                   }
5169                 }
5170                 ty = fir::unwrapSequenceType(ty);
5171               },
5172               [&](const Fortran::evaluate::Component *x) {
5173                 auto fieldTy = fir::FieldType::get(builder.getContext());
5174                 llvm::StringRef name = toStringRef(x->GetLastSymbol().name());
5175                 auto recTy = ty.cast<fir::RecordType>();
5176                 ty = recTy.getType(name);
5177                 auto fld = builder.create<fir::FieldIndexOp>(
5178                     loc, fieldTy, name, recTy, fir::getTypeParams(arrayExv));
5179                 addComponent(fld);
5180               }},
5181           v);
5182       atBase = false;
5183     }
5184     semant = saveSemant;
5185     ty = fir::unwrapSequenceType(ty);
5186     components.applied = true;
5187     return ty;
5188   }
5189 
5190   llvm::SmallVector<mlir::Value> genSubstringBounds(ComponentPath &components) {
5191     llvm::SmallVector<mlir::Value> result;
5192     if (components.substring)
5193       populateBounds(result, components.substring);
5194     return result;
5195   }
5196 
5197   CC applyPathToArrayLoad(fir::ArrayLoadOp load, ComponentPath &components) {
5198     mlir::Location loc = getLoc();
5199     auto revPath = components.reversePath;
5200     fir::ExtendedValue arrayExv =
5201         arrayLoadExtValue(builder, loc, load, {}, load);
5202     mlir::Type eleTy = lowerPath(arrayExv, components);
5203     auto currentPC = components.pc;
5204     auto pc = [=, prefix = components.prefixComponents,
5205                suffix = components.suffixComponents](IterSpace iters) {
5206       IterationSpace newIters = currentPC(iters);
5207       // Add path prefix and suffix.
5208       IterationSpace addIters(newIters, prefix, suffix);
5209       return addIters;
5210     };
5211     components.pc = [=](IterSpace iters) { return iters; };
5212     llvm::SmallVector<mlir::Value> substringBounds =
5213         genSubstringBounds(components);
5214     if (isProjectedCopyInCopyOut()) {
5215       destination = load;
5216       auto lambda = [=, esp = this->explicitSpace](IterSpace iters) mutable {
5217         mlir::Value innerArg = esp->findArgumentOfLoad(load);
5218         if (isAdjustedArrayElementType(eleTy)) {
5219           mlir::Type eleRefTy = builder.getRefType(eleTy);
5220           auto arrayOp = builder.create<fir::ArrayAccessOp>(
5221               loc, eleRefTy, innerArg, iters.iterVec(), load.getTypeparams());
5222           if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
5223             mlir::Value dstLen = fir::factory::genLenOfCharacter(
5224                 builder, loc, load, iters.iterVec(), substringBounds);
5225             fir::ArrayAmendOp amend = createCharArrayAmend(
5226                 loc, builder, arrayOp, dstLen, iters.elementExv(), innerArg,
5227                 substringBounds);
5228             return arrayLoadExtValue(builder, loc, load, iters.iterVec(), amend,
5229                                      dstLen);
5230           } else if (fir::isa_derived(eleTy)) {
5231             fir::ArrayAmendOp amend =
5232                 createDerivedArrayAmend(loc, load, builder, arrayOp,
5233                                         iters.elementExv(), eleTy, innerArg);
5234             return arrayLoadExtValue(builder, loc, load, iters.iterVec(),
5235                                      amend);
5236           }
5237           assert(eleTy.isa<fir::SequenceType>());
5238           TODO(loc, "array (as element) assignment");
5239         }
5240         mlir::Value castedElement =
5241             builder.createConvert(loc, eleTy, iters.getElement());
5242         auto update = builder.create<fir::ArrayUpdateOp>(
5243             loc, innerArg.getType(), innerArg, castedElement, iters.iterVec(),
5244             load.getTypeparams());
5245         return arrayLoadExtValue(builder, loc, load, iters.iterVec(), update);
5246       };
5247       return [=](IterSpace iters) mutable { return lambda(pc(iters)); };
5248     }
5249     if (isCustomCopyInCopyOut()) {
5250       // Create an array_modify to get the LHS element address and indicate
5251       // the assignment, and create the call to the user defined assignment.
5252       destination = load;
5253       auto lambda = [=](IterSpace iters) mutable {
5254         mlir::Value innerArg = explicitSpace->findArgumentOfLoad(load);
5255         mlir::Type refEleTy =
5256             fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy);
5257         auto arrModify = builder.create<fir::ArrayModifyOp>(
5258             loc, mlir::TypeRange{refEleTy, innerArg.getType()}, innerArg,
5259             iters.iterVec(), load.getTypeparams());
5260         return arrayLoadExtValue(builder, loc, load, iters.iterVec(),
5261                                  arrModify.getResult(1));
5262       };
5263       return [=](IterSpace iters) mutable { return lambda(pc(iters)); };
5264     }
5265     auto lambda = [=, semant = this->semant](IterSpace iters) mutable {
5266       if (semant == ConstituentSemantics::RefOpaque ||
5267           isAdjustedArrayElementType(eleTy)) {
5268         mlir::Type resTy = builder.getRefType(eleTy);
5269         // Use array element reference semantics.
5270         auto access = builder.create<fir::ArrayAccessOp>(
5271             loc, resTy, load, iters.iterVec(), load.getTypeparams());
5272         mlir::Value newBase = access;
5273         if (fir::isa_char(eleTy)) {
5274           mlir::Value dstLen = fir::factory::genLenOfCharacter(
5275               builder, loc, load, iters.iterVec(), substringBounds);
5276           if (!substringBounds.empty()) {
5277             fir::CharBoxValue charDst{access, dstLen};
5278             fir::factory::CharacterExprHelper helper{builder, loc};
5279             charDst = helper.createSubstring(charDst, substringBounds);
5280             newBase = charDst.getAddr();
5281           }
5282           return arrayLoadExtValue(builder, loc, load, iters.iterVec(), newBase,
5283                                    dstLen);
5284         }
5285         return arrayLoadExtValue(builder, loc, load, iters.iterVec(), newBase);
5286       }
5287       auto fetch = builder.create<fir::ArrayFetchOp>(
5288           loc, eleTy, load, iters.iterVec(), load.getTypeparams());
5289       return arrayLoadExtValue(builder, loc, load, iters.iterVec(), fetch);
5290     };
5291     return [=](IterSpace iters) mutable {
5292       auto newIters = pc(iters);
5293       return lambda(newIters);
5294     };
5295   }
5296 
5297   template <typename A>
5298   CC genImplicitArrayAccess(const A &x, ComponentPath &components) {
5299     components.reversePath.push_back(ImplicitSubscripts{});
5300     ExtValue exv = asScalarRef(x);
5301     // lowerPath(exv, components);
5302     auto lambda = genarr(exv, components);
5303     return [=](IterSpace iters) { return lambda(components.pc(iters)); };
5304   }
5305   CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x,
5306                             ComponentPath &components) {
5307     if (x.IsSymbol())
5308       return genImplicitArrayAccess(x.GetFirstSymbol(), components);
5309     return genImplicitArrayAccess(x.GetComponent(), components);
5310   }
5311 
5312   template <typename A>
5313   CC genAsScalar(const A &x) {
5314     mlir::Location loc = getLoc();
5315     if (isProjectedCopyInCopyOut()) {
5316       return [=, &x, builder = &converter.getFirOpBuilder()](
5317                  IterSpace iters) -> ExtValue {
5318         ExtValue exv = asScalarRef(x);
5319         mlir::Value val = fir::getBase(exv);
5320         mlir::Type eleTy = fir::unwrapRefType(val.getType());
5321         if (isAdjustedArrayElementType(eleTy)) {
5322           if (fir::isa_char(eleTy)) {
5323             TODO(getLoc(), "assignment of character type");
5324           } else if (fir::isa_derived(eleTy)) {
5325             TODO(loc, "assignment of derived type");
5326           } else {
5327             fir::emitFatalError(loc, "array type not expected in scalar");
5328           }
5329         } else {
5330           builder->create<fir::StoreOp>(loc, iters.getElement(), val);
5331         }
5332         return exv;
5333       };
5334     }
5335     return [=, &x](IterSpace) { return asScalar(x); };
5336   }
5337 
5338   CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) {
5339     if (explicitSpaceIsActive()) {
5340       if (x.Rank() > 0)
5341         components.reversePath.push_back(ImplicitSubscripts{});
5342       if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x))
5343         return applyPathToArrayLoad(load, components);
5344     } else {
5345       return genImplicitArrayAccess(x, components);
5346     }
5347     if (pathIsEmpty(components))
5348       return genAsScalar(x);
5349     mlir::Location loc = getLoc();
5350     return [=](IterSpace) -> ExtValue {
5351       fir::emitFatalError(loc, "reached symbol with path");
5352     };
5353   }
5354 
5355   CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) {
5356     TODO(getLoc(), "genarr Component");
5357   }
5358 
5359   /// Array reference with subscripts. If this has rank > 0, this is a form
5360   /// of an array section (slice).
5361   ///
5362   /// There are two "slicing" primitives that may be applied on a dimension by
5363   /// dimension basis: (1) triple notation and (2) vector addressing. Since
5364   /// dimensions can be selectively sliced, some dimensions may contain
5365   /// regular scalar expressions and those dimensions do not participate in
5366   /// the array expression evaluation.
5367   CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) {
5368     if (explicitSpaceIsActive()) {
5369       if (Fortran::lower::isRankedArrayAccess(x))
5370         components.reversePath.push_back(ImplicitSubscripts{});
5371       if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x)) {
5372         components.reversePath.push_back(&x);
5373         return applyPathToArrayLoad(load, components);
5374       }
5375     } else {
5376       if (Fortran::lower::isRankedArrayAccess(x)) {
5377         components.reversePath.push_back(&x);
5378         return genImplicitArrayAccess(x.base(), components);
5379       }
5380     }
5381     bool atEnd = pathIsEmpty(components);
5382     components.reversePath.push_back(&x);
5383     auto result = genarr(x.base(), components);
5384     if (components.applied)
5385       return result;
5386     mlir::Location loc = getLoc();
5387     if (atEnd) {
5388       if (x.Rank() == 0)
5389         return genAsScalar(x);
5390       fir::emitFatalError(loc, "expected scalar");
5391     }
5392     return [=](IterSpace) -> ExtValue {
5393       fir::emitFatalError(loc, "reached arrayref with path");
5394     };
5395   }
5396 
5397   CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) {
5398     TODO(getLoc(), "coarray reference");
5399   }
5400 
5401   CC genarr(const Fortran::evaluate::NamedEntity &x,
5402             ComponentPath &components) {
5403     return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components)
5404                         : genarr(x.GetComponent(), components);
5405   }
5406 
5407   CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) {
5408     return std::visit([&](const auto &v) { return genarr(v, components); },
5409                       x.u);
5410   }
5411 
5412   bool pathIsEmpty(const ComponentPath &components) {
5413     return components.reversePath.empty();
5414   }
5415 
5416   /// Given an optional fir.box, returns an fir.box that is the original one if
5417   /// it is present and it otherwise an unallocated box.
5418   /// Absent fir.box are implemented as a null pointer descriptor. Generated
5419   /// code may need to unconditionally read a fir.box that can be absent.
5420   /// This helper allows creating a fir.box that can be read in all cases
5421   /// outside of a fir.if (isPresent) region. However, the usages of the value
5422   /// read from such box should still only be done in a fir.if(isPresent).
5423   static fir::ExtendedValue
5424   absentBoxToUnalllocatedBox(fir::FirOpBuilder &builder, mlir::Location loc,
5425                              const fir::ExtendedValue &exv,
5426                              mlir::Value isPresent) {
5427     mlir::Value box = fir::getBase(exv);
5428     mlir::Type boxType = box.getType();
5429     assert(boxType.isa<fir::BoxType>() && "argument must be a fir.box");
5430     mlir::Value emptyBox =
5431         fir::factory::createUnallocatedBox(builder, loc, boxType, llvm::None);
5432     auto safeToReadBox =
5433         builder.create<mlir::arith::SelectOp>(loc, isPresent, box, emptyBox);
5434     return fir::substBase(exv, safeToReadBox);
5435   }
5436 
5437   std::tuple<CC, mlir::Value, mlir::Type>
5438   genOptionalArrayFetch(const Fortran::lower::SomeExpr &expr) {
5439     assert(expr.Rank() > 0 && "expr must be an array");
5440     mlir::Location loc = getLoc();
5441     ExtValue optionalArg = asInquired(expr);
5442     mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
5443     // Generate an array load and access to an array that may be an absent
5444     // optional or an unallocated optional.
5445     mlir::Value base = getBase(optionalArg);
5446     const bool hasOptionalAttr =
5447         fir::valueHasFirAttribute(base, fir::getOptionalAttrName());
5448     mlir::Type baseType = fir::unwrapRefType(base.getType());
5449     const bool isBox = baseType.isa<fir::BoxType>();
5450     const bool isAllocOrPtr = Fortran::evaluate::IsAllocatableOrPointerObject(
5451         expr, converter.getFoldingContext());
5452     mlir::Type arrType = fir::unwrapPassByRefType(baseType);
5453     mlir::Type eleType = fir::unwrapSequenceType(arrType);
5454     ExtValue exv = optionalArg;
5455     if (hasOptionalAttr && isBox && !isAllocOrPtr) {
5456       // Elemental argument cannot be allocatable or pointers (C15100).
5457       // Hence, per 15.5.2.12 3 (8) and (9), the provided Allocatable and
5458       // Pointer optional arrays cannot be absent. The only kind of entities
5459       // that can get here are optional assumed shape and polymorphic entities.
5460       exv = absentBoxToUnalllocatedBox(builder, loc, exv, isPresent);
5461     }
5462     // All the properties can be read from any fir.box but the read values may
5463     // be undefined and should only be used inside a fir.if (canBeRead) region.
5464     if (const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>())
5465       exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox);
5466 
5467     mlir::Value memref = fir::getBase(exv);
5468     mlir::Value shape = builder.createShape(loc, exv);
5469     mlir::Value noSlice;
5470     auto arrLoad = builder.create<fir::ArrayLoadOp>(
5471         loc, arrType, memref, shape, noSlice, fir::getTypeParams(exv));
5472     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
5473     mlir::Value arrLd = arrLoad.getResult();
5474     // Mark the load to tell later passes it is unsafe to use this array_load
5475     // shape unconditionally.
5476     arrLoad->setAttr(fir::getOptionalAttrName(), builder.getUnitAttr());
5477 
5478     // Place the array as optional on the arrayOperands stack so that its
5479     // shape will only be used as a fallback to induce the implicit loop nest
5480     // (that is if there is no non optional array arguments).
5481     arrayOperands.push_back(
5482         ArrayOperand{memref, shape, noSlice, /*mayBeAbsent=*/true});
5483 
5484     // By value semantics.
5485     auto cc = [=](IterSpace iters) -> ExtValue {
5486       auto arrFetch = builder.create<fir::ArrayFetchOp>(
5487           loc, eleType, arrLd, iters.iterVec(), arrLdTypeParams);
5488       return fir::factory::arraySectionElementToExtendedValue(
5489           builder, loc, exv, arrFetch, noSlice);
5490     };
5491     return {cc, isPresent, eleType};
5492   }
5493 
5494   /// Generate a continuation to pass \p expr to an OPTIONAL argument of an
5495   /// elemental procedure. This is meant to handle the cases where \p expr might
5496   /// be dynamically absent (i.e. when it is a POINTER, an ALLOCATABLE or an
5497   /// OPTIONAL variable). If p\ expr is guaranteed to be present genarr() can
5498   /// directly be called instead.
5499   CC genarrForwardOptionalArgumentToCall(const Fortran::lower::SomeExpr &expr) {
5500     mlir::Location loc = getLoc();
5501     // Only by-value numerical and logical so far.
5502     if (semant != ConstituentSemantics::RefTransparent)
5503       TODO(loc, "optional arguments in user defined elemental procedures");
5504 
5505     // Handle scalar argument case (the if-then-else is generated outside of the
5506     // implicit loop nest).
5507     if (expr.Rank() == 0) {
5508       ExtValue optionalArg = asInquired(expr);
5509       mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
5510       mlir::Value elementValue =
5511           fir::getBase(genOptionalValue(builder, loc, optionalArg, isPresent));
5512       return [=](IterSpace iters) -> ExtValue { return elementValue; };
5513     }
5514 
5515     CC cc;
5516     mlir::Value isPresent;
5517     mlir::Type eleType;
5518     std::tie(cc, isPresent, eleType) = genOptionalArrayFetch(expr);
5519     return [=](IterSpace iters) -> ExtValue {
5520       mlir::Value elementValue =
5521           builder
5522               .genIfOp(loc, {eleType}, isPresent,
5523                        /*withElseRegion=*/true)
5524               .genThen([&]() {
5525                 builder.create<fir::ResultOp>(loc, fir::getBase(cc(iters)));
5526               })
5527               .genElse([&]() {
5528                 mlir::Value zero =
5529                     fir::factory::createZeroValue(builder, loc, eleType);
5530                 builder.create<fir::ResultOp>(loc, zero);
5531               })
5532               .getResults()[0];
5533       return elementValue;
5534     };
5535   }
5536 
5537   /// Reduce the rank of a array to be boxed based on the slice's operands.
5538   static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) {
5539     if (slice) {
5540       auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp());
5541       assert(slOp && "expected slice op");
5542       auto seqTy = arrTy.dyn_cast<fir::SequenceType>();
5543       assert(seqTy && "expected array type");
5544       mlir::Operation::operand_range triples = slOp.getTriples();
5545       fir::SequenceType::Shape shape;
5546       // reduce the rank for each invariant dimension
5547       for (unsigned i = 1, end = triples.size(); i < end; i += 3)
5548         if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp()))
5549           shape.push_back(fir::SequenceType::getUnknownExtent());
5550       return fir::SequenceType::get(shape, seqTy.getEleTy());
5551     }
5552     // not sliced, so no change in rank
5553     return arrTy;
5554   }
5555 
5556   CC genarr(const Fortran::evaluate::ComplexPart &x,
5557             ComponentPath &components) {
5558     TODO(getLoc(), "genarr ComplexPart");
5559   }
5560 
5561   CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &,
5562             ComponentPath &components) {
5563     TODO(getLoc(), "genarr StaticDataObject::Pointer");
5564   }
5565 
5566   /// Substrings (see 9.4.1)
5567   CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) {
5568     TODO(getLoc(), "genarr Substring");
5569   }
5570 
5571   /// Base case of generating an array reference,
5572   CC genarr(const ExtValue &extMemref, ComponentPath &components) {
5573     mlir::Location loc = getLoc();
5574     mlir::Value memref = fir::getBase(extMemref);
5575     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType());
5576     assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array");
5577     mlir::Value shape = builder.createShape(loc, extMemref);
5578     mlir::Value slice;
5579     if (components.isSlice()) {
5580       if (isBoxValue() && components.substring) {
5581         // Append the substring operator to emboxing Op as it will become an
5582         // interior adjustment (add offset, adjust LEN) to the CHARACTER value
5583         // being referenced in the descriptor.
5584         llvm::SmallVector<mlir::Value> substringBounds;
5585         populateBounds(substringBounds, components.substring);
5586         // Convert to (offset, size)
5587         mlir::Type iTy = substringBounds[0].getType();
5588         if (substringBounds.size() != 2) {
5589           fir::CharacterType charTy =
5590               fir::factory::CharacterExprHelper::getCharType(arrTy);
5591           if (charTy.hasConstantLen()) {
5592             mlir::IndexType idxTy = builder.getIndexType();
5593             fir::CharacterType::LenType charLen = charTy.getLen();
5594             mlir::Value lenValue =
5595                 builder.createIntegerConstant(loc, idxTy, charLen);
5596             substringBounds.push_back(lenValue);
5597           } else {
5598             llvm::SmallVector<mlir::Value> typeparams =
5599                 fir::getTypeParams(extMemref);
5600             substringBounds.push_back(typeparams.back());
5601           }
5602         }
5603         // Convert the lower bound to 0-based substring.
5604         mlir::Value one =
5605             builder.createIntegerConstant(loc, substringBounds[0].getType(), 1);
5606         substringBounds[0] =
5607             builder.create<mlir::arith::SubIOp>(loc, substringBounds[0], one);
5608         // Convert the upper bound to a length.
5609         mlir::Value cast = builder.createConvert(loc, iTy, substringBounds[1]);
5610         mlir::Value zero = builder.createIntegerConstant(loc, iTy, 0);
5611         auto size =
5612             builder.create<mlir::arith::SubIOp>(loc, cast, substringBounds[0]);
5613         auto cmp = builder.create<mlir::arith::CmpIOp>(
5614             loc, mlir::arith::CmpIPredicate::sgt, size, zero);
5615         // size = MAX(upper - (lower - 1), 0)
5616         substringBounds[1] =
5617             builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero);
5618         slice = builder.create<fir::SliceOp>(loc, components.trips,
5619                                              components.suffixComponents,
5620                                              substringBounds);
5621       } else {
5622         slice = builder.createSlice(loc, extMemref, components.trips,
5623                                     components.suffixComponents);
5624       }
5625       if (components.hasComponents()) {
5626         auto seqTy = arrTy.cast<fir::SequenceType>();
5627         mlir::Type eleTy =
5628             fir::applyPathToType(seqTy.getEleTy(), components.suffixComponents);
5629         if (!eleTy)
5630           fir::emitFatalError(loc, "slicing path is ill-formed");
5631         if (auto realTy = eleTy.dyn_cast<fir::RealType>())
5632           eleTy = Fortran::lower::convertReal(realTy.getContext(),
5633                                               realTy.getFKind());
5634 
5635         // create the type of the projected array.
5636         arrTy = fir::SequenceType::get(seqTy.getShape(), eleTy);
5637         LLVM_DEBUG(llvm::dbgs()
5638                    << "type of array projection from component slicing: "
5639                    << eleTy << ", " << arrTy << '\n');
5640       }
5641     }
5642     arrayOperands.push_back(ArrayOperand{memref, shape, slice});
5643     if (destShape.empty())
5644       destShape = getShape(arrayOperands.back());
5645     if (isBoxValue()) {
5646       // Semantics are a reference to a boxed array.
5647       // This case just requires that an embox operation be created to box the
5648       // value. The value of the box is forwarded in the continuation.
5649       mlir::Type reduceTy = reduceRank(arrTy, slice);
5650       auto boxTy = fir::BoxType::get(reduceTy);
5651       if (components.substring) {
5652         // Adjust char length to substring size.
5653         fir::CharacterType charTy =
5654             fir::factory::CharacterExprHelper::getCharType(reduceTy);
5655         auto seqTy = reduceTy.cast<fir::SequenceType>();
5656         // TODO: Use a constant for fir.char LEN if we can compute it.
5657         boxTy = fir::BoxType::get(
5658             fir::SequenceType::get(fir::CharacterType::getUnknownLen(
5659                                        builder.getContext(), charTy.getFKind()),
5660                                    seqTy.getDimension()));
5661       }
5662       mlir::Value embox =
5663           memref.getType().isa<fir::BoxType>()
5664               ? builder.create<fir::ReboxOp>(loc, boxTy, memref, shape, slice)
5665                     .getResult()
5666               : builder
5667                     .create<fir::EmboxOp>(loc, boxTy, memref, shape, slice,
5668                                           fir::getTypeParams(extMemref))
5669                     .getResult();
5670       return [=](IterSpace) -> ExtValue { return fir::BoxValue(embox); };
5671     }
5672     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
5673     if (isReferentiallyOpaque()) {
5674       // Semantics are an opaque reference to an array.
5675       // This case forwards a continuation that will generate the address
5676       // arithmetic to the array element. This does not have copy-in/copy-out
5677       // semantics. No attempt to copy the array value will be made during the
5678       // interpretation of the Fortran statement.
5679       mlir::Type refEleTy = builder.getRefType(eleTy);
5680       return [=](IterSpace iters) -> ExtValue {
5681         // ArrayCoorOp does not expect zero based indices.
5682         llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
5683             loc, builder, memref.getType(), shape, iters.iterVec());
5684         mlir::Value coor = builder.create<fir::ArrayCoorOp>(
5685             loc, refEleTy, memref, shape, slice, indices,
5686             fir::getTypeParams(extMemref));
5687         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
5688           llvm::SmallVector<mlir::Value> substringBounds;
5689           populateBounds(substringBounds, components.substring);
5690           if (!substringBounds.empty()) {
5691             mlir::Value dstLen = fir::factory::genLenOfCharacter(
5692                 builder, loc, arrTy.cast<fir::SequenceType>(), memref,
5693                 fir::getTypeParams(extMemref), iters.iterVec(),
5694                 substringBounds);
5695             fir::CharBoxValue dstChar(coor, dstLen);
5696             return fir::factory::CharacterExprHelper{builder, loc}
5697                 .createSubstring(dstChar, substringBounds);
5698           }
5699         }
5700         return fir::factory::arraySectionElementToExtendedValue(
5701             builder, loc, extMemref, coor, slice);
5702       };
5703     }
5704     auto arrLoad = builder.create<fir::ArrayLoadOp>(
5705         loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref));
5706     mlir::Value arrLd = arrLoad.getResult();
5707     if (isProjectedCopyInCopyOut()) {
5708       // Semantics are projected copy-in copy-out.
5709       // The backing store of the destination of an array expression may be
5710       // partially modified. These updates are recorded in FIR by forwarding a
5711       // continuation that generates an `array_update` Op. The destination is
5712       // always loaded at the beginning of the statement and merged at the
5713       // end.
5714       destination = arrLoad;
5715       auto lambda = ccStoreToDest.hasValue()
5716                         ? ccStoreToDest.getValue()
5717                         : defaultStoreToDestination(components.substring);
5718       return [=](IterSpace iters) -> ExtValue { return lambda(iters); };
5719     }
5720     if (isCustomCopyInCopyOut()) {
5721       // Create an array_modify to get the LHS element address and indicate
5722       // the assignment, the actual assignment must be implemented in
5723       // ccStoreToDest.
5724       destination = arrLoad;
5725       return [=](IterSpace iters) -> ExtValue {
5726         mlir::Value innerArg = iters.innerArgument();
5727         mlir::Type resTy = innerArg.getType();
5728         mlir::Type eleTy = fir::applyPathToType(resTy, iters.iterVec());
5729         mlir::Type refEleTy =
5730             fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy);
5731         auto arrModify = builder.create<fir::ArrayModifyOp>(
5732             loc, mlir::TypeRange{refEleTy, resTy}, innerArg, iters.iterVec(),
5733             destination.getTypeparams());
5734         return abstractArrayExtValue(arrModify.getResult(1));
5735       };
5736     }
5737     if (isCopyInCopyOut()) {
5738       // Semantics are copy-in copy-out.
5739       // The continuation simply forwards the result of the `array_load` Op,
5740       // which is the value of the array as it was when loaded. All data
5741       // references with rank > 0 in an array expression typically have
5742       // copy-in copy-out semantics.
5743       return [=](IterSpace) -> ExtValue { return arrLd; };
5744     }
5745     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
5746     if (isValueAttribute()) {
5747       // Semantics are value attribute.
5748       // Here the continuation will `array_fetch` a value from an array and
5749       // then store that value in a temporary. One can thus imitate pass by
5750       // value even when the call is pass by reference.
5751       return [=](IterSpace iters) -> ExtValue {
5752         mlir::Value base;
5753         mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
5754         if (isAdjustedArrayElementType(eleTy)) {
5755           mlir::Type eleRefTy = builder.getRefType(eleTy);
5756           base = builder.create<fir::ArrayAccessOp>(
5757               loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
5758         } else {
5759           base = builder.create<fir::ArrayFetchOp>(
5760               loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
5761         }
5762         mlir::Value temp = builder.createTemporary(
5763             loc, base.getType(),
5764             llvm::ArrayRef<mlir::NamedAttribute>{
5765                 Fortran::lower::getAdaptToByRefAttr(builder)});
5766         builder.create<fir::StoreOp>(loc, base, temp);
5767         return fir::factory::arraySectionElementToExtendedValue(
5768             builder, loc, extMemref, temp, slice);
5769       };
5770     }
5771     // In the default case, the array reference forwards an `array_fetch` or
5772     // `array_access` Op in the continuation.
5773     return [=](IterSpace iters) -> ExtValue {
5774       mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
5775       if (isAdjustedArrayElementType(eleTy)) {
5776         mlir::Type eleRefTy = builder.getRefType(eleTy);
5777         mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>(
5778             loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
5779         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
5780           llvm::SmallVector<mlir::Value> substringBounds;
5781           populateBounds(substringBounds, components.substring);
5782           if (!substringBounds.empty()) {
5783             mlir::Value dstLen = fir::factory::genLenOfCharacter(
5784                 builder, loc, arrLoad, iters.iterVec(), substringBounds);
5785             fir::CharBoxValue dstChar(arrayOp, dstLen);
5786             return fir::factory::CharacterExprHelper{builder, loc}
5787                 .createSubstring(dstChar, substringBounds);
5788           }
5789         }
5790         return fir::factory::arraySectionElementToExtendedValue(
5791             builder, loc, extMemref, arrayOp, slice);
5792       }
5793       auto arrFetch = builder.create<fir::ArrayFetchOp>(
5794           loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
5795       return fir::factory::arraySectionElementToExtendedValue(
5796           builder, loc, extMemref, arrFetch, slice);
5797     };
5798   }
5799 
5800 private:
5801   void determineShapeOfDest(const fir::ExtendedValue &lhs) {
5802     destShape = fir::factory::getExtents(builder, getLoc(), lhs);
5803   }
5804 
5805   void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) {
5806     if (!destShape.empty())
5807       return;
5808     // if (explicitSpaceIsActive() && determineShapeWithSlice(lhs))
5809     //   return;
5810     mlir::Type idxTy = builder.getIndexType();
5811     mlir::Location loc = getLoc();
5812     if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape =
5813             Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(),
5814                                                   lhs))
5815       for (Fortran::common::ConstantSubscript extent : *constantShape)
5816         destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent));
5817   }
5818 
5819   ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) {
5820     mlir::Type resTy = converter.genType(exp);
5821     return std::visit(
5822         [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); },
5823         exp.u);
5824   }
5825   ExtValue lowerArrayExpression(const ExtValue &exv) {
5826     assert(!explicitSpace);
5827     mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType());
5828     return lowerArrayExpression(genarr(exv), resTy);
5829   }
5830 
5831   void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds,
5832                       const Fortran::evaluate::Substring *substring) {
5833     if (!substring)
5834       return;
5835     bounds.push_back(fir::getBase(asScalar(substring->lower())));
5836     if (auto upper = substring->upper())
5837       bounds.push_back(fir::getBase(asScalar(*upper)));
5838   }
5839 
5840   /// Default store to destination implementation.
5841   /// This implements the default case, which is to assign the value in
5842   /// `iters.element` into the destination array, `iters.innerArgument`. Handles
5843   /// by value and by reference assignment.
5844   CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) {
5845     return [=](IterSpace iterSpace) -> ExtValue {
5846       mlir::Location loc = getLoc();
5847       mlir::Value innerArg = iterSpace.innerArgument();
5848       fir::ExtendedValue exv = iterSpace.elementExv();
5849       mlir::Type arrTy = innerArg.getType();
5850       mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec());
5851       if (isAdjustedArrayElementType(eleTy)) {
5852         // The elemental update is in the memref domain. Under this semantics,
5853         // we must always copy the computed new element from its location in
5854         // memory into the destination array.
5855         mlir::Type resRefTy = builder.getRefType(eleTy);
5856         // Get a reference to the array element to be amended.
5857         auto arrayOp = builder.create<fir::ArrayAccessOp>(
5858             loc, resRefTy, innerArg, iterSpace.iterVec(),
5859             destination.getTypeparams());
5860         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
5861           llvm::SmallVector<mlir::Value> substringBounds;
5862           populateBounds(substringBounds, substring);
5863           mlir::Value dstLen = fir::factory::genLenOfCharacter(
5864               builder, loc, destination, iterSpace.iterVec(), substringBounds);
5865           fir::ArrayAmendOp amend = createCharArrayAmend(
5866               loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds);
5867           return abstractArrayExtValue(amend, dstLen);
5868         }
5869         if (fir::isa_derived(eleTy)) {
5870           fir::ArrayAmendOp amend = createDerivedArrayAmend(
5871               loc, destination, builder, arrayOp, exv, eleTy, innerArg);
5872           return abstractArrayExtValue(amend /*FIXME: typeparams?*/);
5873         }
5874         assert(eleTy.isa<fir::SequenceType>() && "must be an array");
5875         TODO(loc, "array (as element) assignment");
5876       }
5877       // By value semantics. The element is being assigned by value.
5878       mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv));
5879       auto update = builder.create<fir::ArrayUpdateOp>(
5880           loc, arrTy, innerArg, ele, iterSpace.iterVec(),
5881           destination.getTypeparams());
5882       return abstractArrayExtValue(update);
5883     };
5884   }
5885 
5886   /// For an elemental array expression.
5887   ///   1. Lower the scalars and array loads.
5888   ///   2. Create the iteration space.
5889   ///   3. Create the element-by-element computation in the loop.
5890   ///   4. Return the resulting array value.
5891   /// If no destination was set in the array context, a temporary of
5892   /// \p resultTy will be created to hold the evaluated expression.
5893   /// Otherwise, \p resultTy is ignored and the expression is evaluated
5894   /// in the destination. \p f is a continuation built from an
5895   /// evaluate::Expr or an ExtendedValue.
5896   ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) {
5897     mlir::Location loc = getLoc();
5898     auto [iterSpace, insPt] = genIterSpace(resultTy);
5899     auto exv = f(iterSpace);
5900     iterSpace.setElement(std::move(exv));
5901     auto lambda = ccStoreToDest.hasValue()
5902                       ? ccStoreToDest.getValue()
5903                       : defaultStoreToDestination(/*substring=*/nullptr);
5904     mlir::Value updVal = fir::getBase(lambda(iterSpace));
5905     finalizeElementCtx();
5906     builder.create<fir::ResultOp>(loc, updVal);
5907     builder.restoreInsertionPoint(insPt);
5908     return abstractArrayExtValue(iterSpace.outerResult());
5909   }
5910 
5911   /// Get the shape from an ArrayOperand. The shape of the array is adjusted if
5912   /// the array was sliced.
5913   llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) {
5914     // if (array.slice)
5915     //   return computeSliceShape(array.slice);
5916     if (array.memref.getType().isa<fir::BoxType>())
5917       return fir::factory::readExtents(builder, getLoc(),
5918                                        fir::BoxValue{array.memref});
5919     std::vector<mlir::Value, std::allocator<mlir::Value>> extents =
5920         fir::factory::getExtents(array.shape);
5921     return {extents.begin(), extents.end()};
5922   }
5923 
5924   /// Get the shape from an ArrayLoad.
5925   llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) {
5926     return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(),
5927                                  arrayLoad.getSlice()});
5928   }
5929 
5930   /// Returns the first array operand that may not be absent. If all
5931   /// array operands may be absent, return the first one.
5932   const ArrayOperand &getInducingShapeArrayOperand() const {
5933     assert(!arrayOperands.empty());
5934     for (const ArrayOperand &op : arrayOperands)
5935       if (!op.mayBeAbsent)
5936         return op;
5937     // If all arrays operand appears in optional position, then none of them
5938     // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the
5939     // first operands.
5940     // TODO: There is an opportunity to add a runtime check here that
5941     // this array is present as required.
5942     return arrayOperands[0];
5943   }
5944 
5945   /// Generate the shape of the iteration space over the array expression. The
5946   /// iteration space may be implicit, explicit, or both. If it is implied it is
5947   /// based on the destination and operand array loads, or an optional
5948   /// Fortran::evaluate::Shape from the front end. If the shape is explicit,
5949   /// this returns any implicit shape component, if it exists.
5950   llvm::SmallVector<mlir::Value> genIterationShape() {
5951     // Use the precomputed destination shape.
5952     if (!destShape.empty())
5953       return destShape;
5954     // Otherwise, use the destination's shape.
5955     if (destination)
5956       return getShape(destination);
5957     // Otherwise, use the first ArrayLoad operand shape.
5958     if (!arrayOperands.empty())
5959       return getShape(getInducingShapeArrayOperand());
5960     fir::emitFatalError(getLoc(),
5961                         "failed to compute the array expression shape");
5962   }
5963 
5964   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
5965                              Fortran::lower::StatementContext &stmtCtx,
5966                              Fortran::lower::SymMap &symMap)
5967       : converter{converter}, builder{converter.getFirOpBuilder()},
5968         stmtCtx{stmtCtx}, symMap{symMap} {}
5969 
5970   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
5971                              Fortran::lower::StatementContext &stmtCtx,
5972                              Fortran::lower::SymMap &symMap,
5973                              ConstituentSemantics sem)
5974       : converter{converter}, builder{converter.getFirOpBuilder()},
5975         stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {}
5976 
5977   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
5978                              Fortran::lower::StatementContext &stmtCtx,
5979                              Fortran::lower::SymMap &symMap,
5980                              ConstituentSemantics sem,
5981                              Fortran::lower::ExplicitIterSpace *expSpace,
5982                              Fortran::lower::ImplicitIterSpace *impSpace)
5983       : converter{converter}, builder{converter.getFirOpBuilder()},
5984         stmtCtx{stmtCtx}, symMap{symMap},
5985         explicitSpace(expSpace->isActive() ? expSpace : nullptr),
5986         implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} {
5987     // Generate any mask expressions, as necessary. This is the compute step
5988     // that creates the effective masks. See 10.2.3.2 in particular.
5989     genMasks();
5990   }
5991 
5992   mlir::Location getLoc() { return converter.getCurrentLocation(); }
5993 
5994   /// Array appears in a lhs context such that it is assigned after the rhs is
5995   /// fully evaluated.
5996   inline bool isCopyInCopyOut() {
5997     return semant == ConstituentSemantics::CopyInCopyOut;
5998   }
5999 
6000   /// Array appears in a lhs (or temp) context such that a projected,
6001   /// discontiguous subspace of the array is assigned after the rhs is fully
6002   /// evaluated. That is, the rhs array value is merged into a section of the
6003   /// lhs array.
6004   inline bool isProjectedCopyInCopyOut() {
6005     return semant == ConstituentSemantics::ProjectedCopyInCopyOut;
6006   }
6007 
6008   inline bool isCustomCopyInCopyOut() {
6009     return semant == ConstituentSemantics::CustomCopyInCopyOut;
6010   }
6011 
6012   /// Array appears in a context where it must be boxed.
6013   inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; }
6014 
6015   /// Array appears in a context where differences in the memory reference can
6016   /// be observable in the computational results. For example, an array
6017   /// element is passed to an impure procedure.
6018   inline bool isReferentiallyOpaque() {
6019     return semant == ConstituentSemantics::RefOpaque;
6020   }
6021 
6022   /// Array appears in a context where it is passed as a VALUE argument.
6023   inline bool isValueAttribute() {
6024     return semant == ConstituentSemantics::ByValueArg;
6025   }
6026 
6027   /// Can the loops over the expression be unordered?
6028   inline bool isUnordered() const { return unordered; }
6029 
6030   void setUnordered(bool b) { unordered = b; }
6031 
6032   Fortran::lower::AbstractConverter &converter;
6033   fir::FirOpBuilder &builder;
6034   Fortran::lower::StatementContext &stmtCtx;
6035   bool elementCtx = false;
6036   Fortran::lower::SymMap &symMap;
6037   /// The continuation to generate code to update the destination.
6038   llvm::Optional<CC> ccStoreToDest;
6039   llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude;
6040   llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>>
6041       ccLoadDest;
6042   /// The destination is the loaded array into which the results will be
6043   /// merged.
6044   fir::ArrayLoadOp destination;
6045   /// The shape of the destination.
6046   llvm::SmallVector<mlir::Value> destShape;
6047   /// List of arrays in the expression that have been loaded.
6048   llvm::SmallVector<ArrayOperand> arrayOperands;
6049   /// If there is a user-defined iteration space, explicitShape will hold the
6050   /// information from the front end.
6051   Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr;
6052   Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr;
6053   ConstituentSemantics semant = ConstituentSemantics::RefTransparent;
6054   // Can the array expression be evaluated in any order?
6055   // Will be set to false if any of the expression parts prevent this.
6056   bool unordered = true;
6057 };
6058 } // namespace
6059 
6060 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
6061     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6062     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6063     Fortran::lower::StatementContext &stmtCtx) {
6064   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
6065   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr);
6066 }
6067 
6068 fir::GlobalOp Fortran::lower::createDenseGlobal(
6069     mlir::Location loc, mlir::Type symTy, llvm::StringRef globalName,
6070     mlir::StringAttr linkage, bool isConst,
6071     const Fortran::lower::SomeExpr &expr,
6072     Fortran::lower::AbstractConverter &converter) {
6073 
6074   Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true);
6075   Fortran::lower::SymMap emptyMap;
6076   InitializerData initData(/*genRawVals=*/true);
6077   ScalarExprLowering sel(loc, converter, emptyMap, stmtCtx,
6078                          /*initializer=*/&initData);
6079   sel.genval(expr);
6080 
6081   size_t sz = initData.rawVals.size();
6082   llvm::ArrayRef<mlir::Attribute> ar = {initData.rawVals.data(), sz};
6083 
6084   mlir::RankedTensorType tensorTy;
6085   auto &builder = converter.getFirOpBuilder();
6086   mlir::Type iTy = initData.rawType;
6087   if (!iTy)
6088     return 0; // array extent is probably 0 in this case, so just return 0.
6089   tensorTy = mlir::RankedTensorType::get(sz, iTy);
6090   auto init = mlir::DenseElementsAttr::get(tensorTy, ar);
6091   return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst);
6092 }
6093 
6094 fir::ExtendedValue Fortran::lower::createSomeInitializerExpression(
6095     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6096     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6097     Fortran::lower::StatementContext &stmtCtx) {
6098   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
6099   InitializerData initData; // needed for initializations
6100   return ScalarExprLowering{loc, converter, symMap, stmtCtx,
6101                             /*initializer=*/&initData}
6102       .genval(expr);
6103 }
6104 
6105 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
6106     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6107     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6108     Fortran::lower::StatementContext &stmtCtx) {
6109   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
6110   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr);
6111 }
6112 
6113 fir::ExtendedValue Fortran::lower::createInitializerAddress(
6114     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6115     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6116     Fortran::lower::StatementContext &stmtCtx) {
6117   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
6118   InitializerData init;
6119   return ScalarExprLowering(loc, converter, symMap, stmtCtx, &init).gen(expr);
6120 }
6121 
6122 fir::ExtendedValue
6123 Fortran::lower::createSomeArrayBox(Fortran::lower::AbstractConverter &converter,
6124                                    const Fortran::lower::SomeExpr &expr,
6125                                    Fortran::lower::SymMap &symMap,
6126                                    Fortran::lower::StatementContext &stmtCtx) {
6127   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "box designator: ") << '\n');
6128   return ArrayExprLowering::lowerBoxedArrayExpression(converter, symMap,
6129                                                       stmtCtx, expr);
6130 }
6131 
6132 fir::MutableBoxValue Fortran::lower::createMutableBox(
6133     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6134     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
6135   // MutableBox lowering StatementContext does not need to be propagated
6136   // to the caller because the result value is a variable, not a temporary
6137   // expression. The StatementContext clean-up can occur before using the
6138   // resulting MutableBoxValue. Variables of all other types are handled in the
6139   // bridge.
6140   Fortran::lower::StatementContext dummyStmtCtx;
6141   return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx}
6142       .genMutableBoxValue(expr);
6143 }
6144 
6145 fir::ExtendedValue Fortran::lower::createBoxValue(
6146     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6147     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6148     Fortran::lower::StatementContext &stmtCtx) {
6149   if (expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) &&
6150       !Fortran::evaluate::HasVectorSubscript(expr))
6151     return Fortran::lower::createSomeArrayBox(converter, expr, symMap, stmtCtx);
6152   fir::ExtendedValue addr = Fortran::lower::createSomeExtendedAddress(
6153       loc, converter, expr, symMap, stmtCtx);
6154   return fir::BoxValue(converter.getFirOpBuilder().createBox(loc, addr));
6155 }
6156 
6157 mlir::Value Fortran::lower::createSubroutineCall(
6158     AbstractConverter &converter, const evaluate::ProcedureRef &call,
6159     ExplicitIterSpace &explicitIterSpace, ImplicitIterSpace &implicitIterSpace,
6160     SymMap &symMap, StatementContext &stmtCtx, bool isUserDefAssignment) {
6161   mlir::Location loc = converter.getCurrentLocation();
6162 
6163   if (isUserDefAssignment) {
6164     assert(call.arguments().size() == 2);
6165     const auto *lhs = call.arguments()[0].value().UnwrapExpr();
6166     const auto *rhs = call.arguments()[1].value().UnwrapExpr();
6167     assert(lhs && rhs &&
6168            "user defined assignment arguments must be expressions");
6169     if (call.IsElemental() && lhs->Rank() > 0) {
6170       // Elemental user defined assignment has special requirements to deal with
6171       // LHS/RHS overlaps. See 10.2.1.5 p2.
6172       ArrayExprLowering::lowerElementalUserAssignment(
6173           converter, symMap, stmtCtx, explicitIterSpace, implicitIterSpace,
6174           call);
6175     } else if (explicitIterSpace.isActive() && lhs->Rank() == 0) {
6176       // Scalar defined assignment (elemental or not) in a FORALL context.
6177       mlir::FuncOp func =
6178           Fortran::lower::CallerInterface(call, converter).getFuncOp();
6179       ArrayExprLowering::lowerScalarUserAssignment(
6180           converter, symMap, stmtCtx, explicitIterSpace, func, *lhs, *rhs);
6181     } else if (explicitIterSpace.isActive()) {
6182       // TODO: need to array fetch/modify sub-arrays?
6183       TODO(loc, "non elemental user defined array assignment inside FORALL");
6184     } else {
6185       if (!implicitIterSpace.empty())
6186         fir::emitFatalError(
6187             loc,
6188             "C1032: user defined assignment inside WHERE must be elemental");
6189       // Non elemental user defined assignment outside of FORALL and WHERE.
6190       // FIXME: The non elemental user defined assignment case with array
6191       // arguments must be take into account potential overlap. So far the front
6192       // end does not add parentheses around the RHS argument in the call as it
6193       // should according to 15.4.3.4.3 p2.
6194       Fortran::lower::createSomeExtendedExpression(
6195           loc, converter, toEvExpr(call), symMap, stmtCtx);
6196     }
6197     return {};
6198   }
6199 
6200   assert(implicitIterSpace.empty() && !explicitIterSpace.isActive() &&
6201          "subroutine calls are not allowed inside WHERE and FORALL");
6202 
6203   if (isElementalProcWithArrayArgs(call)) {
6204     ArrayExprLowering::lowerElementalSubroutine(converter, symMap, stmtCtx,
6205                                                 toEvExpr(call));
6206     return {};
6207   }
6208   // Simple subroutine call, with potential alternate return.
6209   auto res = Fortran::lower::createSomeExtendedExpression(
6210       loc, converter, toEvExpr(call), symMap, stmtCtx);
6211   return fir::getBase(res);
6212 }
6213 
6214 template <typename A>
6215 fir::ArrayLoadOp genArrayLoad(mlir::Location loc,
6216                               Fortran::lower::AbstractConverter &converter,
6217                               fir::FirOpBuilder &builder, const A *x,
6218                               Fortran::lower::SymMap &symMap,
6219                               Fortran::lower::StatementContext &stmtCtx) {
6220   auto exv = ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(*x);
6221   mlir::Value addr = fir::getBase(exv);
6222   mlir::Value shapeOp = builder.createShape(loc, exv);
6223   mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(addr.getType());
6224   return builder.create<fir::ArrayLoadOp>(loc, arrTy, addr, shapeOp,
6225                                           /*slice=*/mlir::Value{},
6226                                           fir::getTypeParams(exv));
6227 }
6228 template <>
6229 fir::ArrayLoadOp
6230 genArrayLoad(mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6231              fir::FirOpBuilder &builder, const Fortran::evaluate::ArrayRef *x,
6232              Fortran::lower::SymMap &symMap,
6233              Fortran::lower::StatementContext &stmtCtx) {
6234   if (x->base().IsSymbol())
6235     return genArrayLoad(loc, converter, builder, &x->base().GetLastSymbol(),
6236                         symMap, stmtCtx);
6237   return genArrayLoad(loc, converter, builder, &x->base().GetComponent(),
6238                       symMap, stmtCtx);
6239 }
6240 
6241 void Fortran::lower::createArrayLoads(
6242     Fortran::lower::AbstractConverter &converter,
6243     Fortran::lower::ExplicitIterSpace &esp, Fortran::lower::SymMap &symMap) {
6244   std::size_t counter = esp.getCounter();
6245   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
6246   mlir::Location loc = converter.getCurrentLocation();
6247   Fortran::lower::StatementContext &stmtCtx = esp.stmtContext();
6248   // Gen the fir.array_load ops.
6249   auto genLoad = [&](const auto *x) -> fir::ArrayLoadOp {
6250     return genArrayLoad(loc, converter, builder, x, symMap, stmtCtx);
6251   };
6252   if (esp.lhsBases[counter].hasValue()) {
6253     auto &base = esp.lhsBases[counter].getValue();
6254     auto load = std::visit(genLoad, base);
6255     esp.initialArgs.push_back(load);
6256     esp.resetInnerArgs();
6257     esp.bindLoad(base, load);
6258   }
6259   for (const auto &base : esp.rhsBases[counter])
6260     esp.bindLoad(base, std::visit(genLoad, base));
6261 }
6262 
6263 void Fortran::lower::createArrayMergeStores(
6264     Fortran::lower::AbstractConverter &converter,
6265     Fortran::lower::ExplicitIterSpace &esp) {
6266   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
6267   mlir::Location loc = converter.getCurrentLocation();
6268   builder.setInsertionPointAfter(esp.getOuterLoop());
6269   // Gen the fir.array_merge_store ops for all LHS arrays.
6270   for (auto i : llvm::enumerate(esp.getOuterLoop().getResults()))
6271     if (llvm::Optional<fir::ArrayLoadOp> ldOpt = esp.getLhsLoad(i.index())) {
6272       fir::ArrayLoadOp load = ldOpt.getValue();
6273       builder.create<fir::ArrayMergeStoreOp>(loc, load, i.value(),
6274                                              load.getMemref(), load.getSlice(),
6275                                              load.getTypeparams());
6276     }
6277   if (esp.loopCleanup.hasValue()) {
6278     esp.loopCleanup.getValue()(builder);
6279     esp.loopCleanup = llvm::None;
6280   }
6281   esp.initialArgs.clear();
6282   esp.innerArgs.clear();
6283   esp.outerLoop = llvm::None;
6284   esp.resetBindings();
6285   esp.incrementCounter();
6286 }
6287 
6288 void Fortran::lower::createSomeArrayAssignment(
6289     Fortran::lower::AbstractConverter &converter,
6290     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
6291     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
6292   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
6293              rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
6294   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
6295 }
6296 
6297 void Fortran::lower::createSomeArrayAssignment(
6298     Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
6299     const Fortran::lower::SomeExpr &rhs, Fortran::lower::SymMap &symMap,
6300     Fortran::lower::StatementContext &stmtCtx) {
6301   LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
6302              rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
6303   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
6304 }
6305 
6306 void Fortran::lower::createSomeArrayAssignment(
6307     Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
6308     const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap,
6309     Fortran::lower::StatementContext &stmtCtx) {
6310   LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
6311              llvm::dbgs() << "assign expression: " << rhs << '\n';);
6312   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
6313 }
6314 
6315 void Fortran::lower::createAnyMaskedArrayAssignment(
6316     Fortran::lower::AbstractConverter &converter,
6317     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
6318     Fortran::lower::ExplicitIterSpace &explicitSpace,
6319     Fortran::lower::ImplicitIterSpace &implicitSpace,
6320     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
6321   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
6322              rhs.AsFortran(llvm::dbgs() << "assign expression: ")
6323              << " given the explicit iteration space:\n"
6324              << explicitSpace << "\n and implied mask conditions:\n"
6325              << implicitSpace << '\n';);
6326   ArrayExprLowering::lowerAnyMaskedArrayAssignment(
6327       converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
6328 }
6329 
6330 void Fortran::lower::createAllocatableArrayAssignment(
6331     Fortran::lower::AbstractConverter &converter,
6332     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
6333     Fortran::lower::ExplicitIterSpace &explicitSpace,
6334     Fortran::lower::ImplicitIterSpace &implicitSpace,
6335     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
6336   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n';
6337              rhs.AsFortran(llvm::dbgs() << "assign expression: ")
6338              << " given the explicit iteration space:\n"
6339              << explicitSpace << "\n and implied mask conditions:\n"
6340              << implicitSpace << '\n';);
6341   ArrayExprLowering::lowerAllocatableArrayAssignment(
6342       converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
6343 }
6344 
6345 fir::ExtendedValue Fortran::lower::createSomeArrayTempValue(
6346     Fortran::lower::AbstractConverter &converter,
6347     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6348     Fortran::lower::StatementContext &stmtCtx) {
6349   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
6350   return ArrayExprLowering::lowerNewArrayExpression(converter, symMap, stmtCtx,
6351                                                     expr);
6352 }
6353 
6354 void Fortran::lower::createLazyArrayTempValue(
6355     Fortran::lower::AbstractConverter &converter,
6356     const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader,
6357     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
6358   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
6359   ArrayExprLowering::lowerLazyArrayExpression(converter, symMap, stmtCtx, expr,
6360                                               raggedHeader);
6361 }
6362 
6363 mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder,
6364                                            mlir::Location loc,
6365                                            mlir::Value value) {
6366   mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
6367   if (mlir::Operation *definingOp = value.getDefiningOp())
6368     if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
6369       if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>())
6370         return intAttr.getInt() < 0 ? zero : value;
6371   return Fortran::lower::genMax(builder, loc,
6372                                 llvm::SmallVector<mlir::Value>{value, zero});
6373 }
6374