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