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