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