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