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/real.h"
16 #include "flang/Evaluate/traverse.h"
17 #include "flang/Lower/AbstractConverter.h"
18 #include "flang/Lower/IntrinsicCall.h"
19 #include "flang/Lower/SymbolMap.h"
20 #include "flang/Lower/Todo.h"
21 #include "flang/Semantics/expression.h"
22 #include "flang/Semantics/symbol.h"
23 #include "flang/Semantics/tools.h"
24 #include "flang/Semantics/type.h"
25 #include "mlir/Dialect/StandardOps/IR/Ops.h"
26 #include "llvm/Support/Debug.h"
27 
28 #define DEBUG_TYPE "flang-lower-expr"
29 
30 //===----------------------------------------------------------------------===//
31 // The composition and structure of Fortran::evaluate::Expr is defined in
32 // the various header files in include/flang/Evaluate. You are referred
33 // there for more information on these data structures. Generally speaking,
34 // these data structures are a strongly typed family of abstract data types
35 // that, composed as trees, describe the syntax of Fortran expressions.
36 //
37 // This part of the bridge can traverse these tree structures and lower them
38 // to the correct FIR representation in SSA form.
39 //===----------------------------------------------------------------------===//
40 
41 /// Place \p exv in memory if it is not already a memory reference. If
42 /// \p forceValueType is provided, the value is first casted to the provided
43 /// type before being stored (this is mainly intended for logicals whose value
44 /// may be `i1` but needed to be stored as Fortran logicals).
45 static fir::ExtendedValue
46 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
47                          const fir::ExtendedValue &exv,
48                          mlir::Type storageType) {
49   mlir::Value valBase = fir::getBase(exv);
50   if (fir::conformsWithPassByRef(valBase.getType()))
51     return exv;
52 
53   assert(!fir::hasDynamicSize(storageType) &&
54          "only expect statically sized scalars to be by value");
55 
56   // Since `a` is not itself a valid referent, determine its value and
57   // create a temporary location at the beginning of the function for
58   // referencing.
59   mlir::Value val = builder.createConvert(loc, storageType, valBase);
60   mlir::Value temp = builder.createTemporary(
61       loc, storageType,
62       llvm::ArrayRef<mlir::NamedAttribute>{
63           Fortran::lower::getAdaptToByRefAttr(builder)});
64   builder.create<fir::StoreOp>(loc, val, temp);
65   return fir::substBase(exv, temp);
66 }
67 
68 /// Generate a load of a value from an address. Beware that this will lose
69 /// any dynamic type information for polymorphic entities (note that unlimited
70 /// polymorphic cannot be loaded and must not be provided here).
71 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
72                                   mlir::Location loc,
73                                   const fir::ExtendedValue &addr) {
74   return addr.match(
75       [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
76       [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
77         if (fir::unwrapRefType(fir::getBase(v).getType())
78                 .isa<fir::RecordType>())
79           return v;
80         return builder.create<fir::LoadOp>(loc, fir::getBase(v));
81       },
82       [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
83         TODO(loc, "genLoad for MutableBoxValue");
84       },
85       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
86         TODO(loc, "genLoad for BoxValue");
87       },
88       [&](const auto &) -> fir::ExtendedValue {
89         fir::emitFatalError(
90             loc, "attempting to load whole array or procedure address");
91       });
92 }
93 
94 /// Is this a call to an elemental procedure with at least one array argument?
95 static bool
96 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
97   if (procRef.IsElemental())
98     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
99          procRef.arguments())
100       if (arg && arg->Rank() != 0)
101         return true;
102   return false;
103 }
104 namespace {
105 
106 /// Lowering of Fortran::evaluate::Expr<T> expressions
107 class ScalarExprLowering {
108 public:
109   using ExtValue = fir::ExtendedValue;
110 
111   explicit ScalarExprLowering(mlir::Location loc,
112                               Fortran::lower::AbstractConverter &converter,
113                               Fortran::lower::SymMap &symMap)
114       : location{loc}, converter{converter},
115         builder{converter.getFirOpBuilder()}, symMap{symMap} {}
116 
117   mlir::Location getLoc() { return location; }
118 
119   template <typename A>
120   mlir::Value genunbox(const A &expr) {
121     ExtValue e = genval(expr);
122     if (const fir::UnboxedValue *r = e.getUnboxed())
123       return *r;
124     fir::emitFatalError(getLoc(), "unboxed expression expected");
125   }
126 
127   /// Generate an integral constant of `value`
128   template <int KIND>
129   mlir::Value genIntegerConstant(mlir::MLIRContext *context,
130                                  std::int64_t value) {
131     mlir::Type type =
132         converter.genType(Fortran::common::TypeCategory::Integer, KIND);
133     return builder.createIntegerConstant(getLoc(), type, value);
134   }
135 
136   /// Generate a logical/boolean constant of `value`
137   mlir::Value genBoolConstant(bool value) {
138     return builder.createBool(getLoc(), value);
139   }
140 
141   /// Returns a reference to a symbol or its box/boxChar descriptor if it has
142   /// one.
143   ExtValue gen(Fortran::semantics::SymbolRef sym) {
144     if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
145       return val.match([&val](auto &) { return val.toExtendedValue(); });
146     LLVM_DEBUG(llvm::dbgs()
147                << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
148     fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
149   }
150 
151   ExtValue genLoad(const ExtValue &exv) {
152     return ::genLoad(builder, getLoc(), exv);
153   }
154 
155   ExtValue genval(Fortran::semantics::SymbolRef sym) {
156     ExtValue var = gen(sym);
157     if (const fir::UnboxedValue *s = var.getUnboxed())
158       if (fir::isReferenceLike(s->getType()))
159         return genLoad(*s);
160     return var;
161   }
162 
163   ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
164     TODO(getLoc(), "genval BOZ");
165   }
166 
167   /// Return indirection to function designated in ProcedureDesignator.
168   /// The type of the function indirection is not guaranteed to match the one
169   /// of the ProcedureDesignator due to Fortran implicit typing rules.
170   ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
171     TODO(getLoc(), "genval ProcedureDesignator");
172   }
173 
174   ExtValue genval(const Fortran::evaluate::NullPointer &) {
175     TODO(getLoc(), "genval NullPointer");
176   }
177 
178   ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
179     TODO(getLoc(), "genval StructureConstructor");
180   }
181 
182   /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
183   ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
184     TODO(getLoc(), "genval ImpliedDoIndex");
185   }
186 
187   ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
188     TODO(getLoc(), "genval DescriptorInquiry");
189   }
190 
191   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
192     TODO(getLoc(), "genval TypeParamInquiry");
193   }
194 
195   template <int KIND>
196   ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
197     TODO(getLoc(), "genval ComplexComponent");
198   }
199 
200   template <int KIND>
201   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
202                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
203     mlir::Value input = genunbox(op.left());
204     // Like LLVM, integer negation is the binary op "0 - value"
205     mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
206     return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
207   }
208 
209   template <int KIND>
210   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
211                       Fortran::common::TypeCategory::Real, KIND>> &op) {
212     return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
213   }
214   template <int KIND>
215   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
216                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
217     return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
218   }
219 
220   template <typename OpTy>
221   mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
222     assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
223     mlir::Value lhs = fir::getBase(left);
224     mlir::Value rhs = fir::getBase(right);
225     assert(lhs.getType() == rhs.getType() && "types must be the same");
226     return builder.create<OpTy>(getLoc(), lhs, rhs);
227   }
228 
229   template <typename OpTy, typename A>
230   mlir::Value createBinaryOp(const A &ex) {
231     ExtValue left = genval(ex.left());
232     return createBinaryOp<OpTy>(left, genval(ex.right()));
233   }
234 
235 #undef GENBIN
236 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
237   template <int KIND>                                                          \
238   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
239                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
240     return createBinaryOp<GenBinFirOp>(x);                                     \
241   }
242 
243   GENBIN(Add, Integer, mlir::arith::AddIOp)
244   GENBIN(Add, Real, mlir::arith::AddFOp)
245   GENBIN(Add, Complex, fir::AddcOp)
246   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
247   GENBIN(Subtract, Real, mlir::arith::SubFOp)
248   GENBIN(Subtract, Complex, fir::SubcOp)
249   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
250   GENBIN(Multiply, Real, mlir::arith::MulFOp)
251   GENBIN(Multiply, Complex, fir::MulcOp)
252   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
253   GENBIN(Divide, Real, mlir::arith::DivFOp)
254   GENBIN(Divide, Complex, fir::DivcOp)
255 
256   template <Fortran::common::TypeCategory TC, int KIND>
257   ExtValue genval(
258       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
259     TODO(getLoc(), "genval Power");
260   }
261 
262   template <Fortran::common::TypeCategory TC, int KIND>
263   ExtValue genval(
264       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
265           &op) {
266     TODO(getLoc(), "genval RealToInt");
267   }
268 
269   template <int KIND>
270   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
271     TODO(getLoc(), "genval ComplexConstructor");
272   }
273 
274   template <int KIND>
275   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
276     TODO(getLoc(), "genval Concat<KIND>");
277   }
278 
279   /// MIN and MAX operations
280   template <Fortran::common::TypeCategory TC, int KIND>
281   ExtValue
282   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
283              &op) {
284     TODO(getLoc(), "genval Extremum<TC, KIND>");
285   }
286 
287   template <int KIND>
288   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
289     TODO(getLoc(), "genval SetLength<KIND>");
290   }
291 
292   template <int KIND>
293   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
294                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
295     TODO(getLoc(), "genval integer comparison");
296   }
297   template <int KIND>
298   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
299                       Fortran::common::TypeCategory::Real, KIND>> &op) {
300     TODO(getLoc(), "genval real comparison");
301   }
302   template <int KIND>
303   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
304                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
305     TODO(getLoc(), "genval complex comparison");
306   }
307   template <int KIND>
308   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
309                       Fortran::common::TypeCategory::Character, KIND>> &op) {
310     TODO(getLoc(), "genval char comparison");
311   }
312 
313   ExtValue
314   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
315     TODO(getLoc(), "genval comparison");
316   }
317 
318   template <Fortran::common::TypeCategory TC1, int KIND,
319             Fortran::common::TypeCategory TC2>
320   ExtValue
321   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
322                                           TC2> &convert) {
323     mlir::Type ty = converter.genType(TC1, KIND);
324     mlir::Value operand = genunbox(convert.left());
325     return builder.convertWithSemantics(getLoc(), ty, operand);
326   }
327 
328   template <typename A>
329   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
330     TODO(getLoc(), "genval parentheses<A>");
331   }
332 
333   template <int KIND>
334   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
335     TODO(getLoc(), "genval Not<KIND>");
336   }
337 
338   template <int KIND>
339   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
340     TODO(getLoc(), "genval LogicalOperation<KIND>");
341   }
342 
343   /// Convert a scalar literal constant to IR.
344   template <Fortran::common::TypeCategory TC, int KIND>
345   ExtValue genScalarLit(
346       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
347           &value) {
348     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
349       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
350     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
351       return genBoolConstant(value.IsTrue());
352     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
353       TODO(getLoc(), "genval real constant");
354     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
355       TODO(getLoc(), "genval complex constant");
356     } else /*constexpr*/ {
357       llvm_unreachable("unhandled constant");
358     }
359   }
360 
361   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
362   ExtValue
363   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
364                         Fortran::common::TypeCategory::Character, 1>> &value,
365                     int64_t len) {
366     assert(value.size() == static_cast<std::uint64_t>(len) &&
367            "value.size() doesn't match with len");
368     return fir::factory::createStringLiteral(builder, getLoc(), value);
369   }
370 
371   template <Fortran::common::TypeCategory TC, int KIND>
372   ExtValue
373   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
374              &con) {
375     if (con.Rank() > 0)
376       TODO(getLoc(), "genval array constant");
377     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
378         opt = con.GetScalarValue();
379     assert(opt.has_value() && "constant has no value");
380     if constexpr (TC == Fortran::common::TypeCategory::Character) {
381       if constexpr (KIND == 1)
382         return genAsciiScalarLit(opt.value(), con.LEN());
383       TODO(getLoc(), "genval for Character with KIND != 1");
384     } else {
385       return genScalarLit<TC, KIND>(opt.value());
386     }
387   }
388 
389   fir::ExtendedValue genval(
390       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
391     TODO(getLoc(), "genval constant derived");
392   }
393 
394   template <typename A>
395   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
396     TODO(getLoc(), "genval ArrayConstructor<A>");
397   }
398 
399   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
400     TODO(getLoc(), "gen ComplexPart");
401   }
402   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
403     TODO(getLoc(), "genval ComplexPart");
404   }
405 
406   ExtValue gen(const Fortran::evaluate::Substring &s) {
407     TODO(getLoc(), "gen Substring");
408   }
409   ExtValue genval(const Fortran::evaluate::Substring &ss) {
410     TODO(getLoc(), "genval Substring");
411   }
412 
413   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
414     TODO(getLoc(), "genval Subscript");
415   }
416 
417   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
418     TODO(getLoc(), "gen DataRef");
419   }
420   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
421     TODO(getLoc(), "genval DataRef");
422   }
423 
424   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
425     TODO(getLoc(), "gen Component");
426   }
427   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
428     TODO(getLoc(), "genval Component");
429   }
430 
431   ExtValue genval(const Fortran::semantics::Bound &bound) {
432     TODO(getLoc(), "genval Bound");
433   }
434 
435   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
436     TODO(getLoc(), "gen ArrayRef");
437   }
438   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
439     TODO(getLoc(), "genval ArrayRef");
440   }
441 
442   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
443     TODO(getLoc(), "gen CoarrayRef");
444   }
445   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
446     TODO(getLoc(), "genval CoarrayRef");
447   }
448 
449   template <typename A>
450   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
451     return std::visit([&](const auto &x) { return gen(x); }, des.u);
452   }
453   template <typename A>
454   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
455     return std::visit([&](const auto &x) { return genval(x); }, des.u);
456   }
457 
458   mlir::Type genType(const Fortran::evaluate::DynamicType &dt) {
459     if (dt.category() != Fortran::common::TypeCategory::Derived)
460       return converter.genType(dt.category(), dt.kind());
461     TODO(getLoc(), "genType Derived Type");
462   }
463 
464   /// Lower a function reference
465   template <typename A>
466   ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) {
467     if (!funcRef.GetType().has_value())
468       fir::emitFatalError(getLoc(), "internal: a function must have a type");
469     mlir::Type resTy = genType(*funcRef.GetType());
470     return genProcedureRef(funcRef, {resTy});
471   }
472 
473   /// Lower function call `funcRef` and return a reference to the resultant
474   /// value. This is required for lowering expressions such as `f1(f2(v))`.
475   template <typename A>
476   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
477     TODO(getLoc(), "gen FunctionRef<A>");
478   }
479 
480   template <typename A>
481   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
482     ExtValue result = genFunctionRef(funcRef);
483     if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
484       return genLoad(result);
485     return result;
486   }
487 
488   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
489     TODO(getLoc(), "genval ProcedureRef");
490   }
491 
492   /// Generate a call to an intrinsic function.
493   ExtValue
494   genIntrinsicRef(const Fortran::evaluate::ProcedureRef &procRef,
495                   const Fortran::evaluate::SpecificIntrinsic &intrinsic,
496                   llvm::Optional<mlir::Type> resultType) {
497     llvm::SmallVector<ExtValue> operands;
498 
499     llvm::StringRef name = intrinsic.name;
500     mlir::Location loc = getLoc();
501 
502     const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
503         Fortran::lower::getIntrinsicArgumentLowering(name);
504     for (const auto &[arg, dummy] :
505          llvm::zip(procRef.arguments(),
506                    intrinsic.characteristics.value().dummyArguments)) {
507       auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
508       if (!expr) {
509         // Absent optional.
510         operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
511         continue;
512       }
513       if (!argLowering) {
514         // No argument lowering instruction, lower by value.
515         operands.emplace_back(genval(*expr));
516         continue;
517       }
518       // Ad-hoc argument lowering handling.
519       Fortran::lower::ArgLoweringRule argRules =
520           Fortran::lower::lowerIntrinsicArgumentAs(loc, *argLowering,
521                                                    dummy.name);
522       switch (argRules.lowerAs) {
523       case Fortran::lower::LowerIntrinsicArgAs::Value:
524         operands.emplace_back(genval(*expr));
525         continue;
526       case Fortran::lower::LowerIntrinsicArgAs::Addr:
527         TODO(getLoc(), "argument lowering for Addr");
528         continue;
529       case Fortran::lower::LowerIntrinsicArgAs::Box:
530         TODO(getLoc(), "argument lowering for Box");
531         continue;
532       case Fortran::lower::LowerIntrinsicArgAs::Inquired:
533         TODO(getLoc(), "argument lowering for Inquired");
534         continue;
535       }
536       llvm_unreachable("bad switch");
537     }
538     // Let the intrinsic library lower the intrinsic procedure call
539     return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
540                                             operands);
541   }
542 
543   template <typename A>
544   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
545     if (isScalar(x))
546       return std::visit([&](const auto &e) { return genval(e); }, x.u);
547     TODO(getLoc(), "genval Expr<A> arrays");
548   }
549 
550   /// Lower a non-elemental procedure reference.
551   // TODO: Handle read allocatable and pointer results.
552   ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
553                            llvm::Optional<mlir::Type> resultType) {
554     ExtValue res = genRawProcedureRef(procRef, resultType);
555     return res;
556   }
557 
558   /// Lower a non-elemental procedure reference.
559   ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
560                               llvm::Optional<mlir::Type> resultType) {
561     mlir::Location loc = getLoc();
562     if (isElementalProcWithArrayArgs(procRef))
563       fir::emitFatalError(loc, "trying to lower elemental procedure with array "
564                                "arguments as normal procedure");
565     if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
566             procRef.proc().GetSpecificIntrinsic())
567       return genIntrinsicRef(procRef, *intrinsic, resultType);
568 
569     return {};
570   }
571 
572   /// Helper to detect Transformational function reference.
573   template <typename T>
574   bool isTransformationalRef(const T &) {
575     return false;
576   }
577   template <typename T>
578   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
579     return !funcRef.IsElemental() && funcRef.Rank();
580   }
581   template <typename T>
582   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
583     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
584                       expr.u);
585   }
586 
587   template <typename A>
588   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
589     // Whole array symbols or components, and results of transformational
590     // functions already have a storage and the scalar expression lowering path
591     // is used to not create a new temporary storage.
592     if (isScalar(x) ||
593         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
594         isTransformationalRef(x))
595       return std::visit([&](const auto &e) { return genref(e); }, x.u);
596     TODO(getLoc(), "gen Expr non-scalar");
597   }
598 
599   template <typename A>
600   bool isScalar(const A &x) {
601     return x.Rank() == 0;
602   }
603 
604   template <int KIND>
605   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
606                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
607     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
608   }
609 
610   using RefSet =
611       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
612                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
613                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
614                  Fortran::semantics::SymbolRef>;
615   template <typename A>
616   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
617 
618   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
619   ExtValue genref(const A &a) {
620     return gen(a);
621   }
622   template <typename A>
623   ExtValue genref(const A &a) {
624     mlir::Type storageType = converter.genType(toEvExpr(a));
625     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
626   }
627 
628   template <typename A, template <typename> typename T,
629             typename B = std::decay_t<T<A>>,
630             std::enable_if_t<
631                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
632                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
633                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
634                 bool> = true>
635   ExtValue genref(const T<A> &x) {
636     return gen(x);
637   }
638 
639 private:
640   mlir::Location location;
641   Fortran::lower::AbstractConverter &converter;
642   fir::FirOpBuilder &builder;
643   Fortran::lower::SymMap &symMap;
644 };
645 } // namespace
646 
647 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
648     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
649     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
650   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
651   return ScalarExprLowering{loc, converter, symMap}.genval(expr);
652 }
653 
654 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
655     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
656     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
657   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
658   return ScalarExprLowering{loc, converter, symMap}.gen(expr);
659 }
660