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