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