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>(loc, stackSaveFn.getType().getResults(),
2054                                       stackSaveSymbol, mlir::ValueRange{})
2055                 .getResult(0);
2056         stmtCtx.attachCleanup([bldr, loc, sp]() {
2057           auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr);
2058           auto stackRestoreSymbol =
2059               bldr->getSymbolRefAttr(stackRestoreFn.getName());
2060           bldr->create<fir::CallOp>(loc, stackRestoreFn.getType().getResults(),
2061                                     stackRestoreSymbol, mlir::ValueRange{sp});
2062         });
2063       }
2064       mlir::Value temp =
2065           builder.createTemporary(loc, type, ".result", extents, resultLengths);
2066       return toExtendedValue(loc, temp, extents, lengths);
2067     }();
2068 
2069     if (mustPopSymMap)
2070       symMap.popScope();
2071 
2072     // Place allocated result or prepare the fir.save_result arguments.
2073     mlir::Value arrayResultShape;
2074     if (allocatedResult) {
2075       if (std::optional<Fortran::lower::CallInterface<
2076               Fortran::lower::CallerInterface>::PassedEntity>
2077               resultArg = caller.getPassedResult()) {
2078         if (resultArg->passBy == PassBy::AddressAndLength)
2079           caller.placeAddressAndLengthInput(*resultArg,
2080                                             fir::getBase(*allocatedResult),
2081                                             fir::getLen(*allocatedResult));
2082         else if (resultArg->passBy == PassBy::BaseAddress)
2083           caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
2084         else
2085           fir::emitFatalError(
2086               loc, "only expect character scalar result to be passed by ref");
2087       } else {
2088         assert(caller.mustSaveResult());
2089         arrayResultShape = allocatedResult->match(
2090             [&](const fir::CharArrayBoxValue &) {
2091               return builder.createShape(loc, *allocatedResult);
2092             },
2093             [&](const fir::ArrayBoxValue &) {
2094               return builder.createShape(loc, *allocatedResult);
2095             },
2096             [&](const auto &) { return mlir::Value{}; });
2097       }
2098     }
2099 
2100     // In older Fortran, procedure argument types are inferred. This may lead
2101     // different view of what the function signature is in different locations.
2102     // Casts are inserted as needed below to accommodate this.
2103 
2104     // The mlir::FuncOp type prevails, unless it has a different number of
2105     // arguments which can happen in legal program if it was passed as a dummy
2106     // procedure argument earlier with no further type information.
2107     mlir::SymbolRefAttr funcSymbolAttr;
2108     bool addHostAssociations = false;
2109     if (!funcPointer) {
2110       mlir::FunctionType funcOpType = caller.getFuncOp().getType();
2111       mlir::SymbolRefAttr symbolAttr =
2112           builder.getSymbolRefAttr(caller.getMangledName());
2113       if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
2114           callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
2115           fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
2116                                    fir::getHostAssocAttrName())) {
2117         // The number of arguments is off by one, and we're lowering a function
2118         // with host associations. Modify call to include host associations
2119         // argument by appending the value at the end of the operands.
2120         assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
2121                converter.hostAssocTupleValue().getType());
2122         addHostAssociations = true;
2123       }
2124       if (!addHostAssociations &&
2125           (callSiteType.getNumResults() != funcOpType.getNumResults() ||
2126            callSiteType.getNumInputs() != funcOpType.getNumInputs())) {
2127         // Deal with argument number mismatch by making a function pointer so
2128         // that function type cast can be inserted. Do not emit a warning here
2129         // because this can happen in legal program if the function is not
2130         // defined here and it was first passed as an argument without any more
2131         // information.
2132         funcPointer =
2133             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
2134       } else if (callSiteType.getResults() != funcOpType.getResults()) {
2135         // Implicit interface result type mismatch are not standard Fortran, but
2136         // some compilers are not complaining about it.  The front end is not
2137         // protecting lowering from this currently. Support this with a
2138         // discouraging warning.
2139         LLVM_DEBUG(mlir::emitWarning(
2140             loc, "a return type mismatch is not standard compliant and may "
2141                  "lead to undefined behavior."));
2142         // Cast the actual function to the current caller implicit type because
2143         // that is the behavior we would get if we could not see the definition.
2144         funcPointer =
2145             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
2146       } else {
2147         funcSymbolAttr = symbolAttr;
2148       }
2149     }
2150 
2151     mlir::FunctionType funcType =
2152         funcPointer ? callSiteType : caller.getFuncOp().getType();
2153     llvm::SmallVector<mlir::Value> operands;
2154     // First operand of indirect call is the function pointer. Cast it to
2155     // required function type for the call to handle procedures that have a
2156     // compatible interface in Fortran, but that have different signatures in
2157     // FIR.
2158     if (funcPointer) {
2159       operands.push_back(
2160           funcPointer.getType().isa<fir::BoxProcType>()
2161               ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
2162               : builder.createConvert(loc, funcType, funcPointer));
2163     }
2164 
2165     // Deal with potential mismatches in arguments types. Passing an array to a
2166     // scalar argument should for instance be tolerated here.
2167     bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
2168     for (auto [fst, snd] :
2169          llvm::zip(caller.getInputs(), funcType.getInputs())) {
2170       // When passing arguments to a procedure that can be called an implicit
2171       // interface, allow character actual arguments to be passed to dummy
2172       // arguments of any type and vice versa
2173       mlir::Value cast;
2174       auto *context = builder.getContext();
2175       if (snd.isa<fir::BoxProcType>() &&
2176           fst.getType().isa<mlir::FunctionType>()) {
2177         auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None);
2178         auto boxProcTy = builder.getBoxProcType(funcTy);
2179         if (mlir::Value host = argumentHostAssocs(converter, fst)) {
2180           cast = builder.create<fir::EmboxProcOp>(
2181               loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
2182         } else {
2183           cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
2184         }
2185       } else {
2186         cast = builder.convertWithSemantics(loc, snd, fst,
2187                                             callingImplicitInterface);
2188       }
2189       operands.push_back(cast);
2190     }
2191 
2192     // Add host associations as necessary.
2193     if (addHostAssociations)
2194       operands.push_back(converter.hostAssocTupleValue());
2195 
2196     auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
2197                                             funcSymbolAttr, operands);
2198 
2199     if (caller.mustSaveResult())
2200       builder.create<fir::SaveResultOp>(
2201           loc, call.getResult(0), fir::getBase(allocatedResult.getValue()),
2202           arrayResultShape, resultLengths);
2203 
2204     if (allocatedResult) {
2205       allocatedResult->match(
2206           [&](const fir::MutableBoxValue &box) {
2207             if (box.isAllocatable()) {
2208               // 9.7.3.2 point 4. Finalize allocatables.
2209               fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
2210               stmtCtx.attachCleanup([bldr, loc, box]() {
2211                 fir::factory::genFinalization(*bldr, loc, box);
2212               });
2213             }
2214           },
2215           [](const auto &) {});
2216       return *allocatedResult;
2217     }
2218 
2219     if (!resultType.hasValue())
2220       return mlir::Value{}; // subroutine call
2221     // For now, Fortran return values are implemented with a single MLIR
2222     // function return value.
2223     assert(call.getNumResults() == 1 &&
2224            "Expected exactly one result in FUNCTION call");
2225     return call.getResult(0);
2226   }
2227 
2228   /// Like genExtAddr, but ensure the address returned is a temporary even if \p
2229   /// expr is variable inside parentheses.
2230   ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
2231     // In general, genExtAddr might not create a temp for variable inside
2232     // parentheses to avoid creating array temporary in sub-expressions. It only
2233     // ensures the sub-expression is not re-associated with other parts of the
2234     // expression. In the call semantics, there is a difference between expr and
2235     // variable (see R1524). For expressions, a variable storage must not be
2236     // argument associated since it could be modified inside the call, or the
2237     // variable could also be modified by other means during the call.
2238     if (!isParenthesizedVariable(expr))
2239       return genExtAddr(expr);
2240     mlir::Location loc = getLoc();
2241     if (expr.Rank() > 0)
2242       TODO(loc, "genTempExtAddr array");
2243     return genExtValue(expr).match(
2244         [&](const fir::CharBoxValue &boxChar) -> ExtValue {
2245           TODO(loc, "genTempExtAddr CharBoxValue");
2246         },
2247         [&](const fir::UnboxedValue &v) -> ExtValue {
2248           mlir::Type type = v.getType();
2249           mlir::Value value = v;
2250           if (fir::isa_ref_type(type))
2251             value = builder.create<fir::LoadOp>(loc, value);
2252           mlir::Value temp = builder.createTemporary(loc, value.getType());
2253           builder.create<fir::StoreOp>(loc, value, temp);
2254           return temp;
2255         },
2256         [&](const fir::BoxValue &x) -> ExtValue {
2257           // Derived type scalar that may be polymorphic.
2258           assert(!x.hasRank() && x.isDerived());
2259           if (x.isDerivedWithLengthParameters())
2260             fir::emitFatalError(
2261                 loc, "making temps for derived type with length parameters");
2262           // TODO: polymorphic aspects should be kept but for now the temp
2263           // created always has the declared type.
2264           mlir::Value var =
2265               fir::getBase(fir::factory::readBoxValue(builder, loc, x));
2266           auto value = builder.create<fir::LoadOp>(loc, var);
2267           mlir::Value temp = builder.createTemporary(loc, value.getType());
2268           builder.create<fir::StoreOp>(loc, value, temp);
2269           return temp;
2270         },
2271         [&](const auto &) -> ExtValue {
2272           fir::emitFatalError(loc, "expr is not a scalar value");
2273         });
2274   }
2275 
2276   /// Helper structure to track potential copy-in of non contiguous variable
2277   /// argument into a contiguous temp. It is used to deallocate the temp that
2278   /// may have been created as well as to the copy-out from the temp to the
2279   /// variable after the call.
2280   struct CopyOutPair {
2281     ExtValue var;
2282     ExtValue temp;
2283     // Flag to indicate if the argument may have been modified by the
2284     // callee, in which case it must be copied-out to the variable.
2285     bool argMayBeModifiedByCall;
2286     // Optional boolean value that, if present and false, prevents
2287     // the copy-out and temp deallocation.
2288     llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime;
2289   };
2290   using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>;
2291 
2292   /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories
2293   /// not based on fir.box.
2294   /// This will lose any non contiguous stride information and dynamic type and
2295   /// should only be called if \p exv is known to be contiguous or if its base
2296   /// address will be replaced by a contiguous one. If \p exv is not a
2297   /// fir::BoxValue, this is a no-op.
2298   ExtValue readIfBoxValue(const ExtValue &exv) {
2299     if (const auto *box = exv.getBoxOf<fir::BoxValue>())
2300       return fir::factory::readBoxValue(builder, getLoc(), *box);
2301     return exv;
2302   }
2303 
2304   /// Generate a contiguous temp to pass \p actualArg as argument \p arg. The
2305   /// creation of the temp and copy-in can be made conditional at runtime by
2306   /// providing a runtime boolean flag \p restrictCopyAtRuntime (in which case
2307   /// the temp and copy will only be made if the value is true at runtime).
2308   ExtValue genCopyIn(const ExtValue &actualArg,
2309                      const Fortran::lower::CallerInterface::PassedEntity &arg,
2310                      CopyOutPairs &copyOutPairs,
2311                      llvm::Optional<mlir::Value> restrictCopyAtRuntime) {
2312     if (!restrictCopyAtRuntime) {
2313       ExtValue temp = genArrayTempFromMold(actualArg, ".copyinout");
2314       if (arg.mayBeReadByCall())
2315         genArrayCopy(temp, actualArg);
2316       copyOutPairs.emplace_back(CopyOutPair{
2317           actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime});
2318       return temp;
2319     }
2320     // Otherwise, need to be careful to only copy-in if allowed at runtime.
2321     mlir::Location loc = getLoc();
2322     auto addrType = fir::HeapType::get(
2323         fir::unwrapPassByRefType(fir::getBase(actualArg).getType()));
2324     mlir::Value addr =
2325         builder
2326             .genIfOp(loc, {addrType}, *restrictCopyAtRuntime,
2327                      /*withElseRegion=*/true)
2328             .genThen([&]() {
2329               auto temp = genArrayTempFromMold(actualArg, ".copyinout");
2330               if (arg.mayBeReadByCall())
2331                 genArrayCopy(temp, actualArg);
2332               builder.create<fir::ResultOp>(loc, fir::getBase(temp));
2333             })
2334             .genElse([&]() {
2335               auto nullPtr = builder.createNullConstant(loc, addrType);
2336               builder.create<fir::ResultOp>(loc, nullPtr);
2337             })
2338             .getResults()[0];
2339     // Associate the temp address with actualArg lengths and extents.
2340     fir::ExtendedValue temp = fir::substBase(readIfBoxValue(actualArg), addr);
2341     copyOutPairs.emplace_back(CopyOutPair{
2342         actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime});
2343     return temp;
2344   }
2345 
2346   /// Generate copy-out if needed and free the temporary for an argument that
2347   /// has been copied-in into a contiguous temp.
2348   void genCopyOut(const CopyOutPair &copyOutPair) {
2349     mlir::Location loc = getLoc();
2350     if (!copyOutPair.restrictCopyAndFreeAtRuntime) {
2351       if (copyOutPair.argMayBeModifiedByCall)
2352         genArrayCopy(copyOutPair.var, copyOutPair.temp);
2353       builder.create<fir::FreeMemOp>(loc, fir::getBase(copyOutPair.temp));
2354       return;
2355     }
2356     builder.genIfThen(loc, *copyOutPair.restrictCopyAndFreeAtRuntime)
2357         .genThen([&]() {
2358           if (copyOutPair.argMayBeModifiedByCall)
2359             genArrayCopy(copyOutPair.var, copyOutPair.temp);
2360           builder.create<fir::FreeMemOp>(loc, fir::getBase(copyOutPair.temp));
2361         })
2362         .end();
2363   }
2364 
2365   /// Lower a non-elemental procedure reference.
2366   ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
2367                               llvm::Optional<mlir::Type> resultType) {
2368     mlir::Location loc = getLoc();
2369     if (isElementalProcWithArrayArgs(procRef))
2370       fir::emitFatalError(loc, "trying to lower elemental procedure with array "
2371                                "arguments as normal procedure");
2372     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
2373             procRef.proc().GetSpecificIntrinsic())
2374       return genIntrinsicRef(procRef, *intrinsic, resultType);
2375 
2376     if (isStatementFunctionCall(procRef))
2377       return genStmtFunctionRef(procRef);
2378 
2379     Fortran::lower::CallerInterface caller(procRef, converter);
2380     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
2381 
2382     llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall;
2383     // List of <var, temp> where temp must be copied into var after the call.
2384     CopyOutPairs copyOutPairs;
2385 
2386     mlir::FunctionType callSiteType = caller.genFunctionType();
2387 
2388     // Lower the actual arguments and map the lowered values to the dummy
2389     // arguments.
2390     for (const Fortran::lower::CallInterface<
2391              Fortran::lower::CallerInterface>::PassedEntity &arg :
2392          caller.getPassedArguments()) {
2393       const auto *actual = arg.entity;
2394       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
2395       if (!actual) {
2396         // Optional dummy argument for which there is no actual argument.
2397         caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy));
2398         continue;
2399       }
2400       const auto *expr = actual->UnwrapExpr();
2401       if (!expr)
2402         TODO(loc, "assumed type actual argument lowering");
2403 
2404       if (arg.passBy == PassBy::Value) {
2405         ExtValue argVal = genval(*expr);
2406         if (!fir::isUnboxedValue(argVal))
2407           fir::emitFatalError(
2408               loc, "internal error: passing non trivial value by value");
2409         caller.placeInput(arg, fir::getBase(argVal));
2410         continue;
2411       }
2412 
2413       if (arg.passBy == PassBy::MutableBox) {
2414         if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
2415                 *expr)) {
2416           // If expr is NULL(), the mutableBox created must be a deallocated
2417           // pointer with the dummy argument characteristics (see table 16.5
2418           // in Fortran 2018 standard).
2419           // No length parameters are set for the created box because any non
2420           // deferred type parameters of the dummy will be evaluated on the
2421           // callee side, and it is illegal to use NULL without a MOLD if any
2422           // dummy length parameters are assumed.
2423           mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);
2424           assert(boxTy && boxTy.isa<fir::BoxType>() &&
2425                  "must be a fir.box type");
2426           mlir::Value boxStorage = builder.createTemporary(loc, boxTy);
2427           mlir::Value nullBox = fir::factory::createUnallocatedBox(
2428               builder, loc, boxTy, /*nonDeferredParams=*/{});
2429           builder.create<fir::StoreOp>(loc, nullBox, boxStorage);
2430           caller.placeInput(arg, boxStorage);
2431           continue;
2432         }
2433         fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
2434         mlir::Value irBox =
2435             fir::factory::getMutableIRBox(builder, loc, mutableBox);
2436         caller.placeInput(arg, irBox);
2437         if (arg.mayBeModifiedByCall())
2438           mutableModifiedByCall.emplace_back(std::move(mutableBox));
2439         continue;
2440       }
2441       const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr);
2442       if (arg.passBy == PassBy::BaseAddressValueAttribute) {
2443         mlir::Value temp;
2444         if (isArray(*expr)) {
2445           auto val = genBoxArg(*expr);
2446           if (!actualArgIsVariable)
2447             temp = getBase(val);
2448           else {
2449             ExtValue copy = genArrayTempFromMold(val, ".copy");
2450             genArrayCopy(copy, val);
2451             temp = fir::getBase(copy);
2452           }
2453         } else {
2454           mlir::Value val = fir::getBase(genval(*expr));
2455           temp = builder.createTemporary(
2456               loc, val.getType(),
2457               llvm::ArrayRef<mlir::NamedAttribute>{
2458                   Fortran::lower::getAdaptToByRefAttr(builder)});
2459           builder.create<fir::StoreOp>(loc, val, temp);
2460         }
2461         caller.placeInput(arg, temp);
2462         continue;
2463       }
2464       if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) {
2465         const bool actualIsSimplyContiguous =
2466             !actualArgIsVariable || Fortran::evaluate::IsSimplyContiguous(
2467                                         *expr, converter.getFoldingContext());
2468         auto argAddr = [&]() -> ExtValue {
2469           ExtValue baseAddr;
2470           if (actualArgIsVariable && arg.isOptional()) {
2471             if (Fortran::evaluate::IsAllocatableOrPointerObject(
2472                     *expr, converter.getFoldingContext())) {
2473               // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated,
2474               // it is as if the argument was absent. The main care here is to
2475               // not do a copy-in/copy-out because the temp address, even though
2476               // pointing to a null size storage, would not be a nullptr and
2477               // therefore the argument would not be considered absent on the
2478               // callee side. Note: if wholeSymbol is optional, it cannot be
2479               // absent as per 15.5.2.12 point 7. and 8. We rely on this to
2480               // un-conditionally read the allocatable/pointer descriptor here.
2481               if (actualIsSimplyContiguous)
2482                 return genBoxArg(*expr);
2483               fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
2484               mlir::Value isAssociated =
2485                   fir::factory::genIsAllocatedOrAssociatedTest(builder, loc,
2486                                                                mutableBox);
2487               fir::ExtendedValue actualExv =
2488                   fir::factory::genMutableBoxRead(builder, loc, mutableBox);
2489               return genCopyIn(actualExv, arg, copyOutPairs, isAssociated);
2490             }
2491             if (const Fortran::semantics::Symbol *wholeSymbol =
2492                     Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(
2493                         *expr))
2494               if (Fortran::semantics::IsOptional(*wholeSymbol)) {
2495                 ExtValue actualArg = gen(*expr);
2496                 mlir::Value actualArgBase = fir::getBase(actualArg);
2497                 if (!actualArgBase.getType().isa<fir::BoxType>())
2498                   return actualArg;
2499                 // Do not read wholeSymbol descriptor that may be a nullptr in
2500                 // case wholeSymbol is absent.
2501                 // Absent descriptor cannot be read. To avoid any issue in
2502                 // copy-in/copy-out, and when retrieving the address/length
2503                 // create an descriptor pointing to a null address here if the
2504                 // fir.box is absent.
2505                 mlir::Value isPresent = builder.create<fir::IsPresentOp>(
2506                     loc, builder.getI1Type(), actualArgBase);
2507                 mlir::Type boxType = actualArgBase.getType();
2508                 mlir::Value emptyBox = fir::factory::createUnallocatedBox(
2509                     builder, loc, boxType, llvm::None);
2510                 auto safeToReadBox = builder.create<mlir::arith::SelectOp>(
2511                     loc, isPresent, actualArgBase, emptyBox);
2512                 fir::ExtendedValue safeToReadExv =
2513                     fir::substBase(actualArg, safeToReadBox);
2514                 if (actualIsSimplyContiguous)
2515                   return safeToReadExv;
2516                 return genCopyIn(safeToReadExv, arg, copyOutPairs, isPresent);
2517               }
2518             // Fall through: The actual argument can safely be
2519             // copied-in/copied-out without any care if needed.
2520           }
2521           if (actualArgIsVariable && expr->Rank() > 0) {
2522             ExtValue box = genBoxArg(*expr);
2523             if (!actualIsSimplyContiguous)
2524               return genCopyIn(box, arg, copyOutPairs,
2525                                /*restrictCopyAtRuntime=*/llvm::None);
2526             // Contiguous: just use the box we created above!
2527             // This gets "unboxed" below, if needed.
2528             return box;
2529           }
2530           // Actual argument is a non optional/non pointer/non allocatable
2531           // scalar.
2532           if (actualArgIsVariable)
2533             return genExtAddr(*expr);
2534           // Actual argument is not a variable. Make sure a variable address is
2535           // not passed.
2536           return genTempExtAddr(*expr);
2537         }();
2538         // Scalar and contiguous expressions may be lowered to a fir.box,
2539         // either to account for potential polymorphism, or because lowering
2540         // did not account for some contiguity hints.
2541         // Here, polymorphism does not matter (an entity of the declared type
2542         // is passed, not one of the dynamic type), and the expr is known to
2543         // be simply contiguous, so it is safe to unbox it and pass the
2544         // address without making a copy.
2545         argAddr = readIfBoxValue(argAddr);
2546 
2547         if (arg.passBy == PassBy::BaseAddress) {
2548           caller.placeInput(arg, fir::getBase(argAddr));
2549         } else {
2550           assert(arg.passBy == PassBy::BoxChar);
2551           auto helper = fir::factory::CharacterExprHelper{builder, loc};
2552           auto boxChar = argAddr.match(
2553               [&](const fir::CharBoxValue &x) { return helper.createEmbox(x); },
2554               [&](const fir::CharArrayBoxValue &x) {
2555                 return helper.createEmbox(x);
2556               },
2557               [&](const auto &x) -> mlir::Value {
2558                 // Fortran allows an actual argument of a completely different
2559                 // type to be passed to a procedure expecting a CHARACTER in the
2560                 // dummy argument position. When this happens, the data pointer
2561                 // argument is simply assumed to point to CHARACTER data and the
2562                 // LEN argument used is garbage. Simulate this behavior by
2563                 // free-casting the base address to be a !fir.char reference and
2564                 // setting the LEN argument to undefined. What could go wrong?
2565                 auto dataPtr = fir::getBase(x);
2566                 assert(!dataPtr.getType().template isa<fir::BoxType>());
2567                 return builder.convertWithSemantics(
2568                     loc, argTy, dataPtr,
2569                     /*allowCharacterConversion=*/true);
2570               });
2571           caller.placeInput(arg, boxChar);
2572         }
2573       } else if (arg.passBy == PassBy::Box) {
2574         // Before lowering to an address, handle the allocatable/pointer actual
2575         // argument to optional fir.box dummy. It is legal to pass
2576         // unallocated/disassociated entity to an optional. In this case, an
2577         // absent fir.box must be created instead of a fir.box with a null value
2578         // (Fortran 2018 15.5.2.12 point 1).
2579         if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject(
2580                                     *expr, converter.getFoldingContext())) {
2581           // Note that passing an absent allocatable to a non-allocatable
2582           // optional dummy argument is illegal (15.5.2.12 point 3 (8)). So
2583           // nothing has to be done to generate an absent argument in this case,
2584           // and it is OK to unconditionally read the mutable box here.
2585           fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
2586           mlir::Value isAllocated =
2587               fir::factory::genIsAllocatedOrAssociatedTest(builder, loc,
2588                                                            mutableBox);
2589           auto absent = builder.create<fir::AbsentOp>(loc, argTy);
2590           /// For now, assume it is not OK to pass the allocatable/pointer
2591           /// descriptor to a non pointer/allocatable dummy. That is a strict
2592           /// interpretation of 18.3.6 point 4 that stipulates the descriptor
2593           /// has the dummy attributes in BIND(C) contexts.
2594           mlir::Value box = builder.createBox(
2595               loc, fir::factory::genMutableBoxRead(builder, loc, mutableBox));
2596           // Need the box types to be exactly similar for the selectOp.
2597           mlir::Value convertedBox = builder.createConvert(loc, argTy, box);
2598           caller.placeInput(arg, builder.create<mlir::arith::SelectOp>(
2599                                      loc, isAllocated, convertedBox, absent));
2600         } else {
2601           // Make sure a variable address is only passed if the expression is
2602           // actually a variable.
2603           mlir::Value box =
2604               actualArgIsVariable
2605                   ? builder.createBox(loc, genBoxArg(*expr))
2606                   : builder.createBox(getLoc(), genTempExtAddr(*expr));
2607           caller.placeInput(arg, box);
2608         }
2609       } else if (arg.passBy == PassBy::AddressAndLength) {
2610         ExtValue argRef = genExtAddr(*expr);
2611         caller.placeAddressAndLengthInput(arg, fir::getBase(argRef),
2612                                           fir::getLen(argRef));
2613       } else if (arg.passBy == PassBy::CharProcTuple) {
2614         ExtValue argRef = genExtAddr(*expr);
2615         mlir::Value tuple = createBoxProcCharTuple(
2616             converter, argTy, fir::getBase(argRef), fir::getLen(argRef));
2617         caller.placeInput(arg, tuple);
2618       } else {
2619         TODO(loc, "pass by value in non elemental function call");
2620       }
2621     }
2622 
2623     ExtValue result = genCallOpAndResult(caller, callSiteType, resultType);
2624 
2625     // Sync pointers and allocatables that may have been modified during the
2626     // call.
2627     for (const auto &mutableBox : mutableModifiedByCall)
2628       fir::factory::syncMutableBoxFromIRBox(builder, loc, mutableBox);
2629     // Handle case where result was passed as argument
2630 
2631     // Copy-out temps that were created for non contiguous variable arguments if
2632     // needed.
2633     for (const auto &copyOutPair : copyOutPairs)
2634       genCopyOut(copyOutPair);
2635 
2636     return result;
2637   }
2638 
2639   template <typename A>
2640   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
2641     ExtValue result = genFunctionRef(funcRef);
2642     if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
2643       return genLoad(result);
2644     return result;
2645   }
2646 
2647   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
2648     llvm::Optional<mlir::Type> resTy;
2649     if (procRef.hasAlternateReturns())
2650       resTy = builder.getIndexType();
2651     return genProcedureRef(procRef, resTy);
2652   }
2653 
2654   /// Helper to lower intrinsic arguments for inquiry intrinsic.
2655   ExtValue
2656   lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr &expr) {
2657     if (Fortran::evaluate::IsAllocatableOrPointerObject(
2658             expr, converter.getFoldingContext()))
2659       return genMutableBoxValue(expr);
2660     return gen(expr);
2661   }
2662 
2663   /// Helper to lower intrinsic arguments to a fir::BoxValue.
2664   /// It preserves all the non default lower bounds/non deferred length
2665   /// parameter information.
2666   ExtValue lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr &expr) {
2667     mlir::Location loc = getLoc();
2668     ExtValue exv = genBoxArg(expr);
2669     mlir::Value box = builder.createBox(loc, exv);
2670     return fir::BoxValue(
2671         box, fir::factory::getNonDefaultLowerBounds(builder, loc, exv),
2672         fir::factory::getNonDeferredLengthParams(exv));
2673   }
2674 
2675   /// Generate a call to an intrinsic function.
2676   ExtValue
2677   genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
2678                   const Fortran::evaluate::SpecificIntrinsic &intrinsic,
2679                   llvm::Optional<mlir::Type> resultType) {
2680     llvm::SmallVector<ExtValue> operands;
2681 
2682     llvm::StringRef name = intrinsic.name;
2683     mlir::Location loc = getLoc();
2684     if (Fortran::lower::intrinsicRequiresCustomOptionalHandling(
2685             procRef, intrinsic, converter)) {
2686       using ExvAndPresence = std::pair<ExtValue, llvm::Optional<mlir::Value>>;
2687       llvm::SmallVector<ExvAndPresence, 4> operands;
2688       auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
2689         ExtValue optionalArg = lowerIntrinsicArgumentAsInquired(expr);
2690         mlir::Value isPresent =
2691             genActualIsPresentTest(builder, loc, optionalArg);
2692         operands.emplace_back(optionalArg, isPresent);
2693       };
2694       auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) {
2695         operands.emplace_back(genval(expr), llvm::None);
2696       };
2697       Fortran::lower::prepareCustomIntrinsicArgument(
2698           procRef, intrinsic, resultType, prepareOptionalArg, prepareOtherArg,
2699           converter);
2700 
2701       auto getArgument = [&](std::size_t i) -> ExtValue {
2702         if (fir::conformsWithPassByRef(
2703                 fir::getBase(operands[i].first).getType()))
2704           return genLoad(operands[i].first);
2705         return operands[i].first;
2706       };
2707       auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> {
2708         return operands[i].second;
2709       };
2710       return Fortran::lower::lowerCustomIntrinsic(
2711           builder, loc, name, resultType, isPresent, getArgument,
2712           operands.size(), stmtCtx);
2713     }
2714 
2715     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
2716         Fortran::lower::getIntrinsicArgumentLowering(name);
2717     for (const auto &[arg, dummy] :
2718          llvm::zip(procRef.arguments(),
2719                    intrinsic.characteristics.value().dummyArguments)) {
2720       auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
2721       if (!expr) {
2722         // Absent optional.
2723         operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
2724         continue;
2725       }
2726       if (!argLowering) {
2727         // No argument lowering instruction, lower by value.
2728         operands.emplace_back(genval(*expr));
2729         continue;
2730       }
2731       // Ad-hoc argument lowering handling.
2732       Fortran::lower::ArgLoweringRule argRules =
2733           Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering,
2734                                                    dummy.name);
2735       if (argRules.handleDynamicOptional &&
2736           Fortran::evaluate::MayBePassedAsAbsentOptional(
2737               *expr, converter.getFoldingContext())) {
2738         ExtValue optional = lowerIntrinsicArgumentAsInquired(*expr);
2739         mlir::Value isPresent = genActualIsPresentTest(builder, loc, optional);
2740         switch (argRules.lowerAs) {
2741         case Fortran::lower::LowerIntrinsicArgAs::Value:
2742           operands.emplace_back(
2743               genOptionalValue(builder, loc, optional, isPresent));
2744           continue;
2745         case Fortran::lower::LowerIntrinsicArgAs::Addr:
2746           operands.emplace_back(
2747               genOptionalAddr(builder, loc, optional, isPresent));
2748           continue;
2749         case Fortran::lower::LowerIntrinsicArgAs::Box:
2750           operands.emplace_back(
2751               genOptionalBox(builder, loc, optional, isPresent));
2752           continue;
2753         case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2754           operands.emplace_back(optional);
2755           continue;
2756         }
2757         llvm_unreachable("bad switch");
2758       }
2759       switch (argRules.lowerAs) {
2760       case Fortran::lower::LowerIntrinsicArgAs::Value:
2761         operands.emplace_back(genval(*expr));
2762         continue;
2763       case Fortran::lower::LowerIntrinsicArgAs::Addr:
2764         operands.emplace_back(gen(*expr));
2765         continue;
2766       case Fortran::lower::LowerIntrinsicArgAs::Box:
2767         operands.emplace_back(lowerIntrinsicArgumentAsBox(*expr));
2768         continue;
2769       case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2770         operands.emplace_back(lowerIntrinsicArgumentAsInquired(*expr));
2771         continue;
2772       }
2773       llvm_unreachable("bad switch");
2774     }
2775     // Let the intrinsic library lower the intrinsic procedure call
2776     return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
2777                                             operands, stmtCtx);
2778   }
2779 
2780   template <typename A>
2781   bool isScalar(const A &x) {
2782     return x.Rank() == 0;
2783   }
2784 
2785   /// Helper to detect Transformational function reference.
2786   template <typename T>
2787   bool isTransformationalRef(const T &) {
2788     return false;
2789   }
2790   template <typename T>
2791   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
2792     return !funcRef.IsElemental() && funcRef.Rank();
2793   }
2794   template <typename T>
2795   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
2796     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
2797                       expr.u);
2798   }
2799 
2800   template <typename A>
2801   ExtValue asArray(const A &x) {
2802     return Fortran::lower::createSomeArrayTempValue(converter, toEvExpr(x),
2803                                                     symMap, stmtCtx);
2804   }
2805 
2806   /// Lower an array value as an argument. This argument can be passed as a box
2807   /// value, so it may be possible to avoid making a temporary.
2808   template <typename A>
2809   ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x) {
2810     return std::visit([&](const auto &e) { return asArrayArg(e, x); }, x.u);
2811   }
2812   template <typename A, typename B>
2813   ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x, const B &y) {
2814     return std::visit([&](const auto &e) { return asArrayArg(e, y); }, x.u);
2815   }
2816   template <typename A, typename B>
2817   ExtValue asArrayArg(const Fortran::evaluate::Designator<A> &, const B &x) {
2818     // Designator is being passed as an argument to a procedure. Lower the
2819     // expression to a boxed value.
2820     auto someExpr = toEvExpr(x);
2821     return Fortran::lower::createBoxValue(getLoc(), converter, someExpr, symMap,
2822                                           stmtCtx);
2823   }
2824   template <typename A, typename B>
2825   ExtValue asArrayArg(const A &, const B &x) {
2826     // If the expression to pass as an argument is not a designator, then create
2827     // an array temp.
2828     return asArray(x);
2829   }
2830 
2831   template <typename A>
2832   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
2833     // Whole array symbols or components, and results of transformational
2834     // functions already have a storage and the scalar expression lowering path
2835     // is used to not create a new temporary storage.
2836     if (isScalar(x) ||
2837         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
2838         isTransformationalRef(x))
2839       return std::visit([&](const auto &e) { return genref(e); }, x.u);
2840     if (useBoxArg)
2841       return asArrayArg(x);
2842     return asArray(x);
2843   }
2844   template <typename A>
2845   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
2846     if (isScalar(x) || Fortran::evaluate::UnwrapWholeSymbolDataRef(x) ||
2847         inInitializer)
2848       return std::visit([&](const auto &e) { return genval(e); }, x.u);
2849     return asArray(x);
2850   }
2851 
2852   template <int KIND>
2853   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
2854                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
2855     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
2856   }
2857 
2858   using RefSet =
2859       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
2860                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
2861                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
2862                  Fortran::semantics::SymbolRef>;
2863   template <typename A>
2864   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
2865 
2866   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
2867   ExtValue genref(const A &a) {
2868     return gen(a);
2869   }
2870   template <typename A>
2871   ExtValue genref(const A &a) {
2872     if (inInitializer) {
2873       // Initialization expressions can never allocate memory.
2874       return genval(a);
2875     }
2876     mlir::Type storageType = converter.genType(toEvExpr(a));
2877     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
2878   }
2879 
2880   template <typename A, template <typename> typename T,
2881             typename B = std::decay_t<T<A>>,
2882             std::enable_if_t<
2883                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
2884                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
2885                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
2886                 bool> = true>
2887   ExtValue genref(const T<A> &x) {
2888     return gen(x);
2889   }
2890 
2891 private:
2892   mlir::Location location;
2893   Fortran::lower::AbstractConverter &converter;
2894   fir::FirOpBuilder &builder;
2895   Fortran::lower::StatementContext &stmtCtx;
2896   Fortran::lower::SymMap &symMap;
2897   InitializerData *inInitializer = nullptr;
2898   bool useBoxArg = false; // expression lowered as argument
2899 };
2900 } // namespace
2901 
2902 // Helper for changing the semantics in a given context. Preserves the current
2903 // semantics which is resumed when the "push" goes out of scope.
2904 #define PushSemantics(PushVal)                                                 \
2905   [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ =                 \
2906       Fortran::common::ScopedSet(semant, PushVal);
2907 
2908 static bool isAdjustedArrayElementType(mlir::Type t) {
2909   return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>();
2910 }
2911 static bool elementTypeWasAdjusted(mlir::Type t) {
2912   if (auto ty = t.dyn_cast<fir::ReferenceType>())
2913     return isAdjustedArrayElementType(ty.getEleTy());
2914   return false;
2915 }
2916 
2917 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting
2918 /// the actual extents and lengths. This is only to allow their propagation as
2919 /// ExtendedValue without triggering verifier failures when propagating
2920 /// character/arrays as unboxed values. Only the base of the resulting
2921 /// ExtendedValue should be used, it is undefined to use the length or extents
2922 /// of the extended value returned,
2923 inline static fir::ExtendedValue
2924 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder,
2925                        mlir::Value val, mlir::Value len) {
2926   mlir::Type ty = fir::unwrapRefType(val.getType());
2927   mlir::IndexType idxTy = builder.getIndexType();
2928   auto seqTy = ty.cast<fir::SequenceType>();
2929   auto undef = builder.create<fir::UndefOp>(loc, idxTy);
2930   llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef);
2931   if (fir::isa_char(seqTy.getEleTy()))
2932     return fir::CharArrayBoxValue(val, len ? len : undef, extents);
2933   return fir::ArrayBoxValue(val, extents);
2934 }
2935 
2936 /// Helper to generate calls to scalar user defined assignment procedures.
2937 static void genScalarUserDefinedAssignmentCall(fir::FirOpBuilder &builder,
2938                                                mlir::Location loc,
2939                                                mlir::FuncOp func,
2940                                                const fir::ExtendedValue &lhs,
2941                                                const fir::ExtendedValue &rhs) {
2942   auto prepareUserDefinedArg =
2943       [](fir::FirOpBuilder &builder, mlir::Location loc,
2944          const fir::ExtendedValue &value, mlir::Type argType) -> mlir::Value {
2945     if (argType.isa<fir::BoxCharType>()) {
2946       const fir::CharBoxValue *charBox = value.getCharBox();
2947       assert(charBox && "argument type mismatch in elemental user assignment");
2948       return fir::factory::CharacterExprHelper{builder, loc}.createEmbox(
2949           *charBox);
2950     }
2951     if (argType.isa<fir::BoxType>()) {
2952       mlir::Value box = builder.createBox(loc, value);
2953       return builder.createConvert(loc, argType, box);
2954     }
2955     // Simple pass by address.
2956     mlir::Type argBaseType = fir::unwrapRefType(argType);
2957     assert(!fir::hasDynamicSize(argBaseType));
2958     mlir::Value from = fir::getBase(value);
2959     if (argBaseType != fir::unwrapRefType(from.getType())) {
2960       // With logicals, it is possible that from is i1 here.
2961       if (fir::isa_ref_type(from.getType()))
2962         from = builder.create<fir::LoadOp>(loc, from);
2963       from = builder.createConvert(loc, argBaseType, from);
2964     }
2965     if (!fir::isa_ref_type(from.getType())) {
2966       mlir::Value temp = builder.createTemporary(loc, argBaseType);
2967       builder.create<fir::StoreOp>(loc, from, temp);
2968       from = temp;
2969     }
2970     return builder.createConvert(loc, argType, from);
2971   };
2972   assert(func.getNumArguments() == 2);
2973   mlir::Type lhsType = func.getType().getInput(0);
2974   mlir::Type rhsType = func.getType().getInput(1);
2975   mlir::Value lhsArg = prepareUserDefinedArg(builder, loc, lhs, lhsType);
2976   mlir::Value rhsArg = prepareUserDefinedArg(builder, loc, rhs, rhsType);
2977   builder.create<fir::CallOp>(loc, func, mlir::ValueRange{lhsArg, rhsArg});
2978 }
2979 
2980 /// Convert the result of a fir.array_modify to an ExtendedValue given the
2981 /// related fir.array_load.
2982 static fir::ExtendedValue arrayModifyToExv(fir::FirOpBuilder &builder,
2983                                            mlir::Location loc,
2984                                            fir::ArrayLoadOp load,
2985                                            mlir::Value elementAddr) {
2986   mlir::Type eleTy = fir::unwrapPassByRefType(elementAddr.getType());
2987   if (fir::isa_char(eleTy)) {
2988     auto len = fir::factory::CharacterExprHelper{builder, loc}.getLength(
2989         load.getMemref());
2990     if (!len) {
2991       assert(load.getTypeparams().size() == 1 &&
2992              "length must be in array_load");
2993       len = load.getTypeparams()[0];
2994     }
2995     return fir::CharBoxValue{elementAddr, len};
2996   }
2997   return elementAddr;
2998 }
2999 
3000 //===----------------------------------------------------------------------===//
3001 //
3002 // Lowering of scalar expressions in an explicit iteration space context.
3003 //
3004 //===----------------------------------------------------------------------===//
3005 
3006 // Shared code for creating a copy of a derived type element. This function is
3007 // called from a continuation.
3008 inline static fir::ArrayAmendOp
3009 createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad,
3010                         fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc,
3011                         const fir::ExtendedValue &elementExv, mlir::Type eleTy,
3012                         mlir::Value innerArg) {
3013   if (destLoad.getTypeparams().empty()) {
3014     fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv);
3015   } else {
3016     auto boxTy = fir::BoxType::get(eleTy);
3017     auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(),
3018                                               mlir::Value{}, mlir::Value{},
3019                                               destLoad.getTypeparams());
3020     auto fromBox = builder.create<fir::EmboxOp>(
3021         loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{},
3022         destLoad.getTypeparams());
3023     fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox),
3024                                       fir::BoxValue(fromBox));
3025   }
3026   return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg,
3027                                            destAcc);
3028 }
3029 
3030 inline static fir::ArrayAmendOp
3031 createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder,
3032                      fir::ArrayAccessOp dstOp, mlir::Value &dstLen,
3033                      const fir::ExtendedValue &srcExv, mlir::Value innerArg,
3034                      llvm::ArrayRef<mlir::Value> bounds) {
3035   fir::CharBoxValue dstChar(dstOp, dstLen);
3036   fir::factory::CharacterExprHelper helper{builder, loc};
3037   if (!bounds.empty()) {
3038     dstChar = helper.createSubstring(dstChar, bounds);
3039     fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv),
3040                                    dstChar.getAddr(), dstChar.getLen(), builder,
3041                                    loc);
3042     // Update the LEN to the substring's LEN.
3043     dstLen = dstChar.getLen();
3044   }
3045   // For a CHARACTER, we generate the element assignment loops inline.
3046   helper.createAssign(fir::ExtendedValue{dstChar}, srcExv);
3047   // Mark this array element as amended.
3048   mlir::Type ty = innerArg.getType();
3049   auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp);
3050   return amend;
3051 }
3052 
3053 //===----------------------------------------------------------------------===//
3054 //
3055 // Lowering of array expressions.
3056 //
3057 //===----------------------------------------------------------------------===//
3058 
3059 namespace {
3060 class ArrayExprLowering {
3061   using ExtValue = fir::ExtendedValue;
3062 
3063   /// Structure to keep track of lowered array operands in the
3064   /// array expression. Useful to later deduce the shape of the
3065   /// array expression.
3066   struct ArrayOperand {
3067     /// Array base (can be a fir.box).
3068     mlir::Value memref;
3069     /// ShapeOp, ShapeShiftOp or ShiftOp
3070     mlir::Value shape;
3071     /// SliceOp
3072     mlir::Value slice;
3073     /// Can this operand be absent ?
3074     bool mayBeAbsent = false;
3075   };
3076 
3077   using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts;
3078   using PathComponent = Fortran::lower::PathComponent;
3079 
3080   /// Active iteration space.
3081   using IterationSpace = Fortran::lower::IterationSpace;
3082   using IterSpace = const Fortran::lower::IterationSpace &;
3083 
3084   /// Current continuation. Function that will generate IR for a single
3085   /// iteration of the pending iterative loop structure.
3086   using CC = Fortran::lower::GenerateElementalArrayFunc;
3087 
3088   /// Projection continuation. Function that will project one iteration space
3089   /// into another.
3090   using PC = std::function<IterationSpace(IterSpace)>;
3091   using ArrayBaseTy =
3092       std::variant<std::monostate, const Fortran::evaluate::ArrayRef *,
3093                    const Fortran::evaluate::DataRef *>;
3094   using ComponentPath = Fortran::lower::ComponentPath;
3095 
3096 public:
3097   //===--------------------------------------------------------------------===//
3098   // Regular array assignment
3099   //===--------------------------------------------------------------------===//
3100 
3101   /// Entry point for array assignments. Both the left-hand and right-hand sides
3102   /// can either be ExtendedValue or evaluate::Expr.
3103   template <typename TL, typename TR>
3104   static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter,
3105                                    Fortran::lower::SymMap &symMap,
3106                                    Fortran::lower::StatementContext &stmtCtx,
3107                                    const TL &lhs, const TR &rhs) {
3108     ArrayExprLowering ael{converter, stmtCtx, symMap,
3109                           ConstituentSemantics::CopyInCopyOut};
3110     ael.lowerArrayAssignment(lhs, rhs);
3111   }
3112 
3113   template <typename TL, typename TR>
3114   void lowerArrayAssignment(const TL &lhs, const TR &rhs) {
3115     mlir::Location loc = getLoc();
3116     /// Here the target subspace is not necessarily contiguous. The ArrayUpdate
3117     /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad
3118     /// in `destination`.
3119     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
3120     ccStoreToDest = genarr(lhs);
3121     determineShapeOfDest(lhs);
3122     semant = ConstituentSemantics::RefTransparent;
3123     ExtValue exv = lowerArrayExpression(rhs);
3124     if (explicitSpaceIsActive()) {
3125       explicitSpace->finalizeContext();
3126       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
3127     } else {
3128       builder.create<fir::ArrayMergeStoreOp>(
3129           loc, destination, fir::getBase(exv), destination.getMemref(),
3130           destination.getSlice(), destination.getTypeparams());
3131     }
3132   }
3133 
3134   //===--------------------------------------------------------------------===//
3135   // WHERE array assignment, FORALL assignment, and FORALL+WHERE array
3136   // assignment
3137   //===--------------------------------------------------------------------===//
3138 
3139   /// Entry point for array assignment when the iteration space is explicitly
3140   /// defined (Fortran's FORALL) with or without masks, and/or the implied
3141   /// iteration space involves masks (Fortran's WHERE). Both contexts (explicit
3142   /// space and implicit space with masks) may be present.
3143   static void lowerAnyMaskedArrayAssignment(
3144       Fortran::lower::AbstractConverter &converter,
3145       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3146       const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
3147       Fortran::lower::ExplicitIterSpace &explicitSpace,
3148       Fortran::lower::ImplicitIterSpace &implicitSpace) {
3149     if (explicitSpace.isActive() && lhs.Rank() == 0) {
3150       // Scalar assignment expression in a FORALL context.
3151       ArrayExprLowering ael(converter, stmtCtx, symMap,
3152                             ConstituentSemantics::RefTransparent,
3153                             &explicitSpace, &implicitSpace);
3154       ael.lowerScalarAssignment(lhs, rhs);
3155       return;
3156     }
3157     // Array assignment expression in a FORALL and/or WHERE context.
3158     ArrayExprLowering ael(converter, stmtCtx, symMap,
3159                           ConstituentSemantics::CopyInCopyOut, &explicitSpace,
3160                           &implicitSpace);
3161     ael.lowerArrayAssignment(lhs, rhs);
3162   }
3163 
3164   //===--------------------------------------------------------------------===//
3165   // Array assignment to allocatable array
3166   //===--------------------------------------------------------------------===//
3167 
3168   /// Entry point for assignment to allocatable array.
3169   static void lowerAllocatableArrayAssignment(
3170       Fortran::lower::AbstractConverter &converter,
3171       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3172       const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
3173       Fortran::lower::ExplicitIterSpace &explicitSpace,
3174       Fortran::lower::ImplicitIterSpace &implicitSpace) {
3175     ArrayExprLowering ael(converter, stmtCtx, symMap,
3176                           ConstituentSemantics::CopyInCopyOut, &explicitSpace,
3177                           &implicitSpace);
3178     ael.lowerAllocatableArrayAssignment(lhs, rhs);
3179   }
3180 
3181   /// Assignment to allocatable array.
3182   ///
3183   /// The semantics are reverse that of a "regular" array assignment. The rhs
3184   /// defines the iteration space of the computation and the lhs is
3185   /// resized/reallocated to fit if necessary.
3186   void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs,
3187                                        const Fortran::lower::SomeExpr &rhs) {
3188     // With assignment to allocatable, we want to lower the rhs first and use
3189     // its shape to determine if we need to reallocate, etc.
3190     mlir::Location loc = getLoc();
3191     // FIXME: If the lhs is in an explicit iteration space, the assignment may
3192     // be to an array of allocatable arrays rather than a single allocatable
3193     // array.
3194     fir::MutableBoxValue mutableBox =
3195         createMutableBox(loc, converter, lhs, symMap);
3196     mlir::Type resultTy = converter.genType(rhs);
3197     if (rhs.Rank() > 0)
3198       determineShapeOfDest(rhs);
3199     auto rhsCC = [&]() {
3200       PushSemantics(ConstituentSemantics::RefTransparent);
3201       return genarr(rhs);
3202     }();
3203 
3204     llvm::SmallVector<mlir::Value> lengthParams;
3205     // Currently no safe way to gather length from rhs (at least for
3206     // character, it cannot be taken from array_loads since it may be
3207     // changed by concatenations).
3208     if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) ||
3209         mutableBox.isDerivedWithLengthParameters())
3210       TODO(loc, "gather rhs length parameters in assignment to allocatable");
3211 
3212     // The allocatable must take lower bounds from the expr if it is
3213     // reallocated and the right hand side is not a scalar.
3214     const bool takeLboundsIfRealloc = rhs.Rank() > 0;
3215     llvm::SmallVector<mlir::Value> lbounds;
3216     // When the reallocated LHS takes its lower bounds from the RHS,
3217     // they will be non default only if the RHS is a whole array
3218     // variable. Otherwise, lbounds is left empty and default lower bounds
3219     // will be used.
3220     if (takeLboundsIfRealloc &&
3221         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) {
3222       assert(arrayOperands.size() == 1 &&
3223              "lbounds can only come from one array");
3224       std::vector<mlir::Value> lbs =
3225           fir::factory::getOrigins(arrayOperands[0].shape);
3226       lbounds.append(lbs.begin(), lbs.end());
3227     }
3228     fir::factory::MutableBoxReallocation realloc =
3229         fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape,
3230                                          lengthParams);
3231     // Create ArrayLoad for the mutable box and save it into `destination`.
3232     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
3233     ccStoreToDest = genarr(realloc.newValue);
3234     // If the rhs is scalar, get shape from the allocatable ArrayLoad.
3235     if (destShape.empty())
3236       destShape = getShape(destination);
3237     // Finish lowering the loop nest.
3238     assert(destination && "destination must have been set");
3239     ExtValue exv = lowerArrayExpression(rhsCC, resultTy);
3240     if (explicitSpaceIsActive()) {
3241       explicitSpace->finalizeContext();
3242       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
3243     } else {
3244       builder.create<fir::ArrayMergeStoreOp>(
3245           loc, destination, fir::getBase(exv), destination.getMemref(),
3246           destination.getSlice(), destination.getTypeparams());
3247     }
3248     fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds,
3249                                   takeLboundsIfRealloc, realloc);
3250   }
3251 
3252   /// Entry point for when an array expression appears in a context where the
3253   /// result must be boxed. (BoxValue semantics.)
3254   static ExtValue
3255   lowerBoxedArrayExpression(Fortran::lower::AbstractConverter &converter,
3256                             Fortran::lower::SymMap &symMap,
3257                             Fortran::lower::StatementContext &stmtCtx,
3258                             const Fortran::lower::SomeExpr &expr) {
3259     ArrayExprLowering ael{converter, stmtCtx, symMap,
3260                           ConstituentSemantics::BoxValue};
3261     return ael.lowerBoxedArrayExpr(expr);
3262   }
3263 
3264   ExtValue lowerBoxedArrayExpr(const Fortran::lower::SomeExpr &exp) {
3265     return std::visit(
3266         [&](const auto &e) {
3267           auto f = genarr(e);
3268           ExtValue exv = f(IterationSpace{});
3269           if (fir::getBase(exv).getType().template isa<fir::BoxType>())
3270             return exv;
3271           fir::emitFatalError(getLoc(), "array must be emboxed");
3272         },
3273         exp.u);
3274   }
3275 
3276   /// Entry point into lowering an expression with rank. This entry point is for
3277   /// lowering a rhs expression, for example. (RefTransparent semantics.)
3278   static ExtValue
3279   lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter,
3280                           Fortran::lower::SymMap &symMap,
3281                           Fortran::lower::StatementContext &stmtCtx,
3282                           const Fortran::lower::SomeExpr &expr) {
3283     ArrayExprLowering ael{converter, stmtCtx, symMap};
3284     ael.determineShapeOfDest(expr);
3285     ExtValue loopRes = ael.lowerArrayExpression(expr);
3286     fir::ArrayLoadOp dest = ael.destination;
3287     mlir::Value tempRes = dest.getMemref();
3288     fir::FirOpBuilder &builder = converter.getFirOpBuilder();
3289     mlir::Location loc = converter.getCurrentLocation();
3290     builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes),
3291                                            tempRes, dest.getSlice(),
3292                                            dest.getTypeparams());
3293 
3294     auto arrTy =
3295         fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>();
3296     if (auto charTy =
3297             arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) {
3298       if (fir::characterWithDynamicLen(charTy))
3299         TODO(loc, "CHARACTER does not have constant LEN");
3300       mlir::Value len = builder.createIntegerConstant(
3301           loc, builder.getCharacterLengthType(), charTy.getLen());
3302       return fir::CharArrayBoxValue(tempRes, len, dest.getExtents());
3303     }
3304     return fir::ArrayBoxValue(tempRes, dest.getExtents());
3305   }
3306 
3307   static void lowerLazyArrayExpression(
3308       Fortran::lower::AbstractConverter &converter,
3309       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3310       const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader) {
3311     ArrayExprLowering ael(converter, stmtCtx, symMap);
3312     ael.lowerLazyArrayExpression(expr, raggedHeader);
3313   }
3314 
3315   /// Lower the expression \p expr into a buffer that is created on demand. The
3316   /// variable containing the pointer to the buffer is \p var and the variable
3317   /// containing the shape of the buffer is \p shapeBuffer.
3318   void lowerLazyArrayExpression(const Fortran::lower::SomeExpr &expr,
3319                                 mlir::Value header) {
3320     mlir::Location loc = getLoc();
3321     mlir::TupleType hdrTy = fir::factory::getRaggedArrayHeaderType(builder);
3322     mlir::IntegerType i32Ty = builder.getIntegerType(32);
3323 
3324     // Once the loop extents have been computed, which may require being inside
3325     // some explicit loops, lazily allocate the expression on the heap. The
3326     // following continuation creates the buffer as needed.
3327     ccPrelude = [=](llvm::ArrayRef<mlir::Value> shape) {
3328       mlir::IntegerType i64Ty = builder.getIntegerType(64);
3329       mlir::Value byteSize = builder.createIntegerConstant(loc, i64Ty, 1);
3330       fir::runtime::genRaggedArrayAllocate(
3331           loc, builder, header, /*asHeaders=*/false, byteSize, shape);
3332     };
3333 
3334     // Create a dummy array_load before the loop. We're storing to a lazy
3335     // temporary, so there will be no conflict and no copy-in. TODO: skip this
3336     // as there isn't any necessity for it.
3337     ccLoadDest = [=](llvm::ArrayRef<mlir::Value> shape) -> fir::ArrayLoadOp {
3338       mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
3339       auto var = builder.create<fir::CoordinateOp>(
3340           loc, builder.getRefType(hdrTy.getType(1)), header, one);
3341       auto load = builder.create<fir::LoadOp>(loc, var);
3342       mlir::Type eleTy =
3343           fir::unwrapSequenceType(fir::unwrapRefType(load.getType()));
3344       auto seqTy = fir::SequenceType::get(eleTy, shape.size());
3345       mlir::Value castTo =
3346           builder.createConvert(loc, fir::HeapType::get(seqTy), load);
3347       mlir::Value shapeOp = builder.genShape(loc, shape);
3348       return builder.create<fir::ArrayLoadOp>(
3349           loc, seqTy, castTo, shapeOp, /*slice=*/mlir::Value{}, llvm::None);
3350     };
3351     // Custom lowering of the element store to deal with the extra indirection
3352     // to the lazy allocated buffer.
3353     ccStoreToDest = [=](IterSpace iters) {
3354       mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
3355       auto var = builder.create<fir::CoordinateOp>(
3356           loc, builder.getRefType(hdrTy.getType(1)), header, one);
3357       auto load = builder.create<fir::LoadOp>(loc, var);
3358       mlir::Type eleTy =
3359           fir::unwrapSequenceType(fir::unwrapRefType(load.getType()));
3360       auto seqTy = fir::SequenceType::get(eleTy, iters.iterVec().size());
3361       auto toTy = fir::HeapType::get(seqTy);
3362       mlir::Value castTo = builder.createConvert(loc, toTy, load);
3363       mlir::Value shape = builder.genShape(loc, genIterationShape());
3364       llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
3365           loc, builder, castTo.getType(), shape, iters.iterVec());
3366       auto eleAddr = builder.create<fir::ArrayCoorOp>(
3367           loc, builder.getRefType(eleTy), castTo, shape,
3368           /*slice=*/mlir::Value{}, indices, destination.getTypeparams());
3369       mlir::Value eleVal =
3370           builder.createConvert(loc, eleTy, iters.getElement());
3371       builder.create<fir::StoreOp>(loc, eleVal, eleAddr);
3372       return iters.innerArgument();
3373     };
3374 
3375     // Lower the array expression now. Clean-up any temps that may have
3376     // been generated when lowering `expr` right after the lowered value
3377     // was stored to the ragged array temporary. The local temps will not
3378     // be needed afterwards.
3379     stmtCtx.pushScope();
3380     [[maybe_unused]] ExtValue loopRes = lowerArrayExpression(expr);
3381     stmtCtx.finalize(/*popScope=*/true);
3382     assert(fir::getBase(loopRes));
3383   }
3384 
3385   static void
3386   lowerElementalUserAssignment(Fortran::lower::AbstractConverter &converter,
3387                                Fortran::lower::SymMap &symMap,
3388                                Fortran::lower::StatementContext &stmtCtx,
3389                                Fortran::lower::ExplicitIterSpace &explicitSpace,
3390                                Fortran::lower::ImplicitIterSpace &implicitSpace,
3391                                const Fortran::evaluate::ProcedureRef &procRef) {
3392     ArrayExprLowering ael(converter, stmtCtx, symMap,
3393                           ConstituentSemantics::CustomCopyInCopyOut,
3394                           &explicitSpace, &implicitSpace);
3395     assert(procRef.arguments().size() == 2);
3396     const auto *lhs = procRef.arguments()[0].value().UnwrapExpr();
3397     const auto *rhs = procRef.arguments()[1].value().UnwrapExpr();
3398     assert(lhs && rhs &&
3399            "user defined assignment arguments must be expressions");
3400     mlir::FuncOp func =
3401         Fortran::lower::CallerInterface(procRef, converter).getFuncOp();
3402     ael.lowerElementalUserAssignment(func, *lhs, *rhs);
3403   }
3404 
3405   void lowerElementalUserAssignment(mlir::FuncOp userAssignment,
3406                                     const Fortran::lower::SomeExpr &lhs,
3407                                     const Fortran::lower::SomeExpr &rhs) {
3408     mlir::Location loc = getLoc();
3409     PushSemantics(ConstituentSemantics::CustomCopyInCopyOut);
3410     auto genArrayModify = genarr(lhs);
3411     ccStoreToDest = [=](IterSpace iters) -> ExtValue {
3412       auto modifiedArray = genArrayModify(iters);
3413       auto arrayModify = mlir::dyn_cast_or_null<fir::ArrayModifyOp>(
3414           fir::getBase(modifiedArray).getDefiningOp());
3415       assert(arrayModify && "must be created by ArrayModifyOp");
3416       fir::ExtendedValue lhs =
3417           arrayModifyToExv(builder, loc, destination, arrayModify.getResult(0));
3418       genScalarUserDefinedAssignmentCall(builder, loc, userAssignment, lhs,
3419                                          iters.elementExv());
3420       return modifiedArray;
3421     };
3422     determineShapeOfDest(lhs);
3423     semant = ConstituentSemantics::RefTransparent;
3424     auto exv = lowerArrayExpression(rhs);
3425     if (explicitSpaceIsActive()) {
3426       explicitSpace->finalizeContext();
3427       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
3428     } else {
3429       builder.create<fir::ArrayMergeStoreOp>(
3430           loc, destination, fir::getBase(exv), destination.getMemref(),
3431           destination.getSlice(), destination.getTypeparams());
3432     }
3433   }
3434 
3435   /// Lower an elemental subroutine call with at least one array argument.
3436   /// An elemental subroutine is an exception and does not have copy-in/copy-out
3437   /// semantics. See 15.8.3.
3438   /// Do NOT use this for user defined assignments.
3439   static void
3440   lowerElementalSubroutine(Fortran::lower::AbstractConverter &converter,
3441                            Fortran::lower::SymMap &symMap,
3442                            Fortran::lower::StatementContext &stmtCtx,
3443                            const Fortran::lower::SomeExpr &call) {
3444     ArrayExprLowering ael(converter, stmtCtx, symMap,
3445                           ConstituentSemantics::RefTransparent);
3446     ael.lowerElementalSubroutine(call);
3447   }
3448 
3449   // TODO: See the comment in genarr(const Fortran::lower::Parentheses<T>&).
3450   // This is skipping generation of copy-in/copy-out code for analysis that is
3451   // required when arguments are in parentheses.
3452   void lowerElementalSubroutine(const Fortran::lower::SomeExpr &call) {
3453     auto f = genarr(call);
3454     llvm::SmallVector<mlir::Value> shape = genIterationShape();
3455     auto [iterSpace, insPt] = genImplicitLoops(shape, /*innerArg=*/{});
3456     f(iterSpace);
3457     finalizeElementCtx();
3458     builder.restoreInsertionPoint(insPt);
3459   }
3460 
3461   template <typename A, typename B>
3462   ExtValue lowerScalarAssignment(const A &lhs, const B &rhs) {
3463     // 1) Lower the rhs expression with array_fetch op(s).
3464     IterationSpace iters;
3465     iters.setElement(genarr(rhs)(iters));
3466     fir::ExtendedValue elementalExv = iters.elementExv();
3467     // 2) Lower the lhs expression to an array_update.
3468     semant = ConstituentSemantics::ProjectedCopyInCopyOut;
3469     auto lexv = genarr(lhs)(iters);
3470     // 3) Finalize the inner context.
3471     explicitSpace->finalizeContext();
3472     // 4) Thread the array value updated forward. Note: the lhs might be
3473     // ill-formed (performing scalar assignment in an array context),
3474     // in which case there is no array to thread.
3475     auto createResult = [&](auto op) {
3476       mlir::Value oldInnerArg = op.getSequence();
3477       std::size_t offset = explicitSpace->argPosition(oldInnerArg);
3478       explicitSpace->setInnerArg(offset, fir::getBase(lexv));
3479       builder.create<fir::ResultOp>(getLoc(), fir::getBase(lexv));
3480     };
3481     if (auto updateOp = mlir::dyn_cast<fir::ArrayUpdateOp>(
3482             fir::getBase(lexv).getDefiningOp()))
3483       createResult(updateOp);
3484     else if (auto amend = mlir::dyn_cast<fir::ArrayAmendOp>(
3485                  fir::getBase(lexv).getDefiningOp()))
3486       createResult(amend);
3487     else if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>(
3488                  fir::getBase(lexv).getDefiningOp()))
3489       createResult(modifyOp);
3490     return lexv;
3491   }
3492 
3493   static ExtValue lowerScalarUserAssignment(
3494       Fortran::lower::AbstractConverter &converter,
3495       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3496       Fortran::lower::ExplicitIterSpace &explicitIterSpace,
3497       mlir::FuncOp userAssignmentFunction, const Fortran::lower::SomeExpr &lhs,
3498       const Fortran::lower::SomeExpr &rhs) {
3499     Fortran::lower::ImplicitIterSpace implicit;
3500     ArrayExprLowering ael(converter, stmtCtx, symMap,
3501                           ConstituentSemantics::RefTransparent,
3502                           &explicitIterSpace, &implicit);
3503     return ael.lowerScalarUserAssignment(userAssignmentFunction, lhs, rhs);
3504   }
3505 
3506   ExtValue lowerScalarUserAssignment(mlir::FuncOp userAssignment,
3507                                      const Fortran::lower::SomeExpr &lhs,
3508                                      const Fortran::lower::SomeExpr &rhs) {
3509     mlir::Location loc = getLoc();
3510     if (rhs.Rank() > 0)
3511       TODO(loc, "user-defined elemental assigment from expression with rank");
3512     // 1) Lower the rhs expression with array_fetch op(s).
3513     IterationSpace iters;
3514     iters.setElement(genarr(rhs)(iters));
3515     fir::ExtendedValue elementalExv = iters.elementExv();
3516     // 2) Lower the lhs expression to an array_modify.
3517     semant = ConstituentSemantics::CustomCopyInCopyOut;
3518     auto lexv = genarr(lhs)(iters);
3519     bool isIllFormedLHS = false;
3520     // 3) Insert the call
3521     if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>(
3522             fir::getBase(lexv).getDefiningOp())) {
3523       mlir::Value oldInnerArg = modifyOp.getSequence();
3524       std::size_t offset = explicitSpace->argPosition(oldInnerArg);
3525       explicitSpace->setInnerArg(offset, fir::getBase(lexv));
3526       fir::ExtendedValue exv = arrayModifyToExv(
3527           builder, loc, explicitSpace->getLhsLoad(0).getValue(),
3528           modifyOp.getResult(0));
3529       genScalarUserDefinedAssignmentCall(builder, loc, userAssignment, exv,
3530                                          elementalExv);
3531     } else {
3532       // LHS is ill formed, it is a scalar with no references to FORALL
3533       // subscripts, so there is actually no array assignment here. The user
3534       // code is probably bad, but still insert user assignment call since it
3535       // was not rejected by semantics (a warning was emitted).
3536       isIllFormedLHS = true;
3537       genScalarUserDefinedAssignmentCall(builder, getLoc(), userAssignment,
3538                                          lexv, elementalExv);
3539     }
3540     // 4) Finalize the inner context.
3541     explicitSpace->finalizeContext();
3542     // 5). Thread the array value updated forward.
3543     if (!isIllFormedLHS)
3544       builder.create<fir::ResultOp>(getLoc(), fir::getBase(lexv));
3545     return lexv;
3546   }
3547 
3548   bool explicitSpaceIsActive() const {
3549     return explicitSpace && explicitSpace->isActive();
3550   }
3551 
3552   bool implicitSpaceHasMasks() const {
3553     return implicitSpace && !implicitSpace->empty();
3554   }
3555 
3556   CC genMaskAccess(mlir::Value tmp, mlir::Value shape) {
3557     mlir::Location loc = getLoc();
3558     return [=, builder = &converter.getFirOpBuilder()](IterSpace iters) {
3559       mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(tmp.getType());
3560       auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
3561       mlir::Type eleRefTy = builder->getRefType(eleTy);
3562       mlir::IntegerType i1Ty = builder->getI1Type();
3563       // Adjust indices for any shift of the origin of the array.
3564       llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
3565           loc, *builder, tmp.getType(), shape, iters.iterVec());
3566       auto addr = builder->create<fir::ArrayCoorOp>(
3567           loc, eleRefTy, tmp, shape, /*slice=*/mlir::Value{}, indices,
3568           /*typeParams=*/llvm::None);
3569       auto load = builder->create<fir::LoadOp>(loc, addr);
3570       return builder->createConvert(loc, i1Ty, load);
3571     };
3572   }
3573 
3574   /// Construct the incremental instantiations of the ragged array structure.
3575   /// Rebind the lazy buffer variable, etc. as we go.
3576   template <bool withAllocation = false>
3577   mlir::Value prepareRaggedArrays(Fortran::lower::FrontEndExpr expr) {
3578     assert(explicitSpaceIsActive());
3579     mlir::Location loc = getLoc();
3580     mlir::TupleType raggedTy = fir::factory::getRaggedArrayHeaderType(builder);
3581     llvm::SmallVector<llvm::SmallVector<fir::DoLoopOp>> loopStack =
3582         explicitSpace->getLoopStack();
3583     const std::size_t depth = loopStack.size();
3584     mlir::IntegerType i64Ty = builder.getIntegerType(64);
3585     [[maybe_unused]] mlir::Value byteSize =
3586         builder.createIntegerConstant(loc, i64Ty, 1);
3587     mlir::Value header = implicitSpace->lookupMaskHeader(expr);
3588     for (std::remove_const_t<decltype(depth)> i = 0; i < depth; ++i) {
3589       auto insPt = builder.saveInsertionPoint();
3590       if (i < depth - 1)
3591         builder.setInsertionPoint(loopStack[i + 1][0]);
3592 
3593       // Compute and gather the extents.
3594       llvm::SmallVector<mlir::Value> extents;
3595       for (auto doLoop : loopStack[i])
3596         extents.push_back(builder.genExtentFromTriplet(
3597             loc, doLoop.getLowerBound(), doLoop.getUpperBound(),
3598             doLoop.getStep(), i64Ty));
3599       if constexpr (withAllocation) {
3600         fir::runtime::genRaggedArrayAllocate(
3601             loc, builder, header, /*asHeader=*/true, byteSize, extents);
3602       }
3603 
3604       // Compute the dynamic position into the header.
3605       llvm::SmallVector<mlir::Value> offsets;
3606       for (auto doLoop : loopStack[i]) {
3607         auto m = builder.create<mlir::arith::SubIOp>(
3608             loc, doLoop.getInductionVar(), doLoop.getLowerBound());
3609         auto n = builder.create<mlir::arith::DivSIOp>(loc, m, doLoop.getStep());
3610         mlir::Value one = builder.createIntegerConstant(loc, n.getType(), 1);
3611         offsets.push_back(builder.create<mlir::arith::AddIOp>(loc, n, one));
3612       }
3613       mlir::IntegerType i32Ty = builder.getIntegerType(32);
3614       mlir::Value uno = builder.createIntegerConstant(loc, i32Ty, 1);
3615       mlir::Type coorTy = builder.getRefType(raggedTy.getType(1));
3616       auto hdOff = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno);
3617       auto toTy = fir::SequenceType::get(raggedTy, offsets.size());
3618       mlir::Type toRefTy = builder.getRefType(toTy);
3619       auto ldHdr = builder.create<fir::LoadOp>(loc, hdOff);
3620       mlir::Value hdArr = builder.createConvert(loc, toRefTy, ldHdr);
3621       auto shapeOp = builder.genShape(loc, extents);
3622       header = builder.create<fir::ArrayCoorOp>(
3623           loc, builder.getRefType(raggedTy), hdArr, shapeOp,
3624           /*slice=*/mlir::Value{}, offsets,
3625           /*typeparams=*/mlir::ValueRange{});
3626       auto hdrVar = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno);
3627       auto inVar = builder.create<fir::LoadOp>(loc, hdrVar);
3628       mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2);
3629       mlir::Type coorTy2 = builder.getRefType(raggedTy.getType(2));
3630       auto hdrSh = builder.create<fir::CoordinateOp>(loc, coorTy2, header, two);
3631       auto shapePtr = builder.create<fir::LoadOp>(loc, hdrSh);
3632       // Replace the binding.
3633       implicitSpace->rebind(expr, genMaskAccess(inVar, shapePtr));
3634       if (i < depth - 1)
3635         builder.restoreInsertionPoint(insPt);
3636     }
3637     return header;
3638   }
3639 
3640   /// Lower mask expressions with implied iteration spaces from the variants of
3641   /// WHERE syntax. Since it is legal for mask expressions to have side-effects
3642   /// and modify values that will be used for the lhs, rhs, or both of
3643   /// subsequent assignments, the mask must be evaluated before the assignment
3644   /// is processed.
3645   /// Mask expressions are array expressions too.
3646   void genMasks() {
3647     // Lower the mask expressions, if any.
3648     if (implicitSpaceHasMasks()) {
3649       mlir::Location loc = getLoc();
3650       // Mask expressions are array expressions too.
3651       for (const auto *e : implicitSpace->getExprs())
3652         if (e && !implicitSpace->isLowered(e)) {
3653           if (mlir::Value var = implicitSpace->lookupMaskVariable(e)) {
3654             // Allocate the mask buffer lazily.
3655             assert(explicitSpaceIsActive());
3656             mlir::Value header =
3657                 prepareRaggedArrays</*withAllocations=*/true>(e);
3658             Fortran::lower::createLazyArrayTempValue(converter, *e, header,
3659                                                      symMap, stmtCtx);
3660             // Close the explicit loops.
3661             builder.create<fir::ResultOp>(loc, explicitSpace->getInnerArgs());
3662             builder.setInsertionPointAfter(explicitSpace->getOuterLoop());
3663             // Open a new copy of the explicit loop nest.
3664             explicitSpace->genLoopNest();
3665             continue;
3666           }
3667           fir::ExtendedValue tmp = Fortran::lower::createSomeArrayTempValue(
3668               converter, *e, symMap, stmtCtx);
3669           mlir::Value shape = builder.createShape(loc, tmp);
3670           implicitSpace->bind(e, genMaskAccess(fir::getBase(tmp), shape));
3671         }
3672 
3673       // Set buffer from the header.
3674       for (const auto *e : implicitSpace->getExprs()) {
3675         if (!e)
3676           continue;
3677         if (implicitSpace->lookupMaskVariable(e)) {
3678           // Index into the ragged buffer to retrieve cached results.
3679           const int rank = e->Rank();
3680           assert(destShape.empty() ||
3681                  static_cast<std::size_t>(rank) == destShape.size());
3682           mlir::Value header = prepareRaggedArrays(e);
3683           mlir::TupleType raggedTy =
3684               fir::factory::getRaggedArrayHeaderType(builder);
3685           mlir::IntegerType i32Ty = builder.getIntegerType(32);
3686           mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
3687           auto coor1 = builder.create<fir::CoordinateOp>(
3688               loc, builder.getRefType(raggedTy.getType(1)), header, one);
3689           auto db = builder.create<fir::LoadOp>(loc, coor1);
3690           mlir::Type eleTy =
3691               fir::unwrapSequenceType(fir::unwrapRefType(db.getType()));
3692           mlir::Type buffTy =
3693               builder.getRefType(fir::SequenceType::get(eleTy, rank));
3694           // Address of ragged buffer data.
3695           mlir::Value buff = builder.createConvert(loc, buffTy, db);
3696 
3697           mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2);
3698           auto coor2 = builder.create<fir::CoordinateOp>(
3699               loc, builder.getRefType(raggedTy.getType(2)), header, two);
3700           auto shBuff = builder.create<fir::LoadOp>(loc, coor2);
3701           mlir::IntegerType i64Ty = builder.getIntegerType(64);
3702           mlir::IndexType idxTy = builder.getIndexType();
3703           llvm::SmallVector<mlir::Value> extents;
3704           for (std::remove_const_t<decltype(rank)> i = 0; i < rank; ++i) {
3705             mlir::Value off = builder.createIntegerConstant(loc, i32Ty, i);
3706             auto coor = builder.create<fir::CoordinateOp>(
3707                 loc, builder.getRefType(i64Ty), shBuff, off);
3708             auto ldExt = builder.create<fir::LoadOp>(loc, coor);
3709             extents.push_back(builder.createConvert(loc, idxTy, ldExt));
3710           }
3711           if (destShape.empty())
3712             destShape = extents;
3713           // Construct shape of buffer.
3714           mlir::Value shapeOp = builder.genShape(loc, extents);
3715 
3716           // Replace binding with the local result.
3717           implicitSpace->rebind(e, genMaskAccess(buff, shapeOp));
3718         }
3719       }
3720     }
3721   }
3722 
3723   // FIXME: should take multiple inner arguments.
3724   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
3725   genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) {
3726     mlir::Location loc = getLoc();
3727     mlir::IndexType idxTy = builder.getIndexType();
3728     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
3729     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
3730     llvm::SmallVector<mlir::Value> loopUppers;
3731 
3732     // Convert any implied shape to closed interval form. The fir.do_loop will
3733     // run from 0 to `extent - 1` inclusive.
3734     for (auto extent : shape)
3735       loopUppers.push_back(
3736           builder.create<mlir::arith::SubIOp>(loc, extent, one));
3737 
3738     // Iteration space is created with outermost columns, innermost rows
3739     llvm::SmallVector<fir::DoLoopOp> loops;
3740 
3741     const std::size_t loopDepth = loopUppers.size();
3742     llvm::SmallVector<mlir::Value> ivars;
3743 
3744     for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) {
3745       if (i.index() > 0) {
3746         assert(!loops.empty());
3747         builder.setInsertionPointToStart(loops.back().getBody());
3748       }
3749       fir::DoLoopOp loop;
3750       if (innerArg) {
3751         loop = builder.create<fir::DoLoopOp>(
3752             loc, zero, i.value(), one, isUnordered(),
3753             /*finalCount=*/false, mlir::ValueRange{innerArg});
3754         innerArg = loop.getRegionIterArgs().front();
3755         if (explicitSpaceIsActive())
3756           explicitSpace->setInnerArg(0, innerArg);
3757       } else {
3758         loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one,
3759                                              isUnordered(),
3760                                              /*finalCount=*/false);
3761       }
3762       ivars.push_back(loop.getInductionVar());
3763       loops.push_back(loop);
3764     }
3765 
3766     if (innerArg)
3767       for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth;
3768            ++i) {
3769         builder.setInsertionPointToEnd(loops[i].getBody());
3770         builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0));
3771       }
3772 
3773     // Move insertion point to the start of the innermost loop in the nest.
3774     builder.setInsertionPointToStart(loops.back().getBody());
3775     // Set `afterLoopNest` to just after the entire loop nest.
3776     auto currPt = builder.saveInsertionPoint();
3777     builder.setInsertionPointAfter(loops[0]);
3778     auto afterLoopNest = builder.saveInsertionPoint();
3779     builder.restoreInsertionPoint(currPt);
3780 
3781     // Put the implicit loop variables in row to column order to match FIR's
3782     // Ops. (The loops were constructed from outermost column to innermost
3783     // row.)
3784     mlir::Value outerRes = loops[0].getResult(0);
3785     return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)),
3786             afterLoopNest};
3787   }
3788 
3789   /// Build the iteration space into which the array expression will be
3790   /// lowered. The resultType is used to create a temporary, if needed.
3791   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
3792   genIterSpace(mlir::Type resultType) {
3793     mlir::Location loc = getLoc();
3794     llvm::SmallVector<mlir::Value> shape = genIterationShape();
3795     if (!destination) {
3796       // Allocate storage for the result if it is not already provided.
3797       destination = createAndLoadSomeArrayTemp(resultType, shape);
3798     }
3799 
3800     // Generate the lazy mask allocation, if one was given.
3801     if (ccPrelude.hasValue())
3802       ccPrelude.getValue()(shape);
3803 
3804     // Now handle the implicit loops.
3805     mlir::Value inner = explicitSpaceIsActive()
3806                             ? explicitSpace->getInnerArgs().front()
3807                             : destination.getResult();
3808     auto [iters, afterLoopNest] = genImplicitLoops(shape, inner);
3809     mlir::Value innerArg = iters.innerArgument();
3810 
3811     // Generate the mask conditional structure, if there are masks. Unlike the
3812     // explicit masks, which are interleaved, these mask expression appear in
3813     // the innermost loop.
3814     if (implicitSpaceHasMasks()) {
3815       // Recover the cached condition from the mask buffer.
3816       auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) {
3817         return implicitSpace->getBoundClosure(e)(iters);
3818       };
3819 
3820       // Handle the negated conditions in topological order of the WHERE
3821       // clauses. See 10.2.3.2p4 as to why this control structure is produced.
3822       for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs :
3823            implicitSpace->getMasks()) {
3824         const std::size_t size = maskExprs.size() - 1;
3825         auto genFalseBlock = [&](const auto *e, auto &&cond) {
3826           auto ifOp = builder.create<fir::IfOp>(
3827               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
3828               /*withElseRegion=*/true);
3829           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
3830           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
3831           builder.create<fir::ResultOp>(loc, innerArg);
3832           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
3833         };
3834         auto genTrueBlock = [&](const auto *e, auto &&cond) {
3835           auto ifOp = builder.create<fir::IfOp>(
3836               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
3837               /*withElseRegion=*/true);
3838           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
3839           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
3840           builder.create<fir::ResultOp>(loc, innerArg);
3841           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
3842         };
3843         for (std::size_t i = 0; i < size; ++i)
3844           if (const auto *e = maskExprs[i])
3845             genFalseBlock(e, genCond(e, iters));
3846 
3847         // The last condition is either non-negated or unconditionally negated.
3848         if (const auto *e = maskExprs[size])
3849           genTrueBlock(e, genCond(e, iters));
3850       }
3851     }
3852 
3853     // We're ready to lower the body (an assignment statement) for this context
3854     // of loop nests at this point.
3855     return {iters, afterLoopNest};
3856   }
3857 
3858   fir::ArrayLoadOp
3859   createAndLoadSomeArrayTemp(mlir::Type type,
3860                              llvm::ArrayRef<mlir::Value> shape) {
3861     if (ccLoadDest.hasValue())
3862       return ccLoadDest.getValue()(shape);
3863     auto seqTy = type.dyn_cast<fir::SequenceType>();
3864     assert(seqTy && "must be an array");
3865     mlir::Location loc = getLoc();
3866     // TODO: Need to thread the length parameters here. For character, they may
3867     // differ from the operands length (e.g concatenation). So the array loads
3868     // type parameters are not enough.
3869     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>())
3870       if (charTy.hasDynamicLen())
3871         TODO(loc, "character array expression temp with dynamic length");
3872     if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>())
3873       if (recTy.getNumLenParams() > 0)
3874         TODO(loc, "derived type array expression temp with length parameters");
3875     mlir::Value temp = seqTy.hasConstantShape()
3876                            ? builder.create<fir::AllocMemOp>(loc, type)
3877                            : builder.create<fir::AllocMemOp>(
3878                                  loc, type, ".array.expr", llvm::None, shape);
3879     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
3880     stmtCtx.attachCleanup(
3881         [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); });
3882     mlir::Value shapeOp = genShapeOp(shape);
3883     return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp,
3884                                             /*slice=*/mlir::Value{},
3885                                             llvm::None);
3886   }
3887 
3888   static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder,
3889                                  llvm::ArrayRef<mlir::Value> shape) {
3890     mlir::IndexType idxTy = builder.getIndexType();
3891     llvm::SmallVector<mlir::Value> idxShape;
3892     for (auto s : shape)
3893       idxShape.push_back(builder.createConvert(loc, idxTy, s));
3894     auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size());
3895     return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape);
3896   }
3897 
3898   fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) {
3899     return genShapeOp(getLoc(), builder, shape);
3900   }
3901 
3902   //===--------------------------------------------------------------------===//
3903   // Expression traversal and lowering.
3904   //===--------------------------------------------------------------------===//
3905 
3906   /// Lower the expression, \p x, in a scalar context.
3907   template <typename A>
3908   ExtValue asScalar(const A &x) {
3909     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x);
3910   }
3911 
3912   /// Lower the expression, \p x, in a scalar context. If this is an explicit
3913   /// space, the expression may be scalar and refer to an array. We want to
3914   /// raise the array access to array operations in FIR to analyze potential
3915   /// conflicts even when the result is a scalar element.
3916   template <typename A>
3917   ExtValue asScalarArray(const A &x) {
3918     return explicitSpaceIsActive() ? genarr(x)(IterationSpace{}) : asScalar(x);
3919   }
3920 
3921   /// Lower the expression in a scalar context to a memory reference.
3922   template <typename A>
3923   ExtValue asScalarRef(const A &x) {
3924     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x);
3925   }
3926 
3927   /// Lower an expression without dereferencing any indirection that may be
3928   /// a nullptr (because this is an absent optional or unallocated/disassociated
3929   /// descriptor). The returned expression cannot be addressed directly, it is
3930   /// meant to inquire about its status before addressing the related entity.
3931   template <typename A>
3932   ExtValue asInquired(const A &x) {
3933     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}
3934         .lowerIntrinsicArgumentAsInquired(x);
3935   }
3936 
3937   // An expression with non-zero rank is an array expression.
3938   template <typename A>
3939   bool isArray(const A &x) const {
3940     return x.Rank() != 0;
3941   }
3942 
3943   /// Some temporaries are allocated on an element-by-element basis during the
3944   /// array expression evaluation. Collect the cleanups here so the resources
3945   /// can be freed before the next loop iteration, avoiding memory leaks. etc.
3946   Fortran::lower::StatementContext &getElementCtx() {
3947     if (!elementCtx) {
3948       stmtCtx.pushScope();
3949       elementCtx = true;
3950     }
3951     return stmtCtx;
3952   }
3953 
3954   /// If there were temporaries created for this element evaluation, finalize
3955   /// and deallocate the resources now. This should be done just prior the the
3956   /// fir::ResultOp at the end of the innermost loop.
3957   void finalizeElementCtx() {
3958     if (elementCtx) {
3959       stmtCtx.finalize(/*popScope=*/true);
3960       elementCtx = false;
3961     }
3962   }
3963 
3964   /// Lower an elemental function array argument. This ensures array
3965   /// sub-expressions that are not variables and must be passed by address
3966   /// are lowered by value and placed in memory.
3967   template <typename A>
3968   CC genElementalArgument(const A &x) {
3969     // Ensure the returned element is in memory if this is what was requested.
3970     if ((semant == ConstituentSemantics::RefOpaque ||
3971          semant == ConstituentSemantics::DataAddr ||
3972          semant == ConstituentSemantics::ByValueArg)) {
3973       if (!Fortran::evaluate::IsVariable(x)) {
3974         PushSemantics(ConstituentSemantics::DataValue);
3975         CC cc = genarr(x);
3976         mlir::Location loc = getLoc();
3977         if (isParenthesizedVariable(x)) {
3978           // Parenthesised variables are lowered to a reference to the variable
3979           // storage. When passing it as an argument, a copy must be passed.
3980           return [=](IterSpace iters) -> ExtValue {
3981             return createInMemoryScalarCopy(builder, loc, cc(iters));
3982           };
3983         }
3984         mlir::Type storageType =
3985             fir::unwrapSequenceType(converter.genType(toEvExpr(x)));
3986         return [=](IterSpace iters) -> ExtValue {
3987           return placeScalarValueInMemory(builder, loc, cc(iters), storageType);
3988         };
3989       }
3990     }
3991     return genarr(x);
3992   }
3993 
3994   // A procedure reference to a Fortran elemental intrinsic procedure.
3995   CC genElementalIntrinsicProcRef(
3996       const Fortran::evaluate::ProcedureRef &procRef,
3997       llvm::Optional<mlir::Type> retTy,
3998       const Fortran::evaluate::SpecificIntrinsic &intrinsic) {
3999     llvm::SmallVector<CC> operands;
4000     llvm::StringRef name = intrinsic.name;
4001     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
4002         Fortran::lower::getIntrinsicArgumentLowering(name);
4003     mlir::Location loc = getLoc();
4004     if (Fortran::lower::intrinsicRequiresCustomOptionalHandling(
4005             procRef, intrinsic, converter)) {
4006       using CcPairT = std::pair<CC, llvm::Optional<mlir::Value>>;
4007       llvm::SmallVector<CcPairT> operands;
4008       auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
4009         if (expr.Rank() == 0) {
4010           ExtValue optionalArg = this->asInquired(expr);
4011           mlir::Value isPresent =
4012               genActualIsPresentTest(builder, loc, optionalArg);
4013           operands.emplace_back(
4014               [=](IterSpace iters) -> ExtValue {
4015                 return genLoad(builder, loc, optionalArg);
4016               },
4017               isPresent);
4018         } else {
4019           auto [cc, isPresent, _] = this->genOptionalArrayFetch(expr);
4020           operands.emplace_back(cc, isPresent);
4021         }
4022       };
4023       auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) {
4024         PushSemantics(ConstituentSemantics::RefTransparent);
4025         operands.emplace_back(genElementalArgument(expr), llvm::None);
4026       };
4027       Fortran::lower::prepareCustomIntrinsicArgument(
4028           procRef, intrinsic, retTy, prepareOptionalArg, prepareOtherArg,
4029           converter);
4030 
4031       fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
4032       llvm::StringRef name = intrinsic.name;
4033       return [=](IterSpace iters) -> ExtValue {
4034         auto getArgument = [&](std::size_t i) -> ExtValue {
4035           return operands[i].first(iters);
4036         };
4037         auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> {
4038           return operands[i].second;
4039         };
4040         return Fortran::lower::lowerCustomIntrinsic(
4041             *bldr, loc, name, retTy, isPresent, getArgument, operands.size(),
4042             getElementCtx());
4043       };
4044     }
4045     /// Otherwise, pre-lower arguments and use intrinsic lowering utility.
4046     for (const auto &[arg, dummy] :
4047          llvm::zip(procRef.arguments(),
4048                    intrinsic.characteristics.value().dummyArguments)) {
4049       const auto *expr =
4050           Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
4051       if (!expr) {
4052         // Absent optional.
4053         operands.emplace_back([=](IterSpace) { return mlir::Value{}; });
4054       } else if (!argLowering) {
4055         // No argument lowering instruction, lower by value.
4056         PushSemantics(ConstituentSemantics::RefTransparent);
4057         operands.emplace_back(genElementalArgument(*expr));
4058       } else {
4059         // Ad-hoc argument lowering handling.
4060         Fortran::lower::ArgLoweringRule argRules =
4061             Fortran::lower::lowerIntrinsicArgumentAs(getLoc(), *argLowering,
4062                                                      dummy.name);
4063         if (argRules.handleDynamicOptional &&
4064             Fortran::evaluate::MayBePassedAsAbsentOptional(
4065                 *expr, converter.getFoldingContext())) {
4066           // Currently, there is not elemental intrinsic that requires lowering
4067           // a potentially absent argument to something else than a value (apart
4068           // from character MAX/MIN that are handled elsewhere.)
4069           if (argRules.lowerAs != Fortran::lower::LowerIntrinsicArgAs::Value)
4070             TODO(loc, "lowering non trivial optional elemental intrinsic array "
4071                       "argument");
4072           PushSemantics(ConstituentSemantics::RefTransparent);
4073           operands.emplace_back(genarrForwardOptionalArgumentToCall(*expr));
4074           continue;
4075         }
4076         switch (argRules.lowerAs) {
4077         case Fortran::lower::LowerIntrinsicArgAs::Value: {
4078           PushSemantics(ConstituentSemantics::RefTransparent);
4079           operands.emplace_back(genElementalArgument(*expr));
4080         } break;
4081         case Fortran::lower::LowerIntrinsicArgAs::Addr: {
4082           // Note: assume does not have Fortran VALUE attribute semantics.
4083           PushSemantics(ConstituentSemantics::RefOpaque);
4084           operands.emplace_back(genElementalArgument(*expr));
4085         } break;
4086         case Fortran::lower::LowerIntrinsicArgAs::Box: {
4087           PushSemantics(ConstituentSemantics::RefOpaque);
4088           auto lambda = genElementalArgument(*expr);
4089           operands.emplace_back([=](IterSpace iters) {
4090             return builder.createBox(loc, lambda(iters));
4091           });
4092         } break;
4093         case Fortran::lower::LowerIntrinsicArgAs::Inquired:
4094           TODO(loc, "intrinsic function with inquired argument");
4095           break;
4096         }
4097       }
4098     }
4099 
4100     // Let the intrinsic library lower the intrinsic procedure call
4101     return [=](IterSpace iters) {
4102       llvm::SmallVector<ExtValue> args;
4103       for (const auto &cc : operands)
4104         args.push_back(cc(iters));
4105       return Fortran::lower::genIntrinsicCall(builder, loc, name, retTy, args,
4106                                               getElementCtx());
4107     };
4108   }
4109 
4110   /// Lower a procedure reference to a user-defined elemental procedure.
4111   CC genElementalUserDefinedProcRef(
4112       const Fortran::evaluate::ProcedureRef &procRef,
4113       llvm::Optional<mlir::Type> retTy) {
4114     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
4115 
4116     // 10.1.4 p5. Impure elemental procedures must be called in element order.
4117     if (const Fortran::semantics::Symbol *procSym = procRef.proc().GetSymbol())
4118       if (!Fortran::semantics::IsPureProcedure(*procSym))
4119         setUnordered(false);
4120 
4121     Fortran::lower::CallerInterface caller(procRef, converter);
4122     llvm::SmallVector<CC> operands;
4123     operands.reserve(caller.getPassedArguments().size());
4124     mlir::Location loc = getLoc();
4125     mlir::FunctionType callSiteType = caller.genFunctionType();
4126     for (const Fortran::lower::CallInterface<
4127              Fortran::lower::CallerInterface>::PassedEntity &arg :
4128          caller.getPassedArguments()) {
4129       // 15.8.3 p1. Elemental procedure with intent(out)/intent(inout)
4130       // arguments must be called in element order.
4131       if (arg.mayBeModifiedByCall())
4132         setUnordered(false);
4133       const auto *actual = arg.entity;
4134       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
4135       if (!actual) {
4136         // Optional dummy argument for which there is no actual argument.
4137         auto absent = builder.create<fir::AbsentOp>(loc, argTy);
4138         operands.emplace_back([=](IterSpace) { return absent; });
4139         continue;
4140       }
4141       const auto *expr = actual->UnwrapExpr();
4142       if (!expr)
4143         TODO(loc, "assumed type actual argument lowering");
4144 
4145       LLVM_DEBUG(expr->AsFortran(llvm::dbgs()
4146                                  << "argument: " << arg.firArgument << " = [")
4147                  << "]\n");
4148       if (arg.isOptional() && Fortran::evaluate::MayBePassedAsAbsentOptional(
4149                                   *expr, converter.getFoldingContext()))
4150         TODO(loc,
4151              "passing dynamically optional argument to elemental procedures");
4152       switch (arg.passBy) {
4153       case PassBy::Value: {
4154         // True pass-by-value semantics.
4155         PushSemantics(ConstituentSemantics::RefTransparent);
4156         operands.emplace_back(genElementalArgument(*expr));
4157       } break;
4158       case PassBy::BaseAddressValueAttribute: {
4159         // VALUE attribute or pass-by-reference to a copy semantics. (byval*)
4160         if (isArray(*expr)) {
4161           PushSemantics(ConstituentSemantics::ByValueArg);
4162           operands.emplace_back(genElementalArgument(*expr));
4163         } else {
4164           // Store scalar value in a temp to fulfill VALUE attribute.
4165           mlir::Value val = fir::getBase(asScalar(*expr));
4166           mlir::Value temp = builder.createTemporary(
4167               loc, val.getType(),
4168               llvm::ArrayRef<mlir::NamedAttribute>{
4169                   Fortran::lower::getAdaptToByRefAttr(builder)});
4170           builder.create<fir::StoreOp>(loc, val, temp);
4171           operands.emplace_back(
4172               [=](IterSpace iters) -> ExtValue { return temp; });
4173         }
4174       } break;
4175       case PassBy::BaseAddress: {
4176         if (isArray(*expr)) {
4177           PushSemantics(ConstituentSemantics::RefOpaque);
4178           operands.emplace_back(genElementalArgument(*expr));
4179         } else {
4180           ExtValue exv = asScalarRef(*expr);
4181           operands.emplace_back([=](IterSpace iters) { return exv; });
4182         }
4183       } break;
4184       case PassBy::CharBoxValueAttribute: {
4185         if (isArray(*expr)) {
4186           PushSemantics(ConstituentSemantics::DataValue);
4187           auto lambda = genElementalArgument(*expr);
4188           operands.emplace_back([=](IterSpace iters) {
4189             return fir::factory::CharacterExprHelper{builder, loc}
4190                 .createTempFrom(lambda(iters));
4191           });
4192         } else {
4193           fir::factory::CharacterExprHelper helper(builder, loc);
4194           fir::CharBoxValue argVal = helper.createTempFrom(asScalarRef(*expr));
4195           operands.emplace_back(
4196               [=](IterSpace iters) -> ExtValue { return argVal; });
4197         }
4198       } break;
4199       case PassBy::BoxChar: {
4200         PushSemantics(ConstituentSemantics::RefOpaque);
4201         operands.emplace_back(genElementalArgument(*expr));
4202       } break;
4203       case PassBy::AddressAndLength:
4204         // PassBy::AddressAndLength is only used for character results. Results
4205         // are not handled here.
4206         fir::emitFatalError(
4207             loc, "unexpected PassBy::AddressAndLength in elemental call");
4208         break;
4209       case PassBy::CharProcTuple: {
4210         ExtValue argRef = asScalarRef(*expr);
4211         mlir::Value tuple = createBoxProcCharTuple(
4212             converter, argTy, fir::getBase(argRef), fir::getLen(argRef));
4213         operands.emplace_back(
4214             [=](IterSpace iters) -> ExtValue { return tuple; });
4215       } break;
4216       case PassBy::Box:
4217       case PassBy::MutableBox:
4218         // See C15100 and C15101
4219         fir::emitFatalError(loc, "cannot be POINTER, ALLOCATABLE");
4220       }
4221     }
4222 
4223     if (caller.getIfIndirectCallSymbol())
4224       fir::emitFatalError(loc, "cannot be indirect call");
4225 
4226     // The lambda is mutable so that `caller` copy can be modified inside it.
4227     return
4228         [=, caller = std::move(caller)](IterSpace iters) mutable -> ExtValue {
4229           for (const auto &[cc, argIface] :
4230                llvm::zip(operands, caller.getPassedArguments())) {
4231             auto exv = cc(iters);
4232             auto arg = exv.match(
4233                 [&](const fir::CharBoxValue &cb) -> mlir::Value {
4234                   return fir::factory::CharacterExprHelper{builder, loc}
4235                       .createEmbox(cb);
4236                 },
4237                 [&](const auto &) { return fir::getBase(exv); });
4238             caller.placeInput(argIface, arg);
4239           }
4240           return ScalarExprLowering{loc, converter, symMap, getElementCtx()}
4241               .genCallOpAndResult(caller, callSiteType, retTy);
4242         };
4243   }
4244 
4245   /// Generate a procedure reference. This code is shared for both functions and
4246   /// subroutines, the difference being reflected by `retTy`.
4247   CC genProcRef(const Fortran::evaluate::ProcedureRef &procRef,
4248                 llvm::Optional<mlir::Type> retTy) {
4249     mlir::Location loc = getLoc();
4250     if (procRef.IsElemental()) {
4251       if (const Fortran::evaluate::SpecificIntrinsic *intrin =
4252               procRef.proc().GetSpecificIntrinsic()) {
4253         // All elemental intrinsic functions are pure and cannot modify their
4254         // arguments. The only elemental subroutine, MVBITS has an Intent(inout)
4255         // argument. So for this last one, loops must be in element order
4256         // according to 15.8.3 p1.
4257         if (!retTy)
4258           setUnordered(false);
4259 
4260         // Elemental intrinsic call.
4261         // The intrinsic procedure is called once per element of the array.
4262         return genElementalIntrinsicProcRef(procRef, retTy, *intrin);
4263       }
4264       if (ScalarExprLowering::isStatementFunctionCall(procRef))
4265         fir::emitFatalError(loc, "statement function cannot be elemental");
4266 
4267       // Elemental call.
4268       // The procedure is called once per element of the array argument(s).
4269       return genElementalUserDefinedProcRef(procRef, retTy);
4270     }
4271 
4272     // Transformational call.
4273     // The procedure is called once and produces a value of rank > 0.
4274     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
4275             procRef.proc().GetSpecificIntrinsic()) {
4276       if (explicitSpaceIsActive() && procRef.Rank() == 0) {
4277         // Elide any implicit loop iters.
4278         return [=, &procRef](IterSpace) {
4279           return ScalarExprLowering{loc, converter, symMap, stmtCtx}
4280               .genIntrinsicRef(procRef, *intrinsic, retTy);
4281         };
4282       }
4283       return genarr(
4284           ScalarExprLowering{loc, converter, symMap, stmtCtx}.genIntrinsicRef(
4285               procRef, *intrinsic, retTy));
4286     }
4287 
4288     if (explicitSpaceIsActive() && procRef.Rank() == 0) {
4289       // Elide any implicit loop iters.
4290       return [=, &procRef](IterSpace) {
4291         return ScalarExprLowering{loc, converter, symMap, stmtCtx}
4292             .genProcedureRef(procRef, retTy);
4293       };
4294     }
4295     // In the default case, the call can be hoisted out of the loop nest. Apply
4296     // the iterations to the result, which may be an array value.
4297     return genarr(
4298         ScalarExprLowering{loc, converter, symMap, stmtCtx}.genProcedureRef(
4299             procRef, retTy));
4300   }
4301 
4302   template <typename A>
4303   CC genScalarAndForwardValue(const A &x) {
4304     ExtValue result = asScalar(x);
4305     return [=](IterSpace) { return result; };
4306   }
4307 
4308   template <typename A, typename = std::enable_if_t<Fortran::common::HasMember<
4309                             A, Fortran::evaluate::TypelessExpression>>>
4310   CC genarr(const A &x) {
4311     return genScalarAndForwardValue(x);
4312   }
4313 
4314   template <typename A>
4315   CC genarr(const Fortran::evaluate::Expr<A> &x) {
4316     LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x));
4317     if (isArray(x) || explicitSpaceIsActive() ||
4318         isElementalProcWithArrayArgs(x))
4319       return std::visit([&](const auto &e) { return genarr(e); }, x.u);
4320     return genScalarAndForwardValue(x);
4321   }
4322 
4323   // Converting a value of memory bound type requires creating a temp and
4324   // copying the value.
4325   static ExtValue convertAdjustedType(fir::FirOpBuilder &builder,
4326                                       mlir::Location loc, mlir::Type toType,
4327                                       const ExtValue &exv) {
4328     return exv.match(
4329         [&](const fir::CharBoxValue &cb) -> ExtValue {
4330           mlir::Value len = cb.getLen();
4331           auto mem =
4332               builder.create<fir::AllocaOp>(loc, toType, mlir::ValueRange{len});
4333           fir::CharBoxValue result(mem, len);
4334           fir::factory::CharacterExprHelper{builder, loc}.createAssign(
4335               ExtValue{result}, exv);
4336           return result;
4337         },
4338         [&](const auto &) -> ExtValue {
4339           fir::emitFatalError(loc, "convert on adjusted extended value");
4340         });
4341   }
4342   template <Fortran::common::TypeCategory TC1, int KIND,
4343             Fortran::common::TypeCategory TC2>
4344   CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
4345                                              TC2> &x) {
4346     mlir::Location loc = getLoc();
4347     auto lambda = genarr(x.left());
4348     mlir::Type ty = converter.genType(TC1, KIND);
4349     return [=](IterSpace iters) -> ExtValue {
4350       auto exv = lambda(iters);
4351       mlir::Value val = fir::getBase(exv);
4352       auto valTy = val.getType();
4353       if (elementTypeWasAdjusted(valTy) &&
4354           !(fir::isa_ref_type(valTy) && fir::isa_integer(ty)))
4355         return convertAdjustedType(builder, loc, ty, exv);
4356       return builder.createConvert(loc, ty, val);
4357     };
4358   }
4359 
4360   template <int KIND>
4361   CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) {
4362     TODO(getLoc(), "ComplexComponent<KIND>");
4363   }
4364 
4365   template <typename T>
4366   CC genarr(const Fortran::evaluate::Parentheses<T> &x) {
4367     mlir::Location loc = getLoc();
4368     if (isReferentiallyOpaque()) {
4369       // Context is a call argument in, for example, an elemental procedure
4370       // call. TODO: all array arguments should use array_load, array_access,
4371       // array_amend, and INTENT(OUT), INTENT(INOUT) arguments should have
4372       // array_merge_store ops.
4373       TODO(loc, "parentheses on argument in elemental call");
4374     }
4375     auto f = genarr(x.left());
4376     return [=](IterSpace iters) -> ExtValue {
4377       auto val = f(iters);
4378       mlir::Value base = fir::getBase(val);
4379       auto newBase =
4380           builder.create<fir::NoReassocOp>(loc, base.getType(), base);
4381       return fir::substBase(val, newBase);
4382     };
4383   }
4384   template <int KIND>
4385   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
4386                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
4387     mlir::Location loc = getLoc();
4388     auto f = genarr(x.left());
4389     return [=](IterSpace iters) -> ExtValue {
4390       mlir::Value val = fir::getBase(f(iters));
4391       mlir::Type ty =
4392           converter.genType(Fortran::common::TypeCategory::Integer, KIND);
4393       mlir::Value zero = builder.createIntegerConstant(loc, ty, 0);
4394       return builder.create<mlir::arith::SubIOp>(loc, zero, val);
4395     };
4396   }
4397   template <int KIND>
4398   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
4399                 Fortran::common::TypeCategory::Real, KIND>> &x) {
4400     mlir::Location loc = getLoc();
4401     auto f = genarr(x.left());
4402     return [=](IterSpace iters) -> ExtValue {
4403       return builder.create<mlir::arith::NegFOp>(loc, fir::getBase(f(iters)));
4404     };
4405   }
4406   template <int KIND>
4407   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
4408                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
4409     mlir::Location loc = getLoc();
4410     auto f = genarr(x.left());
4411     return [=](IterSpace iters) -> ExtValue {
4412       return builder.create<fir::NegcOp>(loc, fir::getBase(f(iters)));
4413     };
4414   }
4415 
4416   //===--------------------------------------------------------------------===//
4417   // Binary elemental ops
4418   //===--------------------------------------------------------------------===//
4419 
4420   template <typename OP, typename A>
4421   CC createBinaryOp(const A &evEx) {
4422     mlir::Location loc = getLoc();
4423     auto lambda = genarr(evEx.left());
4424     auto rf = genarr(evEx.right());
4425     return [=](IterSpace iters) -> ExtValue {
4426       mlir::Value left = fir::getBase(lambda(iters));
4427       mlir::Value right = fir::getBase(rf(iters));
4428       return builder.create<OP>(loc, left, right);
4429     };
4430   }
4431 
4432 #undef GENBIN
4433 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
4434   template <int KIND>                                                          \
4435   CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type<       \
4436                 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) {       \
4437     return createBinaryOp<GenBinFirOp>(x);                                     \
4438   }
4439 
4440   GENBIN(Add, Integer, mlir::arith::AddIOp)
4441   GENBIN(Add, Real, mlir::arith::AddFOp)
4442   GENBIN(Add, Complex, fir::AddcOp)
4443   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
4444   GENBIN(Subtract, Real, mlir::arith::SubFOp)
4445   GENBIN(Subtract, Complex, fir::SubcOp)
4446   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
4447   GENBIN(Multiply, Real, mlir::arith::MulFOp)
4448   GENBIN(Multiply, Complex, fir::MulcOp)
4449   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
4450   GENBIN(Divide, Real, mlir::arith::DivFOp)
4451   GENBIN(Divide, Complex, fir::DivcOp)
4452 
4453   template <Fortran::common::TypeCategory TC, int KIND>
4454   CC genarr(
4455       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
4456     mlir::Location loc = getLoc();
4457     mlir::Type ty = converter.genType(TC, KIND);
4458     auto lf = genarr(x.left());
4459     auto rf = genarr(x.right());
4460     return [=](IterSpace iters) -> ExtValue {
4461       mlir::Value lhs = fir::getBase(lf(iters));
4462       mlir::Value rhs = fir::getBase(rf(iters));
4463       return Fortran::lower::genPow(builder, loc, ty, lhs, rhs);
4464     };
4465   }
4466   template <Fortran::common::TypeCategory TC, int KIND>
4467   CC genarr(
4468       const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
4469     TODO(getLoc(), "genarr Extremum<Fortran::evaluate::Type<TC, KIND>>");
4470   }
4471   template <Fortran::common::TypeCategory TC, int KIND>
4472   CC genarr(
4473       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
4474           &x) {
4475     TODO(getLoc(), "genarr RealToIntPower<Fortran::evaluate::Type<TC, KIND>>");
4476   }
4477   template <int KIND>
4478   CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
4479     TODO(getLoc(), "genarr ComplexConstructor<KIND>");
4480   }
4481 
4482   template <int KIND>
4483   CC genarr(const Fortran::evaluate::Concat<KIND> &x) {
4484     TODO(getLoc(), "genarr Concat<KIND>");
4485   }
4486 
4487   template <int KIND>
4488   CC genarr(const Fortran::evaluate::SetLength<KIND> &x) {
4489     TODO(getLoc(), "genarr SetLength<KIND>");
4490   }
4491 
4492   template <typename A>
4493   CC genarr(const Fortran::evaluate::Constant<A> &x) {
4494     if (/*explicitSpaceIsActive() &&*/ x.Rank() == 0)
4495       return genScalarAndForwardValue(x);
4496     mlir::Location loc = getLoc();
4497     mlir::IndexType idxTy = builder.getIndexType();
4498     mlir::Type arrTy = converter.genType(toEvExpr(x));
4499     std::string globalName = Fortran::lower::mangle::mangleArrayLiteral(x);
4500     fir::GlobalOp global = builder.getNamedGlobal(globalName);
4501     if (!global) {
4502       mlir::Type symTy = arrTy;
4503       mlir::Type eleTy = symTy.cast<fir::SequenceType>().getEleTy();
4504       // If we have a rank-1 array of integer, real, or logical, then we can
4505       // create a global array with the dense attribute.
4506       //
4507       // The mlir tensor type can only handle integer, real, or logical. It
4508       // does not currently support nested structures which is required for
4509       // complex.
4510       //
4511       // Also, we currently handle just rank-1 since tensor type assumes
4512       // row major array ordering. We will need to reorder the dimensions
4513       // in the tensor type to support Fortran's column major array ordering.
4514       // How to create this tensor type is to be determined.
4515       if (x.Rank() == 1 &&
4516           eleTy.isa<fir::LogicalType, mlir::IntegerType, mlir::FloatType>())
4517         global = Fortran::lower::createDenseGlobal(
4518             loc, arrTy, globalName, builder.createInternalLinkage(), true,
4519             toEvExpr(x), converter);
4520       // Note: If call to createDenseGlobal() returns 0, then call
4521       // createGlobalConstant() below.
4522       if (!global)
4523         global = builder.createGlobalConstant(
4524             loc, arrTy, globalName,
4525             [&](fir::FirOpBuilder &builder) {
4526               Fortran::lower::StatementContext stmtCtx(
4527                   /*cleanupProhibited=*/true);
4528               fir::ExtendedValue result =
4529                   Fortran::lower::createSomeInitializerExpression(
4530                       loc, converter, toEvExpr(x), symMap, stmtCtx);
4531               mlir::Value castTo =
4532                   builder.createConvert(loc, arrTy, fir::getBase(result));
4533               builder.create<fir::HasValueOp>(loc, castTo);
4534             },
4535             builder.createInternalLinkage());
4536     }
4537     auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(),
4538                                               global.getSymbol());
4539     auto seqTy = global.getType().cast<fir::SequenceType>();
4540     llvm::SmallVector<mlir::Value> extents;
4541     for (auto extent : seqTy.getShape())
4542       extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
4543     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>()) {
4544       mlir::Value len = builder.createIntegerConstant(loc, builder.getI64Type(),
4545                                                       charTy.getLen());
4546       return genarr(fir::CharArrayBoxValue{addr, len, extents});
4547     }
4548     return genarr(fir::ArrayBoxValue{addr, extents});
4549   }
4550 
4551   //===--------------------------------------------------------------------===//
4552   // A vector subscript expression may be wrapped with a cast to INTEGER*8.
4553   // Get rid of it here so the vector can be loaded. Add it back when
4554   // generating the elemental evaluation (inside the loop nest).
4555 
4556   static Fortran::lower::SomeExpr
4557   ignoreEvConvert(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
4558                       Fortran::common::TypeCategory::Integer, 8>> &x) {
4559     return std::visit([&](const auto &v) { return ignoreEvConvert(v); }, x.u);
4560   }
4561   template <Fortran::common::TypeCategory FROM>
4562   static Fortran::lower::SomeExpr ignoreEvConvert(
4563       const Fortran::evaluate::Convert<
4564           Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 8>,
4565           FROM> &x) {
4566     return toEvExpr(x.left());
4567   }
4568   template <typename A>
4569   static Fortran::lower::SomeExpr ignoreEvConvert(const A &x) {
4570     return toEvExpr(x);
4571   }
4572 
4573   //===--------------------------------------------------------------------===//
4574   // Get the `Se::Symbol*` for the subscript expression, `x`. This symbol can
4575   // be used to determine the lbound, ubound of the vector.
4576 
4577   template <typename A>
4578   static const Fortran::semantics::Symbol *
4579   extractSubscriptSymbol(const Fortran::evaluate::Expr<A> &x) {
4580     return std::visit([&](const auto &v) { return extractSubscriptSymbol(v); },
4581                       x.u);
4582   }
4583   template <typename A>
4584   static const Fortran::semantics::Symbol *
4585   extractSubscriptSymbol(const Fortran::evaluate::Designator<A> &x) {
4586     return Fortran::evaluate::UnwrapWholeSymbolDataRef(x);
4587   }
4588   template <typename A>
4589   static const Fortran::semantics::Symbol *extractSubscriptSymbol(const A &x) {
4590     return nullptr;
4591   }
4592 
4593   //===--------------------------------------------------------------------===//
4594 
4595   /// Get the declared lower bound value of the array `x` in dimension `dim`.
4596   /// The argument `one` must be an ssa-value for the constant 1.
4597   mlir::Value getLBound(const ExtValue &x, unsigned dim, mlir::Value one) {
4598     return fir::factory::readLowerBound(builder, getLoc(), x, dim, one);
4599   }
4600 
4601   /// Get the declared upper bound value of the array `x` in dimension `dim`.
4602   /// The argument `one` must be an ssa-value for the constant 1.
4603   mlir::Value getUBound(const ExtValue &x, unsigned dim, mlir::Value one) {
4604     mlir::Location loc = getLoc();
4605     mlir::Value lb = getLBound(x, dim, one);
4606     mlir::Value extent = fir::factory::readExtent(builder, loc, x, dim);
4607     auto add = builder.create<mlir::arith::AddIOp>(loc, lb, extent);
4608     return builder.create<mlir::arith::SubIOp>(loc, add, one);
4609   }
4610 
4611   /// Return the extent of the boxed array `x` in dimesion `dim`.
4612   mlir::Value getExtent(const ExtValue &x, unsigned dim) {
4613     return fir::factory::readExtent(builder, getLoc(), x, dim);
4614   }
4615 
4616   template <typename A>
4617   ExtValue genArrayBase(const A &base) {
4618     ScalarExprLowering sel{getLoc(), converter, symMap, stmtCtx};
4619     return base.IsSymbol() ? sel.gen(base.GetFirstSymbol())
4620                            : sel.gen(base.GetComponent());
4621   }
4622 
4623   template <typename A>
4624   bool hasEvArrayRef(const A &x) {
4625     struct HasEvArrayRefHelper
4626         : public Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper> {
4627       HasEvArrayRefHelper()
4628           : Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper>(*this) {}
4629       using Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper>::operator();
4630       bool operator()(const Fortran::evaluate::ArrayRef &) const {
4631         return true;
4632       }
4633     } helper;
4634     return helper(x);
4635   }
4636 
4637   CC genVectorSubscriptArrayFetch(const Fortran::lower::SomeExpr &expr,
4638                                   std::size_t dim) {
4639     PushSemantics(ConstituentSemantics::RefTransparent);
4640     auto saved = Fortran::common::ScopedSet(explicitSpace, nullptr);
4641     llvm::SmallVector<mlir::Value> savedDestShape = destShape;
4642     destShape.clear();
4643     auto result = genarr(expr);
4644     if (destShape.empty())
4645       TODO(getLoc(), "expected vector to have an extent");
4646     assert(destShape.size() == 1 && "vector has rank > 1");
4647     if (destShape[0] != savedDestShape[dim]) {
4648       // Not the same, so choose the smaller value.
4649       mlir::Location loc = getLoc();
4650       auto cmp = builder.create<mlir::arith::CmpIOp>(
4651           loc, mlir::arith::CmpIPredicate::sgt, destShape[0],
4652           savedDestShape[dim]);
4653       auto sel = builder.create<mlir::arith::SelectOp>(
4654           loc, cmp, savedDestShape[dim], destShape[0]);
4655       savedDestShape[dim] = sel;
4656       destShape = savedDestShape;
4657     }
4658     return result;
4659   }
4660 
4661   /// Generate an access by vector subscript using the index in the iteration
4662   /// vector at `dim`.
4663   mlir::Value genAccessByVector(mlir::Location loc, CC genArrFetch,
4664                                 IterSpace iters, std::size_t dim) {
4665     IterationSpace vecIters(iters,
4666                             llvm::ArrayRef<mlir::Value>{iters.iterValue(dim)});
4667     fir::ExtendedValue fetch = genArrFetch(vecIters);
4668     mlir::IndexType idxTy = builder.getIndexType();
4669     return builder.createConvert(loc, idxTy, fir::getBase(fetch));
4670   }
4671 
4672   /// When we have an array reference, the expressions specified in each
4673   /// dimension may be slice operations (e.g. `i:j:k`), vectors, or simple
4674   /// (loop-invarianet) scalar expressions. This returns the base entity, the
4675   /// resulting type, and a continuation to adjust the default iteration space.
4676   void genSliceIndices(ComponentPath &cmptData, const ExtValue &arrayExv,
4677                        const Fortran::evaluate::ArrayRef &x, bool atBase) {
4678     mlir::Location loc = getLoc();
4679     mlir::IndexType idxTy = builder.getIndexType();
4680     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
4681     llvm::SmallVector<mlir::Value> &trips = cmptData.trips;
4682     LLVM_DEBUG(llvm::dbgs() << "array: " << arrayExv << '\n');
4683     auto &pc = cmptData.pc;
4684     const bool useTripsForSlice = !explicitSpaceIsActive();
4685     const bool createDestShape = destShape.empty();
4686     bool useSlice = false;
4687     std::size_t shapeIndex = 0;
4688     for (auto sub : llvm::enumerate(x.subscript())) {
4689       const std::size_t subsIndex = sub.index();
4690       std::visit(
4691           Fortran::common::visitors{
4692               [&](const Fortran::evaluate::Triplet &t) {
4693                 mlir::Value lowerBound;
4694                 if (auto optLo = t.lower())
4695                   lowerBound = fir::getBase(asScalar(*optLo));
4696                 else
4697                   lowerBound = getLBound(arrayExv, subsIndex, one);
4698                 lowerBound = builder.createConvert(loc, idxTy, lowerBound);
4699                 mlir::Value stride = fir::getBase(asScalar(t.stride()));
4700                 stride = builder.createConvert(loc, idxTy, stride);
4701                 if (useTripsForSlice || createDestShape) {
4702                   // Generate a slice operation for the triplet. The first and
4703                   // second position of the triplet may be omitted, and the
4704                   // declared lbound and/or ubound expression values,
4705                   // respectively, should be used instead.
4706                   trips.push_back(lowerBound);
4707                   mlir::Value upperBound;
4708                   if (auto optUp = t.upper())
4709                     upperBound = fir::getBase(asScalar(*optUp));
4710                   else
4711                     upperBound = getUBound(arrayExv, subsIndex, one);
4712                   upperBound = builder.createConvert(loc, idxTy, upperBound);
4713                   trips.push_back(upperBound);
4714                   trips.push_back(stride);
4715                   if (createDestShape) {
4716                     auto extent = builder.genExtentFromTriplet(
4717                         loc, lowerBound, upperBound, stride, idxTy);
4718                     destShape.push_back(extent);
4719                   }
4720                   useSlice = true;
4721                 }
4722                 if (!useTripsForSlice) {
4723                   auto currentPC = pc;
4724                   pc = [=](IterSpace iters) {
4725                     IterationSpace newIters = currentPC(iters);
4726                     mlir::Value impliedIter = newIters.iterValue(subsIndex);
4727                     // FIXME: must use the lower bound of this component.
4728                     auto arrLowerBound =
4729                         atBase ? getLBound(arrayExv, subsIndex, one) : one;
4730                     auto initial = builder.create<mlir::arith::SubIOp>(
4731                         loc, lowerBound, arrLowerBound);
4732                     auto prod = builder.create<mlir::arith::MulIOp>(
4733                         loc, impliedIter, stride);
4734                     auto result =
4735                         builder.create<mlir::arith::AddIOp>(loc, initial, prod);
4736                     newIters.setIndexValue(subsIndex, result);
4737                     return newIters;
4738                   };
4739                 }
4740                 shapeIndex++;
4741               },
4742               [&](const Fortran::evaluate::IndirectSubscriptIntegerExpr &ie) {
4743                 const auto &e = ie.value(); // dereference
4744                 if (isArray(e)) {
4745                   // This is a vector subscript. Use the index values as read
4746                   // from a vector to determine the temporary array value.
4747                   // Note: 9.5.3.3.3(3) specifies undefined behavior for
4748                   // multiple updates to any specific array element through a
4749                   // vector subscript with replicated values.
4750                   assert(!isBoxValue() &&
4751                          "fir.box cannot be created with vector subscripts");
4752                   auto arrExpr = ignoreEvConvert(e);
4753                   if (createDestShape) {
4754                     destShape.push_back(fir::getExtentAtDimension(
4755                         arrayExv, builder, loc, subsIndex));
4756                   }
4757                   auto genArrFetch =
4758                       genVectorSubscriptArrayFetch(arrExpr, shapeIndex);
4759                   auto currentPC = pc;
4760                   pc = [=](IterSpace iters) {
4761                     IterationSpace newIters = currentPC(iters);
4762                     auto val = genAccessByVector(loc, genArrFetch, newIters,
4763                                                  subsIndex);
4764                     // Value read from vector subscript array and normalized
4765                     // using the base array's lower bound value.
4766                     mlir::Value lb = fir::factory::readLowerBound(
4767                         builder, loc, arrayExv, subsIndex, one);
4768                     auto origin = builder.create<mlir::arith::SubIOp>(
4769                         loc, idxTy, val, lb);
4770                     newIters.setIndexValue(subsIndex, origin);
4771                     return newIters;
4772                   };
4773                   if (useTripsForSlice) {
4774                     LLVM_ATTRIBUTE_UNUSED auto vectorSubscriptShape =
4775                         getShape(arrayOperands.back());
4776                     auto undef = builder.create<fir::UndefOp>(loc, idxTy);
4777                     trips.push_back(undef);
4778                     trips.push_back(undef);
4779                     trips.push_back(undef);
4780                   }
4781                   shapeIndex++;
4782                 } else {
4783                   // This is a regular scalar subscript.
4784                   if (useTripsForSlice) {
4785                     // A regular scalar index, which does not yield an array
4786                     // section. Use a degenerate slice operation
4787                     // `(e:undef:undef)` in this dimension as a placeholder.
4788                     // This does not necessarily change the rank of the original
4789                     // array, so the iteration space must also be extended to
4790                     // include this expression in this dimension to adjust to
4791                     // the array's declared rank.
4792                     mlir::Value v = fir::getBase(asScalar(e));
4793                     trips.push_back(v);
4794                     auto undef = builder.create<fir::UndefOp>(loc, idxTy);
4795                     trips.push_back(undef);
4796                     trips.push_back(undef);
4797                     auto currentPC = pc;
4798                     // Cast `e` to index type.
4799                     mlir::Value iv = builder.createConvert(loc, idxTy, v);
4800                     // Normalize `e` by subtracting the declared lbound.
4801                     mlir::Value lb = fir::factory::readLowerBound(
4802                         builder, loc, arrayExv, subsIndex, one);
4803                     mlir::Value ivAdj =
4804                         builder.create<mlir::arith::SubIOp>(loc, idxTy, iv, lb);
4805                     // Add lbound adjusted value of `e` to the iteration vector
4806                     // (except when creating a box because the iteration vector
4807                     // is empty).
4808                     if (!isBoxValue())
4809                       pc = [=](IterSpace iters) {
4810                         IterationSpace newIters = currentPC(iters);
4811                         newIters.insertIndexValue(subsIndex, ivAdj);
4812                         return newIters;
4813                       };
4814                   } else {
4815                     auto currentPC = pc;
4816                     mlir::Value newValue = fir::getBase(asScalarArray(e));
4817                     mlir::Value result =
4818                         builder.createConvert(loc, idxTy, newValue);
4819                     mlir::Value lb = fir::factory::readLowerBound(
4820                         builder, loc, arrayExv, subsIndex, one);
4821                     result = builder.create<mlir::arith::SubIOp>(loc, idxTy,
4822                                                                  result, lb);
4823                     pc = [=](IterSpace iters) {
4824                       IterationSpace newIters = currentPC(iters);
4825                       newIters.insertIndexValue(subsIndex, result);
4826                       return newIters;
4827                     };
4828                   }
4829                 }
4830               }},
4831           sub.value().u);
4832     }
4833     if (!useSlice)
4834       trips.clear();
4835   }
4836 
4837   CC genarr(const Fortran::semantics::SymbolRef &sym,
4838             ComponentPath &components) {
4839     return genarr(sym.get(), components);
4840   }
4841 
4842   ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) {
4843     return convertToArrayBoxValue(getLoc(), builder, val, len);
4844   }
4845 
4846   CC genarr(const ExtValue &extMemref) {
4847     ComponentPath dummy(/*isImplicit=*/true);
4848     return genarr(extMemref, dummy);
4849   }
4850 
4851   //===--------------------------------------------------------------------===//
4852   // Array construction
4853   //===--------------------------------------------------------------------===//
4854 
4855   /// Target agnostic computation of the size of an element in the array.
4856   /// Returns the size in bytes with type `index` or a null Value if the element
4857   /// size is not constant.
4858   mlir::Value computeElementSize(const ExtValue &exv, mlir::Type eleTy,
4859                                  mlir::Type resTy) {
4860     mlir::Location loc = getLoc();
4861     mlir::IndexType idxTy = builder.getIndexType();
4862     mlir::Value multiplier = builder.createIntegerConstant(loc, idxTy, 1);
4863     if (fir::hasDynamicSize(eleTy)) {
4864       if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4865         // Array of char with dynamic length parameter. Downcast to an array
4866         // of singleton char, and scale by the len type parameter from
4867         // `exv`.
4868         exv.match(
4869             [&](const fir::CharBoxValue &cb) { multiplier = cb.getLen(); },
4870             [&](const fir::CharArrayBoxValue &cb) { multiplier = cb.getLen(); },
4871             [&](const fir::BoxValue &box) {
4872               multiplier = fir::factory::CharacterExprHelper(builder, loc)
4873                                .readLengthFromBox(box.getAddr());
4874             },
4875             [&](const fir::MutableBoxValue &box) {
4876               multiplier = fir::factory::CharacterExprHelper(builder, loc)
4877                                .readLengthFromBox(box.getAddr());
4878             },
4879             [&](const auto &) {
4880               fir::emitFatalError(loc,
4881                                   "array constructor element has unknown size");
4882             });
4883         fir::CharacterType newEleTy = fir::CharacterType::getSingleton(
4884             eleTy.getContext(), charTy.getFKind());
4885         if (auto seqTy = resTy.dyn_cast<fir::SequenceType>()) {
4886           assert(eleTy == seqTy.getEleTy());
4887           resTy = fir::SequenceType::get(seqTy.getShape(), newEleTy);
4888         }
4889         eleTy = newEleTy;
4890       } else {
4891         TODO(loc, "dynamic sized type");
4892       }
4893     }
4894     mlir::Type eleRefTy = builder.getRefType(eleTy);
4895     mlir::Type resRefTy = builder.getRefType(resTy);
4896     mlir::Value nullPtr = builder.createNullConstant(loc, resRefTy);
4897     auto offset = builder.create<fir::CoordinateOp>(
4898         loc, eleRefTy, nullPtr, mlir::ValueRange{multiplier});
4899     return builder.createConvert(loc, idxTy, offset);
4900   }
4901 
4902   /// Get the function signature of the LLVM memcpy intrinsic.
4903   mlir::FunctionType memcpyType() {
4904     return fir::factory::getLlvmMemcpy(builder).getType();
4905   }
4906 
4907   /// Create a call to the LLVM memcpy intrinsic.
4908   void createCallMemcpy(llvm::ArrayRef<mlir::Value> args) {
4909     mlir::Location loc = getLoc();
4910     mlir::FuncOp memcpyFunc = fir::factory::getLlvmMemcpy(builder);
4911     mlir::SymbolRefAttr funcSymAttr =
4912         builder.getSymbolRefAttr(memcpyFunc.getName());
4913     mlir::FunctionType funcTy = memcpyFunc.getType();
4914     builder.create<fir::CallOp>(loc, funcTy.getResults(), funcSymAttr, args);
4915   }
4916 
4917   // Construct code to check for a buffer overrun and realloc the buffer when
4918   // space is depleted. This is done between each item in the ac-value-list.
4919   mlir::Value growBuffer(mlir::Value mem, mlir::Value needed,
4920                          mlir::Value bufferSize, mlir::Value buffSize,
4921                          mlir::Value eleSz) {
4922     mlir::Location loc = getLoc();
4923     mlir::FuncOp reallocFunc = fir::factory::getRealloc(builder);
4924     auto cond = builder.create<mlir::arith::CmpIOp>(
4925         loc, mlir::arith::CmpIPredicate::sle, bufferSize, needed);
4926     auto ifOp = builder.create<fir::IfOp>(loc, mem.getType(), cond,
4927                                           /*withElseRegion=*/true);
4928     auto insPt = builder.saveInsertionPoint();
4929     builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
4930     // Not enough space, resize the buffer.
4931     mlir::IndexType idxTy = builder.getIndexType();
4932     mlir::Value two = builder.createIntegerConstant(loc, idxTy, 2);
4933     auto newSz = builder.create<mlir::arith::MulIOp>(loc, needed, two);
4934     builder.create<fir::StoreOp>(loc, newSz, buffSize);
4935     mlir::Value byteSz = builder.create<mlir::arith::MulIOp>(loc, newSz, eleSz);
4936     mlir::SymbolRefAttr funcSymAttr =
4937         builder.getSymbolRefAttr(reallocFunc.getName());
4938     mlir::FunctionType funcTy = reallocFunc.getType();
4939     auto newMem = builder.create<fir::CallOp>(
4940         loc, funcTy.getResults(), funcSymAttr,
4941         llvm::ArrayRef<mlir::Value>{
4942             builder.createConvert(loc, funcTy.getInputs()[0], mem),
4943             builder.createConvert(loc, funcTy.getInputs()[1], byteSz)});
4944     mlir::Value castNewMem =
4945         builder.createConvert(loc, mem.getType(), newMem.getResult(0));
4946     builder.create<fir::ResultOp>(loc, castNewMem);
4947     builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
4948     // Otherwise, just forward the buffer.
4949     builder.create<fir::ResultOp>(loc, mem);
4950     builder.restoreInsertionPoint(insPt);
4951     return ifOp.getResult(0);
4952   }
4953 
4954   /// Copy the next value (or vector of values) into the array being
4955   /// constructed.
4956   mlir::Value copyNextArrayCtorSection(const ExtValue &exv, mlir::Value buffPos,
4957                                        mlir::Value buffSize, mlir::Value mem,
4958                                        mlir::Value eleSz, mlir::Type eleTy,
4959                                        mlir::Type eleRefTy, mlir::Type resTy) {
4960     mlir::Location loc = getLoc();
4961     auto off = builder.create<fir::LoadOp>(loc, buffPos);
4962     auto limit = builder.create<fir::LoadOp>(loc, buffSize);
4963     mlir::IndexType idxTy = builder.getIndexType();
4964     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
4965 
4966     if (fir::isRecordWithAllocatableMember(eleTy))
4967       TODO(loc, "deep copy on allocatable members");
4968 
4969     if (!eleSz) {
4970       // Compute the element size at runtime.
4971       assert(fir::hasDynamicSize(eleTy));
4972       if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4973         auto charBytes =
4974             builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8;
4975         mlir::Value bytes =
4976             builder.createIntegerConstant(loc, idxTy, charBytes);
4977         mlir::Value length = fir::getLen(exv);
4978         if (!length)
4979           fir::emitFatalError(loc, "result is not boxed character");
4980         eleSz = builder.create<mlir::arith::MulIOp>(loc, bytes, length);
4981       } else {
4982         TODO(loc, "PDT size");
4983         // Will call the PDT's size function with the type parameters.
4984       }
4985     }
4986 
4987     // Compute the coordinate using `fir.coordinate_of`, or, if the type has
4988     // dynamic size, generating the pointer arithmetic.
4989     auto computeCoordinate = [&](mlir::Value buff, mlir::Value off) {
4990       mlir::Type refTy = eleRefTy;
4991       if (fir::hasDynamicSize(eleTy)) {
4992         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4993           // Scale a simple pointer using dynamic length and offset values.
4994           auto chTy = fir::CharacterType::getSingleton(charTy.getContext(),
4995                                                        charTy.getFKind());
4996           refTy = builder.getRefType(chTy);
4997           mlir::Type toTy = builder.getRefType(builder.getVarLenSeqTy(chTy));
4998           buff = builder.createConvert(loc, toTy, buff);
4999           off = builder.create<mlir::arith::MulIOp>(loc, off, eleSz);
5000         } else {
5001           TODO(loc, "PDT offset");
5002         }
5003       }
5004       auto coor = builder.create<fir::CoordinateOp>(loc, refTy, buff,
5005                                                     mlir::ValueRange{off});
5006       return builder.createConvert(loc, eleRefTy, coor);
5007     };
5008 
5009     // Lambda to lower an abstract array box value.
5010     auto doAbstractArray = [&](const auto &v) {
5011       // Compute the array size.
5012       mlir::Value arrSz = one;
5013       for (auto ext : v.getExtents())
5014         arrSz = builder.create<mlir::arith::MulIOp>(loc, arrSz, ext);
5015 
5016       // Grow the buffer as needed.
5017       auto endOff = builder.create<mlir::arith::AddIOp>(loc, off, arrSz);
5018       mem = growBuffer(mem, endOff, limit, buffSize, eleSz);
5019 
5020       // Copy the elements to the buffer.
5021       mlir::Value byteSz =
5022           builder.create<mlir::arith::MulIOp>(loc, arrSz, eleSz);
5023       auto buff = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
5024       mlir::Value buffi = computeCoordinate(buff, off);
5025       llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
5026           builder, loc, memcpyType(), buffi, v.getAddr(), byteSz,
5027           /*volatile=*/builder.createBool(loc, false));
5028       createCallMemcpy(args);
5029 
5030       // Save the incremented buffer position.
5031       builder.create<fir::StoreOp>(loc, endOff, buffPos);
5032     };
5033 
5034     // Copy a trivial scalar value into the buffer.
5035     auto doTrivialScalar = [&](const ExtValue &v, mlir::Value len = {}) {
5036       // Increment the buffer position.
5037       auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
5038 
5039       // Grow the buffer as needed.
5040       mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
5041 
5042       // Store the element in the buffer.
5043       mlir::Value buff =
5044           builder.createConvert(loc, fir::HeapType::get(resTy), mem);
5045       auto buffi = builder.create<fir::CoordinateOp>(loc, eleRefTy, buff,
5046                                                      mlir::ValueRange{off});
5047       fir::factory::genScalarAssignment(
5048           builder, loc,
5049           [&]() -> ExtValue {
5050             if (len)
5051               return fir::CharBoxValue(buffi, len);
5052             return buffi;
5053           }(),
5054           v);
5055       builder.create<fir::StoreOp>(loc, plusOne, buffPos);
5056     };
5057 
5058     // Copy the value.
5059     exv.match(
5060         [&](mlir::Value) { doTrivialScalar(exv); },
5061         [&](const fir::CharBoxValue &v) {
5062           auto buffer = v.getBuffer();
5063           if (fir::isa_char(buffer.getType())) {
5064             doTrivialScalar(exv, eleSz);
5065           } else {
5066             // Increment the buffer position.
5067             auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
5068 
5069             // Grow the buffer as needed.
5070             mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
5071 
5072             // Store the element in the buffer.
5073             mlir::Value buff =
5074                 builder.createConvert(loc, fir::HeapType::get(resTy), mem);
5075             mlir::Value buffi = computeCoordinate(buff, off);
5076             llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
5077                 builder, loc, memcpyType(), buffi, v.getAddr(), eleSz,
5078                 /*volatile=*/builder.createBool(loc, false));
5079             createCallMemcpy(args);
5080 
5081             builder.create<fir::StoreOp>(loc, plusOne, buffPos);
5082           }
5083         },
5084         [&](const fir::ArrayBoxValue &v) { doAbstractArray(v); },
5085         [&](const fir::CharArrayBoxValue &v) { doAbstractArray(v); },
5086         [&](const auto &) {
5087           TODO(loc, "unhandled array constructor expression");
5088         });
5089     return mem;
5090   }
5091 
5092   // Lower the expr cases in an ac-value-list.
5093   template <typename A>
5094   std::pair<ExtValue, bool>
5095   genArrayCtorInitializer(const Fortran::evaluate::Expr<A> &x, mlir::Type,
5096                           mlir::Value, mlir::Value, mlir::Value,
5097                           Fortran::lower::StatementContext &stmtCtx) {
5098     if (isArray(x))
5099       return {lowerNewArrayExpression(converter, symMap, stmtCtx, toEvExpr(x)),
5100               /*needCopy=*/true};
5101     return {asScalar(x), /*needCopy=*/true};
5102   }
5103 
5104   // Lower an ac-implied-do in an ac-value-list.
5105   template <typename A>
5106   std::pair<ExtValue, bool>
5107   genArrayCtorInitializer(const Fortran::evaluate::ImpliedDo<A> &x,
5108                           mlir::Type resTy, mlir::Value mem,
5109                           mlir::Value buffPos, mlir::Value buffSize,
5110                           Fortran::lower::StatementContext &) {
5111     mlir::Location loc = getLoc();
5112     mlir::IndexType idxTy = builder.getIndexType();
5113     mlir::Value lo =
5114         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.lower())));
5115     mlir::Value up =
5116         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.upper())));
5117     mlir::Value step =
5118         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.stride())));
5119     auto seqTy = resTy.template cast<fir::SequenceType>();
5120     mlir::Type eleTy = fir::unwrapSequenceType(seqTy);
5121     auto loop =
5122         builder.create<fir::DoLoopOp>(loc, lo, up, step, /*unordered=*/false,
5123                                       /*finalCount=*/false, mem);
5124     // create a new binding for x.name(), to ac-do-variable, to the iteration
5125     // value.
5126     symMap.pushImpliedDoBinding(toStringRef(x.name()), loop.getInductionVar());
5127     auto insPt = builder.saveInsertionPoint();
5128     builder.setInsertionPointToStart(loop.getBody());
5129     // Thread mem inside the loop via loop argument.
5130     mem = loop.getRegionIterArgs()[0];
5131 
5132     mlir::Type eleRefTy = builder.getRefType(eleTy);
5133 
5134     // Any temps created in the loop body must be freed inside the loop body.
5135     stmtCtx.pushScope();
5136     llvm::Optional<mlir::Value> charLen;
5137     for (const Fortran::evaluate::ArrayConstructorValue<A> &acv : x.values()) {
5138       auto [exv, copyNeeded] = std::visit(
5139           [&](const auto &v) {
5140             return genArrayCtorInitializer(v, resTy, mem, buffPos, buffSize,
5141                                            stmtCtx);
5142           },
5143           acv.u);
5144       mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
5145       mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
5146                                                   eleSz, eleTy, eleRefTy, resTy)
5147                        : fir::getBase(exv);
5148       if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) {
5149         charLen = builder.createTemporary(loc, builder.getI64Type());
5150         mlir::Value castLen =
5151             builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
5152         builder.create<fir::StoreOp>(loc, castLen, charLen.getValue());
5153       }
5154     }
5155     stmtCtx.finalize(/*popScope=*/true);
5156 
5157     builder.create<fir::ResultOp>(loc, mem);
5158     builder.restoreInsertionPoint(insPt);
5159     mem = loop.getResult(0);
5160     symMap.popImpliedDoBinding();
5161     llvm::SmallVector<mlir::Value> extents = {
5162         builder.create<fir::LoadOp>(loc, buffPos).getResult()};
5163 
5164     // Convert to extended value.
5165     if (fir::isa_char(seqTy.getEleTy())) {
5166       auto len = builder.create<fir::LoadOp>(loc, charLen.getValue());
5167       return {fir::CharArrayBoxValue{mem, len, extents}, /*needCopy=*/false};
5168     }
5169     return {fir::ArrayBoxValue{mem, extents}, /*needCopy=*/false};
5170   }
5171 
5172   // To simplify the handling and interaction between the various cases, array
5173   // constructors are always lowered to the incremental construction code
5174   // pattern, even if the extent of the array value is constant. After the
5175   // MemToReg pass and constant folding, the optimizer should be able to
5176   // determine that all the buffer overrun tests are false when the
5177   // incremental construction wasn't actually required.
5178   template <typename A>
5179   CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) {
5180     mlir::Location loc = getLoc();
5181     auto evExpr = toEvExpr(x);
5182     mlir::Type resTy = translateSomeExprToFIRType(converter, evExpr);
5183     mlir::IndexType idxTy = builder.getIndexType();
5184     auto seqTy = resTy.template cast<fir::SequenceType>();
5185     mlir::Type eleTy = fir::unwrapSequenceType(resTy);
5186     mlir::Value buffSize = builder.createTemporary(loc, idxTy, ".buff.size");
5187     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
5188     mlir::Value buffPos = builder.createTemporary(loc, idxTy, ".buff.pos");
5189     builder.create<fir::StoreOp>(loc, zero, buffPos);
5190     // Allocate space for the array to be constructed.
5191     mlir::Value mem;
5192     if (fir::hasDynamicSize(resTy)) {
5193       if (fir::hasDynamicSize(eleTy)) {
5194         // The size of each element may depend on a general expression. Defer
5195         // creating the buffer until after the expression is evaluated.
5196         mem = builder.createNullConstant(loc, builder.getRefType(eleTy));
5197         builder.create<fir::StoreOp>(loc, zero, buffSize);
5198       } else {
5199         mlir::Value initBuffSz =
5200             builder.createIntegerConstant(loc, idxTy, clInitialBufferSize);
5201         mem = builder.create<fir::AllocMemOp>(
5202             loc, eleTy, /*typeparams=*/llvm::None, initBuffSz);
5203         builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
5204       }
5205     } else {
5206       mem = builder.create<fir::AllocMemOp>(loc, resTy);
5207       int64_t buffSz = 1;
5208       for (auto extent : seqTy.getShape())
5209         buffSz *= extent;
5210       mlir::Value initBuffSz =
5211           builder.createIntegerConstant(loc, idxTy, buffSz);
5212       builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
5213     }
5214     // Compute size of element
5215     mlir::Type eleRefTy = builder.getRefType(eleTy);
5216 
5217     // Populate the buffer with the elements, growing as necessary.
5218     llvm::Optional<mlir::Value> charLen;
5219     for (const auto &expr : x) {
5220       auto [exv, copyNeeded] = std::visit(
5221           [&](const auto &e) {
5222             return genArrayCtorInitializer(e, resTy, mem, buffPos, buffSize,
5223                                            stmtCtx);
5224           },
5225           expr.u);
5226       mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
5227       mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
5228                                                   eleSz, eleTy, eleRefTy, resTy)
5229                        : fir::getBase(exv);
5230       if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) {
5231         charLen = builder.createTemporary(loc, builder.getI64Type());
5232         mlir::Value castLen =
5233             builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
5234         builder.create<fir::StoreOp>(loc, castLen, charLen.getValue());
5235       }
5236     }
5237     mem = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
5238     llvm::SmallVector<mlir::Value> extents = {
5239         builder.create<fir::LoadOp>(loc, buffPos)};
5240 
5241     // Cleanup the temporary.
5242     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
5243     stmtCtx.attachCleanup(
5244         [bldr, loc, mem]() { bldr->create<fir::FreeMemOp>(loc, mem); });
5245 
5246     // Return the continuation.
5247     if (fir::isa_char(seqTy.getEleTy())) {
5248       if (charLen.hasValue()) {
5249         auto len = builder.create<fir::LoadOp>(loc, charLen.getValue());
5250         return genarr(fir::CharArrayBoxValue{mem, len, extents});
5251       }
5252       return genarr(fir::CharArrayBoxValue{mem, zero, extents});
5253     }
5254     return genarr(fir::ArrayBoxValue{mem, extents});
5255   }
5256 
5257   CC genarr(const Fortran::evaluate::ImpliedDoIndex &) {
5258     TODO(getLoc(), "genarr ImpliedDoIndex");
5259   }
5260 
5261   CC genarr(const Fortran::evaluate::TypeParamInquiry &x) {
5262     TODO(getLoc(), "genarr TypeParamInquiry");
5263   }
5264 
5265   CC genarr(const Fortran::evaluate::DescriptorInquiry &x) {
5266     TODO(getLoc(), "genarr DescriptorInquiry");
5267   }
5268 
5269   CC genarr(const Fortran::evaluate::StructureConstructor &x) {
5270     TODO(getLoc(), "genarr StructureConstructor");
5271   }
5272 
5273   //===--------------------------------------------------------------------===//
5274   // LOCICAL operators (.NOT., .AND., .EQV., etc.)
5275   //===--------------------------------------------------------------------===//
5276 
5277   template <int KIND>
5278   CC genarr(const Fortran::evaluate::Not<KIND> &x) {
5279     mlir::Location loc = getLoc();
5280     mlir::IntegerType i1Ty = builder.getI1Type();
5281     auto lambda = genarr(x.left());
5282     mlir::Value truth = builder.createBool(loc, true);
5283     return [=](IterSpace iters) -> ExtValue {
5284       mlir::Value logical = fir::getBase(lambda(iters));
5285       mlir::Value val = builder.createConvert(loc, i1Ty, logical);
5286       return builder.create<mlir::arith::XOrIOp>(loc, val, truth);
5287     };
5288   }
5289   template <typename OP, typename A>
5290   CC createBinaryBoolOp(const A &x) {
5291     mlir::Location loc = getLoc();
5292     mlir::IntegerType i1Ty = builder.getI1Type();
5293     auto lf = genarr(x.left());
5294     auto rf = genarr(x.right());
5295     return [=](IterSpace iters) -> ExtValue {
5296       mlir::Value left = fir::getBase(lf(iters));
5297       mlir::Value right = fir::getBase(rf(iters));
5298       mlir::Value lhs = builder.createConvert(loc, i1Ty, left);
5299       mlir::Value rhs = builder.createConvert(loc, i1Ty, right);
5300       return builder.create<OP>(loc, lhs, rhs);
5301     };
5302   }
5303   template <typename OP, typename A>
5304   CC createCompareBoolOp(mlir::arith::CmpIPredicate pred, const A &x) {
5305     mlir::Location loc = getLoc();
5306     mlir::IntegerType i1Ty = builder.getI1Type();
5307     auto lf = genarr(x.left());
5308     auto rf = genarr(x.right());
5309     return [=](IterSpace iters) -> ExtValue {
5310       mlir::Value left = fir::getBase(lf(iters));
5311       mlir::Value right = fir::getBase(rf(iters));
5312       mlir::Value lhs = builder.createConvert(loc, i1Ty, left);
5313       mlir::Value rhs = builder.createConvert(loc, i1Ty, right);
5314       return builder.create<OP>(loc, pred, lhs, rhs);
5315     };
5316   }
5317   template <int KIND>
5318   CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) {
5319     switch (x.logicalOperator) {
5320     case Fortran::evaluate::LogicalOperator::And:
5321       return createBinaryBoolOp<mlir::arith::AndIOp>(x);
5322     case Fortran::evaluate::LogicalOperator::Or:
5323       return createBinaryBoolOp<mlir::arith::OrIOp>(x);
5324     case Fortran::evaluate::LogicalOperator::Eqv:
5325       return createCompareBoolOp<mlir::arith::CmpIOp>(
5326           mlir::arith::CmpIPredicate::eq, x);
5327     case Fortran::evaluate::LogicalOperator::Neqv:
5328       return createCompareBoolOp<mlir::arith::CmpIOp>(
5329           mlir::arith::CmpIPredicate::ne, x);
5330     case Fortran::evaluate::LogicalOperator::Not:
5331       llvm_unreachable(".NOT. handled elsewhere");
5332     }
5333     llvm_unreachable("unhandled case");
5334   }
5335 
5336   //===--------------------------------------------------------------------===//
5337   // Relational operators (<, <=, ==, etc.)
5338   //===--------------------------------------------------------------------===//
5339 
5340   template <typename OP, typename PRED, typename A>
5341   CC createCompareOp(PRED pred, const A &x) {
5342     mlir::Location loc = getLoc();
5343     auto lf = genarr(x.left());
5344     auto rf = genarr(x.right());
5345     return [=](IterSpace iters) -> ExtValue {
5346       mlir::Value lhs = fir::getBase(lf(iters));
5347       mlir::Value rhs = fir::getBase(rf(iters));
5348       return builder.create<OP>(loc, pred, lhs, rhs);
5349     };
5350   }
5351   template <typename A>
5352   CC createCompareCharOp(mlir::arith::CmpIPredicate pred, const A &x) {
5353     mlir::Location loc = getLoc();
5354     auto lf = genarr(x.left());
5355     auto rf = genarr(x.right());
5356     return [=](IterSpace iters) -> ExtValue {
5357       auto lhs = lf(iters);
5358       auto rhs = rf(iters);
5359       return fir::runtime::genCharCompare(builder, loc, pred, lhs, rhs);
5360     };
5361   }
5362   template <int KIND>
5363   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
5364                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
5365     return createCompareOp<mlir::arith::CmpIOp>(translateRelational(x.opr), x);
5366   }
5367   template <int KIND>
5368   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
5369                 Fortran::common::TypeCategory::Character, KIND>> &x) {
5370     return createCompareCharOp(translateRelational(x.opr), x);
5371   }
5372   template <int KIND>
5373   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
5374                 Fortran::common::TypeCategory::Real, KIND>> &x) {
5375     return createCompareOp<mlir::arith::CmpFOp>(translateFloatRelational(x.opr),
5376                                                 x);
5377   }
5378   template <int KIND>
5379   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
5380                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
5381     return createCompareOp<fir::CmpcOp>(translateFloatRelational(x.opr), x);
5382   }
5383   CC genarr(
5384       const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) {
5385     return std::visit([&](const auto &x) { return genarr(x); }, r.u);
5386   }
5387 
5388   template <typename A>
5389   CC genarr(const Fortran::evaluate::Designator<A> &des) {
5390     ComponentPath components(des.Rank() > 0);
5391     return std::visit([&](const auto &x) { return genarr(x, components); },
5392                       des.u);
5393   }
5394 
5395   template <typename T>
5396   CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) {
5397     // Note that it's possible that the function being called returns either an
5398     // array or a scalar.  In the first case, use the element type of the array.
5399     return genProcRef(
5400         funRef, fir::unwrapSequenceType(converter.genType(toEvExpr(funRef))));
5401   }
5402 
5403   //===-------------------------------------------------------------------===//
5404   // Array data references in an explicit iteration space.
5405   //
5406   // Use the base array that was loaded before the loop nest.
5407   //===-------------------------------------------------------------------===//
5408 
5409   /// Lower the path (`revPath`, in reverse) to be appended to an array_fetch or
5410   /// array_update op. \p ty is the initial type of the array
5411   /// (reference). Returns the type of the element after application of the
5412   /// path in \p components.
5413   ///
5414   /// TODO: This needs to deal with array's with initial bounds other than 1.
5415   /// TODO: Thread type parameters correctly.
5416   mlir::Type lowerPath(const ExtValue &arrayExv, ComponentPath &components) {
5417     mlir::Location loc = getLoc();
5418     mlir::Type ty = fir::getBase(arrayExv).getType();
5419     auto &revPath = components.reversePath;
5420     ty = fir::unwrapPassByRefType(ty);
5421     bool prefix = true;
5422     auto addComponent = [&](mlir::Value v) {
5423       if (prefix)
5424         components.prefixComponents.push_back(v);
5425       else
5426         components.suffixComponents.push_back(v);
5427     };
5428     mlir::IndexType idxTy = builder.getIndexType();
5429     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
5430     bool atBase = true;
5431     auto saveSemant = semant;
5432     if (isProjectedCopyInCopyOut())
5433       semant = ConstituentSemantics::RefTransparent;
5434     for (const auto &v : llvm::reverse(revPath)) {
5435       std::visit(
5436           Fortran::common::visitors{
5437               [&](const ImplicitSubscripts &) {
5438                 prefix = false;
5439                 ty = fir::unwrapSequenceType(ty);
5440               },
5441               [&](const Fortran::evaluate::ComplexPart *x) {
5442                 assert(!prefix && "complex part must be at end");
5443                 mlir::Value offset = builder.createIntegerConstant(
5444                     loc, builder.getI32Type(),
5445                     x->part() == Fortran::evaluate::ComplexPart::Part::RE ? 0
5446                                                                           : 1);
5447                 components.suffixComponents.push_back(offset);
5448                 ty = fir::applyPathToType(ty, mlir::ValueRange{offset});
5449               },
5450               [&](const Fortran::evaluate::ArrayRef *x) {
5451                 if (Fortran::lower::isRankedArrayAccess(*x)) {
5452                   genSliceIndices(components, arrayExv, *x, atBase);
5453                 } else {
5454                   // Array access where the expressions are scalar and cannot
5455                   // depend upon the implied iteration space.
5456                   unsigned ssIndex = 0u;
5457                   for (const auto &ss : x->subscript()) {
5458                     std::visit(
5459                         Fortran::common::visitors{
5460                             [&](const Fortran::evaluate::
5461                                     IndirectSubscriptIntegerExpr &ie) {
5462                               const auto &e = ie.value();
5463                               if (isArray(e))
5464                                 fir::emitFatalError(
5465                                     loc,
5466                                     "multiple components along single path "
5467                                     "generating array subexpressions");
5468                               // Lower scalar index expression, append it to
5469                               // subs.
5470                               mlir::Value subscriptVal =
5471                                   fir::getBase(asScalarArray(e));
5472                               // arrayExv is the base array. It needs to reflect
5473                               // the current array component instead.
5474                               // FIXME: must use lower bound of this component,
5475                               // not just the constant 1.
5476                               mlir::Value lb =
5477                                   atBase ? fir::factory::readLowerBound(
5478                                                builder, loc, arrayExv, ssIndex,
5479                                                one)
5480                                          : one;
5481                               mlir::Value val = builder.createConvert(
5482                                   loc, idxTy, subscriptVal);
5483                               mlir::Value ivAdj =
5484                                   builder.create<mlir::arith::SubIOp>(
5485                                       loc, idxTy, val, lb);
5486                               addComponent(
5487                                   builder.createConvert(loc, idxTy, ivAdj));
5488                             },
5489                             [&](const auto &) {
5490                               fir::emitFatalError(
5491                                   loc, "multiple components along single path "
5492                                        "generating array subexpressions");
5493                             }},
5494                         ss.u);
5495                     ssIndex++;
5496                   }
5497                 }
5498                 ty = fir::unwrapSequenceType(ty);
5499               },
5500               [&](const Fortran::evaluate::Component *x) {
5501                 auto fieldTy = fir::FieldType::get(builder.getContext());
5502                 llvm::StringRef name = toStringRef(getLastSym(*x).name());
5503                 auto recTy = ty.cast<fir::RecordType>();
5504                 ty = recTy.getType(name);
5505                 auto fld = builder.create<fir::FieldIndexOp>(
5506                     loc, fieldTy, name, recTy, fir::getTypeParams(arrayExv));
5507                 addComponent(fld);
5508               }},
5509           v);
5510       atBase = false;
5511     }
5512     semant = saveSemant;
5513     ty = fir::unwrapSequenceType(ty);
5514     components.applied = true;
5515     return ty;
5516   }
5517 
5518   llvm::SmallVector<mlir::Value> genSubstringBounds(ComponentPath &components) {
5519     llvm::SmallVector<mlir::Value> result;
5520     if (components.substring)
5521       populateBounds(result, components.substring);
5522     return result;
5523   }
5524 
5525   CC applyPathToArrayLoad(fir::ArrayLoadOp load, ComponentPath &components) {
5526     mlir::Location loc = getLoc();
5527     auto revPath = components.reversePath;
5528     fir::ExtendedValue arrayExv =
5529         arrayLoadExtValue(builder, loc, load, {}, load);
5530     mlir::Type eleTy = lowerPath(arrayExv, components);
5531     auto currentPC = components.pc;
5532     auto pc = [=, prefix = components.prefixComponents,
5533                suffix = components.suffixComponents](IterSpace iters) {
5534       IterationSpace newIters = currentPC(iters);
5535       // Add path prefix and suffix.
5536       IterationSpace addIters(newIters, prefix, suffix);
5537       return addIters;
5538     };
5539     components.pc = [=](IterSpace iters) { return iters; };
5540     llvm::SmallVector<mlir::Value> substringBounds =
5541         genSubstringBounds(components);
5542     if (isProjectedCopyInCopyOut()) {
5543       destination = load;
5544       auto lambda = [=, esp = this->explicitSpace](IterSpace iters) mutable {
5545         mlir::Value innerArg = esp->findArgumentOfLoad(load);
5546         if (isAdjustedArrayElementType(eleTy)) {
5547           mlir::Type eleRefTy = builder.getRefType(eleTy);
5548           auto arrayOp = builder.create<fir::ArrayAccessOp>(
5549               loc, eleRefTy, innerArg, iters.iterVec(), load.getTypeparams());
5550           if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
5551             mlir::Value dstLen = fir::factory::genLenOfCharacter(
5552                 builder, loc, load, iters.iterVec(), substringBounds);
5553             fir::ArrayAmendOp amend = createCharArrayAmend(
5554                 loc, builder, arrayOp, dstLen, iters.elementExv(), innerArg,
5555                 substringBounds);
5556             return arrayLoadExtValue(builder, loc, load, iters.iterVec(), amend,
5557                                      dstLen);
5558           } else if (fir::isa_derived(eleTy)) {
5559             fir::ArrayAmendOp amend =
5560                 createDerivedArrayAmend(loc, load, builder, arrayOp,
5561                                         iters.elementExv(), eleTy, innerArg);
5562             return arrayLoadExtValue(builder, loc, load, iters.iterVec(),
5563                                      amend);
5564           }
5565           assert(eleTy.isa<fir::SequenceType>());
5566           TODO(loc, "array (as element) assignment");
5567         }
5568         mlir::Value castedElement =
5569             builder.createConvert(loc, eleTy, iters.getElement());
5570         auto update = builder.create<fir::ArrayUpdateOp>(
5571             loc, innerArg.getType(), innerArg, castedElement, iters.iterVec(),
5572             load.getTypeparams());
5573         return arrayLoadExtValue(builder, loc, load, iters.iterVec(), update);
5574       };
5575       return [=](IterSpace iters) mutable { return lambda(pc(iters)); };
5576     }
5577     if (isCustomCopyInCopyOut()) {
5578       // Create an array_modify to get the LHS element address and indicate
5579       // the assignment, and create the call to the user defined assignment.
5580       destination = load;
5581       auto lambda = [=](IterSpace iters) mutable {
5582         mlir::Value innerArg = explicitSpace->findArgumentOfLoad(load);
5583         mlir::Type refEleTy =
5584             fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy);
5585         auto arrModify = builder.create<fir::ArrayModifyOp>(
5586             loc, mlir::TypeRange{refEleTy, innerArg.getType()}, innerArg,
5587             iters.iterVec(), load.getTypeparams());
5588         return arrayLoadExtValue(builder, loc, load, iters.iterVec(),
5589                                  arrModify.getResult(1));
5590       };
5591       return [=](IterSpace iters) mutable { return lambda(pc(iters)); };
5592     }
5593     auto lambda = [=, semant = this->semant](IterSpace iters) mutable {
5594       if (semant == ConstituentSemantics::RefOpaque ||
5595           isAdjustedArrayElementType(eleTy)) {
5596         mlir::Type resTy = builder.getRefType(eleTy);
5597         // Use array element reference semantics.
5598         auto access = builder.create<fir::ArrayAccessOp>(
5599             loc, resTy, load, iters.iterVec(), load.getTypeparams());
5600         mlir::Value newBase = access;
5601         if (fir::isa_char(eleTy)) {
5602           mlir::Value dstLen = fir::factory::genLenOfCharacter(
5603               builder, loc, load, iters.iterVec(), substringBounds);
5604           if (!substringBounds.empty()) {
5605             fir::CharBoxValue charDst{access, dstLen};
5606             fir::factory::CharacterExprHelper helper{builder, loc};
5607             charDst = helper.createSubstring(charDst, substringBounds);
5608             newBase = charDst.getAddr();
5609           }
5610           return arrayLoadExtValue(builder, loc, load, iters.iterVec(), newBase,
5611                                    dstLen);
5612         }
5613         return arrayLoadExtValue(builder, loc, load, iters.iterVec(), newBase);
5614       }
5615       auto fetch = builder.create<fir::ArrayFetchOp>(
5616           loc, eleTy, load, iters.iterVec(), load.getTypeparams());
5617       return arrayLoadExtValue(builder, loc, load, iters.iterVec(), fetch);
5618     };
5619     return [=](IterSpace iters) mutable {
5620       auto newIters = pc(iters);
5621       return lambda(newIters);
5622     };
5623   }
5624 
5625   template <typename A>
5626   CC genImplicitArrayAccess(const A &x, ComponentPath &components) {
5627     components.reversePath.push_back(ImplicitSubscripts{});
5628     ExtValue exv = asScalarRef(x);
5629     lowerPath(exv, components);
5630     auto lambda = genarr(exv, components);
5631     return [=](IterSpace iters) { return lambda(components.pc(iters)); };
5632   }
5633   CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x,
5634                             ComponentPath &components) {
5635     if (x.IsSymbol())
5636       return genImplicitArrayAccess(x.GetFirstSymbol(), components);
5637     return genImplicitArrayAccess(x.GetComponent(), components);
5638   }
5639 
5640   template <typename A>
5641   CC genAsScalar(const A &x) {
5642     mlir::Location loc = getLoc();
5643     if (isProjectedCopyInCopyOut()) {
5644       return [=, &x, builder = &converter.getFirOpBuilder()](
5645                  IterSpace iters) -> ExtValue {
5646         ExtValue exv = asScalarRef(x);
5647         mlir::Value val = fir::getBase(exv);
5648         mlir::Type eleTy = fir::unwrapRefType(val.getType());
5649         if (isAdjustedArrayElementType(eleTy)) {
5650           if (fir::isa_char(eleTy)) {
5651             TODO(getLoc(), "assignment of character type");
5652           } else if (fir::isa_derived(eleTy)) {
5653             TODO(loc, "assignment of derived type");
5654           } else {
5655             fir::emitFatalError(loc, "array type not expected in scalar");
5656           }
5657         } else {
5658           builder->create<fir::StoreOp>(loc, iters.getElement(), val);
5659         }
5660         return exv;
5661       };
5662     }
5663     return [=, &x](IterSpace) { return asScalar(x); };
5664   }
5665 
5666   CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) {
5667     if (explicitSpaceIsActive()) {
5668       if (x.Rank() > 0)
5669         components.reversePath.push_back(ImplicitSubscripts{});
5670       if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x))
5671         return applyPathToArrayLoad(load, components);
5672     } else {
5673       return genImplicitArrayAccess(x, components);
5674     }
5675     if (pathIsEmpty(components))
5676       return genAsScalar(x);
5677     mlir::Location loc = getLoc();
5678     return [=](IterSpace) -> ExtValue {
5679       fir::emitFatalError(loc, "reached symbol with path");
5680     };
5681   }
5682 
5683   CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) {
5684     TODO(getLoc(), "genarr Component");
5685   }
5686 
5687   /// Array reference with subscripts. If this has rank > 0, this is a form
5688   /// of an array section (slice).
5689   ///
5690   /// There are two "slicing" primitives that may be applied on a dimension by
5691   /// dimension basis: (1) triple notation and (2) vector addressing. Since
5692   /// dimensions can be selectively sliced, some dimensions may contain
5693   /// regular scalar expressions and those dimensions do not participate in
5694   /// the array expression evaluation.
5695   CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) {
5696     if (explicitSpaceIsActive()) {
5697       if (Fortran::lower::isRankedArrayAccess(x))
5698         components.reversePath.push_back(ImplicitSubscripts{});
5699       if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x)) {
5700         components.reversePath.push_back(&x);
5701         return applyPathToArrayLoad(load, components);
5702       }
5703     } else {
5704       if (Fortran::lower::isRankedArrayAccess(x)) {
5705         components.reversePath.push_back(&x);
5706         return genImplicitArrayAccess(x.base(), components);
5707       }
5708     }
5709     bool atEnd = pathIsEmpty(components);
5710     components.reversePath.push_back(&x);
5711     auto result = genarr(x.base(), components);
5712     if (components.applied)
5713       return result;
5714     mlir::Location loc = getLoc();
5715     if (atEnd) {
5716       if (x.Rank() == 0)
5717         return genAsScalar(x);
5718       fir::emitFatalError(loc, "expected scalar");
5719     }
5720     return [=](IterSpace) -> ExtValue {
5721       fir::emitFatalError(loc, "reached arrayref with path");
5722     };
5723   }
5724 
5725   CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) {
5726     TODO(getLoc(), "coarray reference");
5727   }
5728 
5729   CC genarr(const Fortran::evaluate::NamedEntity &x,
5730             ComponentPath &components) {
5731     return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components)
5732                         : genarr(x.GetComponent(), components);
5733   }
5734 
5735   CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) {
5736     return std::visit([&](const auto &v) { return genarr(v, components); },
5737                       x.u);
5738   }
5739 
5740   bool pathIsEmpty(const ComponentPath &components) {
5741     return components.reversePath.empty();
5742   }
5743 
5744   /// Given an optional fir.box, returns an fir.box that is the original one if
5745   /// it is present and it otherwise an unallocated box.
5746   /// Absent fir.box are implemented as a null pointer descriptor. Generated
5747   /// code may need to unconditionally read a fir.box that can be absent.
5748   /// This helper allows creating a fir.box that can be read in all cases
5749   /// outside of a fir.if (isPresent) region. However, the usages of the value
5750   /// read from such box should still only be done in a fir.if(isPresent).
5751   static fir::ExtendedValue
5752   absentBoxToUnalllocatedBox(fir::FirOpBuilder &builder, mlir::Location loc,
5753                              const fir::ExtendedValue &exv,
5754                              mlir::Value isPresent) {
5755     mlir::Value box = fir::getBase(exv);
5756     mlir::Type boxType = box.getType();
5757     assert(boxType.isa<fir::BoxType>() && "argument must be a fir.box");
5758     mlir::Value emptyBox =
5759         fir::factory::createUnallocatedBox(builder, loc, boxType, llvm::None);
5760     auto safeToReadBox =
5761         builder.create<mlir::arith::SelectOp>(loc, isPresent, box, emptyBox);
5762     return fir::substBase(exv, safeToReadBox);
5763   }
5764 
5765   std::tuple<CC, mlir::Value, mlir::Type>
5766   genOptionalArrayFetch(const Fortran::lower::SomeExpr &expr) {
5767     assert(expr.Rank() > 0 && "expr must be an array");
5768     mlir::Location loc = getLoc();
5769     ExtValue optionalArg = asInquired(expr);
5770     mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
5771     // Generate an array load and access to an array that may be an absent
5772     // optional or an unallocated optional.
5773     mlir::Value base = getBase(optionalArg);
5774     const bool hasOptionalAttr =
5775         fir::valueHasFirAttribute(base, fir::getOptionalAttrName());
5776     mlir::Type baseType = fir::unwrapRefType(base.getType());
5777     const bool isBox = baseType.isa<fir::BoxType>();
5778     const bool isAllocOrPtr = Fortran::evaluate::IsAllocatableOrPointerObject(
5779         expr, converter.getFoldingContext());
5780     mlir::Type arrType = fir::unwrapPassByRefType(baseType);
5781     mlir::Type eleType = fir::unwrapSequenceType(arrType);
5782     ExtValue exv = optionalArg;
5783     if (hasOptionalAttr && isBox && !isAllocOrPtr) {
5784       // Elemental argument cannot be allocatable or pointers (C15100).
5785       // Hence, per 15.5.2.12 3 (8) and (9), the provided Allocatable and
5786       // Pointer optional arrays cannot be absent. The only kind of entities
5787       // that can get here are optional assumed shape and polymorphic entities.
5788       exv = absentBoxToUnalllocatedBox(builder, loc, exv, isPresent);
5789     }
5790     // All the properties can be read from any fir.box but the read values may
5791     // be undefined and should only be used inside a fir.if (canBeRead) region.
5792     if (const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>())
5793       exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox);
5794 
5795     mlir::Value memref = fir::getBase(exv);
5796     mlir::Value shape = builder.createShape(loc, exv);
5797     mlir::Value noSlice;
5798     auto arrLoad = builder.create<fir::ArrayLoadOp>(
5799         loc, arrType, memref, shape, noSlice, fir::getTypeParams(exv));
5800     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
5801     mlir::Value arrLd = arrLoad.getResult();
5802     // Mark the load to tell later passes it is unsafe to use this array_load
5803     // shape unconditionally.
5804     arrLoad->setAttr(fir::getOptionalAttrName(), builder.getUnitAttr());
5805 
5806     // Place the array as optional on the arrayOperands stack so that its
5807     // shape will only be used as a fallback to induce the implicit loop nest
5808     // (that is if there is no non optional array arguments).
5809     arrayOperands.push_back(
5810         ArrayOperand{memref, shape, noSlice, /*mayBeAbsent=*/true});
5811 
5812     // By value semantics.
5813     auto cc = [=](IterSpace iters) -> ExtValue {
5814       auto arrFetch = builder.create<fir::ArrayFetchOp>(
5815           loc, eleType, arrLd, iters.iterVec(), arrLdTypeParams);
5816       return fir::factory::arraySectionElementToExtendedValue(
5817           builder, loc, exv, arrFetch, noSlice);
5818     };
5819     return {cc, isPresent, eleType};
5820   }
5821 
5822   /// Generate a continuation to pass \p expr to an OPTIONAL argument of an
5823   /// elemental procedure. This is meant to handle the cases where \p expr might
5824   /// be dynamically absent (i.e. when it is a POINTER, an ALLOCATABLE or an
5825   /// OPTIONAL variable). If p\ expr is guaranteed to be present genarr() can
5826   /// directly be called instead.
5827   CC genarrForwardOptionalArgumentToCall(const Fortran::lower::SomeExpr &expr) {
5828     mlir::Location loc = getLoc();
5829     // Only by-value numerical and logical so far.
5830     if (semant != ConstituentSemantics::RefTransparent)
5831       TODO(loc, "optional arguments in user defined elemental procedures");
5832 
5833     // Handle scalar argument case (the if-then-else is generated outside of the
5834     // implicit loop nest).
5835     if (expr.Rank() == 0) {
5836       ExtValue optionalArg = asInquired(expr);
5837       mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
5838       mlir::Value elementValue =
5839           fir::getBase(genOptionalValue(builder, loc, optionalArg, isPresent));
5840       return [=](IterSpace iters) -> ExtValue { return elementValue; };
5841     }
5842 
5843     CC cc;
5844     mlir::Value isPresent;
5845     mlir::Type eleType;
5846     std::tie(cc, isPresent, eleType) = genOptionalArrayFetch(expr);
5847     return [=](IterSpace iters) -> ExtValue {
5848       mlir::Value elementValue =
5849           builder
5850               .genIfOp(loc, {eleType}, isPresent,
5851                        /*withElseRegion=*/true)
5852               .genThen([&]() {
5853                 builder.create<fir::ResultOp>(loc, fir::getBase(cc(iters)));
5854               })
5855               .genElse([&]() {
5856                 mlir::Value zero =
5857                     fir::factory::createZeroValue(builder, loc, eleType);
5858                 builder.create<fir::ResultOp>(loc, zero);
5859               })
5860               .getResults()[0];
5861       return elementValue;
5862     };
5863   }
5864 
5865   /// Reduce the rank of a array to be boxed based on the slice's operands.
5866   static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) {
5867     if (slice) {
5868       auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp());
5869       assert(slOp && "expected slice op");
5870       auto seqTy = arrTy.dyn_cast<fir::SequenceType>();
5871       assert(seqTy && "expected array type");
5872       mlir::Operation::operand_range triples = slOp.getTriples();
5873       fir::SequenceType::Shape shape;
5874       // reduce the rank for each invariant dimension
5875       for (unsigned i = 1, end = triples.size(); i < end; i += 3)
5876         if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp()))
5877           shape.push_back(fir::SequenceType::getUnknownExtent());
5878       return fir::SequenceType::get(shape, seqTy.getEleTy());
5879     }
5880     // not sliced, so no change in rank
5881     return arrTy;
5882   }
5883 
5884   CC genarr(const Fortran::evaluate::ComplexPart &x,
5885             ComponentPath &components) {
5886     TODO(getLoc(), "genarr ComplexPart");
5887   }
5888 
5889   CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &,
5890             ComponentPath &components) {
5891     TODO(getLoc(), "genarr StaticDataObject::Pointer");
5892   }
5893 
5894   /// Substrings (see 9.4.1)
5895   CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) {
5896     TODO(getLoc(), "genarr Substring");
5897   }
5898 
5899   /// Base case of generating an array reference,
5900   CC genarr(const ExtValue &extMemref, ComponentPath &components) {
5901     mlir::Location loc = getLoc();
5902     mlir::Value memref = fir::getBase(extMemref);
5903     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType());
5904     assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array");
5905     mlir::Value shape = builder.createShape(loc, extMemref);
5906     mlir::Value slice;
5907     if (components.isSlice()) {
5908       if (isBoxValue() && components.substring) {
5909         // Append the substring operator to emboxing Op as it will become an
5910         // interior adjustment (add offset, adjust LEN) to the CHARACTER value
5911         // being referenced in the descriptor.
5912         llvm::SmallVector<mlir::Value> substringBounds;
5913         populateBounds(substringBounds, components.substring);
5914         // Convert to (offset, size)
5915         mlir::Type iTy = substringBounds[0].getType();
5916         if (substringBounds.size() != 2) {
5917           fir::CharacterType charTy =
5918               fir::factory::CharacterExprHelper::getCharType(arrTy);
5919           if (charTy.hasConstantLen()) {
5920             mlir::IndexType idxTy = builder.getIndexType();
5921             fir::CharacterType::LenType charLen = charTy.getLen();
5922             mlir::Value lenValue =
5923                 builder.createIntegerConstant(loc, idxTy, charLen);
5924             substringBounds.push_back(lenValue);
5925           } else {
5926             llvm::SmallVector<mlir::Value> typeparams =
5927                 fir::getTypeParams(extMemref);
5928             substringBounds.push_back(typeparams.back());
5929           }
5930         }
5931         // Convert the lower bound to 0-based substring.
5932         mlir::Value one =
5933             builder.createIntegerConstant(loc, substringBounds[0].getType(), 1);
5934         substringBounds[0] =
5935             builder.create<mlir::arith::SubIOp>(loc, substringBounds[0], one);
5936         // Convert the upper bound to a length.
5937         mlir::Value cast = builder.createConvert(loc, iTy, substringBounds[1]);
5938         mlir::Value zero = builder.createIntegerConstant(loc, iTy, 0);
5939         auto size =
5940             builder.create<mlir::arith::SubIOp>(loc, cast, substringBounds[0]);
5941         auto cmp = builder.create<mlir::arith::CmpIOp>(
5942             loc, mlir::arith::CmpIPredicate::sgt, size, zero);
5943         // size = MAX(upper - (lower - 1), 0)
5944         substringBounds[1] =
5945             builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero);
5946         slice = builder.create<fir::SliceOp>(loc, components.trips,
5947                                              components.suffixComponents,
5948                                              substringBounds);
5949       } else {
5950         slice = builder.createSlice(loc, extMemref, components.trips,
5951                                     components.suffixComponents);
5952       }
5953       if (components.hasComponents()) {
5954         auto seqTy = arrTy.cast<fir::SequenceType>();
5955         mlir::Type eleTy =
5956             fir::applyPathToType(seqTy.getEleTy(), components.suffixComponents);
5957         if (!eleTy)
5958           fir::emitFatalError(loc, "slicing path is ill-formed");
5959         if (auto realTy = eleTy.dyn_cast<fir::RealType>())
5960           eleTy = Fortran::lower::convertReal(realTy.getContext(),
5961                                               realTy.getFKind());
5962 
5963         // create the type of the projected array.
5964         arrTy = fir::SequenceType::get(seqTy.getShape(), eleTy);
5965         LLVM_DEBUG(llvm::dbgs()
5966                    << "type of array projection from component slicing: "
5967                    << eleTy << ", " << arrTy << '\n');
5968       }
5969     }
5970     arrayOperands.push_back(ArrayOperand{memref, shape, slice});
5971     if (destShape.empty())
5972       destShape = getShape(arrayOperands.back());
5973     if (isBoxValue()) {
5974       // Semantics are a reference to a boxed array.
5975       // This case just requires that an embox operation be created to box the
5976       // value. The value of the box is forwarded in the continuation.
5977       mlir::Type reduceTy = reduceRank(arrTy, slice);
5978       auto boxTy = fir::BoxType::get(reduceTy);
5979       if (components.substring) {
5980         // Adjust char length to substring size.
5981         fir::CharacterType charTy =
5982             fir::factory::CharacterExprHelper::getCharType(reduceTy);
5983         auto seqTy = reduceTy.cast<fir::SequenceType>();
5984         // TODO: Use a constant for fir.char LEN if we can compute it.
5985         boxTy = fir::BoxType::get(
5986             fir::SequenceType::get(fir::CharacterType::getUnknownLen(
5987                                        builder.getContext(), charTy.getFKind()),
5988                                    seqTy.getDimension()));
5989       }
5990       mlir::Value embox =
5991           memref.getType().isa<fir::BoxType>()
5992               ? builder.create<fir::ReboxOp>(loc, boxTy, memref, shape, slice)
5993                     .getResult()
5994               : builder
5995                     .create<fir::EmboxOp>(loc, boxTy, memref, shape, slice,
5996                                           fir::getTypeParams(extMemref))
5997                     .getResult();
5998       return [=](IterSpace) -> ExtValue { return fir::BoxValue(embox); };
5999     }
6000     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
6001     if (isReferentiallyOpaque()) {
6002       // Semantics are an opaque reference to an array.
6003       // This case forwards a continuation that will generate the address
6004       // arithmetic to the array element. This does not have copy-in/copy-out
6005       // semantics. No attempt to copy the array value will be made during the
6006       // interpretation of the Fortran statement.
6007       mlir::Type refEleTy = builder.getRefType(eleTy);
6008       return [=](IterSpace iters) -> ExtValue {
6009         // ArrayCoorOp does not expect zero based indices.
6010         llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
6011             loc, builder, memref.getType(), shape, iters.iterVec());
6012         mlir::Value coor = builder.create<fir::ArrayCoorOp>(
6013             loc, refEleTy, memref, shape, slice, indices,
6014             fir::getTypeParams(extMemref));
6015         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
6016           llvm::SmallVector<mlir::Value> substringBounds;
6017           populateBounds(substringBounds, components.substring);
6018           if (!substringBounds.empty()) {
6019             mlir::Value dstLen = fir::factory::genLenOfCharacter(
6020                 builder, loc, arrTy.cast<fir::SequenceType>(), memref,
6021                 fir::getTypeParams(extMemref), iters.iterVec(),
6022                 substringBounds);
6023             fir::CharBoxValue dstChar(coor, dstLen);
6024             return fir::factory::CharacterExprHelper{builder, loc}
6025                 .createSubstring(dstChar, substringBounds);
6026           }
6027         }
6028         return fir::factory::arraySectionElementToExtendedValue(
6029             builder, loc, extMemref, coor, slice);
6030       };
6031     }
6032     auto arrLoad = builder.create<fir::ArrayLoadOp>(
6033         loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref));
6034     mlir::Value arrLd = arrLoad.getResult();
6035     if (isProjectedCopyInCopyOut()) {
6036       // Semantics are projected copy-in copy-out.
6037       // The backing store of the destination of an array expression may be
6038       // partially modified. These updates are recorded in FIR by forwarding a
6039       // continuation that generates an `array_update` Op. The destination is
6040       // always loaded at the beginning of the statement and merged at the
6041       // end.
6042       destination = arrLoad;
6043       auto lambda = ccStoreToDest.hasValue()
6044                         ? ccStoreToDest.getValue()
6045                         : defaultStoreToDestination(components.substring);
6046       return [=](IterSpace iters) -> ExtValue { return lambda(iters); };
6047     }
6048     if (isCustomCopyInCopyOut()) {
6049       // Create an array_modify to get the LHS element address and indicate
6050       // the assignment, the actual assignment must be implemented in
6051       // ccStoreToDest.
6052       destination = arrLoad;
6053       return [=](IterSpace iters) -> ExtValue {
6054         mlir::Value innerArg = iters.innerArgument();
6055         mlir::Type resTy = innerArg.getType();
6056         mlir::Type eleTy = fir::applyPathToType(resTy, iters.iterVec());
6057         mlir::Type refEleTy =
6058             fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy);
6059         auto arrModify = builder.create<fir::ArrayModifyOp>(
6060             loc, mlir::TypeRange{refEleTy, resTy}, innerArg, iters.iterVec(),
6061             destination.getTypeparams());
6062         return abstractArrayExtValue(arrModify.getResult(1));
6063       };
6064     }
6065     if (isCopyInCopyOut()) {
6066       // Semantics are copy-in copy-out.
6067       // The continuation simply forwards the result of the `array_load` Op,
6068       // which is the value of the array as it was when loaded. All data
6069       // references with rank > 0 in an array expression typically have
6070       // copy-in copy-out semantics.
6071       return [=](IterSpace) -> ExtValue { return arrLd; };
6072     }
6073     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
6074     if (isValueAttribute()) {
6075       // Semantics are value attribute.
6076       // Here the continuation will `array_fetch` a value from an array and
6077       // then store that value in a temporary. One can thus imitate pass by
6078       // value even when the call is pass by reference.
6079       return [=](IterSpace iters) -> ExtValue {
6080         mlir::Value base;
6081         mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
6082         if (isAdjustedArrayElementType(eleTy)) {
6083           mlir::Type eleRefTy = builder.getRefType(eleTy);
6084           base = builder.create<fir::ArrayAccessOp>(
6085               loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
6086         } else {
6087           base = builder.create<fir::ArrayFetchOp>(
6088               loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
6089         }
6090         mlir::Value temp = builder.createTemporary(
6091             loc, base.getType(),
6092             llvm::ArrayRef<mlir::NamedAttribute>{
6093                 Fortran::lower::getAdaptToByRefAttr(builder)});
6094         builder.create<fir::StoreOp>(loc, base, temp);
6095         return fir::factory::arraySectionElementToExtendedValue(
6096             builder, loc, extMemref, temp, slice);
6097       };
6098     }
6099     // In the default case, the array reference forwards an `array_fetch` or
6100     // `array_access` Op in the continuation.
6101     return [=](IterSpace iters) -> ExtValue {
6102       mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
6103       if (isAdjustedArrayElementType(eleTy)) {
6104         mlir::Type eleRefTy = builder.getRefType(eleTy);
6105         mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>(
6106             loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
6107         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
6108           llvm::SmallVector<mlir::Value> substringBounds;
6109           populateBounds(substringBounds, components.substring);
6110           if (!substringBounds.empty()) {
6111             mlir::Value dstLen = fir::factory::genLenOfCharacter(
6112                 builder, loc, arrLoad, iters.iterVec(), substringBounds);
6113             fir::CharBoxValue dstChar(arrayOp, dstLen);
6114             return fir::factory::CharacterExprHelper{builder, loc}
6115                 .createSubstring(dstChar, substringBounds);
6116           }
6117         }
6118         return fir::factory::arraySectionElementToExtendedValue(
6119             builder, loc, extMemref, arrayOp, slice);
6120       }
6121       auto arrFetch = builder.create<fir::ArrayFetchOp>(
6122           loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
6123       return fir::factory::arraySectionElementToExtendedValue(
6124           builder, loc, extMemref, arrFetch, slice);
6125     };
6126   }
6127 
6128 private:
6129   void determineShapeOfDest(const fir::ExtendedValue &lhs) {
6130     destShape = fir::factory::getExtents(builder, getLoc(), lhs);
6131   }
6132 
6133   void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) {
6134     if (!destShape.empty())
6135       return;
6136     if (explicitSpaceIsActive() && determineShapeWithSlice(lhs))
6137       return;
6138     mlir::Type idxTy = builder.getIndexType();
6139     mlir::Location loc = getLoc();
6140     if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape =
6141             Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(),
6142                                                   lhs))
6143       for (Fortran::common::ConstantSubscript extent : *constantShape)
6144         destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent));
6145   }
6146 
6147   bool genShapeFromDataRef(const Fortran::semantics::Symbol &x) {
6148     return false;
6149   }
6150   bool genShapeFromDataRef(const Fortran::evaluate::CoarrayRef &) {
6151     TODO(getLoc(), "coarray ref");
6152     return false;
6153   }
6154   bool genShapeFromDataRef(const Fortran::evaluate::Component &x) {
6155     return x.base().Rank() > 0 ? genShapeFromDataRef(x.base()) : false;
6156   }
6157   bool genShapeFromDataRef(const Fortran::evaluate::ArrayRef &x) {
6158     if (x.Rank() == 0)
6159       return false;
6160     if (x.base().Rank() > 0)
6161       if (genShapeFromDataRef(x.base()))
6162         return true;
6163     // x has rank and x.base did not produce a shape.
6164     ExtValue exv = x.base().IsSymbol() ? asScalarRef(getFirstSym(x.base()))
6165                                        : asScalarRef(x.base().GetComponent());
6166     mlir::Location loc = getLoc();
6167     mlir::IndexType idxTy = builder.getIndexType();
6168     llvm::SmallVector<mlir::Value> definedShape =
6169         fir::factory::getExtents(builder, loc, exv);
6170     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
6171     for (auto ss : llvm::enumerate(x.subscript())) {
6172       std::visit(Fortran::common::visitors{
6173                      [&](const Fortran::evaluate::Triplet &trip) {
6174                        // For a subscript of triple notation, we compute the
6175                        // range of this dimension of the iteration space.
6176                        auto lo = [&]() {
6177                          if (auto optLo = trip.lower())
6178                            return fir::getBase(asScalar(*optLo));
6179                          return getLBound(exv, ss.index(), one);
6180                        }();
6181                        auto hi = [&]() {
6182                          if (auto optHi = trip.upper())
6183                            return fir::getBase(asScalar(*optHi));
6184                          return getUBound(exv, ss.index(), one);
6185                        }();
6186                        auto step = builder.createConvert(
6187                            loc, idxTy, fir::getBase(asScalar(trip.stride())));
6188                        auto extent = builder.genExtentFromTriplet(loc, lo, hi,
6189                                                                   step, idxTy);
6190                        destShape.push_back(extent);
6191                      },
6192                      [&](auto) {}},
6193                  ss.value().u);
6194     }
6195     return true;
6196   }
6197   bool genShapeFromDataRef(const Fortran::evaluate::NamedEntity &x) {
6198     if (x.IsSymbol())
6199       return genShapeFromDataRef(getFirstSym(x));
6200     return genShapeFromDataRef(x.GetComponent());
6201   }
6202   bool genShapeFromDataRef(const Fortran::evaluate::DataRef &x) {
6203     return std::visit([&](const auto &v) { return genShapeFromDataRef(v); },
6204                       x.u);
6205   }
6206 
6207   /// When in an explicit space, the ranked component must be evaluated to
6208   /// determine the actual number of iterations when slicing triples are
6209   /// present. Lower these expressions here.
6210   bool determineShapeWithSlice(const Fortran::lower::SomeExpr &lhs) {
6211     LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(
6212         llvm::dbgs() << "determine shape of:\n", lhs));
6213     // FIXME: We may not want to use ExtractDataRef here since it doesn't deal
6214     // with substrings, etc.
6215     std::optional<Fortran::evaluate::DataRef> dref =
6216         Fortran::evaluate::ExtractDataRef(lhs);
6217     return dref.has_value() ? genShapeFromDataRef(*dref) : false;
6218   }
6219 
6220   ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) {
6221     mlir::Type resTy = converter.genType(exp);
6222     return std::visit(
6223         [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); },
6224         exp.u);
6225   }
6226   ExtValue lowerArrayExpression(const ExtValue &exv) {
6227     assert(!explicitSpace);
6228     mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType());
6229     return lowerArrayExpression(genarr(exv), resTy);
6230   }
6231 
6232   void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds,
6233                       const Fortran::evaluate::Substring *substring) {
6234     if (!substring)
6235       return;
6236     bounds.push_back(fir::getBase(asScalar(substring->lower())));
6237     if (auto upper = substring->upper())
6238       bounds.push_back(fir::getBase(asScalar(*upper)));
6239   }
6240 
6241   /// Default store to destination implementation.
6242   /// This implements the default case, which is to assign the value in
6243   /// `iters.element` into the destination array, `iters.innerArgument`. Handles
6244   /// by value and by reference assignment.
6245   CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) {
6246     return [=](IterSpace iterSpace) -> ExtValue {
6247       mlir::Location loc = getLoc();
6248       mlir::Value innerArg = iterSpace.innerArgument();
6249       fir::ExtendedValue exv = iterSpace.elementExv();
6250       mlir::Type arrTy = innerArg.getType();
6251       mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec());
6252       if (isAdjustedArrayElementType(eleTy)) {
6253         // The elemental update is in the memref domain. Under this semantics,
6254         // we must always copy the computed new element from its location in
6255         // memory into the destination array.
6256         mlir::Type resRefTy = builder.getRefType(eleTy);
6257         // Get a reference to the array element to be amended.
6258         auto arrayOp = builder.create<fir::ArrayAccessOp>(
6259             loc, resRefTy, innerArg, iterSpace.iterVec(),
6260             destination.getTypeparams());
6261         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
6262           llvm::SmallVector<mlir::Value> substringBounds;
6263           populateBounds(substringBounds, substring);
6264           mlir::Value dstLen = fir::factory::genLenOfCharacter(
6265               builder, loc, destination, iterSpace.iterVec(), substringBounds);
6266           fir::ArrayAmendOp amend = createCharArrayAmend(
6267               loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds);
6268           return abstractArrayExtValue(amend, dstLen);
6269         }
6270         if (fir::isa_derived(eleTy)) {
6271           fir::ArrayAmendOp amend = createDerivedArrayAmend(
6272               loc, destination, builder, arrayOp, exv, eleTy, innerArg);
6273           return abstractArrayExtValue(amend /*FIXME: typeparams?*/);
6274         }
6275         assert(eleTy.isa<fir::SequenceType>() && "must be an array");
6276         TODO(loc, "array (as element) assignment");
6277       }
6278       // By value semantics. The element is being assigned by value.
6279       mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv));
6280       auto update = builder.create<fir::ArrayUpdateOp>(
6281           loc, arrTy, innerArg, ele, iterSpace.iterVec(),
6282           destination.getTypeparams());
6283       return abstractArrayExtValue(update);
6284     };
6285   }
6286 
6287   /// For an elemental array expression.
6288   ///   1. Lower the scalars and array loads.
6289   ///   2. Create the iteration space.
6290   ///   3. Create the element-by-element computation in the loop.
6291   ///   4. Return the resulting array value.
6292   /// If no destination was set in the array context, a temporary of
6293   /// \p resultTy will be created to hold the evaluated expression.
6294   /// Otherwise, \p resultTy is ignored and the expression is evaluated
6295   /// in the destination. \p f is a continuation built from an
6296   /// evaluate::Expr or an ExtendedValue.
6297   ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) {
6298     mlir::Location loc = getLoc();
6299     auto [iterSpace, insPt] = genIterSpace(resultTy);
6300     auto exv = f(iterSpace);
6301     iterSpace.setElement(std::move(exv));
6302     auto lambda = ccStoreToDest.hasValue()
6303                       ? ccStoreToDest.getValue()
6304                       : defaultStoreToDestination(/*substring=*/nullptr);
6305     mlir::Value updVal = fir::getBase(lambda(iterSpace));
6306     finalizeElementCtx();
6307     builder.create<fir::ResultOp>(loc, updVal);
6308     builder.restoreInsertionPoint(insPt);
6309     return abstractArrayExtValue(iterSpace.outerResult());
6310   }
6311 
6312   /// Compute the shape of a slice.
6313   llvm::SmallVector<mlir::Value> computeSliceShape(mlir::Value slice) {
6314     llvm::SmallVector<mlir::Value> slicedShape;
6315     auto slOp = mlir::cast<fir::SliceOp>(slice.getDefiningOp());
6316     mlir::Operation::operand_range triples = slOp.getTriples();
6317     mlir::IndexType idxTy = builder.getIndexType();
6318     mlir::Location loc = getLoc();
6319     for (unsigned i = 0, end = triples.size(); i < end; i += 3) {
6320       if (!mlir::isa_and_nonnull<fir::UndefOp>(
6321               triples[i + 1].getDefiningOp())) {
6322         // (..., lb:ub:step, ...) case:  extent = max((ub-lb+step)/step, 0)
6323         // See Fortran 2018 9.5.3.3.2 section for more details.
6324         mlir::Value res = builder.genExtentFromTriplet(
6325             loc, triples[i], triples[i + 1], triples[i + 2], idxTy);
6326         slicedShape.emplace_back(res);
6327       } else {
6328         // do nothing. `..., i, ...` case, so dimension is dropped.
6329       }
6330     }
6331     return slicedShape;
6332   }
6333 
6334   /// Get the shape from an ArrayOperand. The shape of the array is adjusted if
6335   /// the array was sliced.
6336   llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) {
6337     if (array.slice)
6338       return computeSliceShape(array.slice);
6339     if (array.memref.getType().isa<fir::BoxType>())
6340       return fir::factory::readExtents(builder, getLoc(),
6341                                        fir::BoxValue{array.memref});
6342     std::vector<mlir::Value, std::allocator<mlir::Value>> extents =
6343         fir::factory::getExtents(array.shape);
6344     return {extents.begin(), extents.end()};
6345   }
6346 
6347   /// Get the shape from an ArrayLoad.
6348   llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) {
6349     return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(),
6350                                  arrayLoad.getSlice()});
6351   }
6352 
6353   /// Returns the first array operand that may not be absent. If all
6354   /// array operands may be absent, return the first one.
6355   const ArrayOperand &getInducingShapeArrayOperand() const {
6356     assert(!arrayOperands.empty());
6357     for (const ArrayOperand &op : arrayOperands)
6358       if (!op.mayBeAbsent)
6359         return op;
6360     // If all arrays operand appears in optional position, then none of them
6361     // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the
6362     // first operands.
6363     // TODO: There is an opportunity to add a runtime check here that
6364     // this array is present as required.
6365     return arrayOperands[0];
6366   }
6367 
6368   /// Generate the shape of the iteration space over the array expression. The
6369   /// iteration space may be implicit, explicit, or both. If it is implied it is
6370   /// based on the destination and operand array loads, or an optional
6371   /// Fortran::evaluate::Shape from the front end. If the shape is explicit,
6372   /// this returns any implicit shape component, if it exists.
6373   llvm::SmallVector<mlir::Value> genIterationShape() {
6374     // Use the precomputed destination shape.
6375     if (!destShape.empty())
6376       return destShape;
6377     // Otherwise, use the destination's shape.
6378     if (destination)
6379       return getShape(destination);
6380     // Otherwise, use the first ArrayLoad operand shape.
6381     if (!arrayOperands.empty())
6382       return getShape(getInducingShapeArrayOperand());
6383     fir::emitFatalError(getLoc(),
6384                         "failed to compute the array expression shape");
6385   }
6386 
6387   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
6388                              Fortran::lower::StatementContext &stmtCtx,
6389                              Fortran::lower::SymMap &symMap)
6390       : converter{converter}, builder{converter.getFirOpBuilder()},
6391         stmtCtx{stmtCtx}, symMap{symMap} {}
6392 
6393   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
6394                              Fortran::lower::StatementContext &stmtCtx,
6395                              Fortran::lower::SymMap &symMap,
6396                              ConstituentSemantics sem)
6397       : converter{converter}, builder{converter.getFirOpBuilder()},
6398         stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {}
6399 
6400   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
6401                              Fortran::lower::StatementContext &stmtCtx,
6402                              Fortran::lower::SymMap &symMap,
6403                              ConstituentSemantics sem,
6404                              Fortran::lower::ExplicitIterSpace *expSpace,
6405                              Fortran::lower::ImplicitIterSpace *impSpace)
6406       : converter{converter}, builder{converter.getFirOpBuilder()},
6407         stmtCtx{stmtCtx}, symMap{symMap},
6408         explicitSpace(expSpace->isActive() ? expSpace : nullptr),
6409         implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} {
6410     // Generate any mask expressions, as necessary. This is the compute step
6411     // that creates the effective masks. See 10.2.3.2 in particular.
6412     genMasks();
6413   }
6414 
6415   mlir::Location getLoc() { return converter.getCurrentLocation(); }
6416 
6417   /// Array appears in a lhs context such that it is assigned after the rhs is
6418   /// fully evaluated.
6419   inline bool isCopyInCopyOut() {
6420     return semant == ConstituentSemantics::CopyInCopyOut;
6421   }
6422 
6423   /// Array appears in a lhs (or temp) context such that a projected,
6424   /// discontiguous subspace of the array is assigned after the rhs is fully
6425   /// evaluated. That is, the rhs array value is merged into a section of the
6426   /// lhs array.
6427   inline bool isProjectedCopyInCopyOut() {
6428     return semant == ConstituentSemantics::ProjectedCopyInCopyOut;
6429   }
6430 
6431   inline bool isCustomCopyInCopyOut() {
6432     return semant == ConstituentSemantics::CustomCopyInCopyOut;
6433   }
6434 
6435   /// Array appears in a context where it must be boxed.
6436   inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; }
6437 
6438   /// Array appears in a context where differences in the memory reference can
6439   /// be observable in the computational results. For example, an array
6440   /// element is passed to an impure procedure.
6441   inline bool isReferentiallyOpaque() {
6442     return semant == ConstituentSemantics::RefOpaque;
6443   }
6444 
6445   /// Array appears in a context where it is passed as a VALUE argument.
6446   inline bool isValueAttribute() {
6447     return semant == ConstituentSemantics::ByValueArg;
6448   }
6449 
6450   /// Can the loops over the expression be unordered?
6451   inline bool isUnordered() const { return unordered; }
6452 
6453   void setUnordered(bool b) { unordered = b; }
6454 
6455   Fortran::lower::AbstractConverter &converter;
6456   fir::FirOpBuilder &builder;
6457   Fortran::lower::StatementContext &stmtCtx;
6458   bool elementCtx = false;
6459   Fortran::lower::SymMap &symMap;
6460   /// The continuation to generate code to update the destination.
6461   llvm::Optional<CC> ccStoreToDest;
6462   llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude;
6463   llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>>
6464       ccLoadDest;
6465   /// The destination is the loaded array into which the results will be
6466   /// merged.
6467   fir::ArrayLoadOp destination;
6468   /// The shape of the destination.
6469   llvm::SmallVector<mlir::Value> destShape;
6470   /// List of arrays in the expression that have been loaded.
6471   llvm::SmallVector<ArrayOperand> arrayOperands;
6472   /// If there is a user-defined iteration space, explicitShape will hold the
6473   /// information from the front end.
6474   Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr;
6475   Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr;
6476   ConstituentSemantics semant = ConstituentSemantics::RefTransparent;
6477   // Can the array expression be evaluated in any order?
6478   // Will be set to false if any of the expression parts prevent this.
6479   bool unordered = true;
6480 };
6481 } // namespace
6482 
6483 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
6484     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6485     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6486     Fortran::lower::StatementContext &stmtCtx) {
6487   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
6488   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr);
6489 }
6490 
6491 fir::GlobalOp Fortran::lower::createDenseGlobal(
6492     mlir::Location loc, mlir::Type symTy, llvm::StringRef globalName,
6493     mlir::StringAttr linkage, bool isConst,
6494     const Fortran::lower::SomeExpr &expr,
6495     Fortran::lower::AbstractConverter &converter) {
6496 
6497   Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true);
6498   Fortran::lower::SymMap emptyMap;
6499   InitializerData initData(/*genRawVals=*/true);
6500   ScalarExprLowering sel(loc, converter, emptyMap, stmtCtx,
6501                          /*initializer=*/&initData);
6502   sel.genval(expr);
6503 
6504   size_t sz = initData.rawVals.size();
6505   llvm::ArrayRef<mlir::Attribute> ar = {initData.rawVals.data(), sz};
6506 
6507   mlir::RankedTensorType tensorTy;
6508   auto &builder = converter.getFirOpBuilder();
6509   mlir::Type iTy = initData.rawType;
6510   if (!iTy)
6511     return 0; // array extent is probably 0 in this case, so just return 0.
6512   tensorTy = mlir::RankedTensorType::get(sz, iTy);
6513   auto init = mlir::DenseElementsAttr::get(tensorTy, ar);
6514   return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst);
6515 }
6516 
6517 fir::ExtendedValue Fortran::lower::createSomeInitializerExpression(
6518     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6519     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6520     Fortran::lower::StatementContext &stmtCtx) {
6521   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
6522   InitializerData initData; // needed for initializations
6523   return ScalarExprLowering{loc, converter, symMap, stmtCtx,
6524                             /*initializer=*/&initData}
6525       .genval(expr);
6526 }
6527 
6528 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
6529     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6530     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6531     Fortran::lower::StatementContext &stmtCtx) {
6532   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
6533   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr);
6534 }
6535 
6536 fir::ExtendedValue Fortran::lower::createInitializerAddress(
6537     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6538     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6539     Fortran::lower::StatementContext &stmtCtx) {
6540   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
6541   InitializerData init;
6542   return ScalarExprLowering(loc, converter, symMap, stmtCtx, &init).gen(expr);
6543 }
6544 
6545 fir::ExtendedValue
6546 Fortran::lower::createSomeArrayBox(Fortran::lower::AbstractConverter &converter,
6547                                    const Fortran::lower::SomeExpr &expr,
6548                                    Fortran::lower::SymMap &symMap,
6549                                    Fortran::lower::StatementContext &stmtCtx) {
6550   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "box designator: ") << '\n');
6551   return ArrayExprLowering::lowerBoxedArrayExpression(converter, symMap,
6552                                                       stmtCtx, expr);
6553 }
6554 
6555 fir::MutableBoxValue Fortran::lower::createMutableBox(
6556     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6557     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
6558   // MutableBox lowering StatementContext does not need to be propagated
6559   // to the caller because the result value is a variable, not a temporary
6560   // expression. The StatementContext clean-up can occur before using the
6561   // resulting MutableBoxValue. Variables of all other types are handled in the
6562   // bridge.
6563   Fortran::lower::StatementContext dummyStmtCtx;
6564   return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx}
6565       .genMutableBoxValue(expr);
6566 }
6567 
6568 fir::ExtendedValue Fortran::lower::createBoxValue(
6569     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6570     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6571     Fortran::lower::StatementContext &stmtCtx) {
6572   if (expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) &&
6573       !Fortran::evaluate::HasVectorSubscript(expr))
6574     return Fortran::lower::createSomeArrayBox(converter, expr, symMap, stmtCtx);
6575   fir::ExtendedValue addr = Fortran::lower::createSomeExtendedAddress(
6576       loc, converter, expr, symMap, stmtCtx);
6577   return fir::BoxValue(converter.getFirOpBuilder().createBox(loc, addr));
6578 }
6579 
6580 mlir::Value Fortran::lower::createSubroutineCall(
6581     AbstractConverter &converter, const evaluate::ProcedureRef &call,
6582     ExplicitIterSpace &explicitIterSpace, ImplicitIterSpace &implicitIterSpace,
6583     SymMap &symMap, StatementContext &stmtCtx, bool isUserDefAssignment) {
6584   mlir::Location loc = converter.getCurrentLocation();
6585 
6586   if (isUserDefAssignment) {
6587     assert(call.arguments().size() == 2);
6588     const auto *lhs = call.arguments()[0].value().UnwrapExpr();
6589     const auto *rhs = call.arguments()[1].value().UnwrapExpr();
6590     assert(lhs && rhs &&
6591            "user defined assignment arguments must be expressions");
6592     if (call.IsElemental() && lhs->Rank() > 0) {
6593       // Elemental user defined assignment has special requirements to deal with
6594       // LHS/RHS overlaps. See 10.2.1.5 p2.
6595       ArrayExprLowering::lowerElementalUserAssignment(
6596           converter, symMap, stmtCtx, explicitIterSpace, implicitIterSpace,
6597           call);
6598     } else if (explicitIterSpace.isActive() && lhs->Rank() == 0) {
6599       // Scalar defined assignment (elemental or not) in a FORALL context.
6600       mlir::FuncOp func =
6601           Fortran::lower::CallerInterface(call, converter).getFuncOp();
6602       ArrayExprLowering::lowerScalarUserAssignment(
6603           converter, symMap, stmtCtx, explicitIterSpace, func, *lhs, *rhs);
6604     } else if (explicitIterSpace.isActive()) {
6605       // TODO: need to array fetch/modify sub-arrays?
6606       TODO(loc, "non elemental user defined array assignment inside FORALL");
6607     } else {
6608       if (!implicitIterSpace.empty())
6609         fir::emitFatalError(
6610             loc,
6611             "C1032: user defined assignment inside WHERE must be elemental");
6612       // Non elemental user defined assignment outside of FORALL and WHERE.
6613       // FIXME: The non elemental user defined assignment case with array
6614       // arguments must be take into account potential overlap. So far the front
6615       // end does not add parentheses around the RHS argument in the call as it
6616       // should according to 15.4.3.4.3 p2.
6617       Fortran::lower::createSomeExtendedExpression(
6618           loc, converter, toEvExpr(call), symMap, stmtCtx);
6619     }
6620     return {};
6621   }
6622 
6623   assert(implicitIterSpace.empty() && !explicitIterSpace.isActive() &&
6624          "subroutine calls are not allowed inside WHERE and FORALL");
6625 
6626   if (isElementalProcWithArrayArgs(call)) {
6627     ArrayExprLowering::lowerElementalSubroutine(converter, symMap, stmtCtx,
6628                                                 toEvExpr(call));
6629     return {};
6630   }
6631   // Simple subroutine call, with potential alternate return.
6632   auto res = Fortran::lower::createSomeExtendedExpression(
6633       loc, converter, toEvExpr(call), symMap, stmtCtx);
6634   return fir::getBase(res);
6635 }
6636 
6637 template <typename A>
6638 fir::ArrayLoadOp genArrayLoad(mlir::Location loc,
6639                               Fortran::lower::AbstractConverter &converter,
6640                               fir::FirOpBuilder &builder, const A *x,
6641                               Fortran::lower::SymMap &symMap,
6642                               Fortran::lower::StatementContext &stmtCtx) {
6643   auto exv = ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(*x);
6644   mlir::Value addr = fir::getBase(exv);
6645   mlir::Value shapeOp = builder.createShape(loc, exv);
6646   mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(addr.getType());
6647   return builder.create<fir::ArrayLoadOp>(loc, arrTy, addr, shapeOp,
6648                                           /*slice=*/mlir::Value{},
6649                                           fir::getTypeParams(exv));
6650 }
6651 template <>
6652 fir::ArrayLoadOp
6653 genArrayLoad(mlir::Location loc, Fortran::lower::AbstractConverter &converter,
6654              fir::FirOpBuilder &builder, const Fortran::evaluate::ArrayRef *x,
6655              Fortran::lower::SymMap &symMap,
6656              Fortran::lower::StatementContext &stmtCtx) {
6657   if (x->base().IsSymbol())
6658     return genArrayLoad(loc, converter, builder, &x->base().GetLastSymbol(),
6659                         symMap, stmtCtx);
6660   return genArrayLoad(loc, converter, builder, &x->base().GetComponent(),
6661                       symMap, stmtCtx);
6662 }
6663 
6664 void Fortran::lower::createArrayLoads(
6665     Fortran::lower::AbstractConverter &converter,
6666     Fortran::lower::ExplicitIterSpace &esp, Fortran::lower::SymMap &symMap) {
6667   std::size_t counter = esp.getCounter();
6668   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
6669   mlir::Location loc = converter.getCurrentLocation();
6670   Fortran::lower::StatementContext &stmtCtx = esp.stmtContext();
6671   // Gen the fir.array_load ops.
6672   auto genLoad = [&](const auto *x) -> fir::ArrayLoadOp {
6673     return genArrayLoad(loc, converter, builder, x, symMap, stmtCtx);
6674   };
6675   if (esp.lhsBases[counter].hasValue()) {
6676     auto &base = esp.lhsBases[counter].getValue();
6677     auto load = std::visit(genLoad, base);
6678     esp.initialArgs.push_back(load);
6679     esp.resetInnerArgs();
6680     esp.bindLoad(base, load);
6681   }
6682   for (const auto &base : esp.rhsBases[counter])
6683     esp.bindLoad(base, std::visit(genLoad, base));
6684 }
6685 
6686 void Fortran::lower::createArrayMergeStores(
6687     Fortran::lower::AbstractConverter &converter,
6688     Fortran::lower::ExplicitIterSpace &esp) {
6689   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
6690   mlir::Location loc = converter.getCurrentLocation();
6691   builder.setInsertionPointAfter(esp.getOuterLoop());
6692   // Gen the fir.array_merge_store ops for all LHS arrays.
6693   for (auto i : llvm::enumerate(esp.getOuterLoop().getResults()))
6694     if (llvm::Optional<fir::ArrayLoadOp> ldOpt = esp.getLhsLoad(i.index())) {
6695       fir::ArrayLoadOp load = ldOpt.getValue();
6696       builder.create<fir::ArrayMergeStoreOp>(loc, load, i.value(),
6697                                              load.getMemref(), load.getSlice(),
6698                                              load.getTypeparams());
6699     }
6700   if (esp.loopCleanup.hasValue()) {
6701     esp.loopCleanup.getValue()(builder);
6702     esp.loopCleanup = llvm::None;
6703   }
6704   esp.initialArgs.clear();
6705   esp.innerArgs.clear();
6706   esp.outerLoop = llvm::None;
6707   esp.resetBindings();
6708   esp.incrementCounter();
6709 }
6710 
6711 void Fortran::lower::createSomeArrayAssignment(
6712     Fortran::lower::AbstractConverter &converter,
6713     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
6714     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
6715   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
6716              rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
6717   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
6718 }
6719 
6720 void Fortran::lower::createSomeArrayAssignment(
6721     Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
6722     const Fortran::lower::SomeExpr &rhs, Fortran::lower::SymMap &symMap,
6723     Fortran::lower::StatementContext &stmtCtx) {
6724   LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
6725              rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
6726   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
6727 }
6728 
6729 void Fortran::lower::createSomeArrayAssignment(
6730     Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
6731     const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap,
6732     Fortran::lower::StatementContext &stmtCtx) {
6733   LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
6734              llvm::dbgs() << "assign expression: " << rhs << '\n';);
6735   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
6736 }
6737 
6738 void Fortran::lower::createAnyMaskedArrayAssignment(
6739     Fortran::lower::AbstractConverter &converter,
6740     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
6741     Fortran::lower::ExplicitIterSpace &explicitSpace,
6742     Fortran::lower::ImplicitIterSpace &implicitSpace,
6743     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
6744   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
6745              rhs.AsFortran(llvm::dbgs() << "assign expression: ")
6746              << " given the explicit iteration space:\n"
6747              << explicitSpace << "\n and implied mask conditions:\n"
6748              << implicitSpace << '\n';);
6749   ArrayExprLowering::lowerAnyMaskedArrayAssignment(
6750       converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
6751 }
6752 
6753 void Fortran::lower::createAllocatableArrayAssignment(
6754     Fortran::lower::AbstractConverter &converter,
6755     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
6756     Fortran::lower::ExplicitIterSpace &explicitSpace,
6757     Fortran::lower::ImplicitIterSpace &implicitSpace,
6758     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
6759   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n';
6760              rhs.AsFortran(llvm::dbgs() << "assign expression: ")
6761              << " given the explicit iteration space:\n"
6762              << explicitSpace << "\n and implied mask conditions:\n"
6763              << implicitSpace << '\n';);
6764   ArrayExprLowering::lowerAllocatableArrayAssignment(
6765       converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
6766 }
6767 
6768 fir::ExtendedValue Fortran::lower::createSomeArrayTempValue(
6769     Fortran::lower::AbstractConverter &converter,
6770     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
6771     Fortran::lower::StatementContext &stmtCtx) {
6772   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
6773   return ArrayExprLowering::lowerNewArrayExpression(converter, symMap, stmtCtx,
6774                                                     expr);
6775 }
6776 
6777 void Fortran::lower::createLazyArrayTempValue(
6778     Fortran::lower::AbstractConverter &converter,
6779     const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader,
6780     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
6781   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
6782   ArrayExprLowering::lowerLazyArrayExpression(converter, symMap, stmtCtx, expr,
6783                                               raggedHeader);
6784 }
6785 
6786 mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder,
6787                                            mlir::Location loc,
6788                                            mlir::Value value) {
6789   mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
6790   if (mlir::Operation *definingOp = value.getDefiningOp())
6791     if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
6792       if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>())
6793         return intAttr.getInt() < 0 ? zero : value;
6794   return Fortran::lower::genMax(builder, loc,
6795                                 llvm::SmallVector<mlir::Value>{value, zero});
6796 }
6797