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/DumpEvaluateExpr.h"
22 #include "flang/Lower/IntrinsicCall.h"
23 #include "flang/Lower/StatementContext.h"
24 #include "flang/Lower/SymbolMap.h"
25 #include "flang/Lower/Todo.h"
26 #include "flang/Optimizer/Builder/Complex.h"
27 #include "flang/Optimizer/Builder/Factory.h"
28 #include "flang/Optimizer/Builder/MutableBox.h"
29 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
30 #include "flang/Semantics/expression.h"
31 #include "flang/Semantics/symbol.h"
32 #include "flang/Semantics/tools.h"
33 #include "flang/Semantics/type.h"
34 #include "mlir/Dialect/StandardOps/IR/Ops.h"
35 #include "llvm/Support/Debug.h"
36 
37 #define DEBUG_TYPE "flang-lower-expr"
38 
39 //===----------------------------------------------------------------------===//
40 // The composition and structure of Fortran::evaluate::Expr is defined in
41 // the various header files in include/flang/Evaluate. You are referred
42 // there for more information on these data structures. Generally speaking,
43 // these data structures are a strongly typed family of abstract data types
44 // that, composed as trees, describe the syntax of Fortran expressions.
45 //
46 // This part of the bridge can traverse these tree structures and lower them
47 // to the correct FIR representation in SSA form.
48 //===----------------------------------------------------------------------===//
49 
50 /// The various semantics of a program constituent (or a part thereof) as it may
51 /// appear in an expression.
52 ///
53 /// Given the following Fortran declarations.
54 /// ```fortran
55 ///   REAL :: v1, v2, v3
56 ///   REAL, POINTER :: vp1
57 ///   REAL :: a1(c), a2(c)
58 ///   REAL ELEMENTAL FUNCTION f1(arg) ! array -> array
59 ///   FUNCTION f2(arg)                ! array -> array
60 ///   vp1 => v3       ! 1
61 ///   v1 = v2 * vp1   ! 2
62 ///   a1 = a1 + a2    ! 3
63 ///   a1 = f1(a2)     ! 4
64 ///   a1 = f2(a2)     ! 5
65 /// ```
66 ///
67 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is
68 /// constructed from the DataAddr of `v3`.
69 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed
70 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double
71 /// dereference in the `vp1` case.
72 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs
73 /// is CopyInCopyOut as `a1` is replaced elementally by the additions.
74 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if
75 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/
76 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut.
77 ///  In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational.
78 ///  `a1` on the lhs is again CopyInCopyOut.
79 enum class ConstituentSemantics {
80   // Scalar data reference semantics.
81   //
82   // For these let `v` be the location in memory of a variable with value `x`
83   DataValue, // refers to the value `x`
84   DataAddr,  // refers to the address `v`
85   BoxValue,  // refers to a box value containing `v`
86   BoxAddr,   // refers to the address of a box value containing `v`
87 
88   // Array data reference semantics.
89   //
90   // For these let `a` be the location in memory of a sequence of value `[xs]`.
91   // Let `x_i` be the `i`-th value in the sequence `[xs]`.
92 
93   // Referentially transparent. Refers to the array's value, `[xs]`.
94   RefTransparent,
95   // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7
96   // note 2). (Passing a copy by reference to simulate pass-by-value.)
97   ByValueArg,
98   // Refers to the merge of array value `[xs]` with another array value `[ys]`.
99   // This merged array value will be written into memory location `a`.
100   CopyInCopyOut,
101   // Similar to CopyInCopyOut but `a` may be a transient projection (rather than
102   // a whole array).
103   ProjectedCopyInCopyOut,
104   // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned
105   // automatically by the framework. Instead, and address for `[xs]` is made
106   // accessible so that custom assignments to `[xs]` can be implemented.
107   CustomCopyInCopyOut,
108   // Referentially opaque. Refers to the address of `x_i`.
109   RefOpaque
110 };
111 
112 /// Place \p exv in memory if it is not already a memory reference. If
113 /// \p forceValueType is provided, the value is first casted to the provided
114 /// type before being stored (this is mainly intended for logicals whose value
115 /// may be `i1` but needed to be stored as Fortran logicals).
116 static fir::ExtendedValue
117 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
118                          const fir::ExtendedValue &exv,
119                          mlir::Type storageType) {
120   mlir::Value valBase = fir::getBase(exv);
121   if (fir::conformsWithPassByRef(valBase.getType()))
122     return exv;
123 
124   assert(!fir::hasDynamicSize(storageType) &&
125          "only expect statically sized scalars to be by value");
126 
127   // Since `a` is not itself a valid referent, determine its value and
128   // create a temporary location at the beginning of the function for
129   // referencing.
130   mlir::Value val = builder.createConvert(loc, storageType, valBase);
131   mlir::Value temp = builder.createTemporary(
132       loc, storageType,
133       llvm::ArrayRef<mlir::NamedAttribute>{
134           Fortran::lower::getAdaptToByRefAttr(builder)});
135   builder.create<fir::StoreOp>(loc, val, temp);
136   return fir::substBase(exv, temp);
137 }
138 
139 /// Is this a variable wrapped in parentheses?
140 template <typename A>
141 static bool isParenthesizedVariable(const A &) {
142   return false;
143 }
144 template <typename T>
145 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) {
146   using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
147   using Parentheses = Fortran::evaluate::Parentheses<T>;
148   if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
149     if (const auto *parentheses = std::get_if<Parentheses>(&expr.u))
150       return Fortran::evaluate::IsVariable(parentheses->left());
151     return false;
152   } else {
153     return std::visit([&](const auto &x) { return isParenthesizedVariable(x); },
154                       expr.u);
155   }
156 }
157 
158 /// Generate a load of a value from an address. Beware that this will lose
159 /// any dynamic type information for polymorphic entities (note that unlimited
160 /// polymorphic cannot be loaded and must not be provided here).
161 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
162                                   mlir::Location loc,
163                                   const fir::ExtendedValue &addr) {
164   return addr.match(
165       [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
166       [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
167         if (fir::unwrapRefType(fir::getBase(v).getType())
168                 .isa<fir::RecordType>())
169           return v;
170         return builder.create<fir::LoadOp>(loc, fir::getBase(v));
171       },
172       [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
173         TODO(loc, "genLoad for MutableBoxValue");
174       },
175       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
176         TODO(loc, "genLoad for BoxValue");
177       },
178       [&](const auto &) -> fir::ExtendedValue {
179         fir::emitFatalError(
180             loc, "attempting to load whole array or procedure address");
181       });
182 }
183 
184 /// Is this a call to an elemental procedure with at least one array argument?
185 static bool
186 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
187   if (procRef.IsElemental())
188     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
189          procRef.arguments())
190       if (arg && arg->Rank() != 0)
191         return true;
192   return false;
193 }
194 template <typename T>
195 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) {
196   return false;
197 }
198 template <>
199 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) {
200   if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u))
201     return isElementalProcWithArrayArgs(*procRef);
202   return false;
203 }
204 
205 /// If \p arg is the address of a function with a denoted host-association tuple
206 /// argument, then return the host-associations tuple value of the current
207 /// procedure. Otherwise, return nullptr.
208 static mlir::Value
209 argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
210                    mlir::Value arg) {
211   if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
212     auto &builder = converter.getFirOpBuilder();
213     if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
214       if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
215         return converter.hostAssocTupleValue();
216   }
217   return {};
218 }
219 
220 namespace {
221 
222 /// Lowering of Fortran::evaluate::Expr<T> expressions
223 class ScalarExprLowering {
224 public:
225   using ExtValue = fir::ExtendedValue;
226 
227   explicit ScalarExprLowering(mlir::Location loc,
228                               Fortran::lower::AbstractConverter &converter,
229                               Fortran::lower::SymMap &symMap,
230                               Fortran::lower::StatementContext &stmtCtx)
231       : location{loc}, converter{converter},
232         builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap} {
233   }
234 
235   ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) {
236     return gen(expr);
237   }
238 
239   /// Lower `expr` to be passed as a fir.box argument. Do not create a temp
240   /// for the expr if it is a variable that can be described as a fir.box.
241   ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) {
242     bool saveUseBoxArg = useBoxArg;
243     useBoxArg = true;
244     ExtValue result = gen(expr);
245     useBoxArg = saveUseBoxArg;
246     return result;
247   }
248 
249   ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) {
250     return genval(expr);
251   }
252 
253   /// Lower an expression that is a pointer or an allocatable to a
254   /// MutableBoxValue.
255   fir::MutableBoxValue
256   genMutableBoxValue(const Fortran::lower::SomeExpr &expr) {
257     // Pointers and allocatables can only be:
258     //    - a simple designator "x"
259     //    - a component designator "a%b(i,j)%x"
260     //    - a function reference "foo()"
261     //    - result of NULL() or NULL(MOLD) intrinsic.
262     //    NULL() requires some context to be lowered, so it is not handled
263     //    here and must be lowered according to the context where it appears.
264     ExtValue exv = std::visit(
265         [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u);
266     const fir::MutableBoxValue *mutableBox =
267         exv.getBoxOf<fir::MutableBoxValue>();
268     if (!mutableBox)
269       fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue");
270     return *mutableBox;
271   }
272 
273   template <typename T>
274   ExtValue genMutableBoxValueImpl(const T &) {
275     // NULL() case should not be handled here.
276     fir::emitFatalError(getLoc(), "NULL() must be lowered in its context");
277   }
278 
279   template <typename T>
280   ExtValue
281   genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) {
282     return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef)));
283   }
284 
285   template <typename T>
286   ExtValue
287   genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) {
288     return std::visit(
289         Fortran::common::visitors{
290             [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue {
291               return symMap.lookupSymbol(*sym).toExtendedValue();
292             },
293             [&](const Fortran::evaluate::Component &comp) -> ExtValue {
294               return genComponent(comp);
295             },
296             [&](const auto &) -> ExtValue {
297               fir::emitFatalError(getLoc(),
298                                   "not an allocatable or pointer designator");
299             }},
300         designator.u);
301   }
302 
303   template <typename T>
304   ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) {
305     return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); },
306                       expr.u);
307   }
308 
309   mlir::Location getLoc() { return location; }
310 
311   template <typename A>
312   mlir::Value genunbox(const A &expr) {
313     ExtValue e = genval(expr);
314     if (const fir::UnboxedValue *r = e.getUnboxed())
315       return *r;
316     fir::emitFatalError(getLoc(), "unboxed expression expected");
317   }
318 
319   /// Generate an integral constant of `value`
320   template <int KIND>
321   mlir::Value genIntegerConstant(mlir::MLIRContext *context,
322                                  std::int64_t value) {
323     mlir::Type type =
324         converter.genType(Fortran::common::TypeCategory::Integer, KIND);
325     return builder.createIntegerConstant(getLoc(), type, value);
326   }
327 
328   /// Generate a logical/boolean constant of `value`
329   mlir::Value genBoolConstant(bool value) {
330     return builder.createBool(getLoc(), value);
331   }
332 
333   /// Generate a real constant with a value `value`.
334   template <int KIND>
335   mlir::Value genRealConstant(mlir::MLIRContext *context,
336                               const llvm::APFloat &value) {
337     mlir::Type fltTy = Fortran::lower::convertReal(context, KIND);
338     return builder.createRealConstant(getLoc(), fltTy, value);
339   }
340 
341   /// Returns a reference to a symbol or its box/boxChar descriptor if it has
342   /// one.
343   ExtValue gen(Fortran::semantics::SymbolRef sym) {
344     if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
345       return val.match([&val](auto &) { return val.toExtendedValue(); });
346     LLVM_DEBUG(llvm::dbgs()
347                << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
348     fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
349   }
350 
351   ExtValue genLoad(const ExtValue &exv) {
352     return ::genLoad(builder, getLoc(), exv);
353   }
354 
355   ExtValue genval(Fortran::semantics::SymbolRef sym) {
356     ExtValue var = gen(sym);
357     if (const fir::UnboxedValue *s = var.getUnboxed())
358       if (fir::isReferenceLike(s->getType()))
359         return genLoad(*s);
360     return var;
361   }
362 
363   ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
364     TODO(getLoc(), "genval BOZ");
365   }
366 
367   /// Return indirection to function designated in ProcedureDesignator.
368   /// The type of the function indirection is not guaranteed to match the one
369   /// of the ProcedureDesignator due to Fortran implicit typing rules.
370   ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
371     TODO(getLoc(), "genval ProcedureDesignator");
372   }
373 
374   ExtValue genval(const Fortran::evaluate::NullPointer &) {
375     TODO(getLoc(), "genval NullPointer");
376   }
377 
378   ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
379     TODO(getLoc(), "genval StructureConstructor");
380   }
381 
382   /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
383   ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
384     TODO(getLoc(), "genval ImpliedDoIndex");
385   }
386 
387   ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
388     TODO(getLoc(), "genval DescriptorInquiry");
389   }
390 
391   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
392     TODO(getLoc(), "genval TypeParamInquiry");
393   }
394 
395   template <int KIND>
396   ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
397     TODO(getLoc(), "genval ComplexComponent");
398   }
399 
400   template <int KIND>
401   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
402                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
403     mlir::Value input = genunbox(op.left());
404     // Like LLVM, integer negation is the binary op "0 - value"
405     mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
406     return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
407   }
408 
409   template <int KIND>
410   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
411                       Fortran::common::TypeCategory::Real, KIND>> &op) {
412     return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
413   }
414   template <int KIND>
415   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
416                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
417     return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
418   }
419 
420   template <typename OpTy>
421   mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
422     assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
423     mlir::Value lhs = fir::getBase(left);
424     mlir::Value rhs = fir::getBase(right);
425     assert(lhs.getType() == rhs.getType() && "types must be the same");
426     return builder.create<OpTy>(getLoc(), lhs, rhs);
427   }
428 
429   template <typename OpTy, typename A>
430   mlir::Value createBinaryOp(const A &ex) {
431     ExtValue left = genval(ex.left());
432     return createBinaryOp<OpTy>(left, genval(ex.right()));
433   }
434 
435 #undef GENBIN
436 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
437   template <int KIND>                                                          \
438   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
439                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
440     return createBinaryOp<GenBinFirOp>(x);                                     \
441   }
442 
443   GENBIN(Add, Integer, mlir::arith::AddIOp)
444   GENBIN(Add, Real, mlir::arith::AddFOp)
445   GENBIN(Add, Complex, fir::AddcOp)
446   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
447   GENBIN(Subtract, Real, mlir::arith::SubFOp)
448   GENBIN(Subtract, Complex, fir::SubcOp)
449   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
450   GENBIN(Multiply, Real, mlir::arith::MulFOp)
451   GENBIN(Multiply, Complex, fir::MulcOp)
452   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
453   GENBIN(Divide, Real, mlir::arith::DivFOp)
454   GENBIN(Divide, Complex, fir::DivcOp)
455 
456   template <Fortran::common::TypeCategory TC, int KIND>
457   ExtValue genval(
458       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
459     TODO(getLoc(), "genval Power");
460   }
461 
462   template <Fortran::common::TypeCategory TC, int KIND>
463   ExtValue genval(
464       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
465           &op) {
466     TODO(getLoc(), "genval RealToInt");
467   }
468 
469   template <int KIND>
470   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
471     mlir::Value realPartValue = genunbox(op.left());
472     return fir::factory::Complex{builder, getLoc()}.createComplex(
473         KIND, realPartValue, genunbox(op.right()));
474   }
475 
476   template <int KIND>
477   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
478     TODO(getLoc(), "genval Concat<KIND>");
479   }
480 
481   /// MIN and MAX operations
482   template <Fortran::common::TypeCategory TC, int KIND>
483   ExtValue
484   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
485              &op) {
486     TODO(getLoc(), "genval Extremum<TC, KIND>");
487   }
488 
489   template <int KIND>
490   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
491     TODO(getLoc(), "genval SetLength<KIND>");
492   }
493 
494   template <int KIND>
495   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
496                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
497     TODO(getLoc(), "genval integer comparison");
498   }
499   template <int KIND>
500   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
501                       Fortran::common::TypeCategory::Real, KIND>> &op) {
502     TODO(getLoc(), "genval real comparison");
503   }
504   template <int KIND>
505   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
506                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
507     TODO(getLoc(), "genval complex comparison");
508   }
509   template <int KIND>
510   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
511                       Fortran::common::TypeCategory::Character, KIND>> &op) {
512     TODO(getLoc(), "genval char comparison");
513   }
514 
515   ExtValue
516   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
517     TODO(getLoc(), "genval comparison");
518   }
519 
520   template <Fortran::common::TypeCategory TC1, int KIND,
521             Fortran::common::TypeCategory TC2>
522   ExtValue
523   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
524                                           TC2> &convert) {
525     mlir::Type ty = converter.genType(TC1, KIND);
526     mlir::Value operand = genunbox(convert.left());
527     return builder.convertWithSemantics(getLoc(), ty, operand);
528   }
529 
530   template <typename A>
531   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
532     TODO(getLoc(), "genval parentheses<A>");
533   }
534 
535   template <int KIND>
536   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
537     TODO(getLoc(), "genval Not<KIND>");
538   }
539 
540   template <int KIND>
541   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
542     TODO(getLoc(), "genval LogicalOperation<KIND>");
543   }
544 
545   /// Convert a scalar literal constant to IR.
546   template <Fortran::common::TypeCategory TC, int KIND>
547   ExtValue genScalarLit(
548       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
549           &value) {
550     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
551       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
552     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
553       return genBoolConstant(value.IsTrue());
554     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
555       std::string str = value.DumpHexadecimal();
556       if constexpr (KIND == 2) {
557         llvm::APFloat floatVal{llvm::APFloatBase::IEEEhalf(), str};
558         return genRealConstant<KIND>(builder.getContext(), floatVal);
559       } else if constexpr (KIND == 3) {
560         llvm::APFloat floatVal{llvm::APFloatBase::BFloat(), str};
561         return genRealConstant<KIND>(builder.getContext(), floatVal);
562       } else if constexpr (KIND == 4) {
563         llvm::APFloat floatVal{llvm::APFloatBase::IEEEsingle(), str};
564         return genRealConstant<KIND>(builder.getContext(), floatVal);
565       } else if constexpr (KIND == 10) {
566         llvm::APFloat floatVal{llvm::APFloatBase::x87DoubleExtended(), str};
567         return genRealConstant<KIND>(builder.getContext(), floatVal);
568       } else if constexpr (KIND == 16) {
569         llvm::APFloat floatVal{llvm::APFloatBase::IEEEquad(), str};
570         return genRealConstant<KIND>(builder.getContext(), floatVal);
571       } else {
572         // convert everything else to double
573         llvm::APFloat floatVal{llvm::APFloatBase::IEEEdouble(), str};
574         return genRealConstant<KIND>(builder.getContext(), floatVal);
575       }
576     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
577       using TR =
578           Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>;
579       Fortran::evaluate::ComplexConstructor<KIND> ctor(
580           Fortran::evaluate::Expr<TR>{
581               Fortran::evaluate::Constant<TR>{value.REAL()}},
582           Fortran::evaluate::Expr<TR>{
583               Fortran::evaluate::Constant<TR>{value.AIMAG()}});
584       return genunbox(ctor);
585     } else /*constexpr*/ {
586       llvm_unreachable("unhandled constant");
587     }
588   }
589 
590   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
591   ExtValue
592   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
593                         Fortran::common::TypeCategory::Character, 1>> &value,
594                     int64_t len) {
595     assert(value.size() == static_cast<std::uint64_t>(len) &&
596            "value.size() doesn't match with len");
597     return fir::factory::createStringLiteral(builder, getLoc(), value);
598   }
599 
600   template <Fortran::common::TypeCategory TC, int KIND>
601   ExtValue
602   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
603              &con) {
604     if (con.Rank() > 0)
605       TODO(getLoc(), "genval array constant");
606     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
607         opt = con.GetScalarValue();
608     assert(opt.has_value() && "constant has no value");
609     if constexpr (TC == Fortran::common::TypeCategory::Character) {
610       if constexpr (KIND == 1)
611         return genAsciiScalarLit(opt.value(), con.LEN());
612       TODO(getLoc(), "genval for Character with KIND != 1");
613     } else {
614       return genScalarLit<TC, KIND>(opt.value());
615     }
616   }
617 
618   fir::ExtendedValue genval(
619       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
620     TODO(getLoc(), "genval constant derived");
621   }
622 
623   template <typename A>
624   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
625     TODO(getLoc(), "genval ArrayConstructor<A>");
626   }
627 
628   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
629     TODO(getLoc(), "gen ComplexPart");
630   }
631   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
632     TODO(getLoc(), "genval ComplexPart");
633   }
634 
635   ExtValue gen(const Fortran::evaluate::Substring &s) {
636     TODO(getLoc(), "gen Substring");
637   }
638   ExtValue genval(const Fortran::evaluate::Substring &ss) {
639     TODO(getLoc(), "genval Substring");
640   }
641 
642   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
643     if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>(
644             &subs.u)) {
645       if (s->value().Rank() > 0)
646         fir::emitFatalError(getLoc(), "vector subscript is not scalar");
647       return {genval(s->value())};
648     }
649     fir::emitFatalError(getLoc(), "subscript triple notation is not scalar");
650   }
651 
652   ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) {
653     return genval(subs);
654   }
655 
656   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
657     TODO(getLoc(), "gen DataRef");
658   }
659   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
660     TODO(getLoc(), "genval DataRef");
661   }
662 
663   // Helper function to turn the Component structure into a list of nested
664   // components, ordered from largest/leftmost to smallest/rightmost:
665   //  - where only the smallest/rightmost item may be allocatable or a pointer
666   //    (nested allocatable/pointer components require nested coordinate_of ops)
667   //  - that does not contain any parent components
668   //    (the front end places parent components directly in the object)
669   // Return the object used as the base coordinate for the component chain.
670   static Fortran::evaluate::DataRef const *
671   reverseComponents(const Fortran::evaluate::Component &cmpt,
672                     std::list<const Fortran::evaluate::Component *> &list) {
673     if (!cmpt.GetLastSymbol().test(
674             Fortran::semantics::Symbol::Flag::ParentComp))
675       list.push_front(&cmpt);
676     return std::visit(
677         Fortran::common::visitors{
678             [&](const Fortran::evaluate::Component &x) {
679               if (Fortran::semantics::IsAllocatableOrPointer(x.GetLastSymbol()))
680                 return &cmpt.base();
681               return reverseComponents(x, list);
682             },
683             [&](auto &) { return &cmpt.base(); },
684         },
685         cmpt.base().u);
686   }
687 
688   // Return the coordinate of the component reference
689   ExtValue genComponent(const Fortran::evaluate::Component &cmpt) {
690     std::list<const Fortran::evaluate::Component *> list;
691     const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list);
692     llvm::SmallVector<mlir::Value> coorArgs;
693     ExtValue obj = gen(*base);
694     mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType());
695     mlir::Location loc = getLoc();
696     auto fldTy = fir::FieldType::get(&converter.getMLIRContext());
697     // FIXME: need to thread the LEN type parameters here.
698     for (const Fortran::evaluate::Component *field : list) {
699       auto recTy = ty.cast<fir::RecordType>();
700       const Fortran::semantics::Symbol &sym = field->GetLastSymbol();
701       llvm::StringRef name = toStringRef(sym.name());
702       coorArgs.push_back(builder.create<fir::FieldIndexOp>(
703           loc, fldTy, name, recTy, fir::getTypeParams(obj)));
704       ty = recTy.getType(name);
705     }
706     ty = builder.getRefType(ty);
707     return fir::factory::componentToExtendedValue(
708         builder, loc,
709         builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj),
710                                           coorArgs));
711   }
712 
713   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
714     TODO(getLoc(), "gen Component");
715   }
716   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
717     TODO(getLoc(), "genval Component");
718   }
719 
720   ExtValue genval(const Fortran::semantics::Bound &bound) {
721     TODO(getLoc(), "genval Bound");
722   }
723 
724   /// Return lower bounds of \p box in dimension \p dim. The returned value
725   /// has type \ty.
726   mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) {
727     assert(box.rank() > 0 && "must be an array");
728     mlir::Location loc = getLoc();
729     mlir::Value one = builder.createIntegerConstant(loc, ty, 1);
730     mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one);
731     return builder.createConvert(loc, ty, lb);
732   }
733 
734   /// Lower an ArrayRef to a fir.coordinate_of given its lowered base.
735   ExtValue genCoordinateOp(const ExtValue &array,
736                            const Fortran::evaluate::ArrayRef &aref) {
737     mlir::Location loc = getLoc();
738     // References to array of rank > 1 with non constant shape that are not
739     // fir.box must be collapsed into an offset computation in lowering already.
740     // The same is needed with dynamic length character arrays of all ranks.
741     mlir::Type baseType =
742         fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType());
743     if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) ||
744         fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType)))
745       if (!array.getBoxOf<fir::BoxValue>())
746         TODO(getLoc(), "genOffsetAndCoordinateOp");
747     // Generate a fir.coordinate_of with zero based array indexes.
748     llvm::SmallVector<mlir::Value> args;
749     for (const auto &subsc : llvm::enumerate(aref.subscript())) {
750       ExtValue subVal = genSubscript(subsc.value());
751       assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar");
752       mlir::Value val = fir::getBase(subVal);
753       mlir::Type ty = val.getType();
754       mlir::Value lb = getLBound(array, subsc.index(), ty);
755       args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb));
756     }
757 
758     mlir::Value base = fir::getBase(array);
759     auto seqTy =
760         fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>();
761     assert(args.size() == seqTy.getDimension());
762     mlir::Type ty = builder.getRefType(seqTy.getEleTy());
763     auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args);
764     return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr);
765   }
766 
767   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
768     ExtValue base = aref.base().IsSymbol() ? gen(aref.base().GetFirstSymbol())
769                                            : gen(aref.base().GetComponent());
770     return genCoordinateOp(base, aref);
771   }
772   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
773     TODO(getLoc(), "genval ArrayRef");
774   }
775 
776   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
777     TODO(getLoc(), "gen CoarrayRef");
778   }
779   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
780     TODO(getLoc(), "genval CoarrayRef");
781   }
782 
783   template <typename A>
784   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
785     return std::visit([&](const auto &x) { return gen(x); }, des.u);
786   }
787   template <typename A>
788   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
789     return std::visit([&](const auto &x) { return genval(x); }, des.u);
790   }
791 
792   mlir::Type genType(const Fortran::evaluate::DynamicType &dt) {
793     if (dt.category() != Fortran::common::TypeCategory::Derived)
794       return converter.genType(dt.category(), dt.kind());
795     TODO(getLoc(), "genType Derived Type");
796   }
797 
798   /// Lower a function reference
799   template <typename A>
800   ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) {
801     if (!funcRef.GetType().has_value())
802       fir::emitFatalError(getLoc(), "internal: a function must have a type");
803     mlir::Type resTy = genType(*funcRef.GetType());
804     return genProcedureRef(funcRef, {resTy});
805   }
806 
807   /// Lower function call `funcRef` and return a reference to the resultant
808   /// value. This is required for lowering expressions such as `f1(f2(v))`.
809   template <typename A>
810   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
811     TODO(getLoc(), "gen FunctionRef<A>");
812   }
813 
814   /// helper to detect statement functions
815   static bool
816   isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) {
817     if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
818       if (const auto *details =
819               symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
820         return details->stmtFunction().has_value();
821     return false;
822   }
823 
824   /// Helper to package a Value and its properties into an ExtendedValue.
825   static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base,
826                                   llvm::ArrayRef<mlir::Value> extents,
827                                   llvm::ArrayRef<mlir::Value> lengths) {
828     mlir::Type type = base.getType();
829     if (type.isa<fir::BoxType>())
830       return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);
831     type = fir::unwrapRefType(type);
832     if (type.isa<fir::BoxType>())
833       return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});
834     if (auto seqTy = type.dyn_cast<fir::SequenceType>()) {
835       if (seqTy.getDimension() != extents.size())
836         fir::emitFatalError(loc, "incorrect number of extents for array");
837       if (seqTy.getEleTy().isa<fir::CharacterType>()) {
838         if (lengths.empty())
839           fir::emitFatalError(loc, "missing length for character");
840         assert(lengths.size() == 1);
841         return fir::CharArrayBoxValue(base, lengths[0], extents);
842       }
843       return fir::ArrayBoxValue(base, extents);
844     }
845     if (type.isa<fir::CharacterType>()) {
846       if (lengths.empty())
847         fir::emitFatalError(loc, "missing length for character");
848       assert(lengths.size() == 1);
849       return fir::CharBoxValue(base, lengths[0]);
850     }
851     return base;
852   }
853 
854   // Find the argument that corresponds to the host associations.
855   // Verify some assumptions about how the signature was built here.
856   [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::FuncOp fn) {
857     // Scan the argument list from last to first as the host associations are
858     // appended for now.
859     for (unsigned i = fn.getNumArguments(); i > 0; --i)
860       if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
861         // Host assoc tuple must be last argument (for now).
862         assert(i == fn.getNumArguments() && "tuple must be last");
863         return i - 1;
864       }
865     llvm_unreachable("anyFuncArgsHaveAttr failed");
866   }
867 
868   /// Lower a non-elemental procedure reference and read allocatable and pointer
869   /// results into normal values.
870   ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
871                            llvm::Optional<mlir::Type> resultType) {
872     ExtValue res = genRawProcedureRef(procRef, resultType);
873     return res;
874   }
875 
876   /// Given a call site for which the arguments were already lowered, generate
877   /// the call and return the result. This function deals with explicit result
878   /// allocation and lowering if needed. It also deals with passing the host
879   /// link to internal procedures.
880   ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller,
881                               mlir::FunctionType callSiteType,
882                               llvm::Optional<mlir::Type> resultType) {
883     mlir::Location loc = getLoc();
884     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
885     // Handle cases where caller must allocate the result or a fir.box for it.
886     bool mustPopSymMap = false;
887     if (caller.mustMapInterfaceSymbols()) {
888       symMap.pushScope();
889       mustPopSymMap = true;
890       Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap);
891     }
892     // If this is an indirect call, retrieve the function address. Also retrieve
893     // the result length if this is a character function (note that this length
894     // will be used only if there is no explicit length in the local interface).
895     mlir::Value funcPointer;
896     mlir::Value charFuncPointerLength;
897     if (caller.getIfIndirectCallSymbol()) {
898       TODO(loc, "genCallOpAndResult indirect call");
899     }
900 
901     mlir::IndexType idxTy = builder.getIndexType();
902     auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
903       return builder.createConvert(
904           loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
905     };
906     llvm::SmallVector<mlir::Value> resultLengths;
907     auto allocatedResult = [&]() -> llvm::Optional<ExtValue> {
908       llvm::SmallVector<mlir::Value> extents;
909       llvm::SmallVector<mlir::Value> lengths;
910       if (!caller.callerAllocateResult())
911         return {};
912       mlir::Type type = caller.getResultStorageType();
913       if (type.isa<fir::SequenceType>())
914         caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) {
915           extents.emplace_back(lowerSpecExpr(e));
916         });
917       caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) {
918         lengths.emplace_back(lowerSpecExpr(e));
919       });
920 
921       // Result length parameters should not be provided to box storage
922       // allocation and save_results, but they are still useful information to
923       // keep in the ExtendedValue if non-deferred.
924       if (!type.isa<fir::BoxType>()) {
925         if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
926           // Calling an assumed length function. This is only possible if this
927           // is a call to a character dummy procedure.
928           if (!charFuncPointerLength)
929             fir::emitFatalError(loc, "failed to retrieve character function "
930                                      "length while calling it");
931           lengths.push_back(charFuncPointerLength);
932         }
933         resultLengths = lengths;
934       }
935 
936       if (!extents.empty() || !lengths.empty()) {
937         TODO(loc, "genCallOpResult extents and length");
938       }
939       mlir::Value temp =
940           builder.createTemporary(loc, type, ".result", extents, resultLengths);
941       return toExtendedValue(loc, temp, extents, lengths);
942     }();
943 
944     if (mustPopSymMap)
945       symMap.popScope();
946 
947     // Place allocated result or prepare the fir.save_result arguments.
948     mlir::Value arrayResultShape;
949     if (allocatedResult) {
950       if (std::optional<Fortran::lower::CallInterface<
951               Fortran::lower::CallerInterface>::PassedEntity>
952               resultArg = caller.getPassedResult()) {
953         if (resultArg->passBy == PassBy::AddressAndLength)
954           caller.placeAddressAndLengthInput(*resultArg,
955                                             fir::getBase(*allocatedResult),
956                                             fir::getLen(*allocatedResult));
957         else if (resultArg->passBy == PassBy::BaseAddress)
958           caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
959         else
960           fir::emitFatalError(
961               loc, "only expect character scalar result to be passed by ref");
962       } else {
963         assert(caller.mustSaveResult());
964         arrayResultShape = allocatedResult->match(
965             [&](const fir::CharArrayBoxValue &) {
966               return builder.createShape(loc, *allocatedResult);
967             },
968             [&](const fir::ArrayBoxValue &) {
969               return builder.createShape(loc, *allocatedResult);
970             },
971             [&](const auto &) { return mlir::Value{}; });
972       }
973     }
974 
975     // In older Fortran, procedure argument types are inferred. This may lead
976     // different view of what the function signature is in different locations.
977     // Casts are inserted as needed below to accommodate this.
978 
979     // The mlir::FuncOp type prevails, unless it has a different number of
980     // arguments which can happen in legal program if it was passed as a dummy
981     // procedure argument earlier with no further type information.
982     mlir::SymbolRefAttr funcSymbolAttr;
983     bool addHostAssociations = false;
984     if (!funcPointer) {
985       mlir::FunctionType funcOpType = caller.getFuncOp().getType();
986       mlir::SymbolRefAttr symbolAttr =
987           builder.getSymbolRefAttr(caller.getMangledName());
988       if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
989           callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
990           fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
991                                    fir::getHostAssocAttrName())) {
992         // The number of arguments is off by one, and we're lowering a function
993         // with host associations. Modify call to include host associations
994         // argument by appending the value at the end of the operands.
995         assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
996                converter.hostAssocTupleValue().getType());
997         addHostAssociations = true;
998       }
999       if (!addHostAssociations &&
1000           (callSiteType.getNumResults() != funcOpType.getNumResults() ||
1001            callSiteType.getNumInputs() != funcOpType.getNumInputs())) {
1002         // Deal with argument number mismatch by making a function pointer so
1003         // that function type cast can be inserted. Do not emit a warning here
1004         // because this can happen in legal program if the function is not
1005         // defined here and it was first passed as an argument without any more
1006         // information.
1007         funcPointer =
1008             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1009       } else if (callSiteType.getResults() != funcOpType.getResults()) {
1010         // Implicit interface result type mismatch are not standard Fortran, but
1011         // some compilers are not complaining about it.  The front end is not
1012         // protecting lowering from this currently. Support this with a
1013         // discouraging warning.
1014         LLVM_DEBUG(mlir::emitWarning(
1015             loc, "a return type mismatch is not standard compliant and may "
1016                  "lead to undefined behavior."));
1017         // Cast the actual function to the current caller implicit type because
1018         // that is the behavior we would get if we could not see the definition.
1019         funcPointer =
1020             builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
1021       } else {
1022         funcSymbolAttr = symbolAttr;
1023       }
1024     }
1025 
1026     mlir::FunctionType funcType =
1027         funcPointer ? callSiteType : caller.getFuncOp().getType();
1028     llvm::SmallVector<mlir::Value> operands;
1029     // First operand of indirect call is the function pointer. Cast it to
1030     // required function type for the call to handle procedures that have a
1031     // compatible interface in Fortran, but that have different signatures in
1032     // FIR.
1033     if (funcPointer) {
1034       operands.push_back(
1035           funcPointer.getType().isa<fir::BoxProcType>()
1036               ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
1037               : builder.createConvert(loc, funcType, funcPointer));
1038     }
1039 
1040     // Deal with potential mismatches in arguments types. Passing an array to a
1041     // scalar argument should for instance be tolerated here.
1042     bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
1043     for (auto [fst, snd] :
1044          llvm::zip(caller.getInputs(), funcType.getInputs())) {
1045       // When passing arguments to a procedure that can be called an implicit
1046       // interface, allow character actual arguments to be passed to dummy
1047       // arguments of any type and vice versa
1048       mlir::Value cast;
1049       auto *context = builder.getContext();
1050       if (snd.isa<fir::BoxProcType>() &&
1051           fst.getType().isa<mlir::FunctionType>()) {
1052         auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None);
1053         auto boxProcTy = builder.getBoxProcType(funcTy);
1054         if (mlir::Value host = argumentHostAssocs(converter, fst)) {
1055           cast = builder.create<fir::EmboxProcOp>(
1056               loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
1057         } else {
1058           cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
1059         }
1060       } else {
1061         cast = builder.convertWithSemantics(loc, snd, fst,
1062                                             callingImplicitInterface);
1063       }
1064       operands.push_back(cast);
1065     }
1066 
1067     // Add host associations as necessary.
1068     if (addHostAssociations)
1069       operands.push_back(converter.hostAssocTupleValue());
1070 
1071     auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
1072                                             funcSymbolAttr, operands);
1073 
1074     if (caller.mustSaveResult())
1075       builder.create<fir::SaveResultOp>(
1076           loc, call.getResult(0), fir::getBase(allocatedResult.getValue()),
1077           arrayResultShape, resultLengths);
1078 
1079     if (allocatedResult) {
1080       allocatedResult->match(
1081           [&](const fir::MutableBoxValue &box) {
1082             if (box.isAllocatable()) {
1083               TODO(loc, "allocatedResult for allocatable");
1084             }
1085           },
1086           [](const auto &) {});
1087       return *allocatedResult;
1088     }
1089 
1090     if (!resultType.hasValue())
1091       return mlir::Value{}; // subroutine call
1092     // For now, Fortran return values are implemented with a single MLIR
1093     // function return value.
1094     assert(call.getNumResults() == 1 &&
1095            "Expected exactly one result in FUNCTION call");
1096     return call.getResult(0);
1097   }
1098 
1099   /// Like genExtAddr, but ensure the address returned is a temporary even if \p
1100   /// expr is variable inside parentheses.
1101   ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
1102     // In general, genExtAddr might not create a temp for variable inside
1103     // parentheses to avoid creating array temporary in sub-expressions. It only
1104     // ensures the sub-expression is not re-associated with other parts of the
1105     // expression. In the call semantics, there is a difference between expr and
1106     // variable (see R1524). For expressions, a variable storage must not be
1107     // argument associated since it could be modified inside the call, or the
1108     // variable could also be modified by other means during the call.
1109     if (!isParenthesizedVariable(expr))
1110       return genExtAddr(expr);
1111     mlir::Location loc = getLoc();
1112     if (expr.Rank() > 0)
1113       TODO(loc, "genTempExtAddr array");
1114     return genExtValue(expr).match(
1115         [&](const fir::CharBoxValue &boxChar) -> ExtValue {
1116           TODO(loc, "genTempExtAddr CharBoxValue");
1117         },
1118         [&](const fir::UnboxedValue &v) -> ExtValue {
1119           mlir::Type type = v.getType();
1120           mlir::Value value = v;
1121           if (fir::isa_ref_type(type))
1122             value = builder.create<fir::LoadOp>(loc, value);
1123           mlir::Value temp = builder.createTemporary(loc, value.getType());
1124           builder.create<fir::StoreOp>(loc, value, temp);
1125           return temp;
1126         },
1127         [&](const fir::BoxValue &x) -> ExtValue {
1128           // Derived type scalar that may be polymorphic.
1129           assert(!x.hasRank() && x.isDerived());
1130           if (x.isDerivedWithLengthParameters())
1131             fir::emitFatalError(
1132                 loc, "making temps for derived type with length parameters");
1133           // TODO: polymorphic aspects should be kept but for now the temp
1134           // created always has the declared type.
1135           mlir::Value var =
1136               fir::getBase(fir::factory::readBoxValue(builder, loc, x));
1137           auto value = builder.create<fir::LoadOp>(loc, var);
1138           mlir::Value temp = builder.createTemporary(loc, value.getType());
1139           builder.create<fir::StoreOp>(loc, value, temp);
1140           return temp;
1141         },
1142         [&](const auto &) -> ExtValue {
1143           fir::emitFatalError(loc, "expr is not a scalar value");
1144         });
1145   }
1146 
1147   /// Helper structure to track potential copy-in of non contiguous variable
1148   /// argument into a contiguous temp. It is used to deallocate the temp that
1149   /// may have been created as well as to the copy-out from the temp to the
1150   /// variable after the call.
1151   struct CopyOutPair {
1152     ExtValue var;
1153     ExtValue temp;
1154     // Flag to indicate if the argument may have been modified by the
1155     // callee, in which case it must be copied-out to the variable.
1156     bool argMayBeModifiedByCall;
1157     // Optional boolean value that, if present and false, prevents
1158     // the copy-out and temp deallocation.
1159     llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime;
1160   };
1161   using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>;
1162 
1163   /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories
1164   /// not based on fir.box.
1165   /// This will lose any non contiguous stride information and dynamic type and
1166   /// should only be called if \p exv is known to be contiguous or if its base
1167   /// address will be replaced by a contiguous one. If \p exv is not a
1168   /// fir::BoxValue, this is a no-op.
1169   ExtValue readIfBoxValue(const ExtValue &exv) {
1170     if (const auto *box = exv.getBoxOf<fir::BoxValue>())
1171       return fir::factory::readBoxValue(builder, getLoc(), *box);
1172     return exv;
1173   }
1174 
1175   /// Lower a non-elemental procedure reference.
1176   ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
1177                               llvm::Optional<mlir::Type> resultType) {
1178     mlir::Location loc = getLoc();
1179     if (isElementalProcWithArrayArgs(procRef))
1180       fir::emitFatalError(loc, "trying to lower elemental procedure with array "
1181                                "arguments as normal procedure");
1182     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
1183             procRef.proc().GetSpecificIntrinsic())
1184       return genIntrinsicRef(procRef, *intrinsic, resultType);
1185 
1186     if (isStatementFunctionCall(procRef))
1187       TODO(loc, "Lower statement function call");
1188 
1189     Fortran::lower::CallerInterface caller(procRef, converter);
1190     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1191 
1192     llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall;
1193     // List of <var, temp> where temp must be copied into var after the call.
1194     CopyOutPairs copyOutPairs;
1195 
1196     mlir::FunctionType callSiteType = caller.genFunctionType();
1197 
1198     // Lower the actual arguments and map the lowered values to the dummy
1199     // arguments.
1200     for (const Fortran::lower::CallInterface<
1201              Fortran::lower::CallerInterface>::PassedEntity &arg :
1202          caller.getPassedArguments()) {
1203       const auto *actual = arg.entity;
1204       mlir::Type argTy = callSiteType.getInput(arg.firArgument);
1205       if (!actual) {
1206         // Optional dummy argument for which there is no actual argument.
1207         caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy));
1208         continue;
1209       }
1210       const auto *expr = actual->UnwrapExpr();
1211       if (!expr)
1212         TODO(loc, "assumed type actual argument lowering");
1213 
1214       if (arg.passBy == PassBy::Value) {
1215         ExtValue argVal = genval(*expr);
1216         if (!fir::isUnboxedValue(argVal))
1217           fir::emitFatalError(
1218               loc, "internal error: passing non trivial value by value");
1219         caller.placeInput(arg, fir::getBase(argVal));
1220         continue;
1221       }
1222 
1223       if (arg.passBy == PassBy::MutableBox) {
1224         TODO(loc, "arg passby MutableBox");
1225       }
1226       const bool actualArgIsVariable = Fortran::evaluate::IsVariable(*expr);
1227       if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar) {
1228         auto argAddr = [&]() -> ExtValue {
1229           ExtValue baseAddr;
1230           if (actualArgIsVariable && arg.isOptional()) {
1231             if (Fortran::evaluate::IsAllocatableOrPointerObject(
1232                     *expr, converter.getFoldingContext())) {
1233               TODO(loc, "Allocatable or pointer argument");
1234             }
1235             if (const Fortran::semantics::Symbol *wholeSymbol =
1236                     Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(
1237                         *expr))
1238               if (Fortran::semantics::IsOptional(*wholeSymbol)) {
1239                 TODO(loc, "procedureref optional arg");
1240               }
1241             // Fall through: The actual argument can safely be
1242             // copied-in/copied-out without any care if needed.
1243           }
1244           if (actualArgIsVariable && expr->Rank() > 0) {
1245             TODO(loc, "procedureref arrays");
1246           }
1247           // Actual argument is a non optional/non pointer/non allocatable
1248           // scalar.
1249           if (actualArgIsVariable)
1250             return genExtAddr(*expr);
1251           // Actual argument is not a variable. Make sure a variable address is
1252           // not passed.
1253           return genTempExtAddr(*expr);
1254         }();
1255         // Scalar and contiguous expressions may be lowered to a fir.box,
1256         // either to account for potential polymorphism, or because lowering
1257         // did not account for some contiguity hints.
1258         // Here, polymorphism does not matter (an entity of the declared type
1259         // is passed, not one of the dynamic type), and the expr is known to
1260         // be simply contiguous, so it is safe to unbox it and pass the
1261         // address without making a copy.
1262         argAddr = readIfBoxValue(argAddr);
1263 
1264         if (arg.passBy == PassBy::BaseAddress) {
1265           caller.placeInput(arg, fir::getBase(argAddr));
1266         } else {
1267           TODO(loc, "procedureref PassBy::BoxChar");
1268         }
1269       } else if (arg.passBy == PassBy::Box) {
1270         // Before lowering to an address, handle the allocatable/pointer actual
1271         // argument to optional fir.box dummy. It is legal to pass
1272         // unallocated/disassociated entity to an optional. In this case, an
1273         // absent fir.box must be created instead of a fir.box with a null value
1274         // (Fortran 2018 15.5.2.12 point 1).
1275         if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject(
1276                                     *expr, converter.getFoldingContext())) {
1277           TODO(loc, "optional allocatable or pointer argument");
1278         } else {
1279           // Make sure a variable address is only passed if the expression is
1280           // actually a variable.
1281           mlir::Value box =
1282               actualArgIsVariable
1283                   ? builder.createBox(loc, genBoxArg(*expr))
1284                   : builder.createBox(getLoc(), genTempExtAddr(*expr));
1285           caller.placeInput(arg, box);
1286         }
1287       } else if (arg.passBy == PassBy::AddressAndLength) {
1288         ExtValue argRef = genExtAddr(*expr);
1289         caller.placeAddressAndLengthInput(arg, fir::getBase(argRef),
1290                                           fir::getLen(argRef));
1291       } else if (arg.passBy == PassBy::CharProcTuple) {
1292         TODO(loc, "procedureref CharProcTuple");
1293       } else {
1294         TODO(loc, "pass by value in non elemental function call");
1295       }
1296     }
1297 
1298     ExtValue result = genCallOpAndResult(caller, callSiteType, resultType);
1299 
1300     // // Copy-out temps that were created for non contiguous variable arguments
1301     // if
1302     // // needed.
1303     // for (const auto &copyOutPair : copyOutPairs)
1304     //   genCopyOut(copyOutPair);
1305 
1306     return result;
1307   }
1308 
1309   template <typename A>
1310   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
1311     ExtValue result = genFunctionRef(funcRef);
1312     if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
1313       return genLoad(result);
1314     return result;
1315   }
1316 
1317   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
1318     llvm::Optional<mlir::Type> resTy;
1319     if (procRef.hasAlternateReturns())
1320       resTy = builder.getIndexType();
1321     return genProcedureRef(procRef, resTy);
1322   }
1323 
1324   /// Generate a call to an intrinsic function.
1325   ExtValue
1326   genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
1327                   const Fortran::evaluate::SpecificIntrinsic &intrinsic,
1328                   llvm::Optional<mlir::Type> resultType) {
1329     llvm::SmallVector<ExtValue> operands;
1330 
1331     llvm::StringRef name = intrinsic.name;
1332     mlir::Location loc = getLoc();
1333 
1334     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
1335         Fortran::lower::getIntrinsicArgumentLowering(name);
1336     for (const auto &[arg, dummy] :
1337          llvm::zip(procRef.arguments(),
1338                    intrinsic.characteristics.value().dummyArguments)) {
1339       auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
1340       if (!expr) {
1341         // Absent optional.
1342         operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
1343         continue;
1344       }
1345       if (!argLowering) {
1346         // No argument lowering instruction, lower by value.
1347         operands.emplace_back(genval(*expr));
1348         continue;
1349       }
1350       // Ad-hoc argument lowering handling.
1351       Fortran::lower::ArgLoweringRule argRules =
1352           Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering,
1353                                                    dummy.name);
1354       switch (argRules.lowerAs) {
1355       case Fortran::lower::LowerIntrinsicArgAs::Value:
1356         operands.emplace_back(genval(*expr));
1357         continue;
1358       case Fortran::lower::LowerIntrinsicArgAs::Addr:
1359         TODO(getLoc(), "argument lowering for Addr");
1360         continue;
1361       case Fortran::lower::LowerIntrinsicArgAs::Box:
1362         TODO(getLoc(), "argument lowering for Box");
1363         continue;
1364       case Fortran::lower::LowerIntrinsicArgAs::Inquired:
1365         TODO(getLoc(), "argument lowering for Inquired");
1366         continue;
1367       }
1368       llvm_unreachable("bad switch");
1369     }
1370     // Let the intrinsic library lower the intrinsic procedure call
1371     return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
1372                                             operands);
1373   }
1374 
1375   template <typename A>
1376   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
1377     if (isScalar(x))
1378       return std::visit([&](const auto &e) { return genval(e); }, x.u);
1379     TODO(getLoc(), "genval Expr<A> arrays");
1380   }
1381 
1382   /// Helper to detect Transformational function reference.
1383   template <typename T>
1384   bool isTransformationalRef(const T &) {
1385     return false;
1386   }
1387   template <typename T>
1388   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
1389     return !funcRef.IsElemental() && funcRef.Rank();
1390   }
1391   template <typename T>
1392   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
1393     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
1394                       expr.u);
1395   }
1396 
1397   template <typename A>
1398   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
1399     // Whole array symbols or components, and results of transformational
1400     // functions already have a storage and the scalar expression lowering path
1401     // is used to not create a new temporary storage.
1402     if (isScalar(x) ||
1403         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
1404         isTransformationalRef(x))
1405       return std::visit([&](const auto &e) { return genref(e); }, x.u);
1406     TODO(getLoc(), "gen Expr non-scalar");
1407   }
1408 
1409   template <typename A>
1410   bool isScalar(const A &x) {
1411     return x.Rank() == 0;
1412   }
1413 
1414   template <int KIND>
1415   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
1416                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
1417     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
1418   }
1419 
1420   using RefSet =
1421       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
1422                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
1423                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
1424                  Fortran::semantics::SymbolRef>;
1425   template <typename A>
1426   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
1427 
1428   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
1429   ExtValue genref(const A &a) {
1430     return gen(a);
1431   }
1432   template <typename A>
1433   ExtValue genref(const A &a) {
1434     mlir::Type storageType = converter.genType(toEvExpr(a));
1435     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
1436   }
1437 
1438   template <typename A, template <typename> typename T,
1439             typename B = std::decay_t<T<A>>,
1440             std::enable_if_t<
1441                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
1442                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
1443                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
1444                 bool> = true>
1445   ExtValue genref(const T<A> &x) {
1446     return gen(x);
1447   }
1448 
1449 private:
1450   mlir::Location location;
1451   Fortran::lower::AbstractConverter &converter;
1452   fir::FirOpBuilder &builder;
1453   Fortran::lower::StatementContext &stmtCtx;
1454   Fortran::lower::SymMap &symMap;
1455   bool useBoxArg = false; // expression lowered as argument
1456 };
1457 } // namespace
1458 
1459 // Helper for changing the semantics in a given context. Preserves the current
1460 // semantics which is resumed when the "push" goes out of scope.
1461 #define PushSemantics(PushVal)                                                 \
1462   [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ =                 \
1463       Fortran::common::ScopedSet(semant, PushVal);
1464 
1465 static bool isAdjustedArrayElementType(mlir::Type t) {
1466   return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>();
1467 }
1468 
1469 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting
1470 /// the actual extents and lengths. This is only to allow their propagation as
1471 /// ExtendedValue without triggering verifier failures when propagating
1472 /// character/arrays as unboxed values. Only the base of the resulting
1473 /// ExtendedValue should be used, it is undefined to use the length or extents
1474 /// of the extended value returned,
1475 inline static fir::ExtendedValue
1476 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder,
1477                        mlir::Value val, mlir::Value len) {
1478   mlir::Type ty = fir::unwrapRefType(val.getType());
1479   mlir::IndexType idxTy = builder.getIndexType();
1480   auto seqTy = ty.cast<fir::SequenceType>();
1481   auto undef = builder.create<fir::UndefOp>(loc, idxTy);
1482   llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef);
1483   if (fir::isa_char(seqTy.getEleTy()))
1484     return fir::CharArrayBoxValue(val, len ? len : undef, extents);
1485   return fir::ArrayBoxValue(val, extents);
1486 }
1487 
1488 //===----------------------------------------------------------------------===//
1489 //
1490 // Lowering of array expressions.
1491 //
1492 //===----------------------------------------------------------------------===//
1493 
1494 namespace {
1495 class ArrayExprLowering {
1496   using ExtValue = fir::ExtendedValue;
1497 
1498   /// Structure to keep track of lowered array operands in the
1499   /// array expression. Useful to later deduce the shape of the
1500   /// array expression.
1501   struct ArrayOperand {
1502     /// Array base (can be a fir.box).
1503     mlir::Value memref;
1504     /// ShapeOp, ShapeShiftOp or ShiftOp
1505     mlir::Value shape;
1506     /// SliceOp
1507     mlir::Value slice;
1508     /// Can this operand be absent ?
1509     bool mayBeAbsent = false;
1510   };
1511 
1512   using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts;
1513   using PathComponent = Fortran::lower::PathComponent;
1514 
1515   /// Active iteration space.
1516   using IterationSpace = Fortran::lower::IterationSpace;
1517   using IterSpace = const Fortran::lower::IterationSpace &;
1518 
1519   /// Current continuation. Function that will generate IR for a single
1520   /// iteration of the pending iterative loop structure.
1521   using CC = Fortran::lower::GenerateElementalArrayFunc;
1522 
1523   /// Projection continuation. Function that will project one iteration space
1524   /// into another.
1525   using PC = std::function<IterationSpace(IterSpace)>;
1526   using ArrayBaseTy =
1527       std::variant<std::monostate, const Fortran::evaluate::ArrayRef *,
1528                    const Fortran::evaluate::DataRef *>;
1529   using ComponentPath = Fortran::lower::ComponentPath;
1530 
1531 public:
1532   //===--------------------------------------------------------------------===//
1533   // Regular array assignment
1534   //===--------------------------------------------------------------------===//
1535 
1536   /// Entry point for array assignments. Both the left-hand and right-hand sides
1537   /// can either be ExtendedValue or evaluate::Expr.
1538   template <typename TL, typename TR>
1539   static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter,
1540                                    Fortran::lower::SymMap &symMap,
1541                                    Fortran::lower::StatementContext &stmtCtx,
1542                                    const TL &lhs, const TR &rhs) {
1543     ArrayExprLowering ael{converter, stmtCtx, symMap,
1544                           ConstituentSemantics::CopyInCopyOut};
1545     ael.lowerArrayAssignment(lhs, rhs);
1546   }
1547 
1548   template <typename TL, typename TR>
1549   void lowerArrayAssignment(const TL &lhs, const TR &rhs) {
1550     mlir::Location loc = getLoc();
1551     /// Here the target subspace is not necessarily contiguous. The ArrayUpdate
1552     /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad
1553     /// in `destination`.
1554     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
1555     ccStoreToDest = genarr(lhs);
1556     determineShapeOfDest(lhs);
1557     semant = ConstituentSemantics::RefTransparent;
1558     ExtValue exv = lowerArrayExpression(rhs);
1559     if (explicitSpaceIsActive()) {
1560       explicitSpace->finalizeContext();
1561       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
1562     } else {
1563       builder.create<fir::ArrayMergeStoreOp>(
1564           loc, destination, fir::getBase(exv), destination.getMemref(),
1565           destination.getSlice(), destination.getTypeparams());
1566     }
1567   }
1568 
1569   //===--------------------------------------------------------------------===//
1570   // Array assignment to allocatable array
1571   //===--------------------------------------------------------------------===//
1572 
1573   /// Entry point for assignment to allocatable array.
1574   static void lowerAllocatableArrayAssignment(
1575       Fortran::lower::AbstractConverter &converter,
1576       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
1577       const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
1578       Fortran::lower::ExplicitIterSpace &explicitSpace,
1579       Fortran::lower::ImplicitIterSpace &implicitSpace) {
1580     ArrayExprLowering ael(converter, stmtCtx, symMap,
1581                           ConstituentSemantics::CopyInCopyOut, &explicitSpace,
1582                           &implicitSpace);
1583     ael.lowerAllocatableArrayAssignment(lhs, rhs);
1584   }
1585 
1586   /// Assignment to allocatable array.
1587   ///
1588   /// The semantics are reverse that of a "regular" array assignment. The rhs
1589   /// defines the iteration space of the computation and the lhs is
1590   /// resized/reallocated to fit if necessary.
1591   void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs,
1592                                        const Fortran::lower::SomeExpr &rhs) {
1593     // With assignment to allocatable, we want to lower the rhs first and use
1594     // its shape to determine if we need to reallocate, etc.
1595     mlir::Location loc = getLoc();
1596     // FIXME: If the lhs is in an explicit iteration space, the assignment may
1597     // be to an array of allocatable arrays rather than a single allocatable
1598     // array.
1599     fir::MutableBoxValue mutableBox =
1600         createMutableBox(loc, converter, lhs, symMap);
1601     mlir::Type resultTy = converter.genType(rhs);
1602     if (rhs.Rank() > 0)
1603       determineShapeOfDest(rhs);
1604     auto rhsCC = [&]() {
1605       PushSemantics(ConstituentSemantics::RefTransparent);
1606       return genarr(rhs);
1607     }();
1608 
1609     llvm::SmallVector<mlir::Value> lengthParams;
1610     // Currently no safe way to gather length from rhs (at least for
1611     // character, it cannot be taken from array_loads since it may be
1612     // changed by concatenations).
1613     if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) ||
1614         mutableBox.isDerivedWithLengthParameters())
1615       TODO(loc, "gather rhs length parameters in assignment to allocatable");
1616 
1617     // The allocatable must take lower bounds from the expr if it is
1618     // reallocated and the right hand side is not a scalar.
1619     const bool takeLboundsIfRealloc = rhs.Rank() > 0;
1620     llvm::SmallVector<mlir::Value> lbounds;
1621     // When the reallocated LHS takes its lower bounds from the RHS,
1622     // they will be non default only if the RHS is a whole array
1623     // variable. Otherwise, lbounds is left empty and default lower bounds
1624     // will be used.
1625     if (takeLboundsIfRealloc &&
1626         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) {
1627       assert(arrayOperands.size() == 1 &&
1628              "lbounds can only come from one array");
1629       std::vector<mlir::Value> lbs =
1630           fir::factory::getOrigins(arrayOperands[0].shape);
1631       lbounds.append(lbs.begin(), lbs.end());
1632     }
1633     fir::factory::MutableBoxReallocation realloc =
1634         fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape,
1635                                          lengthParams);
1636     // Create ArrayLoad for the mutable box and save it into `destination`.
1637     PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
1638     ccStoreToDest = genarr(realloc.newValue);
1639     // If the rhs is scalar, get shape from the allocatable ArrayLoad.
1640     if (destShape.empty())
1641       destShape = getShape(destination);
1642     // Finish lowering the loop nest.
1643     assert(destination && "destination must have been set");
1644     ExtValue exv = lowerArrayExpression(rhsCC, resultTy);
1645     if (explicitSpaceIsActive()) {
1646       explicitSpace->finalizeContext();
1647       builder.create<fir::ResultOp>(loc, fir::getBase(exv));
1648     } else {
1649       builder.create<fir::ArrayMergeStoreOp>(
1650           loc, destination, fir::getBase(exv), destination.getMemref(),
1651           destination.getSlice(), destination.getTypeparams());
1652     }
1653     fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds,
1654                                   takeLboundsIfRealloc, realloc);
1655   }
1656 
1657   /// Entry point into lowering an expression with rank. This entry point is for
1658   /// lowering a rhs expression, for example. (RefTransparent semantics.)
1659   static ExtValue
1660   lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter,
1661                           Fortran::lower::SymMap &symMap,
1662                           Fortran::lower::StatementContext &stmtCtx,
1663                           const Fortran::lower::SomeExpr &expr) {
1664     ArrayExprLowering ael{converter, stmtCtx, symMap};
1665     ael.determineShapeOfDest(expr);
1666     ExtValue loopRes = ael.lowerArrayExpression(expr);
1667     fir::ArrayLoadOp dest = ael.destination;
1668     mlir::Value tempRes = dest.getMemref();
1669     fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1670     mlir::Location loc = converter.getCurrentLocation();
1671     builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes),
1672                                            tempRes, dest.getSlice(),
1673                                            dest.getTypeparams());
1674 
1675     auto arrTy =
1676         fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>();
1677     if (auto charTy =
1678             arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) {
1679       if (fir::characterWithDynamicLen(charTy))
1680         TODO(loc, "CHARACTER does not have constant LEN");
1681       mlir::Value len = builder.createIntegerConstant(
1682           loc, builder.getCharacterLengthType(), charTy.getLen());
1683       return fir::CharArrayBoxValue(tempRes, len, dest.getExtents());
1684     }
1685     return fir::ArrayBoxValue(tempRes, dest.getExtents());
1686   }
1687 
1688   // FIXME: should take multiple inner arguments.
1689   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
1690   genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) {
1691     mlir::Location loc = getLoc();
1692     mlir::IndexType idxTy = builder.getIndexType();
1693     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
1694     mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
1695     llvm::SmallVector<mlir::Value> loopUppers;
1696 
1697     // Convert any implied shape to closed interval form. The fir.do_loop will
1698     // run from 0 to `extent - 1` inclusive.
1699     for (auto extent : shape)
1700       loopUppers.push_back(
1701           builder.create<mlir::arith::SubIOp>(loc, extent, one));
1702 
1703     // Iteration space is created with outermost columns, innermost rows
1704     llvm::SmallVector<fir::DoLoopOp> loops;
1705 
1706     const std::size_t loopDepth = loopUppers.size();
1707     llvm::SmallVector<mlir::Value> ivars;
1708 
1709     for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) {
1710       if (i.index() > 0) {
1711         assert(!loops.empty());
1712         builder.setInsertionPointToStart(loops.back().getBody());
1713       }
1714       fir::DoLoopOp loop;
1715       if (innerArg) {
1716         loop = builder.create<fir::DoLoopOp>(
1717             loc, zero, i.value(), one, isUnordered(),
1718             /*finalCount=*/false, mlir::ValueRange{innerArg});
1719         innerArg = loop.getRegionIterArgs().front();
1720         if (explicitSpaceIsActive())
1721           explicitSpace->setInnerArg(0, innerArg);
1722       } else {
1723         loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one,
1724                                              isUnordered(),
1725                                              /*finalCount=*/false);
1726       }
1727       ivars.push_back(loop.getInductionVar());
1728       loops.push_back(loop);
1729     }
1730 
1731     if (innerArg)
1732       for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth;
1733            ++i) {
1734         builder.setInsertionPointToEnd(loops[i].getBody());
1735         builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0));
1736       }
1737 
1738     // Move insertion point to the start of the innermost loop in the nest.
1739     builder.setInsertionPointToStart(loops.back().getBody());
1740     // Set `afterLoopNest` to just after the entire loop nest.
1741     auto currPt = builder.saveInsertionPoint();
1742     builder.setInsertionPointAfter(loops[0]);
1743     auto afterLoopNest = builder.saveInsertionPoint();
1744     builder.restoreInsertionPoint(currPt);
1745 
1746     // Put the implicit loop variables in row to column order to match FIR's
1747     // Ops. (The loops were constructed from outermost column to innermost
1748     // row.)
1749     mlir::Value outerRes = loops[0].getResult(0);
1750     return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)),
1751             afterLoopNest};
1752   }
1753 
1754   /// Build the iteration space into which the array expression will be
1755   /// lowered. The resultType is used to create a temporary, if needed.
1756   std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
1757   genIterSpace(mlir::Type resultType) {
1758     mlir::Location loc = getLoc();
1759     llvm::SmallVector<mlir::Value> shape = genIterationShape();
1760     if (!destination) {
1761       // Allocate storage for the result if it is not already provided.
1762       destination = createAndLoadSomeArrayTemp(resultType, shape);
1763     }
1764 
1765     // Generate the lazy mask allocation, if one was given.
1766     if (ccPrelude.hasValue())
1767       ccPrelude.getValue()(shape);
1768 
1769     // Now handle the implicit loops.
1770     mlir::Value inner = explicitSpaceIsActive()
1771                             ? explicitSpace->getInnerArgs().front()
1772                             : destination.getResult();
1773     auto [iters, afterLoopNest] = genImplicitLoops(shape, inner);
1774     mlir::Value innerArg = iters.innerArgument();
1775 
1776     // Generate the mask conditional structure, if there are masks. Unlike the
1777     // explicit masks, which are interleaved, these mask expression appear in
1778     // the innermost loop.
1779     if (implicitSpaceHasMasks()) {
1780       // Recover the cached condition from the mask buffer.
1781       auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) {
1782         return implicitSpace->getBoundClosure(e)(iters);
1783       };
1784 
1785       // Handle the negated conditions in topological order of the WHERE
1786       // clauses. See 10.2.3.2p4 as to why this control structure is produced.
1787       for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs :
1788            implicitSpace->getMasks()) {
1789         const std::size_t size = maskExprs.size() - 1;
1790         auto genFalseBlock = [&](const auto *e, auto &&cond) {
1791           auto ifOp = builder.create<fir::IfOp>(
1792               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
1793               /*withElseRegion=*/true);
1794           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
1795           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
1796           builder.create<fir::ResultOp>(loc, innerArg);
1797           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
1798         };
1799         auto genTrueBlock = [&](const auto *e, auto &&cond) {
1800           auto ifOp = builder.create<fir::IfOp>(
1801               loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
1802               /*withElseRegion=*/true);
1803           builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
1804           builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
1805           builder.create<fir::ResultOp>(loc, innerArg);
1806           builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
1807         };
1808         for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i)
1809           if (const auto *e = maskExprs[i])
1810             genFalseBlock(e, genCond(e, iters));
1811 
1812         // The last condition is either non-negated or unconditionally negated.
1813         if (const auto *e = maskExprs[size])
1814           genTrueBlock(e, genCond(e, iters));
1815       }
1816     }
1817 
1818     // We're ready to lower the body (an assignment statement) for this context
1819     // of loop nests at this point.
1820     return {iters, afterLoopNest};
1821   }
1822 
1823   fir::ArrayLoadOp
1824   createAndLoadSomeArrayTemp(mlir::Type type,
1825                              llvm::ArrayRef<mlir::Value> shape) {
1826     if (ccLoadDest.hasValue())
1827       return ccLoadDest.getValue()(shape);
1828     auto seqTy = type.dyn_cast<fir::SequenceType>();
1829     assert(seqTy && "must be an array");
1830     mlir::Location loc = getLoc();
1831     // TODO: Need to thread the length parameters here. For character, they may
1832     // differ from the operands length (e.g concatenation). So the array loads
1833     // type parameters are not enough.
1834     if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>())
1835       if (charTy.hasDynamicLen())
1836         TODO(loc, "character array expression temp with dynamic length");
1837     if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>())
1838       if (recTy.getNumLenParams() > 0)
1839         TODO(loc, "derived type array expression temp with length parameters");
1840     mlir::Value temp = seqTy.hasConstantShape()
1841                            ? builder.create<fir::AllocMemOp>(loc, type)
1842                            : builder.create<fir::AllocMemOp>(
1843                                  loc, type, ".array.expr", llvm::None, shape);
1844     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
1845     stmtCtx.attachCleanup(
1846         [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); });
1847     mlir::Value shapeOp = genShapeOp(shape);
1848     return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp,
1849                                             /*slice=*/mlir::Value{},
1850                                             llvm::None);
1851   }
1852 
1853   static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder,
1854                                  llvm::ArrayRef<mlir::Value> shape) {
1855     mlir::IndexType idxTy = builder.getIndexType();
1856     llvm::SmallVector<mlir::Value> idxShape;
1857     for (auto s : shape)
1858       idxShape.push_back(builder.createConvert(loc, idxTy, s));
1859     auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size());
1860     return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape);
1861   }
1862 
1863   fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) {
1864     return genShapeOp(getLoc(), builder, shape);
1865   }
1866 
1867   //===--------------------------------------------------------------------===//
1868   // Expression traversal and lowering.
1869   //===--------------------------------------------------------------------===//
1870 
1871   /// Lower the expression, \p x, in a scalar context.
1872   template <typename A>
1873   ExtValue asScalar(const A &x) {
1874     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x);
1875   }
1876 
1877   /// Lower the expression in a scalar context to a memory reference.
1878   template <typename A>
1879   ExtValue asScalarRef(const A &x) {
1880     return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x);
1881   }
1882 
1883   // An expression with non-zero rank is an array expression.
1884   template <typename A>
1885   bool isArray(const A &x) const {
1886     return x.Rank() != 0;
1887   }
1888 
1889   /// If there were temporaries created for this element evaluation, finalize
1890   /// and deallocate the resources now. This should be done just prior the the
1891   /// fir::ResultOp at the end of the innermost loop.
1892   void finalizeElementCtx() {
1893     if (elementCtx) {
1894       stmtCtx.finalize(/*popScope=*/true);
1895       elementCtx = false;
1896     }
1897   }
1898 
1899   template <typename A>
1900   CC genScalarAndForwardValue(const A &x) {
1901     ExtValue result = asScalar(x);
1902     return [=](IterSpace) { return result; };
1903   }
1904 
1905   template <typename A, typename = std::enable_if_t<Fortran::common::HasMember<
1906                             A, Fortran::evaluate::TypelessExpression>>>
1907   CC genarr(const A &x) {
1908     return genScalarAndForwardValue(x);
1909   }
1910 
1911   template <typename A>
1912   CC genarr(const Fortran::evaluate::Expr<A> &x) {
1913     LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x));
1914     if (isArray(x) || explicitSpaceIsActive() ||
1915         isElementalProcWithArrayArgs(x))
1916       return std::visit([&](const auto &e) { return genarr(e); }, x.u);
1917     return genScalarAndForwardValue(x);
1918   }
1919 
1920   template <Fortran::common::TypeCategory TC1, int KIND,
1921             Fortran::common::TypeCategory TC2>
1922   CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
1923                                              TC2> &x) {
1924     TODO(getLoc(), "");
1925   }
1926 
1927   template <int KIND>
1928   CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) {
1929     TODO(getLoc(), "");
1930   }
1931 
1932   template <typename T>
1933   CC genarr(const Fortran::evaluate::Parentheses<T> &x) {
1934     TODO(getLoc(), "");
1935   }
1936 
1937   template <int KIND>
1938   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
1939                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
1940     TODO(getLoc(), "");
1941   }
1942 
1943   template <int KIND>
1944   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
1945                 Fortran::common::TypeCategory::Real, KIND>> &x) {
1946     TODO(getLoc(), "");
1947   }
1948   template <int KIND>
1949   CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
1950                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
1951     TODO(getLoc(), "");
1952   }
1953 
1954 #undef GENBIN
1955 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
1956   template <int KIND>                                                          \
1957   CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type<       \
1958                 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) {       \
1959     TODO(getLoc(), "genarr Binary");                                           \
1960   }
1961 
1962   GENBIN(Add, Integer, mlir::arith::AddIOp)
1963   GENBIN(Add, Real, mlir::arith::AddFOp)
1964   GENBIN(Add, Complex, fir::AddcOp)
1965   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
1966   GENBIN(Subtract, Real, mlir::arith::SubFOp)
1967   GENBIN(Subtract, Complex, fir::SubcOp)
1968   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
1969   GENBIN(Multiply, Real, mlir::arith::MulFOp)
1970   GENBIN(Multiply, Complex, fir::MulcOp)
1971   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
1972   GENBIN(Divide, Real, mlir::arith::DivFOp)
1973   GENBIN(Divide, Complex, fir::DivcOp)
1974 
1975   template <Fortran::common::TypeCategory TC, int KIND>
1976   CC genarr(
1977       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
1978     TODO(getLoc(), "genarr ");
1979   }
1980   template <Fortran::common::TypeCategory TC, int KIND>
1981   CC genarr(
1982       const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
1983     TODO(getLoc(), "genarr ");
1984   }
1985   template <Fortran::common::TypeCategory TC, int KIND>
1986   CC genarr(
1987       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
1988           &x) {
1989     TODO(getLoc(), "genarr ");
1990   }
1991   template <int KIND>
1992   CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
1993     TODO(getLoc(), "genarr ");
1994   }
1995 
1996   template <int KIND>
1997   CC genarr(const Fortran::evaluate::Concat<KIND> &x) {
1998     TODO(getLoc(), "genarr ");
1999   }
2000 
2001   template <int KIND>
2002   CC genarr(const Fortran::evaluate::SetLength<KIND> &x) {
2003     TODO(getLoc(), "genarr ");
2004   }
2005 
2006   template <typename A>
2007   CC genarr(const Fortran::evaluate::Constant<A> &x) {
2008     TODO(getLoc(), "genarr ");
2009   }
2010 
2011   CC genarr(const Fortran::semantics::SymbolRef &sym,
2012             ComponentPath &components) {
2013     return genarr(sym.get(), components);
2014   }
2015 
2016   ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) {
2017     return convertToArrayBoxValue(getLoc(), builder, val, len);
2018   }
2019 
2020   CC genarr(const ExtValue &extMemref) {
2021     ComponentPath dummy(/*isImplicit=*/true);
2022     return genarr(extMemref, dummy);
2023   }
2024 
2025   template <typename A>
2026   CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) {
2027     TODO(getLoc(), "genarr ArrayConstructor<A>");
2028   }
2029 
2030   CC genarr(const Fortran::evaluate::ImpliedDoIndex &) {
2031     TODO(getLoc(), "genarr ImpliedDoIndex");
2032   }
2033 
2034   CC genarr(const Fortran::evaluate::TypeParamInquiry &x) {
2035     TODO(getLoc(), "genarr TypeParamInquiry");
2036   }
2037 
2038   CC genarr(const Fortran::evaluate::DescriptorInquiry &x) {
2039     TODO(getLoc(), "genarr DescriptorInquiry");
2040   }
2041 
2042   CC genarr(const Fortran::evaluate::StructureConstructor &x) {
2043     TODO(getLoc(), "genarr StructureConstructor");
2044   }
2045 
2046   template <int KIND>
2047   CC genarr(const Fortran::evaluate::Not<KIND> &x) {
2048     TODO(getLoc(), "genarr Not");
2049   }
2050 
2051   template <int KIND>
2052   CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) {
2053     TODO(getLoc(), "genarr LogicalOperation");
2054   }
2055 
2056   template <int KIND>
2057   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
2058                 Fortran::common::TypeCategory::Integer, KIND>> &x) {
2059     TODO(getLoc(), "genarr Relational Integer");
2060   }
2061   template <int KIND>
2062   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
2063                 Fortran::common::TypeCategory::Character, KIND>> &x) {
2064     TODO(getLoc(), "genarr Relational Character");
2065   }
2066   template <int KIND>
2067   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
2068                 Fortran::common::TypeCategory::Real, KIND>> &x) {
2069     TODO(getLoc(), "genarr Relational Real");
2070   }
2071   template <int KIND>
2072   CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
2073                 Fortran::common::TypeCategory::Complex, KIND>> &x) {
2074     TODO(getLoc(), "genarr Relational Complex");
2075   }
2076   CC genarr(
2077       const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) {
2078     TODO(getLoc(), "genarr Relational SomeType");
2079   }
2080 
2081   template <typename A>
2082   CC genarr(const Fortran::evaluate::Designator<A> &des) {
2083     ComponentPath components(des.Rank() > 0);
2084     return std::visit([&](const auto &x) { return genarr(x, components); },
2085                       des.u);
2086   }
2087 
2088   template <typename T>
2089   CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) {
2090     TODO(getLoc(), "genarr FunctionRef");
2091   }
2092 
2093   template <typename A>
2094   CC genImplicitArrayAccess(const A &x, ComponentPath &components) {
2095     components.reversePath.push_back(ImplicitSubscripts{});
2096     ExtValue exv = asScalarRef(x);
2097     // lowerPath(exv, components);
2098     auto lambda = genarr(exv, components);
2099     return [=](IterSpace iters) { return lambda(components.pc(iters)); };
2100   }
2101   CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x,
2102                             ComponentPath &components) {
2103     if (x.IsSymbol())
2104       return genImplicitArrayAccess(x.GetFirstSymbol(), components);
2105     return genImplicitArrayAccess(x.GetComponent(), components);
2106   }
2107 
2108   template <typename A>
2109   CC genAsScalar(const A &x) {
2110     mlir::Location loc = getLoc();
2111     if (isProjectedCopyInCopyOut()) {
2112       return [=, &x, builder = &converter.getFirOpBuilder()](
2113                  IterSpace iters) -> ExtValue {
2114         ExtValue exv = asScalarRef(x);
2115         mlir::Value val = fir::getBase(exv);
2116         mlir::Type eleTy = fir::unwrapRefType(val.getType());
2117         if (isAdjustedArrayElementType(eleTy)) {
2118           if (fir::isa_char(eleTy)) {
2119             TODO(getLoc(), "assignment of character type");
2120           } else if (fir::isa_derived(eleTy)) {
2121             TODO(loc, "assignment of derived type");
2122           } else {
2123             fir::emitFatalError(loc, "array type not expected in scalar");
2124           }
2125         } else {
2126           builder->create<fir::StoreOp>(loc, iters.getElement(), val);
2127         }
2128         return exv;
2129       };
2130     }
2131     return [=, &x](IterSpace) { return asScalar(x); };
2132   }
2133 
2134   CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) {
2135     if (explicitSpaceIsActive()) {
2136       TODO(getLoc(), "genarr Symbol explicitSpace");
2137     } else {
2138       return genImplicitArrayAccess(x, components);
2139     }
2140   }
2141 
2142   CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) {
2143     TODO(getLoc(), "genarr Component");
2144   }
2145 
2146   CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) {
2147     TODO(getLoc(), "genar  ArrayRef");
2148   }
2149 
2150   CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) {
2151     TODO(getLoc(), "coarray reference");
2152   }
2153 
2154   CC genarr(const Fortran::evaluate::NamedEntity &x,
2155             ComponentPath &components) {
2156     return x.IsSymbol() ? genarr(x.GetFirstSymbol(), components)
2157                         : genarr(x.GetComponent(), components);
2158   }
2159 
2160   CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) {
2161     return std::visit([&](const auto &v) { return genarr(v, components); },
2162                       x.u);
2163   }
2164 
2165   CC genarr(const Fortran::evaluate::ComplexPart &x,
2166             ComponentPath &components) {
2167     TODO(getLoc(), "genarr ComplexPart");
2168   }
2169 
2170   CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &,
2171             ComponentPath &components) {
2172     TODO(getLoc(), "genarr StaticDataObject::Pointer");
2173   }
2174 
2175   /// Substrings (see 9.4.1)
2176   CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) {
2177     TODO(getLoc(), "genarr Substring");
2178   }
2179 
2180   /// Base case of generating an array reference,
2181   CC genarr(const ExtValue &extMemref, ComponentPath &components) {
2182     mlir::Location loc = getLoc();
2183     mlir::Value memref = fir::getBase(extMemref);
2184     mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType());
2185     assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array");
2186     mlir::Value shape = builder.createShape(loc, extMemref);
2187     mlir::Value slice;
2188     if (components.isSlice()) {
2189       TODO(loc, "genarr with Slices");
2190     }
2191     arrayOperands.push_back(ArrayOperand{memref, shape, slice});
2192     if (destShape.empty())
2193       destShape = getShape(arrayOperands.back());
2194     if (isBoxValue()) {
2195       TODO(loc, "genarr BoxValue");
2196     }
2197     if (isReferentiallyOpaque()) {
2198       TODO(loc, "genarr isReferentiallyOpaque");
2199     }
2200     auto arrLoad = builder.create<fir::ArrayLoadOp>(
2201         loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref));
2202     mlir::Value arrLd = arrLoad.getResult();
2203     if (isProjectedCopyInCopyOut()) {
2204       // Semantics are projected copy-in copy-out.
2205       // The backing store of the destination of an array expression may be
2206       // partially modified. These updates are recorded in FIR by forwarding a
2207       // continuation that generates an `array_update` Op. The destination is
2208       // always loaded at the beginning of the statement and merged at the
2209       // end.
2210       destination = arrLoad;
2211       auto lambda = ccStoreToDest.hasValue()
2212                         ? ccStoreToDest.getValue()
2213                         : defaultStoreToDestination(components.substring);
2214       return [=](IterSpace iters) -> ExtValue { return lambda(iters); };
2215     }
2216     if (isCustomCopyInCopyOut()) {
2217       TODO(loc, "isCustomCopyInCopyOut");
2218     }
2219     if (isCopyInCopyOut()) {
2220       // Semantics are copy-in copy-out.
2221       // The continuation simply forwards the result of the `array_load` Op,
2222       // which is the value of the array as it was when loaded. All data
2223       // references with rank > 0 in an array expression typically have
2224       // copy-in copy-out semantics.
2225       return [=](IterSpace) -> ExtValue { return arrLd; };
2226     }
2227     mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
2228     if (isValueAttribute()) {
2229       // Semantics are value attribute.
2230       // Here the continuation will `array_fetch` a value from an array and
2231       // then store that value in a temporary. One can thus imitate pass by
2232       // value even when the call is pass by reference.
2233       return [=](IterSpace iters) -> ExtValue {
2234         mlir::Value base;
2235         mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
2236         if (isAdjustedArrayElementType(eleTy)) {
2237           mlir::Type eleRefTy = builder.getRefType(eleTy);
2238           base = builder.create<fir::ArrayAccessOp>(
2239               loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
2240         } else {
2241           base = builder.create<fir::ArrayFetchOp>(
2242               loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
2243         }
2244         mlir::Value temp = builder.createTemporary(
2245             loc, base.getType(),
2246             llvm::ArrayRef<mlir::NamedAttribute>{
2247                 Fortran::lower::getAdaptToByRefAttr(builder)});
2248         builder.create<fir::StoreOp>(loc, base, temp);
2249         return fir::factory::arraySectionElementToExtendedValue(
2250             builder, loc, extMemref, temp, slice);
2251       };
2252     }
2253     // In the default case, the array reference forwards an `array_fetch` or
2254     // `array_access` Op in the continuation.
2255     return [=](IterSpace iters) -> ExtValue {
2256       mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
2257       if (isAdjustedArrayElementType(eleTy)) {
2258         mlir::Type eleRefTy = builder.getRefType(eleTy);
2259         mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>(
2260             loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
2261         if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
2262           llvm::SmallVector<mlir::Value> substringBounds;
2263           populateBounds(substringBounds, components.substring);
2264           if (!substringBounds.empty()) {
2265             // mlir::Value dstLen = fir::factory::genLenOfCharacter(
2266             //     builder, loc, arrLoad, iters.iterVec(), substringBounds);
2267             // fir::CharBoxValue dstChar(arrayOp, dstLen);
2268             // return fir::factory::CharacterExprHelper{builder, loc}
2269             //     .createSubstring(dstChar, substringBounds);
2270           }
2271         }
2272         return fir::factory::arraySectionElementToExtendedValue(
2273             builder, loc, extMemref, arrayOp, slice);
2274       }
2275       auto arrFetch = builder.create<fir::ArrayFetchOp>(
2276           loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
2277       return fir::factory::arraySectionElementToExtendedValue(
2278           builder, loc, extMemref, arrFetch, slice);
2279     };
2280   }
2281 
2282   /// Reduce the rank of a array to be boxed based on the slice's operands.
2283   static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) {
2284     if (slice) {
2285       auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp());
2286       assert(slOp && "expected slice op");
2287       auto seqTy = arrTy.dyn_cast<fir::SequenceType>();
2288       assert(seqTy && "expected array type");
2289       mlir::Operation::operand_range triples = slOp.getTriples();
2290       fir::SequenceType::Shape shape;
2291       // reduce the rank for each invariant dimension
2292       for (unsigned i = 1, end = triples.size(); i < end; i += 3)
2293         if (!mlir::isa_and_nonnull<fir::UndefOp>(triples[i].getDefiningOp()))
2294           shape.push_back(fir::SequenceType::getUnknownExtent());
2295       return fir::SequenceType::get(shape, seqTy.getEleTy());
2296     }
2297     // not sliced, so no change in rank
2298     return arrTy;
2299   }
2300 
2301 private:
2302   void determineShapeOfDest(const fir::ExtendedValue &lhs) {
2303     destShape = fir::factory::getExtents(builder, getLoc(), lhs);
2304   }
2305 
2306   void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) {
2307     if (!destShape.empty())
2308       return;
2309     // if (explicitSpaceIsActive() && determineShapeWithSlice(lhs))
2310     //   return;
2311     mlir::Type idxTy = builder.getIndexType();
2312     mlir::Location loc = getLoc();
2313     if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape =
2314             Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(),
2315                                                   lhs))
2316       for (Fortran::common::ConstantSubscript extent : *constantShape)
2317         destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent));
2318   }
2319 
2320   ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) {
2321     mlir::Type resTy = converter.genType(exp);
2322     return std::visit(
2323         [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); },
2324         exp.u);
2325   }
2326   ExtValue lowerArrayExpression(const ExtValue &exv) {
2327     assert(!explicitSpace);
2328     mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType());
2329     return lowerArrayExpression(genarr(exv), resTy);
2330   }
2331 
2332   void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds,
2333                       const Fortran::evaluate::Substring *substring) {
2334     if (!substring)
2335       return;
2336     bounds.push_back(fir::getBase(asScalar(substring->lower())));
2337     if (auto upper = substring->upper())
2338       bounds.push_back(fir::getBase(asScalar(*upper)));
2339   }
2340 
2341   /// Default store to destination implementation.
2342   /// This implements the default case, which is to assign the value in
2343   /// `iters.element` into the destination array, `iters.innerArgument`. Handles
2344   /// by value and by reference assignment.
2345   CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) {
2346     return [=](IterSpace iterSpace) -> ExtValue {
2347       mlir::Location loc = getLoc();
2348       mlir::Value innerArg = iterSpace.innerArgument();
2349       fir::ExtendedValue exv = iterSpace.elementExv();
2350       mlir::Type arrTy = innerArg.getType();
2351       mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec());
2352       if (isAdjustedArrayElementType(eleTy)) {
2353         TODO(loc, "isAdjustedArrayElementType");
2354       }
2355       // By value semantics. The element is being assigned by value.
2356       mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv));
2357       auto update = builder.create<fir::ArrayUpdateOp>(
2358           loc, arrTy, innerArg, ele, iterSpace.iterVec(),
2359           destination.getTypeparams());
2360       return abstractArrayExtValue(update);
2361     };
2362   }
2363 
2364   /// For an elemental array expression.
2365   ///   1. Lower the scalars and array loads.
2366   ///   2. Create the iteration space.
2367   ///   3. Create the element-by-element computation in the loop.
2368   ///   4. Return the resulting array value.
2369   /// If no destination was set in the array context, a temporary of
2370   /// \p resultTy will be created to hold the evaluated expression.
2371   /// Otherwise, \p resultTy is ignored and the expression is evaluated
2372   /// in the destination. \p f is a continuation built from an
2373   /// evaluate::Expr or an ExtendedValue.
2374   ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) {
2375     mlir::Location loc = getLoc();
2376     auto [iterSpace, insPt] = genIterSpace(resultTy);
2377     auto exv = f(iterSpace);
2378     iterSpace.setElement(std::move(exv));
2379     auto lambda = ccStoreToDest.hasValue()
2380                       ? ccStoreToDest.getValue()
2381                       : defaultStoreToDestination(/*substring=*/nullptr);
2382     mlir::Value updVal = fir::getBase(lambda(iterSpace));
2383     finalizeElementCtx();
2384     builder.create<fir::ResultOp>(loc, updVal);
2385     builder.restoreInsertionPoint(insPt);
2386     return abstractArrayExtValue(iterSpace.outerResult());
2387   }
2388 
2389   /// Get the shape from an ArrayOperand. The shape of the array is adjusted if
2390   /// the array was sliced.
2391   llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) {
2392     // if (array.slice)
2393     //   return computeSliceShape(array.slice);
2394     if (array.memref.getType().isa<fir::BoxType>())
2395       return fir::factory::readExtents(builder, getLoc(),
2396                                        fir::BoxValue{array.memref});
2397     std::vector<mlir::Value, std::allocator<mlir::Value>> extents =
2398         fir::factory::getExtents(array.shape);
2399     return {extents.begin(), extents.end()};
2400   }
2401 
2402   /// Get the shape from an ArrayLoad.
2403   llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) {
2404     return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(),
2405                                  arrayLoad.getSlice()});
2406   }
2407 
2408   /// Returns the first array operand that may not be absent. If all
2409   /// array operands may be absent, return the first one.
2410   const ArrayOperand &getInducingShapeArrayOperand() const {
2411     assert(!arrayOperands.empty());
2412     for (const ArrayOperand &op : arrayOperands)
2413       if (!op.mayBeAbsent)
2414         return op;
2415     // If all arrays operand appears in optional position, then none of them
2416     // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the
2417     // first operands.
2418     // TODO: There is an opportunity to add a runtime check here that
2419     // this array is present as required.
2420     return arrayOperands[0];
2421   }
2422 
2423   /// Generate the shape of the iteration space over the array expression. The
2424   /// iteration space may be implicit, explicit, or both. If it is implied it is
2425   /// based on the destination and operand array loads, or an optional
2426   /// Fortran::evaluate::Shape from the front end. If the shape is explicit,
2427   /// this returns any implicit shape component, if it exists.
2428   llvm::SmallVector<mlir::Value> genIterationShape() {
2429     // Use the precomputed destination shape.
2430     if (!destShape.empty())
2431       return destShape;
2432     // Otherwise, use the destination's shape.
2433     if (destination)
2434       return getShape(destination);
2435     // Otherwise, use the first ArrayLoad operand shape.
2436     if (!arrayOperands.empty())
2437       return getShape(getInducingShapeArrayOperand());
2438     fir::emitFatalError(getLoc(),
2439                         "failed to compute the array expression shape");
2440   }
2441 
2442   bool explicitSpaceIsActive() const {
2443     return explicitSpace && explicitSpace->isActive();
2444   }
2445 
2446   bool implicitSpaceHasMasks() const {
2447     return implicitSpace && !implicitSpace->empty();
2448   }
2449 
2450   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
2451                              Fortran::lower::StatementContext &stmtCtx,
2452                              Fortran::lower::SymMap &symMap)
2453       : converter{converter}, builder{converter.getFirOpBuilder()},
2454         stmtCtx{stmtCtx}, symMap{symMap} {}
2455 
2456   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
2457                              Fortran::lower::StatementContext &stmtCtx,
2458                              Fortran::lower::SymMap &symMap,
2459                              ConstituentSemantics sem)
2460       : converter{converter}, builder{converter.getFirOpBuilder()},
2461         stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {}
2462 
2463   explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
2464                              Fortran::lower::StatementContext &stmtCtx,
2465                              Fortran::lower::SymMap &symMap,
2466                              ConstituentSemantics sem,
2467                              Fortran::lower::ExplicitIterSpace *expSpace,
2468                              Fortran::lower::ImplicitIterSpace *impSpace)
2469       : converter{converter}, builder{converter.getFirOpBuilder()},
2470         stmtCtx{stmtCtx}, symMap{symMap},
2471         explicitSpace(expSpace->isActive() ? expSpace : nullptr),
2472         implicitSpace(impSpace->empty() ? nullptr : impSpace), semant{sem} {
2473     // Generate any mask expressions, as necessary. This is the compute step
2474     // that creates the effective masks. See 10.2.3.2 in particular.
2475     // genMasks();
2476   }
2477 
2478   mlir::Location getLoc() { return converter.getCurrentLocation(); }
2479 
2480   /// Array appears in a lhs context such that it is assigned after the rhs is
2481   /// fully evaluated.
2482   inline bool isCopyInCopyOut() {
2483     return semant == ConstituentSemantics::CopyInCopyOut;
2484   }
2485 
2486   /// Array appears in a lhs (or temp) context such that a projected,
2487   /// discontiguous subspace of the array is assigned after the rhs is fully
2488   /// evaluated. That is, the rhs array value is merged into a section of the
2489   /// lhs array.
2490   inline bool isProjectedCopyInCopyOut() {
2491     return semant == ConstituentSemantics::ProjectedCopyInCopyOut;
2492   }
2493 
2494   inline bool isCustomCopyInCopyOut() {
2495     return semant == ConstituentSemantics::CustomCopyInCopyOut;
2496   }
2497 
2498   /// Array appears in a context where it must be boxed.
2499   inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; }
2500 
2501   /// Array appears in a context where differences in the memory reference can
2502   /// be observable in the computational results. For example, an array
2503   /// element is passed to an impure procedure.
2504   inline bool isReferentiallyOpaque() {
2505     return semant == ConstituentSemantics::RefOpaque;
2506   }
2507 
2508   /// Array appears in a context where it is passed as a VALUE argument.
2509   inline bool isValueAttribute() {
2510     return semant == ConstituentSemantics::ByValueArg;
2511   }
2512 
2513   /// Can the loops over the expression be unordered?
2514   inline bool isUnordered() const { return unordered; }
2515 
2516   void setUnordered(bool b) { unordered = b; }
2517 
2518   Fortran::lower::AbstractConverter &converter;
2519   fir::FirOpBuilder &builder;
2520   Fortran::lower::StatementContext &stmtCtx;
2521   bool elementCtx = false;
2522   Fortran::lower::SymMap &symMap;
2523   /// The continuation to generate code to update the destination.
2524   llvm::Optional<CC> ccStoreToDest;
2525   llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude;
2526   llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>>
2527       ccLoadDest;
2528   /// The destination is the loaded array into which the results will be
2529   /// merged.
2530   fir::ArrayLoadOp destination;
2531   /// The shape of the destination.
2532   llvm::SmallVector<mlir::Value> destShape;
2533   /// List of arrays in the expression that have been loaded.
2534   llvm::SmallVector<ArrayOperand> arrayOperands;
2535   /// If there is a user-defined iteration space, explicitShape will hold the
2536   /// information from the front end.
2537   Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr;
2538   Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr;
2539   ConstituentSemantics semant = ConstituentSemantics::RefTransparent;
2540   // Can the array expression be evaluated in any order?
2541   // Will be set to false if any of the expression parts prevent this.
2542   bool unordered = true;
2543 };
2544 } // namespace
2545 
2546 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
2547     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
2548     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
2549     Fortran::lower::StatementContext &stmtCtx) {
2550   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
2551   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr);
2552 }
2553 
2554 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
2555     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
2556     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
2557     Fortran::lower::StatementContext &stmtCtx) {
2558   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
2559   return ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(expr);
2560 }
2561 
2562 fir::MutableBoxValue Fortran::lower::createMutableBox(
2563     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
2564     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
2565   // MutableBox lowering StatementContext does not need to be propagated
2566   // to the caller because the result value is a variable, not a temporary
2567   // expression. The StatementContext clean-up can occur before using the
2568   // resulting MutableBoxValue. Variables of all other types are handled in the
2569   // bridge.
2570   Fortran::lower::StatementContext dummyStmtCtx;
2571   return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx}
2572       .genMutableBoxValue(expr);
2573 }
2574 
2575 mlir::Value Fortran::lower::createSubroutineCall(
2576     AbstractConverter &converter, const evaluate::ProcedureRef &call,
2577     SymMap &symMap, StatementContext &stmtCtx) {
2578   mlir::Location loc = converter.getCurrentLocation();
2579 
2580   // Simple subroutine call, with potential alternate return.
2581   auto res = Fortran::lower::createSomeExtendedExpression(
2582       loc, converter, toEvExpr(call), symMap, stmtCtx);
2583   return fir::getBase(res);
2584 }
2585 
2586 void Fortran::lower::createSomeArrayAssignment(
2587     Fortran::lower::AbstractConverter &converter,
2588     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
2589     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
2590   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
2591              rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
2592   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
2593 }
2594 
2595 void Fortran::lower::createSomeArrayAssignment(
2596     Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
2597     const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap,
2598     Fortran::lower::StatementContext &stmtCtx) {
2599   LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
2600              llvm::dbgs() << "assign expression: " << rhs << '\n';);
2601   ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
2602 }
2603 
2604 void Fortran::lower::createAllocatableArrayAssignment(
2605     Fortran::lower::AbstractConverter &converter,
2606     const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
2607     Fortran::lower::ExplicitIterSpace &explicitSpace,
2608     Fortran::lower::ImplicitIterSpace &implicitSpace,
2609     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
2610   LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n';
2611              rhs.AsFortran(llvm::dbgs() << "assign expression: ")
2612              << " given the explicit iteration space:\n"
2613              << explicitSpace << "\n and implied mask conditions:\n"
2614              << implicitSpace << '\n';);
2615   ArrayExprLowering::lowerAllocatableArrayAssignment(
2616       converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
2617 }
2618