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/CallInterface.h"
18 #include "flang/Lower/ComponentPath.h"
19 #include "flang/Lower/ConvertType.h"
20 #include "flang/Lower/ConvertVariable.h"
21 #include "flang/Lower/CustomIntrinsicCall.h"
22 #include "flang/Lower/DumpEvaluateExpr.h"
23 #include "flang/Lower/IntrinsicCall.h"
24 #include "flang/Lower/Mangler.h"
25 #include "flang/Lower/StatementContext.h"
26 #include "flang/Lower/SymbolMap.h"
27 #include "flang/Lower/Todo.h"
28 #include "flang/Optimizer/Builder/Character.h"
29 #include "flang/Optimizer/Builder/Complex.h"
30 #include "flang/Optimizer/Builder/Factory.h"
31 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h"
32 #include "flang/Optimizer/Builder/MutableBox.h"
33 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
34 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
35 #include "flang/Semantics/expression.h"
36 #include "flang/Semantics/symbol.h"
37 #include "flang/Semantics/tools.h"
38 #include "flang/Semantics/type.h"
39 #include "mlir/Dialect/Func/IR/FuncOps.h"
40 #include "llvm/Support/CommandLine.h"
41 #include "llvm/Support/Debug.h"
42 
43 #define DEBUG_TYPE "flang-lower-expr"
44 
45 //===----------------------------------------------------------------------===//
46 // The composition and structure of Fortran::evaluate::Expr is defined in
47 // the various header files in include/flang/Evaluate. You are referred
48 // there for more information on these data structures. Generally speaking,
49 // these data structures are a strongly typed family of abstract data types
50 // that, composed as trees, describe the syntax of Fortran expressions.
51 //
52 // This part of the bridge can traverse these tree structures and lower them
53 // to the correct FIR representation in SSA form.
54 //===----------------------------------------------------------------------===//
55 
56 // The default attempts to balance a modest allocation size with expected user
57 // input to minimize bounds checks and reallocations during dynamic array
58 // construction. Some user codes may have very large array constructors for
59 // which the default can be increased.
60 static llvm::cl::opt<unsigned> clInitialBufferSize(
61     "array-constructor-initial-buffer-size",
62     llvm::cl::desc(
63         "set the incremental array construction buffer size (default=32)"),
64     llvm::cl::init(32u));
65 
66 /// The various semantics of a program constituent (or a part thereof) as it may
67 /// appear in an expression.
68 ///
69 /// Given the following Fortran declarations.
70 /// ```fortran
71 ///   REAL :: v1, v2, v3
72 ///   REAL, POINTER :: vp1
73 ///   REAL :: a1(c), a2(c)
74 ///   REAL ELEMENTAL FUNCTION f1(arg) ! array -> array
75 ///   FUNCTION f2(arg)                ! array -> array
76 ///   vp1 => v3       ! 1
77 ///   v1 = v2 * vp1   ! 2
78 ///   a1 = a1 + a2    ! 3
79 ///   a1 = f1(a2)     ! 4
80 ///   a1 = f2(a2)     ! 5
81 /// ```
82 ///
83 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is
84 /// constructed from the DataAddr of `v3`.
85 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed
86 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double
87 /// dereference in the `vp1` case.
88 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs
89 /// is CopyInCopyOut as `a1` is replaced elementally by the additions.
90 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if
91 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/
92 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut.
93 ///  In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational.
94 ///  `a1` on the lhs is again CopyInCopyOut.
95 enum class ConstituentSemantics {
96   // Scalar data reference semantics.
97   //
98   // For these let `v` be the location in memory of a variable with value `x`
99   DataValue, // refers to the value `x`
100   DataAddr,  // refers to the address `v`
101   BoxValue,  // refers to a box value containing `v`
102   BoxAddr,   // refers to the address of a box value containing `v`
103 
104   // Array data reference semantics.
105   //
106   // For these let `a` be the location in memory of a sequence of value `[xs]`.
107   // Let `x_i` be the `i`-th value in the sequence `[xs]`.
108 
109   // Referentially transparent. Refers to the array's value, `[xs]`.
110   RefTransparent,
111   // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7
112   // note 2). (Passing a copy by reference to simulate pass-by-value.)
113   ByValueArg,
114   // Refers to the merge of array value `[xs]` with another array value `[ys]`.
115   // This merged array value will be written into memory location `a`.
116   CopyInCopyOut,
117   // Similar to CopyInCopyOut but `a` may be a transient projection (rather than
118   // a whole array).
119   ProjectedCopyInCopyOut,
120   // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned
121   // automatically by the framework. Instead, and address for `[xs]` is made
122   // accessible so that custom assignments to `[xs]` can be implemented.
123   CustomCopyInCopyOut,
124   // Referentially opaque. Refers to the address of `x_i`.
125   RefOpaque
126 };
127 
128 /// Convert parser's INTEGER relational operators to MLIR.  TODO: using
129 /// unordered, but we may want to cons ordered in certain situation.
130 static mlir::arith::CmpIPredicate
131 translateRelational(Fortran::common::RelationalOperator rop) {
132   switch (rop) {
133   case Fortran::common::RelationalOperator::LT:
134     return mlir::arith::CmpIPredicate::slt;
135   case Fortran::common::RelationalOperator::LE:
136     return mlir::arith::CmpIPredicate::sle;
137   case Fortran::common::RelationalOperator::EQ:
138     return mlir::arith::CmpIPredicate::eq;
139   case Fortran::common::RelationalOperator::NE:
140     return mlir::arith::CmpIPredicate::ne;
141   case Fortran::common::RelationalOperator::GT:
142     return mlir::arith::CmpIPredicate::sgt;
143   case Fortran::common::RelationalOperator::GE:
144     return mlir::arith::CmpIPredicate::sge;
145   }
146   llvm_unreachable("unhandled INTEGER relational operator");
147 }
148 
149 /// Convert parser's REAL relational operators to MLIR.
150 /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018
151 /// requirements in the IEEE context (table 17.1 of F2018). This choice is
152 /// also applied in other contexts because it is easier and in line with
153 /// other Fortran compilers.
154 /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not
155 /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee
156 /// whether the comparison will signal or not in case of quiet NaN argument.
157 static mlir::arith::CmpFPredicate
158 translateFloatRelational(Fortran::common::RelationalOperator rop) {
159   switch (rop) {
160   case Fortran::common::RelationalOperator::LT:
161     return mlir::arith::CmpFPredicate::OLT;
162   case Fortran::common::RelationalOperator::LE:
163     return mlir::arith::CmpFPredicate::OLE;
164   case Fortran::common::RelationalOperator::EQ:
165     return mlir::arith::CmpFPredicate::OEQ;
166   case Fortran::common::RelationalOperator::NE:
167     return mlir::arith::CmpFPredicate::UNE;
168   case Fortran::common::RelationalOperator::GT:
169     return mlir::arith::CmpFPredicate::OGT;
170   case Fortran::common::RelationalOperator::GE:
171     return mlir::arith::CmpFPredicate::OGE;
172   }
173   llvm_unreachable("unhandled REAL relational operator");
174 }
175 
176 static mlir::Value genActualIsPresentTest(fir::FirOpBuilder &builder,
177                                           mlir::Location loc,
178                                           fir::ExtendedValue actual) {
179   if (const auto *ptrOrAlloc = actual.getBoxOf<fir::MutableBoxValue>())
180     return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc,
181                                                         *ptrOrAlloc);
182   // Optional case (not that optional allocatable/pointer cannot be absent
183   // when passed to CMPLX as per 15.5.2.12 point 3 (7) and (8)). It is
184   // therefore possible to catch them in the `then` case above.
185   return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
186                                           fir::getBase(actual));
187 }
188 
189 /// Place \p exv in memory if it is not already a memory reference. If
190 /// \p forceValueType is provided, the value is first casted to the provided
191 /// type before being stored (this is mainly intended for logicals whose value
192 /// may be `i1` but needed to be stored as Fortran logicals).
193 static fir::ExtendedValue
194 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
195                          const fir::ExtendedValue &exv,
196                          mlir::Type storageType) {
197   mlir::Value valBase = fir::getBase(exv);
198   if (fir::conformsWithPassByRef(valBase.getType()))
199     return exv;
200 
201   assert(!fir::hasDynamicSize(storageType) &&
202          "only expect statically sized scalars to be by value");
203 
204   // Since `a` is not itself a valid referent, determine its value and
205   // create a temporary location at the beginning of the function for
206   // referencing.
207   mlir::Value val = builder.createConvert(loc, storageType, valBase);
208   mlir::Value temp = builder.createTemporary(
209       loc, storageType,
210       llvm::ArrayRef<mlir::NamedAttribute>{
211           Fortran::lower::getAdaptToByRefAttr(builder)});
212   builder.create<fir::StoreOp>(loc, val, temp);
213   return fir::substBase(exv, temp);
214 }
215 
216 // Copy a copy of scalar \p exv in a new temporary.
217 static fir::ExtendedValue
218 createInMemoryScalarCopy(fir::FirOpBuilder &builder, mlir::Location loc,
219                          const fir::ExtendedValue &exv) {
220   assert(exv.rank() == 0 && "input to scalar memory copy must be a scalar");
221   if (exv.getCharBox() != nullptr)
222     return fir::factory::CharacterExprHelper{builder, loc}.createTempFrom(exv);
223   if (fir::isDerivedWithLengthParameters(exv))
224     TODO(loc, "copy derived type with length parameters");
225   mlir::Type type = fir::unwrapPassByRefType(fir::getBase(exv).getType());
226   fir::ExtendedValue temp = builder.createTemporary(loc, type);
227   fir::factory::genScalarAssignment(builder, loc, temp, exv);
228   return temp;
229 }
230 
231 /// Is this a variable wrapped in parentheses?
232 template <typename A>
233 static bool isParenthesizedVariable(const A &) {
234   return false;
235 }
236 template <typename T>
237 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) {
238   using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
239   using Parentheses = Fortran::evaluate::Parentheses<T>;
240   if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
241     if (const auto *parentheses = std::get_if<Parentheses>(&expr.u))
242       return Fortran::evaluate::IsVariable(parentheses->left());
243     return false;
244   } else {
245     return std::visit([&](const auto &x) { return isParenthesizedVariable(x); },
246                       expr.u);
247   }
248 }
249 
250 /// Generate a load of a value from an address. Beware that this will lose
251 /// any dynamic type information for polymorphic entities (note that unlimited
252 /// polymorphic cannot be loaded and must not be provided here).
253 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
254                                   mlir::Location loc,
255                                   const fir::ExtendedValue &addr) {
256   return addr.match(
257       [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
258       [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
259         if (fir::unwrapRefType(fir::getBase(v).getType())
260                 .isa<fir::RecordType>())
261           return v;
262         return builder.create<fir::LoadOp>(loc, fir::getBase(v));
263       },
264       [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
265         TODO(loc, "genLoad for MutableBoxValue");
266       },
267       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
268         TODO(loc, "genLoad for BoxValue");
269       },
270       [&](const auto &) -> fir::ExtendedValue {
271         fir::emitFatalError(
272             loc, "attempting to load whole array or procedure address");
273       });
274 }
275 
276 /// Create an optional dummy argument value from entity \p exv that may be
277 /// absent. This can only be called with numerical or logical scalar \p exv.
278 /// If \p exv is considered absent according to 15.5.2.12 point 1., the returned
279 /// value is zero (or false), otherwise it is the value of \p exv.
280 static fir::ExtendedValue genOptionalValue(fir::FirOpBuilder &builder,
281                                            mlir::Location loc,
282                                            const fir::ExtendedValue &exv,
283                                            mlir::Value isPresent) {
284   mlir::Type eleType = fir::getBaseTypeOf(exv);
285   assert(exv.rank() == 0 && fir::isa_trivial(eleType) &&
286          "must be a numerical or logical scalar");
287   return builder
288       .genIfOp(loc, {eleType}, isPresent,
289                /*withElseRegion=*/true)
290       .genThen([&]() {
291         mlir::Value val = fir::getBase(genLoad(builder, loc, exv));
292         builder.create<fir::ResultOp>(loc, val);
293       })
294       .genElse([&]() {
295         mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType);
296         builder.create<fir::ResultOp>(loc, zero);
297       })
298       .getResults()[0];
299 }
300 
301 /// Create an optional dummy argument address from entity \p exv that may be
302 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the
303 /// returned value is a null pointer, otherwise it is the address of \p exv.
304 static fir::ExtendedValue genOptionalAddr(fir::FirOpBuilder &builder,
305                                           mlir::Location loc,
306                                           const fir::ExtendedValue &exv,
307                                           mlir::Value isPresent) {
308   // If it is an exv pointer/allocatable, then it cannot be absent
309   // because it is passed to a non-pointer/non-allocatable.
310   if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
311     return fir::factory::genMutableBoxRead(builder, loc, *box);
312   // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL
313   // address and can be passed directly.
314   return exv;
315 }
316 
317 /// Create an optional dummy argument address from entity \p exv that may be
318 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the
319 /// returned value is an absent fir.box, otherwise it is a fir.box describing \p
320 /// exv.
321 static fir::ExtendedValue genOptionalBox(fir::FirOpBuilder &builder,
322                                          mlir::Location loc,
323                                          const fir::ExtendedValue &exv,
324                                          mlir::Value isPresent) {
325   // Non allocatable/pointer optional box -> simply forward
326   if (exv.getBoxOf<fir::BoxValue>())
327     return exv;
328 
329   fir::ExtendedValue newExv = exv;
330   // Optional allocatable/pointer -> Cannot be absent, but need to translate
331   // unallocated/diassociated into absent fir.box.
332   if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
333     newExv = fir::factory::genMutableBoxRead(builder, loc, *box);
334 
335   // createBox will not do create any invalid memory dereferences if exv is
336   // absent. The created fir.box will not be usable, but the SelectOp below
337   // ensures it won't be.
338   mlir::Value box = builder.createBox(loc, newExv);
339   mlir::Type boxType = box.getType();
340   auto absent = builder.create<fir::AbsentOp>(loc, boxType);
341   auto boxOrAbsent = builder.create<mlir::arith::SelectOp>(
342       loc, boxType, isPresent, box, absent);
343   return fir::BoxValue(boxOrAbsent);
344 }
345 
346 /// Is this a call to an elemental procedure with at least one array argument?
347 static bool
348 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
349   if (procRef.IsElemental())
350     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
351          procRef.arguments())
352       if (arg && arg->Rank() != 0)
353         return true;
354   return false;
355 }
356 template <typename T>
357 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) {
358   return false;
359 }
360 template <>
361 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) {
362   if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u))
363     return isElementalProcWithArrayArgs(*procRef);
364   return false;
365 }
366 
367 /// Some auxiliary data for processing initialization in ScalarExprLowering
368 /// below. This is currently used for generating dense attributed global
369 /// arrays.
370 struct InitializerData {
371   explicit InitializerData(bool getRawVals = false) : genRawVals{getRawVals} {}
372   llvm::SmallVector<mlir::Attribute> rawVals; // initialization raw values
373   mlir::Type rawType; // Type of elements processed for rawVals vector.
374   bool genRawVals;    // generate the rawVals vector if set.
375 };
376 
377 /// If \p arg is the address of a function with a denoted host-association tuple
378 /// argument, then return the host-associations tuple value of the current
379 /// procedure. Otherwise, return nullptr.
380 static mlir::Value
381 argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
382                    mlir::Value arg) {
383   if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
384     auto &builder = converter.getFirOpBuilder();
385     if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
386       if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
387         return converter.hostAssocTupleValue();
388   }
389   return {};
390 }
391 
392 namespace {
393 
394 /// Lowering of Fortran::evaluate::Expr<T> expressions
395 class ScalarExprLowering {
396 public:
397   using ExtValue = fir::ExtendedValue;
398 
399   explicit ScalarExprLowering(mlir::Location loc,
400                               Fortran::lower::AbstractConverter &converter,
401                               Fortran::lower::SymMap &symMap,
402                               Fortran::lower::StatementContext &stmtCtx,
403                               InitializerData *initializer = nullptr)
404       : location{loc}, converter{converter},
405         builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap},
406         inInitializer{initializer} {}
407 
408   ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) {
409     return gen(expr);
410   }
411 
412   /// Lower `expr` to be passed as a fir.box argument. Do not create a temp
413   /// for the expr if it is a variable that can be described as a fir.box.
414   ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) {
415     bool saveUseBoxArg = useBoxArg;
416     useBoxArg = true;
417     ExtValue result = gen(expr);
418     useBoxArg = saveUseBoxArg;
419     return result;
420   }
421 
422   ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) {
423     return genval(expr);
424   }
425 
426   /// Lower an expression that is a pointer or an allocatable to a
427   /// MutableBoxValue.
428   fir::MutableBoxValue
429   genMutableBoxValue(const Fortran::lower::SomeExpr &expr) {
430     // Pointers and allocatables can only be:
431     //    - a simple designator "x"
432     //    - a component designator "a%b(i,j)%x"
433     //    - a function reference "foo()"
434     //    - result of NULL() or NULL(MOLD) intrinsic.
435     //    NULL() requires some context to be lowered, so it is not handled
436     //    here and must be lowered according to the context where it appears.
437     ExtValue exv = std::visit(
438         [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u);
439     const fir::MutableBoxValue *mutableBox =
440         exv.getBoxOf<fir::MutableBoxValue>();
441     if (!mutableBox)
442       fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue");
443     return *mutableBox;
444   }
445 
446   template <typename T>
447   ExtValue genMutableBoxValueImpl(const T &) {
448     // NULL() case should not be handled here.
449     fir::emitFatalError(getLoc(), "NULL() must be lowered in its context");
450   }
451 
452   template <typename T>
453   ExtValue
454   genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) {
455     return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef)));
456   }
457 
458   template <typename T>
459   ExtValue
460   genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) {
461     return std::visit(
462         Fortran::common::visitors{
463             [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue {
464               return symMap.lookupSymbol(*sym).toExtendedValue();
465             },
466             [&](const Fortran::evaluate::Component &comp) -> ExtValue {
467               return genComponent(comp);
468             },
469             [&](const auto &) -> ExtValue {
470               fir::emitFatalError(getLoc(),
471                                   "not an allocatable or pointer designator");
472             }},
473         designator.u);
474   }
475 
476   template <typename T>
477   ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) {
478     return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); },
479                       expr.u);
480   }
481 
482   mlir::Location getLoc() { return location; }
483 
484   template <typename A>
485   mlir::Value genunbox(const A &expr) {
486     ExtValue e = genval(expr);
487     if (const fir::UnboxedValue *r = e.getUnboxed())
488       return *r;
489     fir::emitFatalError(getLoc(), "unboxed expression expected");
490   }
491 
492   /// Generate an integral constant of `value`
493   template <int KIND>
494   mlir::Value genIntegerConstant(mlir::MLIRContext *context,
495                                  std::int64_t value) {
496     mlir::Type type =
497         converter.genType(Fortran::common::TypeCategory::Integer, KIND);
498     return builder.createIntegerConstant(getLoc(), type, value);
499   }
500 
501   /// Generate a logical/boolean constant of `value`
502   mlir::Value genBoolConstant(bool value) {
503     return builder.createBool(getLoc(), value);
504   }
505 
506   /// Generate a real constant with a value `value`.
507   template <int KIND>
508   mlir::Value genRealConstant(mlir::MLIRContext *context,
509                               const llvm::APFloat &value) {
510     mlir::Type fltTy = Fortran::lower::convertReal(context, KIND);
511     return builder.createRealConstant(getLoc(), fltTy, value);
512   }
513 
514   template <typename OpTy>
515   mlir::Value createCompareOp(mlir::arith::CmpIPredicate pred,
516                               const ExtValue &left, const ExtValue &right) {
517     if (const fir::UnboxedValue *lhs = left.getUnboxed())
518       if (const fir::UnboxedValue *rhs = right.getUnboxed())
519         return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
520     fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
521   }
522   template <typename OpTy, typename A>
523   mlir::Value createCompareOp(const A &ex, mlir::arith::CmpIPredicate pred) {
524     ExtValue left = genval(ex.left());
525     return createCompareOp<OpTy>(pred, left, genval(ex.right()));
526   }
527 
528   template <typename OpTy>
529   mlir::Value createFltCmpOp(mlir::arith::CmpFPredicate pred,
530                              const ExtValue &left, const ExtValue &right) {
531     if (const fir::UnboxedValue *lhs = left.getUnboxed())
532       if (const fir::UnboxedValue *rhs = right.getUnboxed())
533         return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
534     fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
535   }
536   template <typename OpTy, typename A>
537   mlir::Value createFltCmpOp(const A &ex, mlir::arith::CmpFPredicate pred) {
538     ExtValue left = genval(ex.left());
539     return createFltCmpOp<OpTy>(pred, left, genval(ex.right()));
540   }
541 
542   /// Returns a reference to a symbol or its box/boxChar descriptor if it has
543   /// one.
544   ExtValue gen(Fortran::semantics::SymbolRef sym) {
545     if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
546       return val.match(
547           [&](const Fortran::lower::SymbolBox::PointerOrAllocatable &boxAddr) {
548             return fir::factory::genMutableBoxRead(builder, getLoc(), boxAddr);
549           },
550           [&val](auto &) { return val.toExtendedValue(); });
551     LLVM_DEBUG(llvm::dbgs()
552                << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
553     fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
554   }
555 
556   ExtValue genLoad(const ExtValue &exv) {
557     return ::genLoad(builder, getLoc(), exv);
558   }
559 
560   ExtValue genval(Fortran::semantics::SymbolRef sym) {
561     ExtValue var = gen(sym);
562     if (const fir::UnboxedValue *s = var.getUnboxed())
563       if (fir::isReferenceLike(s->getType()))
564         return genLoad(*s);
565     return var;
566   }
567 
568   ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
569     TODO(getLoc(), "genval BOZ");
570   }
571 
572   /// Return indirection to function designated in ProcedureDesignator.
573   /// The type of the function indirection is not guaranteed to match the one
574   /// of the ProcedureDesignator due to Fortran implicit typing rules.
575   ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
576     TODO(getLoc(), "genval ProcedureDesignator");
577   }
578 
579   ExtValue genval(const Fortran::evaluate::NullPointer &) {
580     TODO(getLoc(), "genval NullPointer");
581   }
582 
583   ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
584     TODO(getLoc(), "genval StructureConstructor");
585   }
586 
587   /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
588   ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
589     return converter.impliedDoBinding(toStringRef(var.name));
590   }
591 
592   ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
593     ExtValue exv = desc.base().IsSymbol() ? gen(desc.base().GetLastSymbol())
594                                           : gen(desc.base().GetComponent());
595     mlir::IndexType idxTy = builder.getIndexType();
596     mlir::Location loc = getLoc();
597     auto castResult = [&](mlir::Value v) {
598       using ResTy = Fortran::evaluate::DescriptorInquiry::Result;
599       return builder.createConvert(
600           loc, converter.genType(ResTy::category, ResTy::kind), v);
601     };
602     switch (desc.field()) {
603     case Fortran::evaluate::DescriptorInquiry::Field::Len:
604       return castResult(fir::factory::readCharLen(builder, loc, exv));
605     case Fortran::evaluate::DescriptorInquiry::Field::LowerBound:
606       return castResult(fir::factory::readLowerBound(
607           builder, loc, exv, desc.dimension(),
608           builder.createIntegerConstant(loc, idxTy, 1)));
609     case Fortran::evaluate::DescriptorInquiry::Field::Extent:
610       return castResult(
611           fir::factory::readExtent(builder, loc, exv, desc.dimension()));
612     case Fortran::evaluate::DescriptorInquiry::Field::Rank:
613       TODO(loc, "rank inquiry on assumed rank");
614     case Fortran::evaluate::DescriptorInquiry::Field::Stride:
615       // So far the front end does not generate this inquiry.
616       TODO(loc, "Stride inquiry");
617     }
618     llvm_unreachable("unknown descriptor inquiry");
619   }
620 
621   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
622     TODO(getLoc(), "genval TypeParamInquiry");
623   }
624 
625   template <int KIND>
626   ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
627     TODO(getLoc(), "genval ComplexComponent");
628   }
629 
630   template <int KIND>
631   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
632                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
633     mlir::Value input = genunbox(op.left());
634     // Like LLVM, integer negation is the binary op "0 - value"
635     mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
636     return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
637   }
638 
639   template <int KIND>
640   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
641                       Fortran::common::TypeCategory::Real, KIND>> &op) {
642     return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
643   }
644   template <int KIND>
645   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
646                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
647     return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
648   }
649 
650   template <typename OpTy>
651   mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
652     assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
653     mlir::Value lhs = fir::getBase(left);
654     mlir::Value rhs = fir::getBase(right);
655     assert(lhs.getType() == rhs.getType() && "types must be the same");
656     return builder.create<OpTy>(getLoc(), lhs, rhs);
657   }
658 
659   template <typename OpTy, typename A>
660   mlir::Value createBinaryOp(const A &ex) {
661     ExtValue left = genval(ex.left());
662     return createBinaryOp<OpTy>(left, genval(ex.right()));
663   }
664 
665 #undef GENBIN
666 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
667   template <int KIND>                                                          \
668   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
669                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
670     return createBinaryOp<GenBinFirOp>(x);                                     \
671   }
672 
673   GENBIN(Add, Integer, mlir::arith::AddIOp)
674   GENBIN(Add, Real, mlir::arith::AddFOp)
675   GENBIN(Add, Complex, fir::AddcOp)
676   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
677   GENBIN(Subtract, Real, mlir::arith::SubFOp)
678   GENBIN(Subtract, Complex, fir::SubcOp)
679   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
680   GENBIN(Multiply, Real, mlir::arith::MulFOp)
681   GENBIN(Multiply, Complex, fir::MulcOp)
682   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
683   GENBIN(Divide, Real, mlir::arith::DivFOp)
684   GENBIN(Divide, Complex, fir::DivcOp)
685 
686   template <Fortran::common::TypeCategory TC, int KIND>
687   ExtValue genval(
688       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
689     mlir::Type ty = converter.genType(TC, KIND);
690     mlir::Value lhs = genunbox(op.left());
691     mlir::Value rhs = genunbox(op.right());
692     return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
693   }
694 
695   template <Fortran::common::TypeCategory TC, int KIND>
696   ExtValue genval(
697       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
698           &op) {
699     mlir::Type ty = converter.genType(TC, KIND);
700     mlir::Value lhs = genunbox(op.left());
701     mlir::Value rhs = genunbox(op.right());
702     return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
703   }
704 
705   template <int KIND>
706   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
707     mlir::Value realPartValue = genunbox(op.left());
708     return fir::factory::Complex{builder, getLoc()}.createComplex(
709         KIND, realPartValue, genunbox(op.right()));
710   }
711 
712   template <int KIND>
713   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
714     TODO(getLoc(), "genval Concat<KIND>");
715   }
716 
717   /// MIN and MAX operations
718   template <Fortran::common::TypeCategory TC, int KIND>
719   ExtValue
720   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
721              &op) {
722     TODO(getLoc(), "genval Extremum<TC, KIND>");
723   }
724 
725   template <int KIND>
726   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
727     TODO(getLoc(), "genval SetLength<KIND>");
728   }
729 
730   template <int KIND>
731   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
732                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
733     return createCompareOp<mlir::arith::CmpIOp>(op,
734                                                 translateRelational(op.opr));
735   }
736   template <int KIND>
737   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
738                       Fortran::common::TypeCategory::Real, KIND>> &op) {
739     return createFltCmpOp<mlir::arith::CmpFOp>(
740         op, translateFloatRelational(op.opr));
741   }
742   template <int KIND>
743   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
744                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
745     TODO(getLoc(), "genval complex comparison");
746   }
747   template <int KIND>
748   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
749                       Fortran::common::TypeCategory::Character, KIND>> &op) {
750     TODO(getLoc(), "genval char comparison");
751   }
752 
753   ExtValue
754   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
755     return std::visit([&](const auto &x) { return genval(x); }, op.u);
756   }
757 
758   template <Fortran::common::TypeCategory TC1, int KIND,
759             Fortran::common::TypeCategory TC2>
760   ExtValue
761   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
762                                           TC2> &convert) {
763     mlir::Type ty = converter.genType(TC1, KIND);
764     mlir::Value operand = genunbox(convert.left());
765     return builder.convertWithSemantics(getLoc(), ty, operand);
766   }
767 
768   template <typename A>
769   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
770     TODO(getLoc(), "genval parentheses<A>");
771   }
772 
773   template <int KIND>
774   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
775     mlir::Value logical = genunbox(op.left());
776     mlir::Value one = genBoolConstant(true);
777     mlir::Value val =
778         builder.createConvert(getLoc(), builder.getI1Type(), logical);
779     return builder.create<mlir::arith::XOrIOp>(getLoc(), val, one);
780   }
781 
782   template <int KIND>
783   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
784     mlir::IntegerType i1Type = builder.getI1Type();
785     mlir::Value slhs = genunbox(op.left());
786     mlir::Value srhs = genunbox(op.right());
787     mlir::Value lhs = builder.createConvert(getLoc(), i1Type, slhs);
788     mlir::Value rhs = builder.createConvert(getLoc(), i1Type, srhs);
789     switch (op.logicalOperator) {
790     case Fortran::evaluate::LogicalOperator::And:
791       return createBinaryOp<mlir::arith::AndIOp>(lhs, rhs);
792     case Fortran::evaluate::LogicalOperator::Or:
793       return createBinaryOp<mlir::arith::OrIOp>(lhs, rhs);
794     case Fortran::evaluate::LogicalOperator::Eqv:
795       return createCompareOp<mlir::arith::CmpIOp>(
796           mlir::arith::CmpIPredicate::eq, lhs, rhs);
797     case Fortran::evaluate::LogicalOperator::Neqv:
798       return createCompareOp<mlir::arith::CmpIOp>(
799           mlir::arith::CmpIPredicate::ne, lhs, rhs);
800     case Fortran::evaluate::LogicalOperator::Not:
801       // lib/evaluate expression for .NOT. is Fortran::evaluate::Not<KIND>.
802       llvm_unreachable(".NOT. is not a binary operator");
803     }
804     llvm_unreachable("unhandled logical operation");
805   }
806 
807   /// Convert a scalar literal constant to IR.
808   template <Fortran::common::TypeCategory TC, int KIND>
809   ExtValue genScalarLit(
810       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
811           &value) {
812     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
813       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
814     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
815       return genBoolConstant(value.IsTrue());
816     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
817       std::string str = value.DumpHexadecimal();
818       if constexpr (KIND == 2) {
819         llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str};
820         return genRealConstant<KIND>(builder.getContext(), floatVal);
821       } else if constexpr (KIND == 3) {
822         llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str};
823         return genRealConstant<KIND>(builder.getContext(), floatVal);
824       } else if constexpr (KIND == 4) {
825         llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str};
826         return genRealConstant<KIND>(builder.getContext(), floatVal);
827       } else if constexpr (KIND == 10) {
828         llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str};
829         return genRealConstant<KIND>(builder.getContext(), floatVal);
830       } else if constexpr (KIND == 16) {
831         llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str};
832         return genRealConstant<KIND>(builder.getContext(), floatVal);
833       } else {
834         // convert everything else to double
835         llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str};
836         return genRealConstant<KIND>(builder.getContext(), floatVal);
837       }
838     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
839       using TR =
840           Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>;
841       Fortran::evaluate::ComplexConstructor<KIND> ctor(
842           Fortran::evaluate::Expr<TR>{
843               Fortran::evaluate::Constant<TR>{value.REAL()}},
844           Fortran::evaluate::Expr<TR>{
845               Fortran::evaluate::Constant<TR>{value.AIMAG()}});
846       return genunbox(ctor);
847     } else /*constexpr*/ {
848       llvm_unreachable("unhandled constant");
849     }
850   }
851 
852   /// Generate a raw literal value and store it in the rawVals vector.
853   template <Fortran::common::TypeCategory TC, int KIND>
854   void
855   genRawLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
856                 &value) {
857     mlir::Attribute val;
858     assert(inInitializer != nullptr);
859     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
860       inInitializer->rawType = converter.genType(TC, KIND);
861       val = builder.getIntegerAttr(inInitializer->rawType, value.ToInt64());
862     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
863       inInitializer->rawType =
864           converter.genType(Fortran::common::TypeCategory::Integer, KIND);
865       val = builder.getIntegerAttr(inInitializer->rawType, value.IsTrue());
866     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
867       std::string str = value.DumpHexadecimal();
868       inInitializer->rawType = converter.genType(TC, KIND);
869       llvm::APFloat floatVal{builder.getKindMap().getFloatSemantics(KIND), str};
870       val = builder.getFloatAttr(inInitializer->rawType, floatVal);
871     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
872       std::string strReal = value.REAL().DumpHexadecimal();
873       std::string strImg = value.AIMAG().DumpHexadecimal();
874       inInitializer->rawType = converter.genType(TC, KIND);
875       llvm::APFloat realVal{builder.getKindMap().getFloatSemantics(KIND),
876                             strReal};
877       val = builder.getFloatAttr(inInitializer->rawType, realVal);
878       inInitializer->rawVals.push_back(val);
879       llvm::APFloat imgVal{builder.getKindMap().getFloatSemantics(KIND),
880                            strImg};
881       val = builder.getFloatAttr(inInitializer->rawType, imgVal);
882     }
883     inInitializer->rawVals.push_back(val);
884   }
885 
886   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
887   ExtValue
888   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
889                         Fortran::common::TypeCategory::Character, 1>> &value,
890                     int64_t len) {
891     assert(value.size() == static_cast<std::uint64_t>(len));
892     // Outline character constant in ro data if it is not in an initializer.
893     if (!inInitializer)
894       return fir::factory::createStringLiteral(builder, getLoc(), value);
895     // When in an initializer context, construct the literal op itself and do
896     // not construct another constant object in rodata.
897     fir::StringLitOp stringLit = builder.createStringLitOp(getLoc(), value);
898     mlir::Value lenp = builder.createIntegerConstant(
899         getLoc(), builder.getCharacterLengthType(), len);
900     return fir::CharBoxValue{stringLit.getResult(), lenp};
901   }
902   /// Convert a non ascii scalar literal CHARACTER to IR. (specialization)
903   template <int KIND>
904   ExtValue
905   genScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
906                    Fortran::common::TypeCategory::Character, KIND>> &value,
907                int64_t len) {
908     using ET = typename std::decay_t<decltype(value)>::value_type;
909     if constexpr (KIND == 1) {
910       return genAsciiScalarLit(value, len);
911     }
912     fir::CharacterType type =
913         fir::CharacterType::get(builder.getContext(), KIND, len);
914     auto consLit = [&]() -> fir::StringLitOp {
915       mlir::MLIRContext *context = builder.getContext();
916       std::int64_t size = static_cast<std::int64_t>(value.size());
917       mlir::ShapedType shape = mlir::VectorType::get(
918           llvm::ArrayRef<std::int64_t>{size},
919           mlir::IntegerType::get(builder.getContext(), sizeof(ET) * 8));
920       auto strAttr = mlir::DenseElementsAttr::get(
921           shape, llvm::ArrayRef<ET>{value.data(), value.size()});
922       auto valTag = mlir::StringAttr::get(context, fir::StringLitOp::value());
923       mlir::NamedAttribute dataAttr(valTag, strAttr);
924       auto sizeTag = mlir::StringAttr::get(context, fir::StringLitOp::size());
925       mlir::NamedAttribute sizeAttr(sizeTag, builder.getI64IntegerAttr(len));
926       llvm::SmallVector<mlir::NamedAttribute> attrs = {dataAttr, sizeAttr};
927       return builder.create<fir::StringLitOp>(
928           getLoc(), llvm::ArrayRef<mlir::Type>{type}, llvm::None, attrs);
929     };
930 
931     mlir::Value lenp = builder.createIntegerConstant(
932         getLoc(), builder.getCharacterLengthType(), len);
933     // When in an initializer context, construct the literal op itself and do
934     // not construct another constant object in rodata.
935     if (inInitializer)
936       return fir::CharBoxValue{consLit().getResult(), lenp};
937 
938     // Otherwise, the string is in a plain old expression so "outline" the value
939     // by hashconsing it to a constant literal object.
940 
941     // FIXME: For wider char types, lowering ought to use an array of i16 or
942     // i32. But for now, lowering just fakes that the string value is a range of
943     // i8 to get it past the C++ compiler.
944     std::string globalName =
945         fir::factory::uniqueCGIdent("cl", (const char *)value.c_str());
946     fir::GlobalOp global = builder.getNamedGlobal(globalName);
947     if (!global)
948       global = builder.createGlobalConstant(
949           getLoc(), type, globalName,
950           [&](fir::FirOpBuilder &builder) {
951             fir::StringLitOp str = consLit();
952             builder.create<fir::HasValueOp>(getLoc(), str);
953           },
954           builder.createLinkOnceLinkage());
955     auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(),
956                                               global.getSymbol());
957     return fir::CharBoxValue{addr, lenp};
958   }
959 
960   template <Fortran::common::TypeCategory TC, int KIND>
961   ExtValue genArrayLit(
962       const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
963           &con) {
964     mlir::Location loc = getLoc();
965     mlir::IndexType idxTy = builder.getIndexType();
966     Fortran::evaluate::ConstantSubscript size =
967         Fortran::evaluate::GetSize(con.shape());
968     fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end());
969     mlir::Type eleTy;
970     if constexpr (TC == Fortran::common::TypeCategory::Character)
971       eleTy = converter.genType(TC, KIND, {con.LEN()});
972     else
973       eleTy = converter.genType(TC, KIND);
974     auto arrayTy = fir::SequenceType::get(shape, eleTy);
975     mlir::Value array;
976     llvm::SmallVector<mlir::Value> lbounds;
977     llvm::SmallVector<mlir::Value> extents;
978     if (!inInitializer || !inInitializer->genRawVals) {
979       array = builder.create<fir::UndefOp>(loc, arrayTy);
980       for (auto [lb, extent] : llvm::zip(con.lbounds(), shape)) {
981         lbounds.push_back(builder.createIntegerConstant(loc, idxTy, lb - 1));
982         extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
983       }
984     }
985     if (size == 0) {
986       if constexpr (TC == Fortran::common::TypeCategory::Character) {
987         mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN());
988         return fir::CharArrayBoxValue{array, len, extents, lbounds};
989       } else {
990         return fir::ArrayBoxValue{array, extents, lbounds};
991       }
992     }
993     Fortran::evaluate::ConstantSubscripts subscripts = con.lbounds();
994     auto createIdx = [&]() {
995       llvm::SmallVector<mlir::Attribute> idx;
996       for (size_t i = 0; i < subscripts.size(); ++i)
997         idx.push_back(
998             builder.getIntegerAttr(idxTy, subscripts[i] - con.lbounds()[i]));
999       return idx;
1000     };
1001     if constexpr (TC == Fortran::common::TypeCategory::Character) {
1002       assert(array && "array must not be nullptr");
1003       do {
1004         mlir::Value elementVal =
1005             fir::getBase(genScalarLit<KIND>(con.At(subscripts), con.LEN()));
1006         array = builder.create<fir::InsertValueOp>(
1007             loc, arrayTy, array, elementVal, builder.getArrayAttr(createIdx()));
1008       } while (con.IncrementSubscripts(subscripts));
1009       mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN());
1010       return fir::CharArrayBoxValue{array, len, extents, lbounds};
1011     } else {
1012       llvm::SmallVector<mlir::Attribute> rangeStartIdx;
1013       uint64_t rangeSize = 0;
1014       do {
1015         if (inInitializer && inInitializer->genRawVals) {
1016           genRawLit<TC, KIND>(con.At(subscripts));
1017           continue;
1018         }
1019         auto getElementVal = [&]() {
1020           return builder.createConvert(
1021               loc, eleTy,
1022               fir::getBase(genScalarLit<TC, KIND>(con.At(subscripts))));
1023         };
1024         Fortran::evaluate::ConstantSubscripts nextSubscripts = subscripts;
1025         bool nextIsSame = con.IncrementSubscripts(nextSubscripts) &&
1026                           con.At(subscripts) == con.At(nextSubscripts);
1027         if (!rangeSize && !nextIsSame) { // single (non-range) value
1028           array = builder.create<fir::InsertValueOp>(
1029               loc, arrayTy, array, getElementVal(),
1030               builder.getArrayAttr(createIdx()));
1031         } else if (!rangeSize) { // start a range
1032           rangeStartIdx = createIdx();
1033           rangeSize = 1;
1034         } else if (nextIsSame) { // expand a range
1035           ++rangeSize;
1036         } else { // end a range
1037           llvm::SmallVector<int64_t> rangeBounds;
1038           llvm::SmallVector<mlir::Attribute> idx = createIdx();
1039           for (size_t i = 0; i < idx.size(); ++i) {
1040             rangeBounds.push_back(rangeStartIdx[i]
1041                                       .cast<mlir::IntegerAttr>()
1042                                       .getValue()
1043                                       .getSExtValue());
1044             rangeBounds.push_back(
1045                 idx[i].cast<mlir::IntegerAttr>().getValue().getSExtValue());
1046           }
1047           array = builder.create<fir::InsertOnRangeOp>(
1048               loc, arrayTy, array, getElementVal(),
1049               builder.getIndexVectorAttr(rangeBounds));
1050           rangeSize = 0;
1051         }
1052       } while (con.IncrementSubscripts(subscripts));
1053       return fir::ArrayBoxValue{array, extents, lbounds};
1054     }
1055   }
1056 
1057   template <Fortran::common::TypeCategory TC, int KIND>
1058   ExtValue
1059   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
1060              &con) {
1061     if (con.Rank() > 0)
1062       return genArrayLit(con);
1063     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
1064         opt = con.GetScalarValue();
1065     assert(opt.has_value() && "constant has no value");
1066     if constexpr (TC == Fortran::common::TypeCategory::Character) {
1067       return genScalarLit<KIND>(opt.value(), con.LEN());
1068     } else {
1069       return genScalarLit<TC, KIND>(opt.value());
1070     }
1071   }
1072 
1073   fir::ExtendedValue genval(
1074       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
1075     TODO(getLoc(), "genval constant derived");
1076   }
1077 
1078   template <typename A>
1079   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
1080     TODO(getLoc(), "genval ArrayConstructor<A>");
1081   }
1082 
1083   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
1084     TODO(getLoc(), "gen ComplexPart");
1085   }
1086   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
1087     TODO(getLoc(), "genval ComplexPart");
1088   }
1089 
1090   ExtValue gen(const Fortran::evaluate::Substring &s) {
1091     TODO(getLoc(), "gen Substring");
1092   }
1093   ExtValue genval(const Fortran::evaluate::Substring &ss) {
1094     TODO(getLoc(), "genval Substring");
1095   }
1096 
1097   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
1098     if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>(
1099             &subs.u)) {
1100       if (s->value().Rank() > 0)
1101         fir::emitFatalError(getLoc(), "vector subscript is not scalar");
1102       return {genval(s->value())};
1103     }
1104     fir::emitFatalError(getLoc(), "subscript triple notation is not scalar");
1105   }
1106 
1107   ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) {
1108     return genval(subs);
1109   }
1110 
1111   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
1112     return std::visit([&](const auto &x) { return gen(x); }, dref.u);
1113   }
1114   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
1115     return std::visit([&](const auto &x) { return genval(x); }, dref.u);
1116   }
1117 
1118   // Helper function to turn the Component structure into a list of nested
1119   // components, ordered from largest/leftmost to smallest/rightmost:
1120   //  - where only the smallest/rightmost item may be allocatable or a pointer
1121   //    (nested allocatable/pointer components require nested coordinate_of ops)
1122   //  - that does not contain any parent components
1123   //    (the front end places parent components directly in the object)
1124   // Return the object used as the base coordinate for the component chain.
1125   static Fortran::evaluate::DataRef const *
1126   reverseComponents(const Fortran::evaluate::Component &cmpt,
1127                     std::list<const Fortran::evaluate::Component *> &list) {
1128     if (!cmpt.GetLastSymbol().test(
1129             Fortran::semantics::Symbol::Flag::ParentComp))
1130       list.push_front(&cmpt);
1131     return std::visit(
1132         Fortran::common::visitors{
1133             [&](const Fortran::evaluate::Component &x) {
1134               if (Fortran::semantics::IsAllocatableOrPointer(x.GetLastSymbol()))
1135                 return &cmpt.base();
1136               return reverseComponents(x, list);
1137             },
1138             [&](auto &) { return &cmpt.base(); },
1139         },
1140         cmpt.base().u);
1141   }
1142 
1143   // Return the coordinate of the component reference
1144   ExtValue genComponent(const Fortran::evaluate::Component &cmpt) {
1145     std::list<const Fortran::evaluate::Component *> list;
1146     const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list);
1147     llvm::SmallVector<mlir::Value> coorArgs;
1148     ExtValue obj = gen(*base);
1149     mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType());
1150     mlir::Location loc = getLoc();
1151     auto fldTy = fir::FieldType::get(&converter.getMLIRContext());
1152     // FIXME: need to thread the LEN type parameters here.
1153     for (const Fortran::evaluate::Component *field : list) {
1154       auto recTy = ty.cast<fir::RecordType>();
1155       const Fortran::semantics::Symbol &sym = field->GetLastSymbol();
1156       llvm::StringRef name = toStringRef(sym.name());
1157       coorArgs.push_back(builder.create<fir::FieldIndexOp>(
1158           loc, fldTy, name, recTy, fir::getTypeParams(obj)));
1159       ty = recTy.getType(name);
1160     }
1161     ty = builder.getRefType(ty);
1162     return fir::factory::componentToExtendedValue(
1163         builder, loc,
1164         builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj),
1165                                           coorArgs));
1166   }
1167 
1168   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
1169     // Components may be pointer or allocatable. In the gen() path, the mutable
1170     // aspect is lost to simplify handling on the client side. To retain the
1171     // mutable aspect, genMutableBoxValue should be used.
1172     return genComponent(cmpt).match(
1173         [&](const fir::MutableBoxValue &mutableBox) {
1174           return fir::factory::genMutableBoxRead(builder, getLoc(), mutableBox);
1175         },
1176         [](auto &box) -> ExtValue { return box; });
1177   }
1178 
1179   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
1180     return genLoad(gen(cmpt));
1181   }
1182 
1183   ExtValue genval(const Fortran::semantics::Bound &bound) {
1184     TODO(getLoc(), "genval Bound");
1185   }
1186 
1187   /// Return lower bounds of \p box in dimension \p dim. The returned value
1188   /// has type \ty.
1189   mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) {
1190     assert(box.rank() > 0 && "must be an array");
1191     mlir::Location loc = getLoc();
1192     mlir::Value one = builder.createIntegerConstant(loc, ty, 1);
1193     mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one);
1194     return builder.createConvert(loc, ty, lb);
1195   }
1196 
1197   static bool isSlice(const Fortran::evaluate::ArrayRef &aref) {
1198     for (const Fortran::evaluate::Subscript &sub : aref.subscript())
1199       if (std::holds_alternative<Fortran::evaluate::Triplet>(sub.u))
1200         return true;
1201     return false;
1202   }
1203 
1204   /// Lower an ArrayRef to a fir.coordinate_of given its lowered base.
1205   ExtValue genCoordinateOp(const ExtValue &array,
1206                            const Fortran::evaluate::ArrayRef &aref) {
1207     mlir::Location loc = getLoc();
1208     // References to array of rank > 1 with non constant shape that are not
1209     // fir.box must be collapsed into an offset computation in lowering already.
1210     // The same is needed with dynamic length character arrays of all ranks.
1211     mlir::Type baseType =
1212         fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType());
1213     if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) ||
1214         fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType)))
1215       if (!array.getBoxOf<fir::BoxValue>())
1216         return genOffsetAndCoordinateOp(array, aref);
1217     // Generate a fir.coordinate_of with zero based array indexes.
1218     llvm::SmallVector<mlir::Value> args;
1219     for (const auto &subsc : llvm::enumerate(aref.subscript())) {
1220       ExtValue subVal = genSubscript(subsc.value());
1221       assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar");
1222       mlir::Value val = fir::getBase(subVal);
1223       mlir::Type ty = val.getType();
1224       mlir::Value lb = getLBound(array, subsc.index(), ty);
1225       args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb));
1226     }
1227 
1228     mlir::Value base = fir::getBase(array);
1229     auto seqTy =
1230         fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>();
1231     assert(args.size() == seqTy.getDimension());
1232     mlir::Type ty = builder.getRefType(seqTy.getEleTy());
1233     auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args);
1234     return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr);
1235   }
1236 
1237   /// Lower an ArrayRef to a fir.coordinate_of using an element offset instead
1238   /// of array indexes.
1239   /// This generates offset computation from the indexes and length parameters,
1240   /// and use the offset to access the element with a fir.coordinate_of. This
1241   /// must only be used if it is not possible to generate a normal
1242   /// fir.coordinate_of using array indexes (i.e. when the shape information is
1243   /// unavailable in the IR).
1244   ExtValue genOffsetAndCoordinateOp(const ExtValue &array,
1245                                     const Fortran::evaluate::ArrayRef &aref) {
1246     mlir::Location loc = getLoc();
1247     mlir::Value addr = fir::getBase(array);
1248     mlir::Type arrTy = fir::dyn_cast_ptrEleTy(addr.getType());
1249     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
1250     mlir::Type seqTy = builder.getRefType(builder.getVarLenSeqTy(eleTy));
1251     mlir::Type refTy = builder.getRefType(eleTy);
1252     mlir::Value base = builder.createConvert(loc, seqTy, addr);
1253     mlir::IndexType idxTy = builder.getIndexType();
1254     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
1255     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
1256     auto getLB = [&](const auto &arr, unsigned dim) -> mlir::Value {
1257       return arr.getLBounds().empty() ? one : arr.getLBounds()[dim];
1258     };
1259     auto genFullDim = [&](const auto &arr, mlir::Value delta) -> mlir::Value {
1260       mlir::Value total = zero;
1261       assert(arr.getExtents().size() == aref.subscript().size());
1262       delta = builder.createConvert(loc, idxTy, delta);
1263       unsigned dim = 0;
1264       for (auto [ext, sub] : llvm::zip(arr.getExtents(), aref.subscript())) {
1265         ExtValue subVal = genSubscript(sub);
1266         assert(fir::isUnboxedValue(subVal));
1267         mlir::Value val =
1268             builder.createConvert(loc, idxTy, fir::getBase(subVal));
1269         mlir::Value lb = builder.createConvert(loc, idxTy, getLB(arr, dim));
1270         mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, val, lb);
1271         mlir::Value prod =
1272             builder.create<mlir::arith::MulIOp>(loc, delta, diff);
1273         total = builder.create<mlir::arith::AddIOp>(loc, prod, total);
1274         if (ext)
1275           delta = builder.create<mlir::arith::MulIOp>(loc, delta, ext);
1276         ++dim;
1277       }
1278       mlir::Type origRefTy = refTy;
1279       if (fir::factory::CharacterExprHelper::isCharacterScalar(refTy)) {
1280         fir::CharacterType chTy =
1281             fir::factory::CharacterExprHelper::getCharacterType(refTy);
1282         if (fir::characterWithDynamicLen(chTy)) {
1283           mlir::MLIRContext *ctx = builder.getContext();
1284           fir::KindTy kind =
1285               fir::factory::CharacterExprHelper::getCharacterKind(chTy);
1286           fir::CharacterType singleTy =
1287               fir::CharacterType::getSingleton(ctx, kind);
1288           refTy = builder.getRefType(singleTy);
1289           mlir::Type seqRefTy =
1290               builder.getRefType(builder.getVarLenSeqTy(singleTy));
1291           base = builder.createConvert(loc, seqRefTy, base);
1292         }
1293       }
1294       auto coor = builder.create<fir::CoordinateOp>(
1295           loc, refTy, base, llvm::ArrayRef<mlir::Value>{total});
1296       // Convert to expected, original type after address arithmetic.
1297       return builder.createConvert(loc, origRefTy, coor);
1298     };
1299     return array.match(
1300         [&](const fir::ArrayBoxValue &arr) -> ExtValue {
1301           // FIXME: this check can be removed when slicing is implemented
1302           if (isSlice(aref))
1303             fir::emitFatalError(
1304                 getLoc(),
1305                 "slice should be handled in array expression context");
1306           return genFullDim(arr, one);
1307         },
1308         [&](const fir::CharArrayBoxValue &arr) -> ExtValue {
1309           mlir::Value delta = arr.getLen();
1310           // If the length is known in the type, fir.coordinate_of will
1311           // already take the length into account.
1312           if (fir::factory::CharacterExprHelper::hasConstantLengthInType(arr))
1313             delta = one;
1314           return fir::CharBoxValue(genFullDim(arr, delta), arr.getLen());
1315         },
1316         [&](const fir::BoxValue &arr) -> ExtValue {
1317           // CoordinateOp for BoxValue is not generated here. The dimensions
1318           // must be kept in the fir.coordinate_op so that potential fir.box
1319           // strides can be applied by codegen.
1320           fir::emitFatalError(
1321               loc, "internal: BoxValue in dim-collapsed fir.coordinate_of");
1322         },
1323         [&](const auto &) -> ExtValue {
1324           fir::emitFatalError(loc, "internal: array lowering failed");
1325         });
1326   }
1327 
1328   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
1329     ExtValue base = aref.base().IsSymbol() ? gen(aref.base().GetFirstSymbol())
1330                                            : gen(aref.base().GetComponent());
1331     return genCoordinateOp(base, aref);
1332   }
1333   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
1334     return genLoad(gen(aref));
1335   }
1336 
1337   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
1338     TODO(getLoc(), "gen CoarrayRef");
1339   }
1340   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
1341     TODO(getLoc(), "genval CoarrayRef");
1342   }
1343 
1344   template <typename A>
1345   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
1346     return std::visit([&](const auto &x) { return gen(x); }, des.u);
1347   }
1348   template <typename A>
1349   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
1350     return std::visit([&](const auto &x) { return genval(x); }, des.u);
1351   }
1352 
1353   mlir::Type genType(const Fortran::evaluate::DynamicType &dt) {
1354     if (dt.category() != Fortran::common::TypeCategory::Derived)
1355       return converter.genType(dt.category(), dt.kind());
1356     return converter.genType(dt.GetDerivedTypeSpec());
1357   }
1358 
1359   /// Lower a function reference
1360   template <typename A>
1361   ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1362     if (!funcRef.GetType().has_value())
1363       fir::emitFatalError(getLoc(), "internal: a function must have a type");
1364     mlir::Type resTy = genType(*funcRef.GetType());
1365     return genProcedureRef(funcRef, {resTy});
1366   }
1367 
1368   /// Lower function call `funcRef` and return a reference to the resultant
1369   /// value. This is required for lowering expressions such as `f1(f2(v))`.
1370   template <typename A>
1371   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1372     ExtValue retVal = genFunctionRef(funcRef);
1373     mlir::Value retValBase = fir::getBase(retVal);
1374     if (fir::conformsWithPassByRef(retValBase.getType()))
1375       return retVal;
1376     auto mem = builder.create<fir::AllocaOp>(getLoc(), retValBase.getType());
1377     builder.create<fir::StoreOp>(getLoc(), retValBase, mem);
1378     return fir::substBase(retVal, mem.getResult());
1379   }
1380 
1381   /// helper to detect statement functions
1382   static bool
1383   isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) {
1384     if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
1385       if (const auto *details =
1386               symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
1387         return details->stmtFunction().has_value();
1388     return false;
1389   }
1390 
1391   /// Helper to package a Value and its properties into an ExtendedValue.
1392   static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base,
1393                                   llvm::ArrayRef<mlir::Value> extents,
1394                                   llvm::ArrayRef<mlir::Value> lengths) {
1395     mlir::Type type = base.getType();
1396     if (type.isa<fir::BoxType>())
1397       return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);
1398     type = fir::unwrapRefType(type);
1399     if (type.isa<fir::BoxType>())
1400       return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});
1401     if (auto seqTy = type.dyn_cast<fir::SequenceType>()) {
1402       if (seqTy.getDimension() != extents.size())
1403         fir::emitFatalError(loc, "incorrect number of extents for array");
1404       if (seqTy.getEleTy().isa<fir::CharacterType>()) {
1405         if (lengths.empty())
1406           fir::emitFatalError(loc, "missing length for character");
1407         assert(lengths.size() == 1);
1408         return fir::CharArrayBoxValue(base, lengths[0], extents);
1409       }
1410       return fir::ArrayBoxValue(base, extents);
1411     }
1412     if (type.isa<fir::CharacterType>()) {
1413       if (lengths.empty())
1414         fir::emitFatalError(loc, "missing length for character");
1415       assert(lengths.size() == 1);
1416       return fir::CharBoxValue(base, lengths[0]);
1417     }
1418     return base;
1419   }
1420 
1421   // Find the argument that corresponds to the host associations.
1422   // Verify some assumptions about how the signature was built here.
1423   [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) {
1424     // Scan the argument list from last to first as the host associations are
1425     // appended for now.
1426     for (unsigned i = fn.getNumArguments(); i > 0; --i)
1427       if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
1428         // Host assoc tuple must be last argument (for now).
1429         assert(i == fn.getNumArguments() && "tuple must be last");
1430         return i - 1;
1431       }
1432     llvm_unreachable("anyFuncArgsHaveAttr failed");
1433   }
1434 
1435   /// Create a contiguous temporary array with the same shape,
1436   /// length parameters and type as mold. It is up to the caller to deallocate
1437   /// the temporary.
1438   ExtValue genArrayTempFromMold(const ExtValue &mold,
1439                                 llvm::StringRef tempName) {
1440     mlir::Type type = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(mold).getType());
1441     assert(type && "expected descriptor or memory type");
1442     mlir::Location loc = getLoc();
1443     llvm::SmallVector<mlir::Value> extents =
1444         fir::factory::getExtents(builder, loc, mold);
1445     llvm::SmallVector<mlir::Value> allocMemTypeParams =
1446         fir::getTypeParams(mold);
1447     mlir::Value charLen;
1448     mlir::Type elementType = fir::unwrapSequenceType(type);
1449     if (auto charType = elementType.dyn_cast<fir::CharacterType>()) {
1450       charLen = allocMemTypeParams.empty()
1451                     ? fir::factory::readCharLen(builder, loc, mold)
1452                     : allocMemTypeParams[0];
1453       if (charType.hasDynamicLen() && allocMemTypeParams.empty())
1454         allocMemTypeParams.push_back(charLen);
1455     } else if (fir::hasDynamicSize(elementType)) {
1456       TODO(loc, "Creating temporary for derived type with length parameters");
1457     }
1458 
1459     mlir::Value temp = builder.create<fir::AllocMemOp>(
1460         loc, type, tempName, allocMemTypeParams, extents);
1461     if (fir::unwrapSequenceType(type).isa<fir::CharacterType>())
1462       return fir::CharArrayBoxValue{temp, charLen, extents};
1463     return fir::ArrayBoxValue{temp, extents};
1464   }
1465 
1466   /// Copy \p source array into \p dest array. Both arrays must be
1467   /// conforming, but neither array must be contiguous.
1468   void genArrayCopy(ExtValue dest, ExtValue source) {
1469     return createSomeArrayAssignment(converter, dest, source, symMap, stmtCtx);
1470   }
1471 
1472   /// Lower a non-elemental procedure reference and read allocatable and pointer
1473   /// results into normal values.
1474   ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
1475                            llvm::Optional<mlir::Type> resultType) {
1476     ExtValue res = genRawProcedureRef(procRef, resultType);
1477     return res;
1478   }
1479 
1480   /// Given a call site for which the arguments were already lowered, generate
1481   /// the call and return the result. This function deals with explicit result
1482   /// allocation and lowering if needed. It also deals with passing the host
1483   /// link to internal procedures.
1484   ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller,
1485                               mlir::FunctionType callSiteType,
1486                               llvm::Optional<mlir::Type> resultType) {
1487     mlir::Location loc = getLoc();
1488     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1489     // Handle cases where caller must allocate the result or a fir.box for it.
1490     bool mustPopSymMap = false;
1491     if (caller.mustMapInterfaceSymbols()) {
1492       symMap.pushScope();
1493       mustPopSymMap = true;
1494       Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap);
1495     }
1496     // If this is an indirect call, retrieve the function address. Also retrieve
1497     // the result length if this is a character function (note that this length
1498     // will be used only if there is no explicit length in the local interface).
1499     mlir::Value funcPointer;
1500     mlir::Value charFuncPointerLength;
1501     if (const Fortran::semantics::Symbol *sym =
1502             caller.getIfIndirectCallSymbol()) {
1503       funcPointer = symMap.lookupSymbol(*sym).getAddr();
1504       if (!funcPointer)
1505         fir::emitFatalError(loc, "failed to find indirect call symbol address");
1506       if (fir::isCharacterProcedureTuple(funcPointer.getType(),
1507                                          /*acceptRawFunc=*/false))
1508         std::tie(funcPointer, charFuncPointerLength) =
1509             fir::factory::extractCharacterProcedureTuple(builder, loc,
1510                                                          funcPointer);
1511     }
1512 
1513     mlir::IndexType idxTy = builder.getIndexType();
1514     auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
1515       return builder.createConvert(
1516           loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
1517     };
1518     llvm::SmallVector<mlir::Value> resultLengths;
1519     auto allocatedResult = [&]() -> llvm::Optional<ExtValue> {
1520       llvm::SmallVector<mlir::Value> extents;
1521       llvm::SmallVector<mlir::Value> lengths;
1522       if (!caller.callerAllocateResult())
1523         return {};
1524       mlir::Type type = caller.getResultStorageType();
1525       if (type.isa<fir::SequenceType>())
1526         caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) {
1527           extents.emplace_back(lowerSpecExpr(e));
1528         });
1529       caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) {
1530         lengths.emplace_back(lowerSpecExpr(e));
1531       });
1532 
1533       // Result length parameters should not be provided to box storage
1534       // allocation and save_results, but they are still useful information to
1535       // keep in the ExtendedValue if non-deferred.
1536       if (!type.isa<fir::BoxType>()) {
1537         if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
1538           // Calling an assumed length function. This is only possible if this
1539           // is a call to a character dummy procedure.
1540           if (!charFuncPointerLength)
1541             fir::emitFatalError(loc, "failed to retrieve character function "
1542                                      "length while calling it");
1543           lengths.push_back(charFuncPointerLength);
1544         }
1545         resultLengths = lengths;
1546       }
1547 
1548       if (!extents.empty() || !lengths.empty()) {
1549         auto *bldr = &converter.getFirOpBuilder();
1550         auto stackSaveFn = fir::factory::getLlvmStackSave(builder);
1551         auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName());
1552         mlir::Value sp =
1553             bldr->create<fir::CallOp>(loc, stackSaveFn.getType().getResults(),
1554                                       stackSaveSymbol, mlir::ValueRange{})
1555                 .getResult(0);
1556         stmtCtx.attachCleanup([bldr, loc, sp]() {
1557           auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr);
1558           auto stackRestoreSymbol =
1559               bldr->getSymbolRefAttr(stackRestoreFn.getName());
1560           bldr->create<fir::CallOp>(loc, stackRestoreFn.getType().getResults(),
1561                                     stackRestoreSymbol, mlir::ValueRange{sp});
1562         });
1563       }
1564       mlir::Value temp =
1565           builder.createTemporary(loc, type, ".result", extents, resultLengths);
1566       return toExtendedValue(loc, temp, extents, lengths);
1567     }();
1568 
1569     if (mustPopSymMap)
1570       symMap.popScope();
1571 
1572     // Place allocated result or prepare the fir.save_result arguments.
1573     mlir::Value arrayResultShape;
1574     if (allocatedResult) {
1575       if (std::optional<Fortran::lower::CallInterface<
1576               Fortran::lower::CallerInterface>::PassedEntity>
1577               resultArg = caller.getPassedResult()) {
1578         if (resultArg->passBy == PassBy::AddressAndLength)
1579           caller.placeAddressAndLengthInput(*resultArg,
1580                                             fir::getBase(*allocatedResult),
1581                                             fir::getLen(*allocatedResult));
1582         else if (resultArg->passBy == PassBy::BaseAddress)
1583           caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
1584         else
1585           fir::emitFatalError(
1586               loc, "only expect character scalar result to be passed by ref");
1587       } else {
1588         assert(caller.mustSaveResult());
1589         arrayResultShape = allocatedResult->match(
1590             [&](const fir::CharArrayBoxValue &) {
1591               return builder.createShape(loc, *allocatedResult);
1592             },
1593             [&](const fir::ArrayBoxValue &) {
1594               return builder.createShape(loc, *allocatedResult);
1595             },
1596             [&](const auto &) { return mlir::Value{}; });
1597       }
1598     }
1599 
1600     // In older Fortran, procedure argument types are inferred. This may lead
1601     // different view of what the function signature is in different locations.
1602     // Casts are inserted as needed below to accommodate this.
1603 
1604     // The mlir::FuncOp type prevails, unless it has a different number of
1605     // arguments which can happen in legal program if it was passed as a dummy
1606     // procedure argument earlier with no further type information.
1607     mlir::SymbolRefAttr funcSymbolAttr;
1608     bool addHostAssociations = false;
1609     if (!funcPointer) {
1610       mlir::FunctionType funcOpType = caller.getFuncOp().getType();
1611       mlir::SymbolRefAttr symbolAttr =
1612           builder.getSymbolRefAttr(caller.getMangledName());
1613       if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
1614           callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
1615           fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
1616                                    fir::getHostAssocAttrName())) {
1617         // The number of arguments is off by one, and we're lowering a function
1618         // with host associations. Modify call to include host associations
1619         // argument by appending the value at the end of the operands.
1620         assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
1621                converter.hostAssocTupleValue().getType());
1622         addHostAssociations = true;
1623       }
1624       if (!addHostAssociations &&
1625           (callSiteType.getNumResults() != funcOpType.getNumResults() ||
1626            callSiteType.getNumInputs() != funcOpType.getNumInputs())) {
1627         // Deal with argument number mismatch by making a function pointer so
1628         // that function type cast can be inserted. Do not emit a warning here
1629         // because this can happen in legal program if the function is not
1630         // defined here and it was first passed as an argument without any more
1631         // information.
1632         funcPointer =
1633             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1634       } else if (callSiteType.getResults() != funcOpType.getResults()) {
1635         // Implicit interface result type mismatch are not standard Fortran, but
1636         // some compilers are not complaining about it.  The front end is not
1637         // protecting lowering from this currently. Support this with a
1638         // discouraging warning.
1639         LLVM_DEBUG(mlir::emitWarning(
1640             loc, "a return type mismatch is not standard compliant and may "
1641                  "lead to undefined behavior."));
1642         // Cast the actual function to the current caller implicit type because
1643         // that is the behavior we would get if we could not see the definition.
1644         funcPointer =
1645             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1646       } else {
1647         funcSymbolAttr = symbolAttr;
1648       }
1649     }
1650 
1651     mlir::FunctionType funcType =
1652         funcPointer ? callSiteType : caller.getFuncOp().getType();
1653     llvm::SmallVector<mlir::Value> operands;
1654     // First operand of indirect call is the function pointer. Cast it to
1655     // required function type for the call to handle procedures that have a
1656     // compatible interface in Fortran, but that have different signatures in
1657     // FIR.
1658     if (funcPointer) {
1659       operands.push_back(
1660           funcPointer.getType().isa<fir::BoxProcType>()
1661               ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
1662               : builder.createConvert(loc, funcType, funcPointer));
1663     }
1664 
1665     // Deal with potential mismatches in arguments types. Passing an array to a
1666     // scalar argument should for instance be tolerated here.
1667     bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
1668     for (auto [fst, snd] :
1669          llvm::zip(caller.getInputs(), funcType.getInputs())) {
1670       // When passing arguments to a procedure that can be called an implicit
1671       // interface, allow character actual arguments to be passed to dummy
1672       // arguments of any type and vice versa
1673       mlir::Value cast;
1674       auto *context = builder.getContext();
1675       if (snd.isa<fir::BoxProcType>() &&
1676           fst.getType().isa<mlir::FunctionType>()) {
1677         auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None);
1678         auto boxProcTy = builder.getBoxProcType(funcTy);
1679         if (mlir::Value host = argumentHostAssocs(converter, fst)) {
1680           cast = builder.create<fir::EmboxProcOp>(
1681               loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
1682         } else {
1683           cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
1684         }
1685       } else {
1686         cast = builder.convertWithSemantics(loc, snd, fst,
1687                                             callingImplicitInterface);
1688       }
1689       operands.push_back(cast);
1690     }
1691 
1692     // Add host associations as necessary.
1693     if (addHostAssociations)
1694       operands.push_back(converter.hostAssocTupleValue());
1695 
1696     auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
1697                                             funcSymbolAttr, operands);
1698 
1699     if (caller.mustSaveResult())
1700       builder.create<fir::SaveResultOp>(
1701           loc, call.getResult(0), fir::getBase(allocatedResult.getValue()),
1702           arrayResultShape, resultLengths);
1703 
1704     if (allocatedResult) {
1705       allocatedResult->match(
1706           [&](const fir::MutableBoxValue &box) {
1707             if (box.isAllocatable()) {
1708               // 9.7.3.2 point 4. Finalize allocatables.
1709               fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
1710               stmtCtx.attachCleanup([bldr, loc, box]() {
1711                 fir::factory::genFinalization(*bldr, loc, box);
1712               });
1713             }
1714           },
1715           [](const auto &) {});
1716       return *allocatedResult;
1717     }
1718 
1719     if (!resultType.hasValue())
1720       return mlir::Value{}; // subroutine call
1721     // For now, Fortran return values are implemented with a single MLIR
1722     // function return value.
1723     assert(call.getNumResults() == 1 &&
1724            "Expected exactly one result in FUNCTION call");
1725     return call.getResult(0);
1726   }
1727 
1728   /// Like genExtAddr, but ensure the address returned is a temporary even if \p
1729   /// expr is variable inside parentheses.
1730   ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
1731     // In general, genExtAddr might not create a temp for variable inside
1732     // parentheses to avoid creating array temporary in sub-expressions. It only
1733     // ensures the sub-expression is not re-associated with other parts of the
1734     // expression. In the call semantics, there is a difference between expr and
1735     // variable (see R1524). For expressions, a variable storage must not be
1736     // argument associated since it could be modified inside the call, or the
1737     // variable could also be modified by other means during the call.
1738     if (!isParenthesizedVariable(expr))
1739       return genExtAddr(expr);
1740     mlir::Location loc = getLoc();
1741     if (expr.Rank() > 0)
1742       TODO(loc, "genTempExtAddr array");
1743     return genExtValue(expr).match(
1744         [&](const fir::CharBoxValue &boxChar) -> ExtValue {
1745           TODO(loc, "genTempExtAddr CharBoxValue");
1746         },
1747         [&](const fir::UnboxedValue &v) -> ExtValue {
1748           mlir::Type type = v.getType();
1749           mlir::Value value = v;
1750           if (fir::isa_ref_type(type))
1751             value = builder.create<fir::LoadOp>(loc, value);
1752           mlir::Value temp = builder.createTemporary(loc, value.getType());
1753           builder.create<fir::StoreOp>(loc, value, temp);
1754           return temp;
1755         },
1756         [&](const fir::BoxValue &x) -> ExtValue {
1757           // Derived type scalar that may be polymorphic.
1758           assert(!x.hasRank() && x.isDerived());
1759           if (x.isDerivedWithLengthParameters())
1760             fir::emitFatalError(
1761                 loc, "making temps for derived type with length parameters");
1762           // TODO: polymorphic aspects should be kept but for now the temp
1763           // created always has the declared type.
1764           mlir::Value var =
1765               fir::getBase(fir::factory::readBoxValue(builder, loc, x));
1766           auto value = builder.create<fir::LoadOp>(loc, var);
1767           mlir::Value temp = builder.createTemporary(loc, value.getType());
1768           builder.create<fir::StoreOp>(loc, value, temp);
1769           return temp;
1770         },
1771         [&](const auto &) -> ExtValue {
1772           fir::emitFatalError(loc, "expr is not a scalar value");
1773         });
1774   }
1775 
1776   /// Helper structure to track potential copy-in of non contiguous variable
1777   /// argument into a contiguous temp. It is used to deallocate the temp that
1778   /// may have been created as well as to the copy-out from the temp to the
1779   /// variable after the call.
1780   struct CopyOutPair {
1781     ExtValue var;
1782     ExtValue temp;
1783     // Flag to indicate if the argument may have been modified by the
1784     // callee, in which case it must be copied-out to the variable.
1785     bool argMayBeModifiedByCall;
1786     // Optional boolean value that, if present and false, prevents
1787     // the copy-out and temp deallocation.
1788     llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime;
1789   };
1790   using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>;
1791 
1792   /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories
1793   /// not based on fir.box.
1794   /// This will lose any non contiguous stride information and dynamic type and
1795   /// should only be called if \p exv is known to be contiguous or if its base
1796   /// address will be replaced by a contiguous one. If \p exv is not a
1797   /// fir::BoxValue, this is a no-op.
1798   ExtValue readIfBoxValue(const ExtValue &exv) {
1799     if (const auto *box = exv.getBoxOf<fir::BoxValue>())
1800       return fir::factory::readBoxValue(builder, getLoc(), *box);
1801     return exv;
1802   }
1803 
1804   /// Generate a contiguous temp to pass \p actualArg as argument \p arg. The
1805   /// creation of the temp and copy-in can be made conditional at runtime by
1806   /// providing a runtime boolean flag \p restrictCopyAtRuntime (in which case
1807   /// the temp and copy will only be made if the value is true at runtime).
1808   ExtValue genCopyIn(const ExtValue &actualArg,
1809                      const Fortran::lower::CallerInterface::PassedEntity &arg,
1810                      CopyOutPairs &copyOutPairs,
1811                      llvm::Optional<mlir::Value> restrictCopyAtRuntime) {
1812     if (!restrictCopyAtRuntime) {
1813       ExtValue temp = genArrayTempFromMold(actualArg, ".copyinout");
1814       if (arg.mayBeReadByCall())
1815         genArrayCopy(temp, actualArg);
1816       copyOutPairs.emplace_back(CopyOutPair{
1817           actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime});
1818       return temp;
1819     }
1820     // Otherwise, need to be careful to only copy-in if allowed at runtime.
1821     mlir::Location loc = getLoc();
1822     auto addrType = fir::HeapType::get(
1823         fir::unwrapPassByRefType(fir::getBase(actualArg).getType()));
1824     mlir::Value addr =
1825         builder
1826             .genIfOp(loc, {addrType}, *restrictCopyAtRuntime,
1827                      /*withElseRegion=*/true)
1828             .genThen([&]() {
1829               auto temp = genArrayTempFromMold(actualArg, ".copyinout");
1830               if (arg.mayBeReadByCall())
1831                 genArrayCopy(temp, actualArg);
1832               builder.create<fir::ResultOp>(loc, fir::getBase(temp));
1833             })
1834             .genElse([&]() {
1835               auto nullPtr = builder.createNullConstant(loc, addrType);
1836               builder.create<fir::ResultOp>(loc, nullPtr);
1837             })
1838             .getResults()[0];
1839     // Associate the temp address with actualArg lengths and extents.
1840     fir::ExtendedValue temp = fir::substBase(readIfBoxValue(actualArg), addr);
1841     copyOutPairs.emplace_back(CopyOutPair{
1842         actualArg, temp, arg.mayBeModifiedByCall(), restrictCopyAtRuntime});
1843     return temp;
1844   }
1845 
1846   /// Lower a non-elemental procedure reference.
1847   ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
1848                               llvm::Optional<mlir::Type> resultType) {
1849     mlir::Location loc = getLoc();
1850     if (isElementalProcWithArrayArgs(procRef))
1851       fir::emitFatalError(loc, "trying to lower elemental procedure with array "
1852                                "arguments as normal procedure");
1853     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
1854             procRef.proc().GetSpecificIntrinsic())
1855       return genIntrinsicRef(procRef, *intrinsic, resultType);
1856 
1857     if (isStatementFunctionCall(procRef))
1858       TODO(loc, "Lower statement function call");
1859 
1860     Fortran::lower::CallerInterface caller(procRef, converter);
1861     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1862 
1863     llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall;
1864     // List of <var, temp> where temp must be copied into var after the call.
1865     CopyOutPairs copyOutPairs;
1866 
1867     mlir::FunctionType callSiteType = caller.genFunctionType();
1868 
1869     // Lower the actual arguments and map the lowered values to the dummy
1870     // arguments.
1871     for (const Fortran::lower::CallInterface<
1872              Fortran::lower::CallerInterface>::PassedEntity &arg :
1873          caller.getPassedArguments()) {
1874       const auto *actual = arg.entity;
1875       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
1876       if (!actual) {
1877         // Optional dummy argument for which there is no actual argument.
1878         caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy));
1879         continue;
1880       }
1881       const auto *expr = actual->UnwrapExpr();
1882       if (!expr)
1883         TODO(loc, "assumed type actual argument lowering");
1884 
1885       if (arg.passBy == PassBy::Value) {
1886         ExtValue argVal = genval(*expr);
1887         if (!fir::isUnboxedValue(argVal))
1888           fir::emitFatalError(
1889               loc, "internal error: passing non trivial value by value");
1890         caller.placeInput(arg, fir::getBase(argVal));
1891         continue;
1892       }
1893 
1894       if (arg.passBy == PassBy::MutableBox) {
1895         if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
1896                 *expr)) {
1897           // If expr is NULL(), the mutableBox created must be a deallocated
1898           // pointer with the dummy argument characteristics (see table 16.5
1899           // in Fortran 2018 standard).
1900           // No length parameters are set for the created box because any non
1901           // deferred type parameters of the dummy will be evaluated on the
1902           // callee side, and it is illegal to use NULL without a MOLD if any
1903           // dummy length parameters are assumed.
1904           mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);
1905           assert(boxTy && boxTy.isa<fir::BoxType>() &&
1906                  "must be a fir.box type");
1907           mlir::Value boxStorage = builder.createTemporary(loc, boxTy);
1908           mlir::Value nullBox = fir::factory::createUnallocatedBox(
1909               builder, loc, boxTy, /*nonDeferredParams=*/{});
1910           builder.create<fir::StoreOp>(loc, nullBox, boxStorage);
1911           caller.placeInput(arg, boxStorage);
1912           continue;
1913         }
1914         fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
1915         mlir::Value irBox =
1916             fir::factory::getMutableIRBox(builder, loc, mutableBox);
1917         caller.placeInput(arg, irBox);
1918         if (arg.mayBeModifiedByCall())
1919           mutableModifiedByCall.emplace_back(std::move(mutableBox));
1920         continue;
1921       }
1922       const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr);
1923       if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) {
1924         const bool actualIsSimplyContiguous =
1925             !actualArgIsVariable || Fortran::evaluate::IsSimplyContiguous(
1926                                         *expr, converter.getFoldingContext());
1927         auto argAddr = [&]() -> ExtValue {
1928           ExtValue baseAddr;
1929           if (actualArgIsVariable && arg.isOptional()) {
1930             if (Fortran::evaluate::IsAllocatableOrPointerObject(
1931                     *expr, converter.getFoldingContext())) {
1932               TODO(loc, "Allocatable or pointer argument");
1933             }
1934             if (const Fortran::semantics::Symbol *wholeSymbol =
1935                     Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(
1936                         *expr))
1937               if (Fortran::semantics::IsOptional(*wholeSymbol)) {
1938                 TODO(loc, "procedureref optional arg");
1939               }
1940             // Fall through: The actual argument can safely be
1941             // copied-in/copied-out without any care if needed.
1942           }
1943           if (actualArgIsVariable && expr->Rank() > 0) {
1944             ExtValue box = genBoxArg(*expr);
1945             if (!actualIsSimplyContiguous)
1946               return genCopyIn(box, arg, copyOutPairs,
1947                                /*restrictCopyAtRuntime=*/llvm::None);
1948             // Contiguous: just use the box we created above!
1949             // This gets "unboxed" below, if needed.
1950             return box;
1951           }
1952           // Actual argument is a non optional/non pointer/non allocatable
1953           // scalar.
1954           if (actualArgIsVariable)
1955             return genExtAddr(*expr);
1956           // Actual argument is not a variable. Make sure a variable address is
1957           // not passed.
1958           return genTempExtAddr(*expr);
1959         }();
1960         // Scalar and contiguous expressions may be lowered to a fir.box,
1961         // either to account for potential polymorphism, or because lowering
1962         // did not account for some contiguity hints.
1963         // Here, polymorphism does not matter (an entity of the declared type
1964         // is passed, not one of the dynamic type), and the expr is known to
1965         // be simply contiguous, so it is safe to unbox it and pass the
1966         // address without making a copy.
1967         argAddr = readIfBoxValue(argAddr);
1968 
1969         if (arg.passBy == PassBy::BaseAddress) {
1970           caller.placeInput(arg, fir::getBase(argAddr));
1971         } else {
1972           assert(arg.passBy == PassBy::BoxChar);
1973           auto helper = fir::factory::CharacterExprHelper{builder, loc};
1974           auto boxChar = argAddr.match(
1975               [&](const fir::CharBoxValue &x) { return helper.createEmbox(x); },
1976               [&](const fir::CharArrayBoxValue &x) {
1977                 return helper.createEmbox(x);
1978               },
1979               [&](const auto &x) -> mlir::Value {
1980                 // Fortran allows an actual argument of a completely different
1981                 // type to be passed to a procedure expecting a CHARACTER in the
1982                 // dummy argument position. When this happens, the data pointer
1983                 // argument is simply assumed to point to CHARACTER data and the
1984                 // LEN argument used is garbage. Simulate this behavior by
1985                 // free-casting the base address to be a !fir.char reference and
1986                 // setting the LEN argument to undefined. What could go wrong?
1987                 auto dataPtr = fir::getBase(x);
1988                 assert(!dataPtr.getType().template isa<fir::BoxType>());
1989                 return builder.convertWithSemantics(
1990                     loc, argTy, dataPtr,
1991                     /*allowCharacterConversion=*/true);
1992               });
1993           caller.placeInput(arg, boxChar);
1994         }
1995       } else if (arg.passBy == PassBy::Box) {
1996         // Before lowering to an address, handle the allocatable/pointer actual
1997         // argument to optional fir.box dummy. It is legal to pass
1998         // unallocated/disassociated entity to an optional. In this case, an
1999         // absent fir.box must be created instead of a fir.box with a null value
2000         // (Fortran 2018 15.5.2.12 point 1).
2001         if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject(
2002                                     *expr, converter.getFoldingContext())) {
2003           TODO(loc, "optional allocatable or pointer argument");
2004         } else {
2005           // Make sure a variable address is only passed if the expression is
2006           // actually a variable.
2007           mlir::Value box =
2008               actualArgIsVariable
2009                   ? builder.createBox(loc, genBoxArg(*expr))
2010                   : builder.createBox(getLoc(), genTempExtAddr(*expr));
2011           caller.placeInput(arg, box);
2012         }
2013       } else if (arg.passBy == PassBy::AddressAndLength) {
2014         ExtValue argRef = genExtAddr(*expr);
2015         caller.placeAddressAndLengthInput(arg, fir::getBase(argRef),
2016                                           fir::getLen(argRef));
2017       } else if (arg.passBy == PassBy::CharProcTuple) {
2018         TODO(loc, "procedureref CharProcTuple");
2019       } else {
2020         TODO(loc, "pass by value in non elemental function call");
2021       }
2022     }
2023 
2024     ExtValue result = genCallOpAndResult(caller, callSiteType, resultType);
2025 
2026     // // Copy-out temps that were created for non contiguous variable arguments
2027     // if
2028     // // needed.
2029     // for (const auto &copyOutPair : copyOutPairs)
2030     //   genCopyOut(copyOutPair);
2031 
2032     return result;
2033   }
2034 
2035   template <typename A>
2036   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
2037     ExtValue result = genFunctionRef(funcRef);
2038     if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
2039       return genLoad(result);
2040     return result;
2041   }
2042 
2043   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
2044     llvm::Optional<mlir::Type> resTy;
2045     if (procRef.hasAlternateReturns())
2046       resTy = builder.getIndexType();
2047     return genProcedureRef(procRef, resTy);
2048   }
2049 
2050   /// Helper to lower intrinsic arguments for inquiry intrinsic.
2051   ExtValue
2052   lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr &expr) {
2053     if (Fortran::evaluate::IsAllocatableOrPointerObject(
2054             expr, converter.getFoldingContext()))
2055       return genMutableBoxValue(expr);
2056     return gen(expr);
2057   }
2058 
2059   /// Helper to lower intrinsic arguments to a fir::BoxValue.
2060   /// It preserves all the non default lower bounds/non deferred length
2061   /// parameter information.
2062   ExtValue lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr &expr) {
2063     mlir::Location loc = getLoc();
2064     ExtValue exv = genBoxArg(expr);
2065     mlir::Value box = builder.createBox(loc, exv);
2066     return fir::BoxValue(
2067         box, fir::factory::getNonDefaultLowerBounds(builder, loc, exv),
2068         fir::factory::getNonDeferredLengthParams(exv));
2069   }
2070 
2071   /// Generate a call to an intrinsic function.
2072   ExtValue
2073   genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
2074                   const Fortran::evaluate::SpecificIntrinsic &intrinsic,
2075                   llvm::Optional<mlir::Type> resultType) {
2076     llvm::SmallVector<ExtValue> operands;
2077 
2078     llvm::StringRef name = intrinsic.name;
2079     mlir::Location loc = getLoc();
2080 
2081     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
2082         Fortran::lower::getIntrinsicArgumentLowering(name);
2083     for (const auto &[arg, dummy] :
2084          llvm::zip(procRef.arguments(),
2085                    intrinsic.characteristics.value().dummyArguments)) {
2086       auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
2087       if (!expr) {
2088         // Absent optional.
2089         operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
2090         continue;
2091       }
2092       if (!argLowering) {
2093         // No argument lowering instruction, lower by value.
2094         operands.emplace_back(genval(*expr));
2095         continue;
2096       }
2097       // Ad-hoc argument lowering handling.
2098       Fortran::lower::ArgLoweringRule argRules =
2099           Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering,
2100                                                    dummy.name);
2101       if (argRules.handleDynamicOptional &&
2102           Fortran::evaluate::MayBePassedAsAbsentOptional(
2103               *expr, converter.getFoldingContext())) {
2104         ExtValue optional = lowerIntrinsicArgumentAsInquired(*expr);
2105         mlir::Value isPresent = genActualIsPresentTest(builder, loc, optional);
2106         switch (argRules.lowerAs) {
2107         case Fortran::lower::LowerIntrinsicArgAs::Value:
2108           operands.emplace_back(
2109               genOptionalValue(builder, loc, optional, isPresent));
2110           continue;
2111         case Fortran::lower::LowerIntrinsicArgAs::Addr:
2112           operands.emplace_back(
2113               genOptionalAddr(builder, loc, optional, isPresent));
2114           continue;
2115         case Fortran::lower::LowerIntrinsicArgAs::Box:
2116           operands.emplace_back(
2117               genOptionalBox(builder, loc, optional, isPresent));
2118           continue;
2119         case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2120           operands.emplace_back(optional);
2121           continue;
2122         }
2123         llvm_unreachable("bad switch");
2124       }
2125       switch (argRules.lowerAs) {
2126       case Fortran::lower::LowerIntrinsicArgAs::Value:
2127         operands.emplace_back(genval(*expr));
2128         continue;
2129       case Fortran::lower::LowerIntrinsicArgAs::Addr:
2130         operands.emplace_back(gen(*expr));
2131         continue;
2132       case Fortran::lower::LowerIntrinsicArgAs::Box:
2133         operands.emplace_back(lowerIntrinsicArgumentAsBox(*expr));
2134         continue;
2135       case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2136         operands.emplace_back(lowerIntrinsicArgumentAsInquired(*expr));
2137         continue;
2138       }
2139       llvm_unreachable("bad switch");
2140     }
2141     // Let the intrinsic library lower the intrinsic procedure call
2142     return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
2143                                             operands, stmtCtx);
2144   }
2145 
2146   template <typename A>
2147   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
2148     if (isScalar(x) || Fortran::evaluate::UnwrapWholeSymbolDataRef(x) ||
2149         inInitializer)
2150       return std::visit([&](const auto &e) { return genval(e); }, x.u);
2151     return asArray(x);
2152   }
2153 
2154   /// Helper to detect Transformational function reference.
2155   template <typename T>
2156   bool isTransformationalRef(const T &) {
2157     return false;
2158   }
2159   template <typename T>
2160   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
2161     return !funcRef.IsElemental() && funcRef.Rank();
2162   }
2163   template <typename T>
2164   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
2165     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
2166                       expr.u);
2167   }
2168 
2169   template <typename A>
2170   ExtValue asArray(const A &x) {
2171     return Fortran::lower::createSomeArrayTempValue(converter, toEvExpr(x),
2172                                                     symMap, stmtCtx);
2173   }
2174 
2175   /// Lower an array value as an argument. This argument can be passed as a box
2176   /// value, so it may be possible to avoid making a temporary.
2177   template <typename A>
2178   ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x) {
2179     return std::visit([&](const auto &e) { return asArrayArg(e, x); }, x.u);
2180   }
2181   template <typename A, typename B>
2182   ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x, const B &y) {
2183     return std::visit([&](const auto &e) { return asArrayArg(e, y); }, x.u);
2184   }
2185   template <typename A, typename B>
2186   ExtValue asArrayArg(const Fortran::evaluate::Designator<A> &, const B &x) {
2187     // Designator is being passed as an argument to a procedure. Lower the
2188     // expression to a boxed value.
2189     auto someExpr = toEvExpr(x);
2190     return Fortran::lower::createBoxValue(getLoc(), converter, someExpr, symMap,
2191                                           stmtCtx);
2192   }
2193   template <typename A, typename B>
2194   ExtValue asArrayArg(const A &, const B &x) {
2195     // If the expression to pass as an argument is not a designator, then create
2196     // an array temp.
2197     return asArray(x);
2198   }
2199 
2200   template <typename A>
2201   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
2202     // Whole array symbols or components, and results of transformational
2203     // functions already have a storage and the scalar expression lowering path
2204     // is used to not create a new temporary storage.
2205     if (isScalar(x) ||
2206         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
2207         isTransformationalRef(x))
2208       return std::visit([&](const auto &e) { return genref(e); }, x.u);
2209     if (useBoxArg)
2210       return asArrayArg(x);
2211     return asArray(x);
2212   }
2213 
2214   template <typename A>
2215   bool isScalar(const A &x) {
2216     return x.Rank() == 0;
2217   }
2218 
2219   template <int KIND>
2220   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
2221                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
2222     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
2223   }
2224 
2225   using RefSet =
2226       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
2227                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
2228                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
2229                  Fortran::semantics::SymbolRef>;
2230   template <typename A>
2231   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
2232 
2233   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
2234   ExtValue genref(const A &a) {
2235     return gen(a);
2236   }
2237   template <typename A>
2238   ExtValue genref(const A &a) {
2239     mlir::Type storageType = converter.genType(toEvExpr(a));
2240     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
2241   }
2242 
2243   template <typename A, template <typename> typename T,
2244             typename B = std::decay_t<T<A>>,
2245             std::enable_if_t<
2246                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
2247                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
2248                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
2249                 bool> = true>
2250   ExtValue genref(const T<A> &x) {
2251     return gen(x);
2252   }
2253 
2254 private:
2255   mlir::Location location;
2256   Fortran::lower::AbstractConverter &converter;
2257   fir::FirOpBuilder &builder;
2258   Fortran::lower::StatementContext &stmtCtx;
2259   Fortran::lower::SymMap &symMap;
2260   InitializerData *inInitializer = nullptr;
2261   bool useBoxArg = false; // expression lowered as argument
2262 };
2263 } // namespace
2264 
2265 // Helper for changing the semantics in a given context. Preserves the current
2266 // semantics which is resumed when the "push" goes out of scope.
2267 #define PushSemantics(PushVal)                                                 \
2268   [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ =                 \
2269       Fortran::common::ScopedSet(semant, PushVal);
2270 
2271 static bool isAdjustedArrayElementType(mlir::Type t) {
2272   return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>();
2273 }
2274 
2275 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting
2276 /// the actual extents and lengths. This is only to allow their propagation as
2277 /// ExtendedValue without triggering verifier failures when propagating
2278 /// character/arrays as unboxed values. Only the base of the resulting
2279 /// ExtendedValue should be used, it is undefined to use the length or extents
2280 /// of the extended value returned,
2281 inline static fir::ExtendedValue
2282 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder,
2283                        mlir::Value val, mlir::Value len) {
2284   mlir::Type ty = fir::unwrapRefType(val.getType());
2285   mlir::IndexType idxTy = builder.getIndexType();
2286   auto seqTy = ty.cast<fir::SequenceType>();
2287   auto undef = builder.create<fir::UndefOp>(loc, idxTy);
2288   llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef);
2289   if (fir::isa_char(seqTy.getEleTy()))
2290     return fir::CharArrayBoxValue(val, len ? len : undef, extents);
2291   return fir::ArrayBoxValue(val, extents);
2292 }
2293 
2294 //===----------------------------------------------------------------------===//
2295 //
2296 // Lowering of scalar expressions in an explicit iteration space context.
2297 //
2298 //===----------------------------------------------------------------------===//
2299 
2300 // Shared code for creating a copy of a derived type element. This function is
2301 // called from a continuation.
2302 inline static fir::ArrayAmendOp
2303 createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad,
2304                         fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc,
2305                         const fir::ExtendedValue &elementExv, mlir::Type eleTy,
2306                         mlir::Value innerArg) {
2307   if (destLoad.getTypeparams().empty()) {
2308     fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv);
2309   } else {
2310     auto boxTy = fir::BoxType::get(eleTy);
2311     auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(),
2312                                               mlir::Value{}, mlir::Value{},
2313                                               destLoad.getTypeparams());
2314     auto fromBox = builder.create<fir::EmboxOp>(
2315         loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{},
2316         destLoad.getTypeparams());
2317     fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox),
2318                                       fir::BoxValue(fromBox));
2319   }
2320   return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg,
2321                                            destAcc);
2322 }
2323 
2324 inline static fir::ArrayAmendOp
2325 createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder,
2326                      fir::ArrayAccessOp dstOp, mlir::Value &dstLen,
2327                      const fir::ExtendedValue &srcExv, mlir::Value innerArg,
2328                      llvm::ArrayRef<mlir::Value> bounds) {
2329   fir::CharBoxValue dstChar(dstOp, dstLen);
2330   fir::factory::CharacterExprHelper helper{builder, loc};
2331   if (!bounds.empty()) {
2332     dstChar = helper.createSubstring(dstChar, bounds);
2333     fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv),
2334                                    dstChar.getAddr(), dstChar.getLen(), builder,
2335                                    loc);
2336     // Update the LEN to the substring's LEN.
2337     dstLen = dstChar.getLen();
2338   }
2339   // For a CHARACTER, we generate the element assignment loops inline.
2340   helper.createAssign(fir::ExtendedValue{dstChar}, srcExv);
2341   // Mark this array element as amended.
2342   mlir::Type ty = innerArg.getType();
2343   auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp);
2344   return amend;
2345 }
2346 
2347 //===----------------------------------------------------------------------===//
2348 //
2349 // Lowering of array expressions.
2350 //
2351 //===----------------------------------------------------------------------===//
2352 
2353 namespace {
2354 class ArrayExprLowering {
2355   using ExtValue = fir::ExtendedValue;
2356 
2357   /// Structure to keep track of lowered array operands in the
2358   /// array expression. Useful to later deduce the shape of the
2359   /// array expression.
2360   struct ArrayOperand {
2361     /// Array base (can be a fir.box).
2362     mlir::Value memref;
2363     /// ShapeOp, ShapeShiftOp or ShiftOp
2364     mlir::Value shape;
2365     /// SliceOp
2366     mlir::Value slice;
2367     /// Can this operand be absent ?
2368     bool mayBeAbsent = false;
2369   };
2370 
2371   using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts;
2372   using PathComponent = Fortran::lower::PathComponent;
2373 
2374   /// Active iteration space.
2375   using IterationSpace = Fortran::lower::IterationSpace;
2376   using IterSpace = const Fortran::lower::IterationSpace &;
2377 
2378   /// Current continuation. Function that will generate IR for a single
2379   /// iteration of the pending iterative loop structure.
2380   using CC = Fortran::lower::GenerateElementalArrayFunc;
2381 
2382   /// Projection continuation. Function that will project one iteration space
2383   /// into another.
2384   using PC = std::function<IterationSpace(IterSpace)>;
2385   using ArrayBaseTy =
2386       std::variant<std::monostate, const Fortran::evaluate::ArrayRef *,
2387                    const Fortran::evaluate::DataRef *>;
2388   using ComponentPath = Fortran::lower::ComponentPath;
2389 
2390 public:
2391   //===--------------------------------------------------------------------===//
2392   // Regular array assignment
2393   //===--------------------------------------------------------------------===//
2394 
2395   /// Entry point for array assignments. Both the left-hand and right-hand sides
2396   /// can either be ExtendedValue or evaluate::Expr.
2397   template <typename TL, typename TR>
2398   static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter,
2399                                    Fortran::lower::SymMap &symMap,
2400                                    Fortran::lower::StatementContext &stmtCtx,
2401                                    const TL &lhs, const TR &rhs) {
2402     ArrayExprLowering ael{converter, stmtCtx, symMap,
2403                           ConstituentSemantics::CopyInCopyOut};
2404     ael.lowerArrayAssignment(lhs, rhs);
2405   }
2406 
2407   template <typename TL, typename TR>
2408   void lowerArrayAssignment(const TL &lhs, const TR &rhs) {
2409     mlir::Location loc = getLoc();
2410     /// Here the target subspace is not necessarily contiguous. The ArrayUpdate
2411     /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad
2412     /// in `destination`.
2413     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
2414     ccStoreToDest = genarr(lhs);
2415     determineShapeOfDest(lhs);
2416     semant = ConstituentSemantics::RefTransparent;
2417     ExtValue exv = lowerArrayExpression(rhs);
2418     if (explicitSpaceIsActive()) {
2419       explicitSpace->finalizeContext();
2420       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
2421     } else {
2422       builder.create<fir::ArrayMergeStoreOp>(
2423           loc, destination, fir::getBase(exv), destination.getMemref(),
2424           destination.getSlice(), destination.getTypeparams());
2425     }
2426   }
2427 
2428   //===--------------------------------------------------------------------===//
2429   // Array assignment to allocatable array
2430   //===--------------------------------------------------------------------===//
2431 
2432   /// Entry point for assignment to allocatable array.
2433   static void lowerAllocatableArrayAssignment(
2434       Fortran::lower::AbstractConverter &converter,
2435       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
2436       const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
2437       Fortran::lower::ExplicitIterSpace &explicitSpace,
2438       Fortran::lower::ImplicitIterSpace &implicitSpace) {
2439     ArrayExprLowering ael(converter, stmtCtx, symMap,
2440                           ConstituentSemantics::CopyInCopyOut, &explicitSpace,
2441                           &implicitSpace);
2442     ael.lowerAllocatableArrayAssignment(lhs, rhs);
2443   }
2444 
2445   /// Assignment to allocatable array.
2446   ///
2447   /// The semantics are reverse that of a "regular" array assignment. The rhs
2448   /// defines the iteration space of the computation and the lhs is
2449   /// resized/reallocated to fit if necessary.
2450   void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs,
2451                                        const Fortran::lower::SomeExpr &rhs) {
2452     // With assignment to allocatable, we want to lower the rhs first and use
2453     // its shape to determine if we need to reallocate, etc.
2454     mlir::Location loc = getLoc();
2455     // FIXME: If the lhs is in an explicit iteration space, the assignment may
2456     // be to an array of allocatable arrays rather than a single allocatable
2457     // array.
2458     fir::MutableBoxValue mutableBox =
2459         createMutableBox(loc, converter, lhs, symMap);
2460     mlir::Type resultTy = converter.genType(rhs);
2461     if (rhs.Rank() > 0)
2462       determineShapeOfDest(rhs);
2463     auto rhsCC = [&]() {
2464       PushSemantics(ConstituentSemantics::RefTransparent);
2465       return genarr(rhs);
2466     }();
2467 
2468     llvm::SmallVector<mlir::Value> lengthParams;
2469     // Currently no safe way to gather length from rhs (at least for
2470     // character, it cannot be taken from array_loads since it may be
2471     // changed by concatenations).
2472     if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) ||
2473         mutableBox.isDerivedWithLengthParameters())
2474       TODO(loc, "gather rhs length parameters in assignment to allocatable");
2475 
2476     // The allocatable must take lower bounds from the expr if it is
2477     // reallocated and the right hand side is not a scalar.
2478     const bool takeLboundsIfRealloc = rhs.Rank() > 0;
2479     llvm::SmallVector<mlir::Value> lbounds;
2480     // When the reallocated LHS takes its lower bounds from the RHS,
2481     // they will be non default only if the RHS is a whole array
2482     // variable. Otherwise, lbounds is left empty and default lower bounds
2483     // will be used.
2484     if (takeLboundsIfRealloc &&
2485         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) {
2486       assert(arrayOperands.size() == 1 &&
2487              "lbounds can only come from one array");
2488       std::vector<mlir::Value> lbs =
2489           fir::factory::getOrigins(arrayOperands[0].shape);
2490       lbounds.append(lbs.begin(), lbs.end());
2491     }
2492     fir::factory::MutableBoxReallocation realloc =
2493         fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape,
2494                                          lengthParams);
2495     // Create ArrayLoad for the mutable box and save it into `destination`.
2496     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
2497     ccStoreToDest = genarr(realloc.newValue);
2498     // If the rhs is scalar, get shape from the allocatable ArrayLoad.
2499     if (destShape.empty())
2500       destShape = getShape(destination);
2501     // Finish lowering the loop nest.
2502     assert(destination && "destination must have been set");
2503     ExtValue exv = lowerArrayExpression(rhsCC, resultTy);
2504     if (explicitSpaceIsActive()) {
2505       explicitSpace->finalizeContext();
2506       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
2507     } else {
2508       builder.create<fir::ArrayMergeStoreOp>(
2509           loc, destination, fir::getBase(exv), destination.getMemref(),
2510           destination.getSlice(), destination.getTypeparams());
2511     }
2512     fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds,
2513                                   takeLboundsIfRealloc, realloc);
2514   }
2515 
2516   /// Entry point for when an array expression appears in a context where the
2517   /// result must be boxed. (BoxValue semantics.)
2518   static ExtValue
2519   lowerBoxedArrayExpression(Fortran::lower::AbstractConverter &converter,
2520                             Fortran::lower::SymMap &symMap,
2521                             Fortran::lower::StatementContext &stmtCtx,
2522                             const Fortran::lower::SomeExpr &expr) {
2523     ArrayExprLowering ael{converter, stmtCtx, symMap,
2524                           ConstituentSemantics::BoxValue};
2525     return ael.lowerBoxedArrayExpr(expr);
2526   }
2527 
2528   ExtValue lowerBoxedArrayExpr(const Fortran::lower::SomeExpr &exp) {
2529     return std::visit(
2530         [&](const auto &e) {
2531           auto f = genarr(e);
2532           ExtValue exv = f(IterationSpace{});
2533           if (fir::getBase(exv).getType().template isa<fir::BoxType>())
2534             return exv;
2535           fir::emitFatalError(getLoc(), "array must be emboxed");
2536         },
2537         exp.u);
2538   }
2539 
2540   /// Entry point into lowering an expression with rank. This entry point is for
2541   /// lowering a rhs expression, for example. (RefTransparent semantics.)
2542   static ExtValue
2543   lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter,
2544                           Fortran::lower::SymMap &symMap,
2545                           Fortran::lower::StatementContext &stmtCtx,
2546                           const Fortran::lower::SomeExpr &expr) {
2547     ArrayExprLowering ael{converter, stmtCtx, symMap};
2548     ael.determineShapeOfDest(expr);
2549     ExtValue loopRes = ael.lowerArrayExpression(expr);
2550     fir::ArrayLoadOp dest = ael.destination;
2551     mlir::Value tempRes = dest.getMemref();
2552     fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2553     mlir::Location loc = converter.getCurrentLocation();
2554     builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes),
2555                                            tempRes, dest.getSlice(),
2556                                            dest.getTypeparams());
2557 
2558     auto arrTy =
2559         fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>();
2560     if (auto charTy =
2561             arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) {
2562       if (fir::characterWithDynamicLen(charTy))
2563         TODO(loc, "CHARACTER does not have constant LEN");
2564       mlir::Value len = builder.createIntegerConstant(
2565           loc, builder.getCharacterLengthType(), charTy.getLen());
2566       return fir::CharArrayBoxValue(tempRes, len, dest.getExtents());
2567     }
2568     return fir::ArrayBoxValue(tempRes, dest.getExtents());
2569   }
2570 
2571   // FIXME: should take multiple inner arguments.
2572   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
2573   genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) {
2574     mlir::Location loc = getLoc();
2575     mlir::IndexType idxTy = builder.getIndexType();
2576     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
2577     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
2578     llvm::SmallVector<mlir::Value> loopUppers;
2579 
2580     // Convert any implied shape to closed interval form. The fir.do_loop will
2581     // run from 0 to `extent - 1` inclusive.
2582     for (auto extent : shape)
2583       loopUppers.push_back(
2584           builder.create<mlir::arith::SubIOp>(loc, extent, one));
2585 
2586     // Iteration space is created with outermost columns, innermost rows
2587     llvm::SmallVector<fir::DoLoopOp> loops;
2588 
2589     const std::size_t loopDepth = loopUppers.size();
2590     llvm::SmallVector<mlir::Value> ivars;
2591 
2592     for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) {
2593       if (i.index() > 0) {
2594         assert(!loops.empty());
2595         builder.setInsertionPointToStart(loops.back().getBody());
2596       }
2597       fir::DoLoopOp loop;
2598       if (innerArg) {
2599         loop = builder.create<fir::DoLoopOp>(
2600             loc, zero, i.value(), one, isUnordered(),
2601             /*finalCount=*/false, mlir::ValueRange{innerArg});
2602         innerArg = loop.getRegionIterArgs().front();
2603         if (explicitSpaceIsActive())
2604           explicitSpace->setInnerArg(0, innerArg);
2605       } else {
2606         loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one,
2607                                              isUnordered(),
2608                                              /*finalCount=*/false);
2609       }
2610       ivars.push_back(loop.getInductionVar());
2611       loops.push_back(loop);
2612     }
2613 
2614     if (innerArg)
2615       for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth;
2616            ++i) {
2617         builder.setInsertionPointToEnd(loops[i].getBody());
2618         builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0));
2619       }
2620 
2621     // Move insertion point to the start of the innermost loop in the nest.
2622     builder.setInsertionPointToStart(loops.back().getBody());
2623     // Set `afterLoopNest` to just after the entire loop nest.
2624     auto currPt = builder.saveInsertionPoint();
2625     builder.setInsertionPointAfter(loops[0]);
2626     auto afterLoopNest = builder.saveInsertionPoint();
2627     builder.restoreInsertionPoint(currPt);
2628 
2629     // Put the implicit loop variables in row to column order to match FIR's
2630     // Ops. (The loops were constructed from outermost column to innermost
2631     // row.)
2632     mlir::Value outerRes = loops[0].getResult(0);
2633     return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)),
2634             afterLoopNest};
2635   }
2636 
2637   /// Build the iteration space into which the array expression will be
2638   /// lowered. The resultType is used to create a temporary, if needed.
2639   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
2640   genIterSpace(mlir::Type resultType) {
2641     mlir::Location loc = getLoc();
2642     llvm::SmallVector<mlir::Value> shape = genIterationShape();
2643     if (!destination) {
2644       // Allocate storage for the result if it is not already provided.
2645       destination = createAndLoadSomeArrayTemp(resultType, shape);
2646     }
2647 
2648     // Generate the lazy mask allocation, if one was given.
2649     if (ccPrelude.hasValue())
2650       ccPrelude.getValue()(shape);
2651 
2652     // Now handle the implicit loops.
2653     mlir::Value inner = explicitSpaceIsActive()
2654                             ? explicitSpace->getInnerArgs().front()
2655                             : destination.getResult();
2656     auto [iters, afterLoopNest] = genImplicitLoops(shape, inner);
2657     mlir::Value innerArg = iters.innerArgument();
2658 
2659     // Generate the mask conditional structure, if there are masks. Unlike the
2660     // explicit masks, which are interleaved, these mask expression appear in
2661     // the innermost loop.
2662     if (implicitSpaceHasMasks()) {
2663       // Recover the cached condition from the mask buffer.
2664       auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) {
2665         return implicitSpace->getBoundClosure(e)(iters);
2666       };
2667 
2668       // Handle the negated conditions in topological order of the WHERE
2669       // clauses. See 10.2.3.2p4 as to why this control structure is produced.
2670       for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs :
2671            implicitSpace->getMasks()) {
2672         const std::size_t size = maskExprs.size() - 1;
2673         auto genFalseBlock = [&](const auto *e, auto &&cond) {
2674           auto ifOp = builder.create<fir::IfOp>(
2675               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
2676               /*withElseRegion=*/true);
2677           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
2678           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2679           builder.create<fir::ResultOp>(loc, innerArg);
2680           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
2681         };
2682         auto genTrueBlock = [&](const auto *e, auto &&cond) {
2683           auto ifOp = builder.create<fir::IfOp>(
2684               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
2685               /*withElseRegion=*/true);
2686           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
2687           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
2688           builder.create<fir::ResultOp>(loc, innerArg);
2689           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2690         };
2691         for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i)
2692           if (const auto *e = maskExprs[i])
2693             genFalseBlock(e, genCond(e, iters));
2694 
2695         // The last condition is either non-negated or unconditionally negated.
2696         if (const auto *e = maskExprs[size])
2697           genTrueBlock(e, genCond(e, iters));
2698       }
2699     }
2700 
2701     // We're ready to lower the body (an assignment statement) for this context
2702     // of loop nests at this point.
2703     return {iters, afterLoopNest};
2704   }
2705 
2706   fir::ArrayLoadOp
2707   createAndLoadSomeArrayTemp(mlir::Type type,
2708                              llvm::ArrayRef<mlir::Value> shape) {
2709     if (ccLoadDest.hasValue())
2710       return ccLoadDest.getValue()(shape);
2711     auto seqTy = type.dyn_cast<fir::SequenceType>();
2712     assert(seqTy && "must be an array");
2713     mlir::Location loc = getLoc();
2714     // TODO: Need to thread the length parameters here. For character, they may
2715     // differ from the operands length (e.g concatenation). So the array loads
2716     // type parameters are not enough.
2717     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>())
2718       if (charTy.hasDynamicLen())
2719         TODO(loc, "character array expression temp with dynamic length");
2720     if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>())
2721       if (recTy.getNumLenParams() > 0)
2722         TODO(loc, "derived type array expression temp with length parameters");
2723     mlir::Value temp = seqTy.hasConstantShape()
2724                            ? builder.create<fir::AllocMemOp>(loc, type)
2725                            : builder.create<fir::AllocMemOp>(
2726                                  loc, type, ".array.expr", llvm::None, shape);
2727     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
2728     stmtCtx.attachCleanup(
2729         [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); });
2730     mlir::Value shapeOp = genShapeOp(shape);
2731     return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp,
2732                                             /*slice=*/mlir::Value{},
2733                                             llvm::None);
2734   }
2735 
2736   static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder,
2737                                  llvm::ArrayRef<mlir::Value> shape) {
2738     mlir::IndexType idxTy = builder.getIndexType();
2739     llvm::SmallVector<mlir::Value> idxShape;
2740     for (auto s : shape)
2741       idxShape.push_back(builder.createConvert(loc, idxTy, s));
2742     auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size());
2743     return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape);
2744   }
2745 
2746   fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) {
2747     return genShapeOp(getLoc(), builder, shape);
2748   }
2749 
2750   //===--------------------------------------------------------------------===//
2751   // Expression traversal and lowering.
2752   //===--------------------------------------------------------------------===//
2753 
2754   /// Lower the expression, \p x, in a scalar context.
2755   template <typename A>
2756   ExtValue asScalar(const A &x) {
2757     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x);
2758   }
2759 
2760   /// Lower the expression in a scalar context to a memory reference.
2761   template <typename A>
2762   ExtValue asScalarRef(const A &x) {
2763     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x);
2764   }
2765 
2766   /// Lower an expression without dereferencing any indirection that may be
2767   /// a nullptr (because this is an absent optional or unallocated/disassociated
2768   /// descriptor). The returned expression cannot be addressed directly, it is
2769   /// meant to inquire about its status before addressing the related entity.
2770   template <typename A>
2771   ExtValue asInquired(const A &x) {
2772     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}
2773         .lowerIntrinsicArgumentAsInquired(x);
2774   }
2775 
2776   // An expression with non-zero rank is an array expression.
2777   template <typename A>
2778   bool isArray(const A &x) const {
2779     return x.Rank() != 0;
2780   }
2781 
2782   /// Some temporaries are allocated on an element-by-element basis during the
2783   /// array expression evaluation. Collect the cleanups here so the resources
2784   /// can be freed before the next loop iteration, avoiding memory leaks. etc.
2785   Fortran::lower::StatementContext &getElementCtx() {
2786     if (!elementCtx) {
2787       stmtCtx.pushScope();
2788       elementCtx = true;
2789     }
2790     return stmtCtx;
2791   }
2792 
2793   /// If there were temporaries created for this element evaluation, finalize
2794   /// and deallocate the resources now. This should be done just prior the the
2795   /// fir::ResultOp at the end of the innermost loop.
2796   void finalizeElementCtx() {
2797     if (elementCtx) {
2798       stmtCtx.finalize(/*popScope=*/true);
2799       elementCtx = false;
2800     }
2801   }
2802 
2803   /// Lower an elemental function array argument. This ensures array
2804   /// sub-expressions that are not variables and must be passed by address
2805   /// are lowered by value and placed in memory.
2806   template <typename A>
2807   CC genElementalArgument(const A &x) {
2808     // Ensure the returned element is in memory if this is what was requested.
2809     if ((semant == ConstituentSemantics::RefOpaque ||
2810          semant == ConstituentSemantics::DataAddr ||
2811          semant == ConstituentSemantics::ByValueArg)) {
2812       if (!Fortran::evaluate::IsVariable(x)) {
2813         PushSemantics(ConstituentSemantics::DataValue);
2814         CC cc = genarr(x);
2815         mlir::Location loc = getLoc();
2816         if (isParenthesizedVariable(x)) {
2817           // Parenthesised variables are lowered to a reference to the variable
2818           // storage. When passing it as an argument, a copy must be passed.
2819           return [=](IterSpace iters) -> ExtValue {
2820             return createInMemoryScalarCopy(builder, loc, cc(iters));
2821           };
2822         }
2823         mlir::Type storageType =
2824             fir::unwrapSequenceType(converter.genType(toEvExpr(x)));
2825         return [=](IterSpace iters) -> ExtValue {
2826           return placeScalarValueInMemory(builder, loc, cc(iters), storageType);
2827         };
2828       }
2829     }
2830     return genarr(x);
2831   }
2832 
2833   // A procedure reference to a Fortran elemental intrinsic procedure.
2834   CC genElementalIntrinsicProcRef(
2835       const Fortran::evaluate::ProcedureRef &procRef,
2836       llvm::Optional<mlir::Type> retTy,
2837       const Fortran::evaluate::SpecificIntrinsic &intrinsic) {
2838     llvm::SmallVector<CC> operands;
2839     llvm::StringRef name = intrinsic.name;
2840     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
2841         Fortran::lower::getIntrinsicArgumentLowering(name);
2842     mlir::Location loc = getLoc();
2843     if (Fortran::lower::intrinsicRequiresCustomOptionalHandling(
2844             procRef, intrinsic, converter)) {
2845       using CcPairT = std::pair<CC, llvm::Optional<mlir::Value>>;
2846       llvm::SmallVector<CcPairT> operands;
2847       auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
2848         if (expr.Rank() == 0) {
2849           ExtValue optionalArg = this->asInquired(expr);
2850           mlir::Value isPresent =
2851               genActualIsPresentTest(builder, loc, optionalArg);
2852           operands.emplace_back(
2853               [=](IterSpace iters) -> ExtValue {
2854                 return genLoad(builder, loc, optionalArg);
2855               },
2856               isPresent);
2857         } else {
2858           auto [cc, isPresent, _] = this->genOptionalArrayFetch(expr);
2859           operands.emplace_back(cc, isPresent);
2860         }
2861       };
2862       auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) {
2863         PushSemantics(ConstituentSemantics::RefTransparent);
2864         operands.emplace_back(genElementalArgument(expr), llvm::None);
2865       };
2866       Fortran::lower::prepareCustomIntrinsicArgument(
2867           procRef, intrinsic, retTy, prepareOptionalArg, prepareOtherArg,
2868           converter);
2869 
2870       fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
2871       llvm::StringRef name = intrinsic.name;
2872       return [=](IterSpace iters) -> ExtValue {
2873         auto getArgument = [&](std::size_t i) -> ExtValue {
2874           return operands[i].first(iters);
2875         };
2876         auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> {
2877           return operands[i].second;
2878         };
2879         return Fortran::lower::lowerCustomIntrinsic(
2880             *bldr, loc, name, retTy, isPresent, getArgument, operands.size(),
2881             getElementCtx());
2882       };
2883     }
2884     /// Otherwise, pre-lower arguments and use intrinsic lowering utility.
2885     for (const auto &[arg, dummy] :
2886          llvm::zip(procRef.arguments(),
2887                    intrinsic.characteristics.value().dummyArguments)) {
2888       const auto *expr =
2889           Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
2890       if (!expr) {
2891         // Absent optional.
2892         operands.emplace_back([=](IterSpace) { return mlir::Value{}; });
2893       } else if (!argLowering) {
2894         // No argument lowering instruction, lower by value.
2895         PushSemantics(ConstituentSemantics::RefTransparent);
2896         operands.emplace_back(genElementalArgument(*expr));
2897       } else {
2898         // Ad-hoc argument lowering handling.
2899         Fortran::lower::ArgLoweringRule argRules =
2900             Fortran::lower::lowerIntrinsicArgumentAs(getLoc(), *argLowering,
2901                                                      dummy.name);
2902         if (argRules.handleDynamicOptional &&
2903             Fortran::evaluate::MayBePassedAsAbsentOptional(
2904                 *expr, converter.getFoldingContext())) {
2905           // Currently, there is not elemental intrinsic that requires lowering
2906           // a potentially absent argument to something else than a value (apart
2907           // from character MAX/MIN that are handled elsewhere.)
2908           if (argRules.lowerAs != Fortran::lower::LowerIntrinsicArgAs::Value)
2909             TODO(loc, "lowering non trivial optional elemental intrinsic array "
2910                       "argument");
2911           PushSemantics(ConstituentSemantics::RefTransparent);
2912           operands.emplace_back(genarrForwardOptionalArgumentToCall(*expr));
2913           continue;
2914         }
2915         switch (argRules.lowerAs) {
2916         case Fortran::lower::LowerIntrinsicArgAs::Value: {
2917           PushSemantics(ConstituentSemantics::RefTransparent);
2918           operands.emplace_back(genElementalArgument(*expr));
2919         } break;
2920         case Fortran::lower::LowerIntrinsicArgAs::Addr: {
2921           // Note: assume does not have Fortran VALUE attribute semantics.
2922           PushSemantics(ConstituentSemantics::RefOpaque);
2923           operands.emplace_back(genElementalArgument(*expr));
2924         } break;
2925         case Fortran::lower::LowerIntrinsicArgAs::Box: {
2926           PushSemantics(ConstituentSemantics::RefOpaque);
2927           auto lambda = genElementalArgument(*expr);
2928           operands.emplace_back([=](IterSpace iters) {
2929             return builder.createBox(loc, lambda(iters));
2930           });
2931         } break;
2932         case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2933           TODO(loc, "intrinsic function with inquired argument");
2934           break;
2935         }
2936       }
2937     }
2938 
2939     // Let the intrinsic library lower the intrinsic procedure call
2940     return [=](IterSpace iters) {
2941       llvm::SmallVector<ExtValue> args;
2942       for (const auto &cc : operands)
2943         args.push_back(cc(iters));
2944       return Fortran::lower::genIntrinsicCall(builder, loc, name, retTy, args,
2945                                               getElementCtx());
2946     };
2947   }
2948 
2949   /// Generate a procedure reference. This code is shared for both functions and
2950   /// subroutines, the difference being reflected by `retTy`.
2951   CC genProcRef(const Fortran::evaluate::ProcedureRef &procRef,
2952                 llvm::Optional<mlir::Type> retTy) {
2953     mlir::Location loc = getLoc();
2954     if (procRef.IsElemental()) {
2955       if (const Fortran::evaluate::SpecificIntrinsic *intrin =
2956               procRef.proc().GetSpecificIntrinsic()) {
2957         // All elemental intrinsic functions are pure and cannot modify their
2958         // arguments. The only elemental subroutine, MVBITS has an Intent(inout)
2959         // argument. So for this last one, loops must be in element order
2960         // according to 15.8.3 p1.
2961         if (!retTy)
2962           setUnordered(false);
2963 
2964         // Elemental intrinsic call.
2965         // The intrinsic procedure is called once per element of the array.
2966         return genElementalIntrinsicProcRef(procRef, retTy, *intrin);
2967       }
2968       if (ScalarExprLowering::isStatementFunctionCall(procRef))
2969         fir::emitFatalError(loc, "statement function cannot be elemental");
2970 
2971       TODO(loc, "elemental user defined proc ref");
2972     }
2973 
2974     // Transformational call.
2975     // The procedure is called once and produces a value of rank > 0.
2976     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
2977             procRef.proc().GetSpecificIntrinsic()) {
2978       if (explicitSpaceIsActive() && procRef.Rank() == 0) {
2979         // Elide any implicit loop iters.
2980         return [=, &procRef](IterSpace) {
2981           return ScalarExprLowering{loc, converter, symMap, stmtCtx}
2982               .genIntrinsicRef(procRef, *intrinsic, retTy);
2983         };
2984       }
2985       return genarr(
2986           ScalarExprLowering{loc, converter, symMap, stmtCtx}.genIntrinsicRef(
2987               procRef, *intrinsic, retTy));
2988     }
2989 
2990     if (explicitSpaceIsActive() && procRef.Rank() == 0) {
2991       // Elide any implicit loop iters.
2992       return [=, &procRef](IterSpace) {
2993         return ScalarExprLowering{loc, converter, symMap, stmtCtx}
2994             .genProcedureRef(procRef, retTy);
2995       };
2996     }
2997     // In the default case, the call can be hoisted out of the loop nest. Apply
2998     // the iterations to the result, which may be an array value.
2999     return genarr(
3000         ScalarExprLowering{loc, converter, symMap, stmtCtx}.genProcedureRef(
3001             procRef, retTy));
3002   }
3003 
3004   template <typename A>
3005   CC genScalarAndForwardValue(const A &x) {
3006     ExtValue result = asScalar(x);
3007     return [=](IterSpace) { return result; };
3008   }
3009 
3010   template <typename A, typename = std::enable_if_t<Fortran::common::HasMember<
3011                             A, Fortran::evaluate::TypelessExpression>>>
3012   CC genarr(const A &x) {
3013     return genScalarAndForwardValue(x);
3014   }
3015 
3016   template <typename A>
3017   CC genarr(const Fortran::evaluate::Expr<A> &x) {
3018     LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x));
3019     if (isArray(x) || explicitSpaceIsActive() ||
3020         isElementalProcWithArrayArgs(x))
3021       return std::visit([&](const auto &e) { return genarr(e); }, x.u);
3022     return genScalarAndForwardValue(x);
3023   }
3024 
3025   template <Fortran::common::TypeCategory TC1, int KIND,
3026             Fortran::common::TypeCategory TC2>
3027   CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
3028                                              TC2> &x) {
3029     TODO(getLoc(), "");
3030   }
3031 
3032   template <int KIND>
3033   CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) {
3034     TODO(getLoc(), "");
3035   }
3036 
3037   template <typename T>
3038   CC genarr(const Fortran::evaluate::Parentheses<T> &x) {
3039     TODO(getLoc(), "");
3040   }
3041 
3042   template <int KIND>
3043   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
3044                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
3045     TODO(getLoc(), "");
3046   }
3047 
3048   template <int KIND>
3049   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
3050                 Fortran::common::TypeCategory::Real, KIND>> &x) {
3051     TODO(getLoc(), "");
3052   }
3053   template <int KIND>
3054   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
3055                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
3056     TODO(getLoc(), "");
3057   }
3058 
3059   //===--------------------------------------------------------------------===//
3060   // Binary elemental ops
3061   //===--------------------------------------------------------------------===//
3062 
3063   template <typename OP, typename A>
3064   CC createBinaryOp(const A &evEx) {
3065     mlir::Location loc = getLoc();
3066     auto lambda = genarr(evEx.left());
3067     auto rf = genarr(evEx.right());
3068     return [=](IterSpace iters) -> ExtValue {
3069       mlir::Value left = fir::getBase(lambda(iters));
3070       mlir::Value right = fir::getBase(rf(iters));
3071       return builder.create<OP>(loc, left, right);
3072     };
3073   }
3074 
3075 #undef GENBIN
3076 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
3077   template <int KIND>                                                          \
3078   CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type<       \
3079                 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) {       \
3080     return createBinaryOp<GenBinFirOp>(x);                                     \
3081   }
3082 
3083   GENBIN(Add, Integer, mlir::arith::AddIOp)
3084   GENBIN(Add, Real, mlir::arith::AddFOp)
3085   GENBIN(Add, Complex, fir::AddcOp)
3086   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
3087   GENBIN(Subtract, Real, mlir::arith::SubFOp)
3088   GENBIN(Subtract, Complex, fir::SubcOp)
3089   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
3090   GENBIN(Multiply, Real, mlir::arith::MulFOp)
3091   GENBIN(Multiply, Complex, fir::MulcOp)
3092   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
3093   GENBIN(Divide, Real, mlir::arith::DivFOp)
3094   GENBIN(Divide, Complex, fir::DivcOp)
3095 
3096   template <Fortran::common::TypeCategory TC, int KIND>
3097   CC genarr(
3098       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
3099     TODO(getLoc(), "genarr Power<Fortran::evaluate::Type<TC, KIND>>");
3100   }
3101   template <Fortran::common::TypeCategory TC, int KIND>
3102   CC genarr(
3103       const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
3104     TODO(getLoc(), "genarr Extremum<Fortran::evaluate::Type<TC, KIND>>");
3105   }
3106   template <Fortran::common::TypeCategory TC, int KIND>
3107   CC genarr(
3108       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
3109           &x) {
3110     TODO(getLoc(), "genarr RealToIntPower<Fortran::evaluate::Type<TC, KIND>>");
3111   }
3112   template <int KIND>
3113   CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
3114     TODO(getLoc(), "genarr ComplexConstructor<KIND>");
3115   }
3116 
3117   template <int KIND>
3118   CC genarr(const Fortran::evaluate::Concat<KIND> &x) {
3119     TODO(getLoc(), "genarr Concat<KIND>");
3120   }
3121 
3122   template <int KIND>
3123   CC genarr(const Fortran::evaluate::SetLength<KIND> &x) {
3124     TODO(getLoc(), "genarr SetLength<KIND>");
3125   }
3126 
3127   template <typename A>
3128   CC genarr(const Fortran::evaluate::Constant<A> &x) {
3129     if (/*explicitSpaceIsActive() &&*/ x.Rank() == 0)
3130       return genScalarAndForwardValue(x);
3131     mlir::Location loc = getLoc();
3132     mlir::IndexType idxTy = builder.getIndexType();
3133     mlir::Type arrTy = converter.genType(toEvExpr(x));
3134     std::string globalName = Fortran::lower::mangle::mangleArrayLiteral(x);
3135     fir::GlobalOp global = builder.getNamedGlobal(globalName);
3136     if (!global) {
3137       mlir::Type symTy = arrTy;
3138       mlir::Type eleTy = symTy.cast<fir::SequenceType>().getEleTy();
3139       // If we have a rank-1 array of integer, real, or logical, then we can
3140       // create a global array with the dense attribute.
3141       //
3142       // The mlir tensor type can only handle integer, real, or logical. It
3143       // does not currently support nested structures which is required for
3144       // complex.
3145       //
3146       // Also, we currently handle just rank-1 since tensor type assumes
3147       // row major array ordering. We will need to reorder the dimensions
3148       // in the tensor type to support Fortran's column major array ordering.
3149       // How to create this tensor type is to be determined.
3150       if (x.Rank() == 1 &&
3151           eleTy.isa<fir::LogicalType, mlir::IntegerType, mlir::FloatType>())
3152         global = Fortran::lower::createDenseGlobal(
3153             loc, arrTy, globalName, builder.createInternalLinkage(), true,
3154             toEvExpr(x), converter);
3155       // Note: If call to createDenseGlobal() returns 0, then call
3156       // createGlobalConstant() below.
3157       if (!global)
3158         global = builder.createGlobalConstant(
3159             loc, arrTy, globalName,
3160             [&](fir::FirOpBuilder &builder) {
3161               Fortran::lower::StatementContext stmtCtx(
3162                   /*cleanupProhibited=*/true);
3163               fir::ExtendedValue result =
3164                   Fortran::lower::createSomeInitializerExpression(
3165                       loc, converter, toEvExpr(x), symMap, stmtCtx);
3166               mlir::Value castTo =
3167                   builder.createConvert(loc, arrTy, fir::getBase(result));
3168               builder.create<fir::HasValueOp>(loc, castTo);
3169             },
3170             builder.createInternalLinkage());
3171     }
3172     auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(),
3173                                               global.getSymbol());
3174     auto seqTy = global.getType().cast<fir::SequenceType>();
3175     llvm::SmallVector<mlir::Value> extents;
3176     for (auto extent : seqTy.getShape())
3177       extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
3178     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>()) {
3179       mlir::Value len = builder.createIntegerConstant(loc, builder.getI64Type(),
3180                                                       charTy.getLen());
3181       return genarr(fir::CharArrayBoxValue{addr, len, extents});
3182     }
3183     return genarr(fir::ArrayBoxValue{addr, extents});
3184   }
3185 
3186   CC genarr(const Fortran::semantics::SymbolRef &sym,
3187             ComponentPath &components) {
3188     return genarr(sym.get(), components);
3189   }
3190 
3191   ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) {
3192     return convertToArrayBoxValue(getLoc(), builder, val, len);
3193   }
3194 
3195   CC genarr(const ExtValue &extMemref) {
3196     ComponentPath dummy(/*isImplicit=*/true);
3197     return genarr(extMemref, dummy);
3198   }
3199 
3200   //===--------------------------------------------------------------------===//
3201   // Array construction
3202   //===--------------------------------------------------------------------===//
3203 
3204   /// Target agnostic computation of the size of an element in the array.
3205   /// Returns the size in bytes with type `index` or a null Value if the element
3206   /// size is not constant.
3207   mlir::Value computeElementSize(const ExtValue &exv, mlir::Type eleTy,
3208                                  mlir::Type resTy) {
3209     mlir::Location loc = getLoc();
3210     mlir::IndexType idxTy = builder.getIndexType();
3211     mlir::Value multiplier = builder.createIntegerConstant(loc, idxTy, 1);
3212     if (fir::hasDynamicSize(eleTy)) {
3213       if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
3214         // Array of char with dynamic length parameter. Downcast to an array
3215         // of singleton char, and scale by the len type parameter from
3216         // `exv`.
3217         exv.match(
3218             [&](const fir::CharBoxValue &cb) { multiplier = cb.getLen(); },
3219             [&](const fir::CharArrayBoxValue &cb) { multiplier = cb.getLen(); },
3220             [&](const fir::BoxValue &box) {
3221               multiplier = fir::factory::CharacterExprHelper(builder, loc)
3222                                .readLengthFromBox(box.getAddr());
3223             },
3224             [&](const fir::MutableBoxValue &box) {
3225               multiplier = fir::factory::CharacterExprHelper(builder, loc)
3226                                .readLengthFromBox(box.getAddr());
3227             },
3228             [&](const auto &) {
3229               fir::emitFatalError(loc,
3230                                   "array constructor element has unknown size");
3231             });
3232         fir::CharacterType newEleTy = fir::CharacterType::getSingleton(
3233             eleTy.getContext(), charTy.getFKind());
3234         if (auto seqTy = resTy.dyn_cast<fir::SequenceType>()) {
3235           assert(eleTy == seqTy.getEleTy());
3236           resTy = fir::SequenceType::get(seqTy.getShape(), newEleTy);
3237         }
3238         eleTy = newEleTy;
3239       } else {
3240         TODO(loc, "dynamic sized type");
3241       }
3242     }
3243     mlir::Type eleRefTy = builder.getRefType(eleTy);
3244     mlir::Type resRefTy = builder.getRefType(resTy);
3245     mlir::Value nullPtr = builder.createNullConstant(loc, resRefTy);
3246     auto offset = builder.create<fir::CoordinateOp>(
3247         loc, eleRefTy, nullPtr, mlir::ValueRange{multiplier});
3248     return builder.createConvert(loc, idxTy, offset);
3249   }
3250 
3251   /// Get the function signature of the LLVM memcpy intrinsic.
3252   mlir::FunctionType memcpyType() {
3253     return fir::factory::getLlvmMemcpy(builder).getType();
3254   }
3255 
3256   /// Create a call to the LLVM memcpy intrinsic.
3257   void createCallMemcpy(llvm::ArrayRef<mlir::Value> args) {
3258     mlir::Location loc = getLoc();
3259     mlir::FuncOp memcpyFunc = fir::factory::getLlvmMemcpy(builder);
3260     mlir::SymbolRefAttr funcSymAttr =
3261         builder.getSymbolRefAttr(memcpyFunc.getName());
3262     mlir::FunctionType funcTy = memcpyFunc.getType();
3263     builder.create<fir::CallOp>(loc, funcTy.getResults(), funcSymAttr, args);
3264   }
3265 
3266   // Construct code to check for a buffer overrun and realloc the buffer when
3267   // space is depleted. This is done between each item in the ac-value-list.
3268   mlir::Value growBuffer(mlir::Value mem, mlir::Value needed,
3269                          mlir::Value bufferSize, mlir::Value buffSize,
3270                          mlir::Value eleSz) {
3271     mlir::Location loc = getLoc();
3272     mlir::FuncOp reallocFunc = fir::factory::getRealloc(builder);
3273     auto cond = builder.create<mlir::arith::CmpIOp>(
3274         loc, mlir::arith::CmpIPredicate::sle, bufferSize, needed);
3275     auto ifOp = builder.create<fir::IfOp>(loc, mem.getType(), cond,
3276                                           /*withElseRegion=*/true);
3277     auto insPt = builder.saveInsertionPoint();
3278     builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
3279     // Not enough space, resize the buffer.
3280     mlir::IndexType idxTy = builder.getIndexType();
3281     mlir::Value two = builder.createIntegerConstant(loc, idxTy, 2);
3282     auto newSz = builder.create<mlir::arith::MulIOp>(loc, needed, two);
3283     builder.create<fir::StoreOp>(loc, newSz, buffSize);
3284     mlir::Value byteSz = builder.create<mlir::arith::MulIOp>(loc, newSz, eleSz);
3285     mlir::SymbolRefAttr funcSymAttr =
3286         builder.getSymbolRefAttr(reallocFunc.getName());
3287     mlir::FunctionType funcTy = reallocFunc.getType();
3288     auto newMem = builder.create<fir::CallOp>(
3289         loc, funcTy.getResults(), funcSymAttr,
3290         llvm::ArrayRef<mlir::Value>{
3291             builder.createConvert(loc, funcTy.getInputs()[0], mem),
3292             builder.createConvert(loc, funcTy.getInputs()[1], byteSz)});
3293     mlir::Value castNewMem =
3294         builder.createConvert(loc, mem.getType(), newMem.getResult(0));
3295     builder.create<fir::ResultOp>(loc, castNewMem);
3296     builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
3297     // Otherwise, just forward the buffer.
3298     builder.create<fir::ResultOp>(loc, mem);
3299     builder.restoreInsertionPoint(insPt);
3300     return ifOp.getResult(0);
3301   }
3302 
3303   /// Copy the next value (or vector of values) into the array being
3304   /// constructed.
3305   mlir::Value copyNextArrayCtorSection(const ExtValue &exv, mlir::Value buffPos,
3306                                        mlir::Value buffSize, mlir::Value mem,
3307                                        mlir::Value eleSz, mlir::Type eleTy,
3308                                        mlir::Type eleRefTy, mlir::Type resTy) {
3309     mlir::Location loc = getLoc();
3310     auto off = builder.create<fir::LoadOp>(loc, buffPos);
3311     auto limit = builder.create<fir::LoadOp>(loc, buffSize);
3312     mlir::IndexType idxTy = builder.getIndexType();
3313     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
3314 
3315     if (fir::isRecordWithAllocatableMember(eleTy))
3316       TODO(loc, "deep copy on allocatable members");
3317 
3318     if (!eleSz) {
3319       // Compute the element size at runtime.
3320       assert(fir::hasDynamicSize(eleTy));
3321       if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
3322         auto charBytes =
3323             builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8;
3324         mlir::Value bytes =
3325             builder.createIntegerConstant(loc, idxTy, charBytes);
3326         mlir::Value length = fir::getLen(exv);
3327         if (!length)
3328           fir::emitFatalError(loc, "result is not boxed character");
3329         eleSz = builder.create<mlir::arith::MulIOp>(loc, bytes, length);
3330       } else {
3331         TODO(loc, "PDT size");
3332         // Will call the PDT's size function with the type parameters.
3333       }
3334     }
3335 
3336     // Compute the coordinate using `fir.coordinate_of`, or, if the type has
3337     // dynamic size, generating the pointer arithmetic.
3338     auto computeCoordinate = [&](mlir::Value buff, mlir::Value off) {
3339       mlir::Type refTy = eleRefTy;
3340       if (fir::hasDynamicSize(eleTy)) {
3341         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
3342           // Scale a simple pointer using dynamic length and offset values.
3343           auto chTy = fir::CharacterType::getSingleton(charTy.getContext(),
3344                                                        charTy.getFKind());
3345           refTy = builder.getRefType(chTy);
3346           mlir::Type toTy = builder.getRefType(builder.getVarLenSeqTy(chTy));
3347           buff = builder.createConvert(loc, toTy, buff);
3348           off = builder.create<mlir::arith::MulIOp>(loc, off, eleSz);
3349         } else {
3350           TODO(loc, "PDT offset");
3351         }
3352       }
3353       auto coor = builder.create<fir::CoordinateOp>(loc, refTy, buff,
3354                                                     mlir::ValueRange{off});
3355       return builder.createConvert(loc, eleRefTy, coor);
3356     };
3357 
3358     // Lambda to lower an abstract array box value.
3359     auto doAbstractArray = [&](const auto &v) {
3360       // Compute the array size.
3361       mlir::Value arrSz = one;
3362       for (auto ext : v.getExtents())
3363         arrSz = builder.create<mlir::arith::MulIOp>(loc, arrSz, ext);
3364 
3365       // Grow the buffer as needed.
3366       auto endOff = builder.create<mlir::arith::AddIOp>(loc, off, arrSz);
3367       mem = growBuffer(mem, endOff, limit, buffSize, eleSz);
3368 
3369       // Copy the elements to the buffer.
3370       mlir::Value byteSz =
3371           builder.create<mlir::arith::MulIOp>(loc, arrSz, eleSz);
3372       auto buff = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
3373       mlir::Value buffi = computeCoordinate(buff, off);
3374       llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
3375           builder, loc, memcpyType(), buffi, v.getAddr(), byteSz,
3376           /*volatile=*/builder.createBool(loc, false));
3377       createCallMemcpy(args);
3378 
3379       // Save the incremented buffer position.
3380       builder.create<fir::StoreOp>(loc, endOff, buffPos);
3381     };
3382 
3383     // Copy a trivial scalar value into the buffer.
3384     auto doTrivialScalar = [&](const ExtValue &v, mlir::Value len = {}) {
3385       // Increment the buffer position.
3386       auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
3387 
3388       // Grow the buffer as needed.
3389       mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
3390 
3391       // Store the element in the buffer.
3392       mlir::Value buff =
3393           builder.createConvert(loc, fir::HeapType::get(resTy), mem);
3394       auto buffi = builder.create<fir::CoordinateOp>(loc, eleRefTy, buff,
3395                                                      mlir::ValueRange{off});
3396       fir::factory::genScalarAssignment(
3397           builder, loc,
3398           [&]() -> ExtValue {
3399             if (len)
3400               return fir::CharBoxValue(buffi, len);
3401             return buffi;
3402           }(),
3403           v);
3404       builder.create<fir::StoreOp>(loc, plusOne, buffPos);
3405     };
3406 
3407     // Copy the value.
3408     exv.match(
3409         [&](mlir::Value) { doTrivialScalar(exv); },
3410         [&](const fir::CharBoxValue &v) {
3411           auto buffer = v.getBuffer();
3412           if (fir::isa_char(buffer.getType())) {
3413             doTrivialScalar(exv, eleSz);
3414           } else {
3415             // Increment the buffer position.
3416             auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
3417 
3418             // Grow the buffer as needed.
3419             mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
3420 
3421             // Store the element in the buffer.
3422             mlir::Value buff =
3423                 builder.createConvert(loc, fir::HeapType::get(resTy), mem);
3424             mlir::Value buffi = computeCoordinate(buff, off);
3425             llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
3426                 builder, loc, memcpyType(), buffi, v.getAddr(), eleSz,
3427                 /*volatile=*/builder.createBool(loc, false));
3428             createCallMemcpy(args);
3429 
3430             builder.create<fir::StoreOp>(loc, plusOne, buffPos);
3431           }
3432         },
3433         [&](const fir::ArrayBoxValue &v) { doAbstractArray(v); },
3434         [&](const fir::CharArrayBoxValue &v) { doAbstractArray(v); },
3435         [&](const auto &) {
3436           TODO(loc, "unhandled array constructor expression");
3437         });
3438     return mem;
3439   }
3440 
3441   // Lower the expr cases in an ac-value-list.
3442   template <typename A>
3443   std::pair<ExtValue, bool>
3444   genArrayCtorInitializer(const Fortran::evaluate::Expr<A> &x, mlir::Type,
3445                           mlir::Value, mlir::Value, mlir::Value,
3446                           Fortran::lower::StatementContext &stmtCtx) {
3447     if (isArray(x))
3448       return {lowerNewArrayExpression(converter, symMap, stmtCtx, toEvExpr(x)),
3449               /*needCopy=*/true};
3450     return {asScalar(x), /*needCopy=*/true};
3451   }
3452 
3453   // Lower an ac-implied-do in an ac-value-list.
3454   template <typename A>
3455   std::pair<ExtValue, bool>
3456   genArrayCtorInitializer(const Fortran::evaluate::ImpliedDo<A> &x,
3457                           mlir::Type resTy, mlir::Value mem,
3458                           mlir::Value buffPos, mlir::Value buffSize,
3459                           Fortran::lower::StatementContext &) {
3460     mlir::Location loc = getLoc();
3461     mlir::IndexType idxTy = builder.getIndexType();
3462     mlir::Value lo =
3463         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.lower())));
3464     mlir::Value up =
3465         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.upper())));
3466     mlir::Value step =
3467         builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.stride())));
3468     auto seqTy = resTy.template cast<fir::SequenceType>();
3469     mlir::Type eleTy = fir::unwrapSequenceType(seqTy);
3470     auto loop =
3471         builder.create<fir::DoLoopOp>(loc, lo, up, step, /*unordered=*/false,
3472                                       /*finalCount=*/false, mem);
3473     // create a new binding for x.name(), to ac-do-variable, to the iteration
3474     // value.
3475     symMap.pushImpliedDoBinding(toStringRef(x.name()), loop.getInductionVar());
3476     auto insPt = builder.saveInsertionPoint();
3477     builder.setInsertionPointToStart(loop.getBody());
3478     // Thread mem inside the loop via loop argument.
3479     mem = loop.getRegionIterArgs()[0];
3480 
3481     mlir::Type eleRefTy = builder.getRefType(eleTy);
3482 
3483     // Any temps created in the loop body must be freed inside the loop body.
3484     stmtCtx.pushScope();
3485     llvm::Optional<mlir::Value> charLen;
3486     for (const Fortran::evaluate::ArrayConstructorValue<A> &acv : x.values()) {
3487       auto [exv, copyNeeded] = std::visit(
3488           [&](const auto &v) {
3489             return genArrayCtorInitializer(v, resTy, mem, buffPos, buffSize,
3490                                            stmtCtx);
3491           },
3492           acv.u);
3493       mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
3494       mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
3495                                                   eleSz, eleTy, eleRefTy, resTy)
3496                        : fir::getBase(exv);
3497       if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) {
3498         charLen = builder.createTemporary(loc, builder.getI64Type());
3499         mlir::Value castLen =
3500             builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
3501         builder.create<fir::StoreOp>(loc, castLen, charLen.getValue());
3502       }
3503     }
3504     stmtCtx.finalize(/*popScope=*/true);
3505 
3506     builder.create<fir::ResultOp>(loc, mem);
3507     builder.restoreInsertionPoint(insPt);
3508     mem = loop.getResult(0);
3509     symMap.popImpliedDoBinding();
3510     llvm::SmallVector<mlir::Value> extents = {
3511         builder.create<fir::LoadOp>(loc, buffPos).getResult()};
3512 
3513     // Convert to extended value.
3514     if (fir::isa_char(seqTy.getEleTy())) {
3515       auto len = builder.create<fir::LoadOp>(loc, charLen.getValue());
3516       return {fir::CharArrayBoxValue{mem, len, extents}, /*needCopy=*/false};
3517     }
3518     return {fir::ArrayBoxValue{mem, extents}, /*needCopy=*/false};
3519   }
3520 
3521   // To simplify the handling and interaction between the various cases, array
3522   // constructors are always lowered to the incremental construction code
3523   // pattern, even if the extent of the array value is constant. After the
3524   // MemToReg pass and constant folding, the optimizer should be able to
3525   // determine that all the buffer overrun tests are false when the
3526   // incremental construction wasn't actually required.
3527   template <typename A>
3528   CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) {
3529     mlir::Location loc = getLoc();
3530     auto evExpr = toEvExpr(x);
3531     mlir::Type resTy = translateSomeExprToFIRType(converter, evExpr);
3532     mlir::IndexType idxTy = builder.getIndexType();
3533     auto seqTy = resTy.template cast<fir::SequenceType>();
3534     mlir::Type eleTy = fir::unwrapSequenceType(resTy);
3535     mlir::Value buffSize = builder.createTemporary(loc, idxTy, ".buff.size");
3536     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
3537     mlir::Value buffPos = builder.createTemporary(loc, idxTy, ".buff.pos");
3538     builder.create<fir::StoreOp>(loc, zero, buffPos);
3539     // Allocate space for the array to be constructed.
3540     mlir::Value mem;
3541     if (fir::hasDynamicSize(resTy)) {
3542       if (fir::hasDynamicSize(eleTy)) {
3543         // The size of each element may depend on a general expression. Defer
3544         // creating the buffer until after the expression is evaluated.
3545         mem = builder.createNullConstant(loc, builder.getRefType(eleTy));
3546         builder.create<fir::StoreOp>(loc, zero, buffSize);
3547       } else {
3548         mlir::Value initBuffSz =
3549             builder.createIntegerConstant(loc, idxTy, clInitialBufferSize);
3550         mem = builder.create<fir::AllocMemOp>(
3551             loc, eleTy, /*typeparams=*/llvm::None, initBuffSz);
3552         builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
3553       }
3554     } else {
3555       mem = builder.create<fir::AllocMemOp>(loc, resTy);
3556       int64_t buffSz = 1;
3557       for (auto extent : seqTy.getShape())
3558         buffSz *= extent;
3559       mlir::Value initBuffSz =
3560           builder.createIntegerConstant(loc, idxTy, buffSz);
3561       builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
3562     }
3563     // Compute size of element
3564     mlir::Type eleRefTy = builder.getRefType(eleTy);
3565 
3566     // Populate the buffer with the elements, growing as necessary.
3567     llvm::Optional<mlir::Value> charLen;
3568     for (const auto &expr : x) {
3569       auto [exv, copyNeeded] = std::visit(
3570           [&](const auto &e) {
3571             return genArrayCtorInitializer(e, resTy, mem, buffPos, buffSize,
3572                                            stmtCtx);
3573           },
3574           expr.u);
3575       mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
3576       mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
3577                                                   eleSz, eleTy, eleRefTy, resTy)
3578                        : fir::getBase(exv);
3579       if (fir::isa_char(seqTy.getEleTy()) && !charLen.hasValue()) {
3580         charLen = builder.createTemporary(loc, builder.getI64Type());
3581         mlir::Value castLen =
3582             builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
3583         builder.create<fir::StoreOp>(loc, castLen, charLen.getValue());
3584       }
3585     }
3586     mem = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
3587     llvm::SmallVector<mlir::Value> extents = {
3588         builder.create<fir::LoadOp>(loc, buffPos)};
3589 
3590     // Cleanup the temporary.
3591     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
3592     stmtCtx.attachCleanup(
3593         [bldr, loc, mem]() { bldr->create<fir::FreeMemOp>(loc, mem); });
3594 
3595     // Return the continuation.
3596     if (fir::isa_char(seqTy.getEleTy())) {
3597       if (charLen.hasValue()) {
3598         auto len = builder.create<fir::LoadOp>(loc, charLen.getValue());
3599         return genarr(fir::CharArrayBoxValue{mem, len, extents});
3600       }
3601       return genarr(fir::CharArrayBoxValue{mem, zero, extents});
3602     }
3603     return genarr(fir::ArrayBoxValue{mem, extents});
3604   }
3605 
3606   CC genarr(const Fortran::evaluate::ImpliedDoIndex &) {
3607     TODO(getLoc(), "genarr ImpliedDoIndex");
3608   }
3609 
3610   CC genarr(const Fortran::evaluate::TypeParamInquiry &x) {
3611     TODO(getLoc(), "genarr TypeParamInquiry");
3612   }
3613 
3614   CC genarr(const Fortran::evaluate::DescriptorInquiry &x) {
3615     TODO(getLoc(), "genarr DescriptorInquiry");
3616   }
3617 
3618   CC genarr(const Fortran::evaluate::StructureConstructor &x) {
3619     TODO(getLoc(), "genarr StructureConstructor");
3620   }
3621 
3622   template <int KIND>
3623   CC genarr(const Fortran::evaluate::Not<KIND> &x) {
3624     TODO(getLoc(), "genarr Not");
3625   }
3626 
3627   template <int KIND>
3628   CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) {
3629     TODO(getLoc(), "genarr LogicalOperation");
3630   }
3631 
3632   template <int KIND>
3633   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
3634                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
3635     TODO(getLoc(), "genarr Relational Integer");
3636   }
3637   template <int KIND>
3638   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
3639                 Fortran::common::TypeCategory::Character, KIND>> &x) {
3640     TODO(getLoc(), "genarr Relational Character");
3641   }
3642   template <int KIND>
3643   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
3644                 Fortran::common::TypeCategory::Real, KIND>> &x) {
3645     TODO(getLoc(), "genarr Relational Real");
3646   }
3647   template <int KIND>
3648   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
3649                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
3650     TODO(getLoc(), "genarr Relational Complex");
3651   }
3652   CC genarr(
3653       const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) {
3654     TODO(getLoc(), "genarr Relational SomeType");
3655   }
3656 
3657   template <typename A>
3658   CC genarr(const Fortran::evaluate::Designator<A> &des) {
3659     ComponentPath components(des.Rank() > 0);
3660     return std::visit([&](const auto &x) { return genarr(x, components); },
3661                       des.u);
3662   }
3663 
3664   template <typename T>
3665   CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) {
3666     // Note that it's possible that the function being called returns either an
3667     // array or a scalar.  In the first case, use the element type of the array.
3668     return genProcRef(
3669         funRef, fir::unwrapSequenceType(converter.genType(toEvExpr(funRef))));
3670   }
3671 
3672   template <typename A>
3673   CC genImplicitArrayAccess(const A &x, ComponentPath &components) {
3674     components.reversePath.push_back(ImplicitSubscripts{});
3675     ExtValue exv = asScalarRef(x);
3676     // lowerPath(exv, components);
3677     auto lambda = genarr(exv, components);
3678     return [=](IterSpace iters) { return lambda(components.pc(iters)); };
3679   }
3680   CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x,
3681                             ComponentPath &components) {
3682     if (x.IsSymbol())
3683       return genImplicitArrayAccess(x.GetFirstSymbol(), components);
3684     return genImplicitArrayAccess(x.GetComponent(), components);
3685   }
3686 
3687   template <typename A>
3688   CC genAsScalar(const A &x) {
3689     mlir::Location loc = getLoc();
3690     if (isProjectedCopyInCopyOut()) {
3691       return [=, &x, builder = &converter.getFirOpBuilder()](
3692                  IterSpace iters) -> ExtValue {
3693         ExtValue exv = asScalarRef(x);
3694         mlir::Value val = fir::getBase(exv);
3695         mlir::Type eleTy = fir::unwrapRefType(val.getType());
3696         if (isAdjustedArrayElementType(eleTy)) {
3697           if (fir::isa_char(eleTy)) {
3698             TODO(getLoc(), "assignment of character type");
3699           } else if (fir::isa_derived(eleTy)) {
3700             TODO(loc, "assignment of derived type");
3701           } else {
3702             fir::emitFatalError(loc, "array type not expected in scalar");
3703           }
3704         } else {
3705           builder->create<fir::StoreOp>(loc, iters.getElement(), val);
3706         }
3707         return exv;
3708       };
3709     }
3710     return [=, &x](IterSpace) { return asScalar(x); };
3711   }
3712 
3713   CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) {
3714     if (explicitSpaceIsActive()) {
3715       TODO(getLoc(), "genarr Symbol explicitSpace");
3716     } else {
3717       return genImplicitArrayAccess(x, components);
3718     }
3719   }
3720 
3721   CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) {
3722     TODO(getLoc(), "genarr Component");
3723   }
3724 
3725   /// Array reference with subscripts. If this has rank > 0, this is a form
3726   /// of an array section (slice).
3727   ///
3728   /// There are two "slicing" primitives that may be applied on a dimension by
3729   /// dimension basis: (1) triple notation and (2) vector addressing. Since
3730   /// dimensions can be selectively sliced, some dimensions may contain
3731   /// regular scalar expressions and those dimensions do not participate in
3732   /// the array expression evaluation.
3733   CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) {
3734     if (explicitSpaceIsActive()) {
3735       TODO(getLoc(), "genarr ArrayRef explicitSpace");
3736     } else {
3737       if (Fortran::lower::isRankedArrayAccess(x)) {
3738         components.reversePath.push_back(&x);
3739         return genImplicitArrayAccess(x.base(), components);
3740       }
3741     }
3742     bool atEnd = pathIsEmpty(components);
3743     components.reversePath.push_back(&x);
3744     auto result = genarr(x.base(), components);
3745     if (components.applied)
3746       return result;
3747     mlir::Location loc = getLoc();
3748     if (atEnd) {
3749       if (x.Rank() == 0)
3750         return genAsScalar(x);
3751       fir::emitFatalError(loc, "expected scalar");
3752     }
3753     return [=](IterSpace) -> ExtValue {
3754       fir::emitFatalError(loc, "reached arrayref with path");
3755     };
3756   }
3757 
3758   CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) {
3759     TODO(getLoc(), "coarray reference");
3760   }
3761 
3762   CC genarr(const Fortran::evaluate::NamedEntity &x,
3763             ComponentPath &components) {
3764     return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components)
3765                         : genarr(x.GetComponent(), components);
3766   }
3767 
3768   CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) {
3769     return std::visit([&](const auto &v) { return genarr(v, components); },
3770                       x.u);
3771   }
3772 
3773   bool pathIsEmpty(const ComponentPath &components) {
3774     return components.reversePath.empty();
3775   }
3776 
3777   /// Given an optional fir.box, returns an fir.box that is the original one if
3778   /// it is present and it otherwise an unallocated box.
3779   /// Absent fir.box are implemented as a null pointer descriptor. Generated
3780   /// code may need to unconditionally read a fir.box that can be absent.
3781   /// This helper allows creating a fir.box that can be read in all cases
3782   /// outside of a fir.if (isPresent) region. However, the usages of the value
3783   /// read from such box should still only be done in a fir.if(isPresent).
3784   static fir::ExtendedValue
3785   absentBoxToUnalllocatedBox(fir::FirOpBuilder &builder, mlir::Location loc,
3786                              const fir::ExtendedValue &exv,
3787                              mlir::Value isPresent) {
3788     mlir::Value box = fir::getBase(exv);
3789     mlir::Type boxType = box.getType();
3790     assert(boxType.isa<fir::BoxType>() && "argument must be a fir.box");
3791     mlir::Value emptyBox =
3792         fir::factory::createUnallocatedBox(builder, loc, boxType, llvm::None);
3793     auto safeToReadBox =
3794         builder.create<mlir::arith::SelectOp>(loc, isPresent, box, emptyBox);
3795     return fir::substBase(exv, safeToReadBox);
3796   }
3797 
3798   std::tuple<CC, mlir::Value, mlir::Type>
3799   genOptionalArrayFetch(const Fortran::lower::SomeExpr &expr) {
3800     assert(expr.Rank() > 0 && "expr must be an array");
3801     mlir::Location loc = getLoc();
3802     ExtValue optionalArg = asInquired(expr);
3803     mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
3804     // Generate an array load and access to an array that may be an absent
3805     // optional or an unallocated optional.
3806     mlir::Value base = getBase(optionalArg);
3807     const bool hasOptionalAttr =
3808         fir::valueHasFirAttribute(base, fir::getOptionalAttrName());
3809     mlir::Type baseType = fir::unwrapRefType(base.getType());
3810     const bool isBox = baseType.isa<fir::BoxType>();
3811     const bool isAllocOrPtr = Fortran::evaluate::IsAllocatableOrPointerObject(
3812         expr, converter.getFoldingContext());
3813     mlir::Type arrType = fir::unwrapPassByRefType(baseType);
3814     mlir::Type eleType = fir::unwrapSequenceType(arrType);
3815     ExtValue exv = optionalArg;
3816     if (hasOptionalAttr && isBox && !isAllocOrPtr) {
3817       // Elemental argument cannot be allocatable or pointers (C15100).
3818       // Hence, per 15.5.2.12 3 (8) and (9), the provided Allocatable and
3819       // Pointer optional arrays cannot be absent. The only kind of entities
3820       // that can get here are optional assumed shape and polymorphic entities.
3821       exv = absentBoxToUnalllocatedBox(builder, loc, exv, isPresent);
3822     }
3823     // All the properties can be read from any fir.box but the read values may
3824     // be undefined and should only be used inside a fir.if (canBeRead) region.
3825     if (const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>())
3826       exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox);
3827 
3828     mlir::Value memref = fir::getBase(exv);
3829     mlir::Value shape = builder.createShape(loc, exv);
3830     mlir::Value noSlice;
3831     auto arrLoad = builder.create<fir::ArrayLoadOp>(
3832         loc, arrType, memref, shape, noSlice, fir::getTypeParams(exv));
3833     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
3834     mlir::Value arrLd = arrLoad.getResult();
3835     // Mark the load to tell later passes it is unsafe to use this array_load
3836     // shape unconditionally.
3837     arrLoad->setAttr(fir::getOptionalAttrName(), builder.getUnitAttr());
3838 
3839     // Place the array as optional on the arrayOperands stack so that its
3840     // shape will only be used as a fallback to induce the implicit loop nest
3841     // (that is if there is no non optional array arguments).
3842     arrayOperands.push_back(
3843         ArrayOperand{memref, shape, noSlice, /*mayBeAbsent=*/true});
3844 
3845     // By value semantics.
3846     auto cc = [=](IterSpace iters) -> ExtValue {
3847       auto arrFetch = builder.create<fir::ArrayFetchOp>(
3848           loc, eleType, arrLd, iters.iterVec(), arrLdTypeParams);
3849       return fir::factory::arraySectionElementToExtendedValue(
3850           builder, loc, exv, arrFetch, noSlice);
3851     };
3852     return {cc, isPresent, eleType};
3853   }
3854 
3855   /// Generate a continuation to pass \p expr to an OPTIONAL argument of an
3856   /// elemental procedure. This is meant to handle the cases where \p expr might
3857   /// be dynamically absent (i.e. when it is a POINTER, an ALLOCATABLE or an
3858   /// OPTIONAL variable). If p\ expr is guaranteed to be present genarr() can
3859   /// directly be called instead.
3860   CC genarrForwardOptionalArgumentToCall(const Fortran::lower::SomeExpr &expr) {
3861     mlir::Location loc = getLoc();
3862     // Only by-value numerical and logical so far.
3863     if (semant != ConstituentSemantics::RefTransparent)
3864       TODO(loc, "optional arguments in user defined elemental procedures");
3865 
3866     // Handle scalar argument case (the if-then-else is generated outside of the
3867     // implicit loop nest).
3868     if (expr.Rank() == 0) {
3869       ExtValue optionalArg = asInquired(expr);
3870       mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
3871       mlir::Value elementValue =
3872           fir::getBase(genOptionalValue(builder, loc, optionalArg, isPresent));
3873       return [=](IterSpace iters) -> ExtValue { return elementValue; };
3874     }
3875 
3876     CC cc;
3877     mlir::Value isPresent;
3878     mlir::Type eleType;
3879     std::tie(cc, isPresent, eleType) = genOptionalArrayFetch(expr);
3880     return [=](IterSpace iters) -> ExtValue {
3881       mlir::Value elementValue =
3882           builder
3883               .genIfOp(loc, {eleType}, isPresent,
3884                        /*withElseRegion=*/true)
3885               .genThen([&]() {
3886                 builder.create<fir::ResultOp>(loc, fir::getBase(cc(iters)));
3887               })
3888               .genElse([&]() {
3889                 mlir::Value zero =
3890                     fir::factory::createZeroValue(builder, loc, eleType);
3891                 builder.create<fir::ResultOp>(loc, zero);
3892               })
3893               .getResults()[0];
3894       return elementValue;
3895     };
3896   }
3897 
3898   /// Reduce the rank of a array to be boxed based on the slice's operands.
3899   static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) {
3900     if (slice) {
3901       auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp());
3902       assert(slOp && "expected slice op");
3903       auto seqTy = arrTy.dyn_cast<fir::SequenceType>();
3904       assert(seqTy && "expected array type");
3905       mlir::Operation::operand_range triples = slOp.getTriples();
3906       fir::SequenceType::Shape shape;
3907       // reduce the rank for each invariant dimension
3908       for (unsigned i = 1, end = triples.size(); i < end; i += 3)
3909         if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp()))
3910           shape.push_back(fir::SequenceType::getUnknownExtent());
3911       return fir::SequenceType::get(shape, seqTy.getEleTy());
3912     }
3913     // not sliced, so no change in rank
3914     return arrTy;
3915   }
3916 
3917   CC genarr(const Fortran::evaluate::ComplexPart &x,
3918             ComponentPath &components) {
3919     TODO(getLoc(), "genarr ComplexPart");
3920   }
3921 
3922   CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &,
3923             ComponentPath &components) {
3924     TODO(getLoc(), "genarr StaticDataObject::Pointer");
3925   }
3926 
3927   /// Substrings (see 9.4.1)
3928   CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) {
3929     TODO(getLoc(), "genarr Substring");
3930   }
3931 
3932   /// Base case of generating an array reference,
3933   CC genarr(const ExtValue &extMemref, ComponentPath &components) {
3934     mlir::Location loc = getLoc();
3935     mlir::Value memref = fir::getBase(extMemref);
3936     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType());
3937     assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array");
3938     mlir::Value shape = builder.createShape(loc, extMemref);
3939     mlir::Value slice;
3940     if (components.isSlice()) {
3941       if (isBoxValue() && components.substring) {
3942         // Append the substring operator to emboxing Op as it will become an
3943         // interior adjustment (add offset, adjust LEN) to the CHARACTER value
3944         // being referenced in the descriptor.
3945         llvm::SmallVector<mlir::Value> substringBounds;
3946         populateBounds(substringBounds, components.substring);
3947         // Convert to (offset, size)
3948         mlir::Type iTy = substringBounds[0].getType();
3949         if (substringBounds.size() != 2) {
3950           fir::CharacterType charTy =
3951               fir::factory::CharacterExprHelper::getCharType(arrTy);
3952           if (charTy.hasConstantLen()) {
3953             mlir::IndexType idxTy = builder.getIndexType();
3954             fir::CharacterType::LenType charLen = charTy.getLen();
3955             mlir::Value lenValue =
3956                 builder.createIntegerConstant(loc, idxTy, charLen);
3957             substringBounds.push_back(lenValue);
3958           } else {
3959             llvm::SmallVector<mlir::Value> typeparams =
3960                 fir::getTypeParams(extMemref);
3961             substringBounds.push_back(typeparams.back());
3962           }
3963         }
3964         // Convert the lower bound to 0-based substring.
3965         mlir::Value one =
3966             builder.createIntegerConstant(loc, substringBounds[0].getType(), 1);
3967         substringBounds[0] =
3968             builder.create<mlir::arith::SubIOp>(loc, substringBounds[0], one);
3969         // Convert the upper bound to a length.
3970         mlir::Value cast = builder.createConvert(loc, iTy, substringBounds[1]);
3971         mlir::Value zero = builder.createIntegerConstant(loc, iTy, 0);
3972         auto size =
3973             builder.create<mlir::arith::SubIOp>(loc, cast, substringBounds[0]);
3974         auto cmp = builder.create<mlir::arith::CmpIOp>(
3975             loc, mlir::arith::CmpIPredicate::sgt, size, zero);
3976         // size = MAX(upper - (lower - 1), 0)
3977         substringBounds[1] =
3978             builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero);
3979         slice = builder.create<fir::SliceOp>(loc, components.trips,
3980                                              components.suffixComponents,
3981                                              substringBounds);
3982       } else {
3983         slice = builder.createSlice(loc, extMemref, components.trips,
3984                                     components.suffixComponents);
3985       }
3986       if (components.hasComponents()) {
3987         auto seqTy = arrTy.cast<fir::SequenceType>();
3988         mlir::Type eleTy =
3989             fir::applyPathToType(seqTy.getEleTy(), components.suffixComponents);
3990         if (!eleTy)
3991           fir::emitFatalError(loc, "slicing path is ill-formed");
3992         if (auto realTy = eleTy.dyn_cast<fir::RealType>())
3993           eleTy = Fortran::lower::convertReal(realTy.getContext(),
3994                                               realTy.getFKind());
3995 
3996         // create the type of the projected array.
3997         arrTy = fir::SequenceType::get(seqTy.getShape(), eleTy);
3998         LLVM_DEBUG(llvm::dbgs()
3999                    << "type of array projection from component slicing: "
4000                    << eleTy << ", " << arrTy << '\n');
4001       }
4002     }
4003     arrayOperands.push_back(ArrayOperand{memref, shape, slice});
4004     if (destShape.empty())
4005       destShape = getShape(arrayOperands.back());
4006     if (isBoxValue()) {
4007       // Semantics are a reference to a boxed array.
4008       // This case just requires that an embox operation be created to box the
4009       // value. The value of the box is forwarded in the continuation.
4010       mlir::Type reduceTy = reduceRank(arrTy, slice);
4011       auto boxTy = fir::BoxType::get(reduceTy);
4012       if (components.substring) {
4013         // Adjust char length to substring size.
4014         fir::CharacterType charTy =
4015             fir::factory::CharacterExprHelper::getCharType(reduceTy);
4016         auto seqTy = reduceTy.cast<fir::SequenceType>();
4017         // TODO: Use a constant for fir.char LEN if we can compute it.
4018         boxTy = fir::BoxType::get(
4019             fir::SequenceType::get(fir::CharacterType::getUnknownLen(
4020                                        builder.getContext(), charTy.getFKind()),
4021                                    seqTy.getDimension()));
4022       }
4023       mlir::Value embox =
4024           memref.getType().isa<fir::BoxType>()
4025               ? builder.create<fir::ReboxOp>(loc, boxTy, memref, shape, slice)
4026                     .getResult()
4027               : builder
4028                     .create<fir::EmboxOp>(loc, boxTy, memref, shape, slice,
4029                                           fir::getTypeParams(extMemref))
4030                     .getResult();
4031       return [=](IterSpace) -> ExtValue { return fir::BoxValue(embox); };
4032     }
4033     auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
4034     if (isReferentiallyOpaque()) {
4035       // Semantics are an opaque reference to an array.
4036       // This case forwards a continuation that will generate the address
4037       // arithmetic to the array element. This does not have copy-in/copy-out
4038       // semantics. No attempt to copy the array value will be made during the
4039       // interpretation of the Fortran statement.
4040       mlir::Type refEleTy = builder.getRefType(eleTy);
4041       return [=](IterSpace iters) -> ExtValue {
4042         // ArrayCoorOp does not expect zero based indices.
4043         llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
4044             loc, builder, memref.getType(), shape, iters.iterVec());
4045         mlir::Value coor = builder.create<fir::ArrayCoorOp>(
4046             loc, refEleTy, memref, shape, slice, indices,
4047             fir::getTypeParams(extMemref));
4048         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4049           llvm::SmallVector<mlir::Value> substringBounds;
4050           populateBounds(substringBounds, components.substring);
4051           if (!substringBounds.empty()) {
4052             mlir::Value dstLen = fir::factory::genLenOfCharacter(
4053                 builder, loc, arrTy.cast<fir::SequenceType>(), memref,
4054                 fir::getTypeParams(extMemref), iters.iterVec(),
4055                 substringBounds);
4056             fir::CharBoxValue dstChar(coor, dstLen);
4057             return fir::factory::CharacterExprHelper{builder, loc}
4058                 .createSubstring(dstChar, substringBounds);
4059           }
4060         }
4061         return fir::factory::arraySectionElementToExtendedValue(
4062             builder, loc, extMemref, coor, slice);
4063       };
4064     }
4065     auto arrLoad = builder.create<fir::ArrayLoadOp>(
4066         loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref));
4067     mlir::Value arrLd = arrLoad.getResult();
4068     if (isProjectedCopyInCopyOut()) {
4069       // Semantics are projected copy-in copy-out.
4070       // The backing store of the destination of an array expression may be
4071       // partially modified. These updates are recorded in FIR by forwarding a
4072       // continuation that generates an `array_update` Op. The destination is
4073       // always loaded at the beginning of the statement and merged at the
4074       // end.
4075       destination = arrLoad;
4076       auto lambda = ccStoreToDest.hasValue()
4077                         ? ccStoreToDest.getValue()
4078                         : defaultStoreToDestination(components.substring);
4079       return [=](IterSpace iters) -> ExtValue { return lambda(iters); };
4080     }
4081     if (isCustomCopyInCopyOut()) {
4082       // Create an array_modify to get the LHS element address and indicate
4083       // the assignment, the actual assignment must be implemented in
4084       // ccStoreToDest.
4085       destination = arrLoad;
4086       return [=](IterSpace iters) -> ExtValue {
4087         mlir::Value innerArg = iters.innerArgument();
4088         mlir::Type resTy = innerArg.getType();
4089         mlir::Type eleTy = fir::applyPathToType(resTy, iters.iterVec());
4090         mlir::Type refEleTy =
4091             fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy);
4092         auto arrModify = builder.create<fir::ArrayModifyOp>(
4093             loc, mlir::TypeRange{refEleTy, resTy}, innerArg, iters.iterVec(),
4094             destination.getTypeparams());
4095         return abstractArrayExtValue(arrModify.getResult(1));
4096       };
4097     }
4098     if (isCopyInCopyOut()) {
4099       // Semantics are copy-in copy-out.
4100       // The continuation simply forwards the result of the `array_load` Op,
4101       // which is the value of the array as it was when loaded. All data
4102       // references with rank > 0 in an array expression typically have
4103       // copy-in copy-out semantics.
4104       return [=](IterSpace) -> ExtValue { return arrLd; };
4105     }
4106     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
4107     if (isValueAttribute()) {
4108       // Semantics are value attribute.
4109       // Here the continuation will `array_fetch` a value from an array and
4110       // then store that value in a temporary. One can thus imitate pass by
4111       // value even when the call is pass by reference.
4112       return [=](IterSpace iters) -> ExtValue {
4113         mlir::Value base;
4114         mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
4115         if (isAdjustedArrayElementType(eleTy)) {
4116           mlir::Type eleRefTy = builder.getRefType(eleTy);
4117           base = builder.create<fir::ArrayAccessOp>(
4118               loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
4119         } else {
4120           base = builder.create<fir::ArrayFetchOp>(
4121               loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
4122         }
4123         mlir::Value temp = builder.createTemporary(
4124             loc, base.getType(),
4125             llvm::ArrayRef<mlir::NamedAttribute>{
4126                 Fortran::lower::getAdaptToByRefAttr(builder)});
4127         builder.create<fir::StoreOp>(loc, base, temp);
4128         return fir::factory::arraySectionElementToExtendedValue(
4129             builder, loc, extMemref, temp, slice);
4130       };
4131     }
4132     // In the default case, the array reference forwards an `array_fetch` or
4133     // `array_access` Op in the continuation.
4134     return [=](IterSpace iters) -> ExtValue {
4135       mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
4136       if (isAdjustedArrayElementType(eleTy)) {
4137         mlir::Type eleRefTy = builder.getRefType(eleTy);
4138         mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>(
4139             loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
4140         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4141           llvm::SmallVector<mlir::Value> substringBounds;
4142           populateBounds(substringBounds, components.substring);
4143           if (!substringBounds.empty()) {
4144             mlir::Value dstLen = fir::factory::genLenOfCharacter(
4145                 builder, loc, arrLoad, iters.iterVec(), substringBounds);
4146             fir::CharBoxValue dstChar(arrayOp, dstLen);
4147             return fir::factory::CharacterExprHelper{builder, loc}
4148                 .createSubstring(dstChar, substringBounds);
4149           }
4150         }
4151         return fir::factory::arraySectionElementToExtendedValue(
4152             builder, loc, extMemref, arrayOp, slice);
4153       }
4154       auto arrFetch = builder.create<fir::ArrayFetchOp>(
4155           loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
4156       return fir::factory::arraySectionElementToExtendedValue(
4157           builder, loc, extMemref, arrFetch, slice);
4158     };
4159   }
4160 
4161 private:
4162   void determineShapeOfDest(const fir::ExtendedValue &lhs) {
4163     destShape = fir::factory::getExtents(builder, getLoc(), lhs);
4164   }
4165 
4166   void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) {
4167     if (!destShape.empty())
4168       return;
4169     // if (explicitSpaceIsActive() && determineShapeWithSlice(lhs))
4170     //   return;
4171     mlir::Type idxTy = builder.getIndexType();
4172     mlir::Location loc = getLoc();
4173     if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape =
4174             Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(),
4175                                                   lhs))
4176       for (Fortran::common::ConstantSubscript extent : *constantShape)
4177         destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent));
4178   }
4179 
4180   ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) {
4181     mlir::Type resTy = converter.genType(exp);
4182     return std::visit(
4183         [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); },
4184         exp.u);
4185   }
4186   ExtValue lowerArrayExpression(const ExtValue &exv) {
4187     assert(!explicitSpace);
4188     mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType());
4189     return lowerArrayExpression(genarr(exv), resTy);
4190   }
4191 
4192   void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds,
4193                       const Fortran::evaluate::Substring *substring) {
4194     if (!substring)
4195       return;
4196     bounds.push_back(fir::getBase(asScalar(substring->lower())));
4197     if (auto upper = substring->upper())
4198       bounds.push_back(fir::getBase(asScalar(*upper)));
4199   }
4200 
4201   /// Default store to destination implementation.
4202   /// This implements the default case, which is to assign the value in
4203   /// `iters.element` into the destination array, `iters.innerArgument`. Handles
4204   /// by value and by reference assignment.
4205   CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) {
4206     return [=](IterSpace iterSpace) -> ExtValue {
4207       mlir::Location loc = getLoc();
4208       mlir::Value innerArg = iterSpace.innerArgument();
4209       fir::ExtendedValue exv = iterSpace.elementExv();
4210       mlir::Type arrTy = innerArg.getType();
4211       mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec());
4212       if (isAdjustedArrayElementType(eleTy)) {
4213         // The elemental update is in the memref domain. Under this semantics,
4214         // we must always copy the computed new element from its location in
4215         // memory into the destination array.
4216         mlir::Type resRefTy = builder.getRefType(eleTy);
4217         // Get a reference to the array element to be amended.
4218         auto arrayOp = builder.create<fir::ArrayAccessOp>(
4219             loc, resRefTy, innerArg, iterSpace.iterVec(),
4220             destination.getTypeparams());
4221         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4222           llvm::SmallVector<mlir::Value> substringBounds;
4223           populateBounds(substringBounds, substring);
4224           mlir::Value dstLen = fir::factory::genLenOfCharacter(
4225               builder, loc, destination, iterSpace.iterVec(), substringBounds);
4226           fir::ArrayAmendOp amend = createCharArrayAmend(
4227               loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds);
4228           return abstractArrayExtValue(amend, dstLen);
4229         }
4230         if (fir::isa_derived(eleTy)) {
4231           fir::ArrayAmendOp amend = createDerivedArrayAmend(
4232               loc, destination, builder, arrayOp, exv, eleTy, innerArg);
4233           return abstractArrayExtValue(amend /*FIXME: typeparams?*/);
4234         }
4235         assert(eleTy.isa<fir::SequenceType>() && "must be an array");
4236         TODO(loc, "array (as element) assignment");
4237       }
4238       // By value semantics. The element is being assigned by value.
4239       mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv));
4240       auto update = builder.create<fir::ArrayUpdateOp>(
4241           loc, arrTy, innerArg, ele, iterSpace.iterVec(),
4242           destination.getTypeparams());
4243       return abstractArrayExtValue(update);
4244     };
4245   }
4246 
4247   /// For an elemental array expression.
4248   ///   1. Lower the scalars and array loads.
4249   ///   2. Create the iteration space.
4250   ///   3. Create the element-by-element computation in the loop.
4251   ///   4. Return the resulting array value.
4252   /// If no destination was set in the array context, a temporary of
4253   /// \p resultTy will be created to hold the evaluated expression.
4254   /// Otherwise, \p resultTy is ignored and the expression is evaluated
4255   /// in the destination. \p f is a continuation built from an
4256   /// evaluate::Expr or an ExtendedValue.
4257   ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) {
4258     mlir::Location loc = getLoc();
4259     auto [iterSpace, insPt] = genIterSpace(resultTy);
4260     auto exv = f(iterSpace);
4261     iterSpace.setElement(std::move(exv));
4262     auto lambda = ccStoreToDest.hasValue()
4263                       ? ccStoreToDest.getValue()
4264                       : defaultStoreToDestination(/*substring=*/nullptr);
4265     mlir::Value updVal = fir::getBase(lambda(iterSpace));
4266     finalizeElementCtx();
4267     builder.create<fir::ResultOp>(loc, updVal);
4268     builder.restoreInsertionPoint(insPt);
4269     return abstractArrayExtValue(iterSpace.outerResult());
4270   }
4271 
4272   /// Get the shape from an ArrayOperand. The shape of the array is adjusted if
4273   /// the array was sliced.
4274   llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) {
4275     // if (array.slice)
4276     //   return computeSliceShape(array.slice);
4277     if (array.memref.getType().isa<fir::BoxType>())
4278       return fir::factory::readExtents(builder, getLoc(),
4279                                        fir::BoxValue{array.memref});
4280     std::vector<mlir::Value, std::allocator<mlir::Value>> extents =
4281         fir::factory::getExtents(array.shape);
4282     return {extents.begin(), extents.end()};
4283   }
4284 
4285   /// Get the shape from an ArrayLoad.
4286   llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) {
4287     return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(),
4288                                  arrayLoad.getSlice()});
4289   }
4290 
4291   /// Returns the first array operand that may not be absent. If all
4292   /// array operands may be absent, return the first one.
4293   const ArrayOperand &getInducingShapeArrayOperand() const {
4294     assert(!arrayOperands.empty());
4295     for (const ArrayOperand &op : arrayOperands)
4296       if (!op.mayBeAbsent)
4297         return op;
4298     // If all arrays operand appears in optional position, then none of them
4299     // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the
4300     // first operands.
4301     // TODO: There is an opportunity to add a runtime check here that
4302     // this array is present as required.
4303     return arrayOperands[0];
4304   }
4305 
4306   /// Generate the shape of the iteration space over the array expression. The
4307   /// iteration space may be implicit, explicit, or both. If it is implied it is
4308   /// based on the destination and operand array loads, or an optional
4309   /// Fortran::evaluate::Shape from the front end. If the shape is explicit,
4310   /// this returns any implicit shape component, if it exists.
4311   llvm::SmallVector<mlir::Value> genIterationShape() {
4312     // Use the precomputed destination shape.
4313     if (!destShape.empty())
4314       return destShape;
4315     // Otherwise, use the destination's shape.
4316     if (destination)
4317       return getShape(destination);
4318     // Otherwise, use the first ArrayLoad operand shape.
4319     if (!arrayOperands.empty())
4320       return getShape(getInducingShapeArrayOperand());
4321     fir::emitFatalError(getLoc(),
4322                         "failed to compute the array expression shape");
4323   }
4324 
4325   bool explicitSpaceIsActive() const {
4326     return explicitSpace && explicitSpace->isActive();
4327   }
4328 
4329   bool implicitSpaceHasMasks() const {
4330     return implicitSpace && !implicitSpace->empty();
4331   }
4332 
4333   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
4334                              Fortran::lower::StatementContext &stmtCtx,
4335                              Fortran::lower::SymMap &symMap)
4336       : converter{converter}, builder{converter.getFirOpBuilder()},
4337         stmtCtx{stmtCtx}, symMap{symMap} {}
4338 
4339   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
4340                              Fortran::lower::StatementContext &stmtCtx,
4341                              Fortran::lower::SymMap &symMap,
4342                              ConstituentSemantics sem)
4343       : converter{converter}, builder{converter.getFirOpBuilder()},
4344         stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {}
4345 
4346   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
4347                              Fortran::lower::StatementContext &stmtCtx,
4348                              Fortran::lower::SymMap &symMap,
4349                              ConstituentSemantics sem,
4350                              Fortran::lower::ExplicitIterSpace *expSpace,
4351                              Fortran::lower::ImplicitIterSpace *impSpace)
4352       : converter{converter}, builder{converter.getFirOpBuilder()},
4353         stmtCtx{stmtCtx}, symMap{symMap},
4354         explicitSpace(expSpace->isActive() ? expSpace : nullptr),
4355         implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} {
4356     // Generate any mask expressions, as necessary. This is the compute step
4357     // that creates the effective masks. See 10.2.3.2 in particular.
4358     // genMasks();
4359   }
4360 
4361   mlir::Location getLoc() { return converter.getCurrentLocation(); }
4362 
4363   /// Array appears in a lhs context such that it is assigned after the rhs is
4364   /// fully evaluated.
4365   inline bool isCopyInCopyOut() {
4366     return semant == ConstituentSemantics::CopyInCopyOut;
4367   }
4368 
4369   /// Array appears in a lhs (or temp) context such that a projected,
4370   /// discontiguous subspace of the array is assigned after the rhs is fully
4371   /// evaluated. That is, the rhs array value is merged into a section of the
4372   /// lhs array.
4373   inline bool isProjectedCopyInCopyOut() {
4374     return semant == ConstituentSemantics::ProjectedCopyInCopyOut;
4375   }
4376 
4377   inline bool isCustomCopyInCopyOut() {
4378     return semant == ConstituentSemantics::CustomCopyInCopyOut;
4379   }
4380 
4381   /// Array appears in a context where it must be boxed.
4382   inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; }
4383 
4384   /// Array appears in a context where differences in the memory reference can
4385   /// be observable in the computational results. For example, an array
4386   /// element is passed to an impure procedure.
4387   inline bool isReferentiallyOpaque() {
4388     return semant == ConstituentSemantics::RefOpaque;
4389   }
4390 
4391   /// Array appears in a context where it is passed as a VALUE argument.
4392   inline bool isValueAttribute() {
4393     return semant == ConstituentSemantics::ByValueArg;
4394   }
4395 
4396   /// Can the loops over the expression be unordered?
4397   inline bool isUnordered() const { return unordered; }
4398 
4399   void setUnordered(bool b) { unordered = b; }
4400 
4401   Fortran::lower::AbstractConverter &converter;
4402   fir::FirOpBuilder &builder;
4403   Fortran::lower::StatementContext &stmtCtx;
4404   bool elementCtx = false;
4405   Fortran::lower::SymMap &symMap;
4406   /// The continuation to generate code to update the destination.
4407   llvm::Optional<CC> ccStoreToDest;
4408   llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude;
4409   llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>>
4410       ccLoadDest;
4411   /// The destination is the loaded array into which the results will be
4412   /// merged.
4413   fir::ArrayLoadOp destination;
4414   /// The shape of the destination.
4415   llvm::SmallVector<mlir::Value> destShape;
4416   /// List of arrays in the expression that have been loaded.
4417   llvm::SmallVector<ArrayOperand> arrayOperands;
4418   /// If there is a user-defined iteration space, explicitShape will hold the
4419   /// information from the front end.
4420   Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr;
4421   Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr;
4422   ConstituentSemantics semant = ConstituentSemantics::RefTransparent;
4423   // Can the array expression be evaluated in any order?
4424   // Will be set to false if any of the expression parts prevent this.
4425   bool unordered = true;
4426 };
4427 } // namespace
4428 
4429 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
4430     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4431     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4432     Fortran::lower::StatementContext &stmtCtx) {
4433   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
4434   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr);
4435 }
4436 
4437 fir::GlobalOp Fortran::lower::createDenseGlobal(
4438     mlir::Location loc, mlir::Type symTy, llvm::StringRef globalName,
4439     mlir::StringAttr linkage, bool isConst,
4440     const Fortran::lower::SomeExpr &expr,
4441     Fortran::lower::AbstractConverter &converter) {
4442 
4443   Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true);
4444   Fortran::lower::SymMap emptyMap;
4445   InitializerData initData(/*genRawVals=*/true);
4446   ScalarExprLowering sel(loc, converter, emptyMap, stmtCtx,
4447                          /*initializer=*/&initData);
4448   sel.genval(expr);
4449 
4450   size_t sz = initData.rawVals.size();
4451   llvm::ArrayRef<mlir::Attribute> ar = {initData.rawVals.data(), sz};
4452 
4453   mlir::RankedTensorType tensorTy;
4454   auto &builder = converter.getFirOpBuilder();
4455   mlir::Type iTy = initData.rawType;
4456   if (!iTy)
4457     return 0; // array extent is probably 0 in this case, so just return 0.
4458   tensorTy = mlir::RankedTensorType::get(sz, iTy);
4459   auto init = mlir::DenseElementsAttr::get(tensorTy, ar);
4460   return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst);
4461 }
4462 
4463 fir::ExtendedValue Fortran::lower::createSomeInitializerExpression(
4464     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4465     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4466     Fortran::lower::StatementContext &stmtCtx) {
4467   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
4468   InitializerData initData; // needed for initializations
4469   return ScalarExprLowering{loc, converter, symMap, stmtCtx,
4470                             /*initializer=*/&initData}
4471       .genval(expr);
4472 }
4473 
4474 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
4475     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4476     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4477     Fortran::lower::StatementContext &stmtCtx) {
4478   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
4479   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr);
4480 }
4481 
4482 fir::ExtendedValue Fortran::lower::createInitializerAddress(
4483     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4484     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4485     Fortran::lower::StatementContext &stmtCtx) {
4486   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
4487   InitializerData init;
4488   return ScalarExprLowering(loc, converter, symMap, stmtCtx, &init).gen(expr);
4489 }
4490 
4491 fir::ExtendedValue
4492 Fortran::lower::createSomeArrayBox(Fortran::lower::AbstractConverter &converter,
4493                                    const Fortran::lower::SomeExpr &expr,
4494                                    Fortran::lower::SymMap &symMap,
4495                                    Fortran::lower::StatementContext &stmtCtx) {
4496   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "box designator: ") << '\n');
4497   return ArrayExprLowering::lowerBoxedArrayExpression(converter, symMap,
4498                                                       stmtCtx, expr);
4499 }
4500 
4501 fir::MutableBoxValue Fortran::lower::createMutableBox(
4502     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4503     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
4504   // MutableBox lowering StatementContext does not need to be propagated
4505   // to the caller because the result value is a variable, not a temporary
4506   // expression. The StatementContext clean-up can occur before using the
4507   // resulting MutableBoxValue. Variables of all other types are handled in the
4508   // bridge.
4509   Fortran::lower::StatementContext dummyStmtCtx;
4510   return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx}
4511       .genMutableBoxValue(expr);
4512 }
4513 
4514 fir::ExtendedValue Fortran::lower::createBoxValue(
4515     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
4516     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4517     Fortran::lower::StatementContext &stmtCtx) {
4518   if (expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) &&
4519       !Fortran::evaluate::HasVectorSubscript(expr))
4520     return Fortran::lower::createSomeArrayBox(converter, expr, symMap, stmtCtx);
4521   fir::ExtendedValue addr = Fortran::lower::createSomeExtendedAddress(
4522       loc, converter, expr, symMap, stmtCtx);
4523   return fir::BoxValue(converter.getFirOpBuilder().createBox(loc, addr));
4524 }
4525 
4526 mlir::Value Fortran::lower::createSubroutineCall(
4527     AbstractConverter &converter, const evaluate::ProcedureRef &call,
4528     SymMap &symMap, StatementContext &stmtCtx) {
4529   mlir::Location loc = converter.getCurrentLocation();
4530 
4531   // Simple subroutine call, with potential alternate return.
4532   auto res = Fortran::lower::createSomeExtendedExpression(
4533       loc, converter, toEvExpr(call), symMap, stmtCtx);
4534   return fir::getBase(res);
4535 }
4536 
4537 void Fortran::lower::createSomeArrayAssignment(
4538     Fortran::lower::AbstractConverter &converter,
4539     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
4540     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
4541   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
4542              rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
4543   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
4544 }
4545 
4546 void Fortran::lower::createSomeArrayAssignment(
4547     Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
4548     const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap,
4549     Fortran::lower::StatementContext &stmtCtx) {
4550   LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
4551              llvm::dbgs() << "assign expression: " << rhs << '\n';);
4552   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
4553 }
4554 
4555 void Fortran::lower::createAllocatableArrayAssignment(
4556     Fortran::lower::AbstractConverter &converter,
4557     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
4558     Fortran::lower::ExplicitIterSpace &explicitSpace,
4559     Fortran::lower::ImplicitIterSpace &implicitSpace,
4560     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
4561   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n';
4562              rhs.AsFortran(llvm::dbgs() << "assign expression: ")
4563              << " given the explicit iteration space:\n"
4564              << explicitSpace << "\n and implied mask conditions:\n"
4565              << implicitSpace << '\n';);
4566   ArrayExprLowering::lowerAllocatableArrayAssignment(
4567       converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
4568 }
4569 
4570 fir::ExtendedValue Fortran::lower::createSomeArrayTempValue(
4571     Fortran::lower::AbstractConverter &converter,
4572     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
4573     Fortran::lower::StatementContext &stmtCtx) {
4574   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
4575   return ArrayExprLowering::lowerNewArrayExpression(converter, symMap, stmtCtx,
4576                                                     expr);
4577 }
4578 
4579 mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder,
4580                                            mlir::Location loc,
4581                                            mlir::Value value) {
4582   mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
4583   if (mlir::Operation *definingOp = value.getDefiningOp())
4584     if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
4585       if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>())
4586         return intAttr.getInt() < 0 ? zero : value;
4587   return Fortran::lower::genMax(builder, loc,
4588                                 llvm::SmallVector<mlir::Value>{value, zero});
4589 }
4590