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