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