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