xref: /llvm-project/flang/include/flang/Optimizer/Builder/BoxValue.h (revision 6f8ef5ad2f35321257adbe353f86027bf5209023)
1 //===-- BoxValue.h -- internal box values -----------------------*- C++ -*-===//
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 #ifndef FORTRAN_OPTIMIZER_BUILDER_BOXVALUE_H
14 #define FORTRAN_OPTIMIZER_BUILDER_BOXVALUE_H
15 
16 #include "flang/Optimizer/Dialect/FIRType.h"
17 #include "flang/Optimizer/Support/FatalError.h"
18 #include "flang/Optimizer/Support/Matcher.h"
19 #include "mlir/IR/OperationSupport.h"
20 #include "mlir/IR/Value.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/Support/Compiler.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include <utility>
25 
26 namespace fir {
27 class FirOpBuilder;
28 class ArrayLoadOp;
29 
30 class ArrayBoxValue;
31 class BoxValue;
32 class CharBoxValue;
33 class CharArrayBoxValue;
34 class MutableBoxValue;
35 class PolymorphicValue;
36 class ProcBoxValue;
37 
38 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CharBoxValue &);
39 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ArrayBoxValue &);
40 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CharArrayBoxValue &);
41 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ProcBoxValue &);
42 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const MutableBoxValue &);
43 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const BoxValue &);
44 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const PolymorphicValue &);
45 
46 //===----------------------------------------------------------------------===//
47 //
48 // Boxed values
49 //
50 // Define a set of containers used internally by the lowering bridge to keep
51 // track of extended values associated with a Fortran subexpression. These
52 // associations are maintained during the construction of FIR.
53 //
54 //===----------------------------------------------------------------------===//
55 
56 /// Most expressions of intrinsic type can be passed unboxed. Their properties
57 /// are known statically.
58 using UnboxedValue = mlir::Value;
59 
60 /// Abstract base class.
61 class AbstractBox {
62 public:
63   AbstractBox() = delete;
64   AbstractBox(mlir::Value addr) : addr{addr} {}
65 
66   /// An abstract box most often contains a memory reference to a value. Despite
67   /// the name here, it is possible that `addr` is a scalar value that is not a
68   /// memory reference.
69   mlir::Value getAddr() const { return addr; }
70 
71 protected:
72   mlir::Value addr;
73 };
74 
75 /// Expressions of CHARACTER type have an associated, possibly dynamic LEN
76 /// value.
77 class CharBoxValue : public AbstractBox {
78 public:
79   CharBoxValue(mlir::Value addr, mlir::Value len)
80       : AbstractBox{addr}, len{len} {
81     if (addr && mlir::isa<fir::BoxCharType>(addr.getType()))
82       fir::emitFatalError(addr.getLoc(),
83                           "BoxChar should not be in CharBoxValue");
84   }
85 
86   CharBoxValue clone(mlir::Value newBase) const { return {newBase, len}; }
87 
88   /// Convenience alias to get the memory reference to the buffer.
89   mlir::Value getBuffer() const { return getAddr(); }
90 
91   mlir::Value getLen() const { return len; }
92 
93   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
94                                        const CharBoxValue &);
95   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this; }
96 
97 protected:
98   mlir::Value len;
99 };
100 
101 /// Polymorphic value associated with a dynamic type descriptor.
102 class PolymorphicValue : public AbstractBox {
103 public:
104   PolymorphicValue(mlir::Value addr, mlir::Value sourceBox)
105       : AbstractBox{addr}, sourceBox{sourceBox} {}
106 
107   PolymorphicValue clone(mlir::Value newBase) const {
108     return {newBase, sourceBox};
109   }
110 
111   mlir::Value getSourceBox() const { return sourceBox; }
112 
113   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
114                                        const PolymorphicValue &);
115   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this; }
116 
117 protected:
118   mlir::Value sourceBox;
119 };
120 
121 /// Abstract base class.
122 /// Expressions of type array have at minimum a shape. These expressions may
123 /// have lbound attributes (dynamic values) that affect the interpretation of
124 /// indexing expressions.
125 class AbstractArrayBox {
126 public:
127   AbstractArrayBox() = default;
128   AbstractArrayBox(llvm::ArrayRef<mlir::Value> extents,
129                    llvm::ArrayRef<mlir::Value> lbounds)
130       : extents{extents}, lbounds{lbounds} {}
131 
132   // Every array has extents that describe its shape.
133   const llvm::SmallVectorImpl<mlir::Value> &getExtents() const {
134     return extents;
135   }
136 
137   // An array expression may have user-defined lower bound values.
138   // If this vector is empty, the default in all dimensions in `1`.
139   const llvm::SmallVectorImpl<mlir::Value> &getLBounds() const {
140     return lbounds;
141   }
142 
143   bool lboundsAllOne() const { return lbounds.empty(); }
144   std::size_t rank() const { return extents.size(); }
145 
146 protected:
147   llvm::SmallVector<mlir::Value, 4> extents;
148   llvm::SmallVector<mlir::Value, 4> lbounds;
149 };
150 
151 /// Expressions with rank > 0 have extents. They may also have lbounds that are
152 /// not 1.
153 class ArrayBoxValue : public PolymorphicValue, public AbstractArrayBox {
154 public:
155   ArrayBoxValue(mlir::Value addr, llvm::ArrayRef<mlir::Value> extents,
156                 llvm::ArrayRef<mlir::Value> lbounds = {},
157                 mlir::Value sourceBox = {})
158       : PolymorphicValue{addr, sourceBox}, AbstractArrayBox{extents, lbounds} {}
159 
160   ArrayBoxValue clone(mlir::Value newBase) const {
161     return {newBase, extents, lbounds};
162   }
163 
164   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
165                                        const ArrayBoxValue &);
166   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this; }
167 };
168 
169 /// Expressions of type CHARACTER and with rank > 0.
170 class CharArrayBoxValue : public CharBoxValue, public AbstractArrayBox {
171 public:
172   CharArrayBoxValue(mlir::Value addr, mlir::Value len,
173                     llvm::ArrayRef<mlir::Value> extents,
174                     llvm::ArrayRef<mlir::Value> lbounds = {})
175       : CharBoxValue{addr, len}, AbstractArrayBox{extents, lbounds} {}
176 
177   CharArrayBoxValue clone(mlir::Value newBase) const {
178     return {newBase, len, extents, lbounds};
179   }
180 
181   CharBoxValue cloneElement(mlir::Value newBase) const {
182     return {newBase, len};
183   }
184 
185   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
186                                        const CharArrayBoxValue &);
187   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this; }
188 };
189 
190 /// Expressions that are procedure POINTERs may need a set of references to
191 /// variables in the host scope.
192 class ProcBoxValue : public AbstractBox {
193 public:
194   ProcBoxValue(mlir::Value addr, mlir::Value context)
195       : AbstractBox{addr}, hostContext{context} {}
196 
197   ProcBoxValue clone(mlir::Value newBase) const {
198     return {newBase, hostContext};
199   }
200 
201   mlir::Value getHostContext() const { return hostContext; }
202 
203   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
204                                        const ProcBoxValue &);
205   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this; }
206 
207 protected:
208   mlir::Value hostContext;
209 };
210 
211 /// Base class for values associated to a fir.box or fir.ref<fir.box>.
212 class AbstractIrBox : public AbstractBox, public AbstractArrayBox {
213 public:
214   AbstractIrBox(mlir::Value addr) : AbstractBox{addr} {}
215   AbstractIrBox(mlir::Value addr, llvm::ArrayRef<mlir::Value> lbounds,
216                 llvm::ArrayRef<mlir::Value> extents)
217       : AbstractBox{addr}, AbstractArrayBox(extents, lbounds) {}
218   /// Get the fir.box<type> part of the address type.
219   fir::BaseBoxType getBoxTy() const {
220     auto type = getAddr().getType();
221     if (auto pointedTy = fir::dyn_cast_ptrEleTy(type))
222       type = pointedTy;
223     return mlir::cast<fir::BaseBoxType>(type);
224   }
225   /// Return the part of the address type after memory and box types. That is
226   /// the element type, maybe wrapped in a fir.array type.
227   mlir::Type getBaseTy() const {
228     return fir::dyn_cast_ptrOrBoxEleTy(getBoxTy());
229   }
230 
231   /// Return the memory type of the data address inside the box:
232   /// - for fir.box<fir.ptr<T>>, return fir.ptr<T>
233   /// - for fir.box<fir.heap<T>>, return fir.heap<T>
234   /// - for fir.box<T>, return fir.ref<T>
235   mlir::Type getMemTy() const {
236     auto ty = getBoxTy().getEleTy();
237     if (fir::isa_ref_type(ty))
238       return ty;
239     return fir::ReferenceType::get(ty);
240   }
241 
242   /// Get the scalar type related to the described entity
243   mlir::Type getEleTy() const {
244     auto type = getBaseTy();
245     if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(type))
246       return seqTy.getEleTy();
247     return type;
248   }
249 
250   /// Is the entity an array or an assumed rank ?
251   bool hasRank() const { return mlir::isa<fir::SequenceType>(getBaseTy()); }
252   /// Is this an assumed rank ?
253   bool hasAssumedRank() const {
254     auto seqTy = mlir::dyn_cast<fir::SequenceType>(getBaseTy());
255     return seqTy && seqTy.hasUnknownShape();
256   }
257   /// Returns the rank of the entity. Beware that zero will be returned for
258   /// both scalars and assumed rank.
259   unsigned rank() const {
260     if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(getBaseTy()))
261       return seqTy.getDimension();
262     return 0;
263   }
264 
265   /// Is this a character entity ?
266   bool isCharacter() const { return fir::isa_char(getEleTy()); }
267 
268   /// Is this a derived type entity ?
269   bool isDerived() const { return mlir::isa<fir::RecordType>(getEleTy()); }
270 
271   bool isDerivedWithLenParameters() const {
272     return fir::isRecordWithTypeParameters(getEleTy());
273   }
274 
275   /// Is this a polymorphic entity?
276   bool isPolymorphic() const { return fir::isPolymorphicType(getBoxTy()); }
277 
278   /// Is this a CLASS(*)/TYPE(*)?
279   bool isUnlimitedPolymorphic() const {
280     return fir::isUnlimitedPolymorphicType(getBoxTy());
281   }
282 };
283 
284 /// An entity described by a fir.box value that cannot be read into
285 /// another ExtendedValue category, either because the fir.box may be an
286 /// absent optional and we need to wait until the user is referencing it
287 /// to read it, or because it contains important information that cannot
288 /// be exposed in FIR (e.g. non contiguous byte stride).
289 /// It may also store explicit bounds or LEN parameters that were specified
290 /// for the entity.
291 class BoxValue : public AbstractIrBox {
292 public:
293   BoxValue(mlir::Value addr) : AbstractIrBox{addr} { assert(verify()); }
294   BoxValue(mlir::Value addr, llvm::ArrayRef<mlir::Value> lbounds,
295            llvm::ArrayRef<mlir::Value> explicitParams,
296            llvm::ArrayRef<mlir::Value> explicitExtents = {})
297       : AbstractIrBox{addr, lbounds, explicitExtents},
298         explicitParams{explicitParams} {
299     assert(verify());
300   }
301   // TODO: check contiguous attribute of addr
302   bool isContiguous() const { return false; }
303 
304   // Replace the fir.box, keeping any non-deferred parameters.
305   BoxValue clone(mlir::Value newBox) const {
306     return {newBox, lbounds, explicitParams, extents};
307   }
308 
309   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const BoxValue &);
310   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this; }
311 
312   llvm::ArrayRef<mlir::Value> getLBounds() const { return lbounds; }
313 
314   // The extents member is not guaranteed to be field for arrays. It is only
315   // guaranteed to be field for explicit shape arrays. In general,
316   // explicit-shape will not come as descriptors, so this field will be empty in
317   // most cases. The exception are derived types with LEN parameters and
318   // polymorphic dummy argument arrays. It may be possible for the explicit
319   // extents to conflict with the shape information that is in the box according
320   // to 15.5.2.11 sequence association rules.
321   llvm::ArrayRef<mlir::Value> getExplicitExtents() const { return extents; }
322 
323   llvm::ArrayRef<mlir::Value> getExplicitParameters() const {
324     return explicitParams;
325   }
326 
327 protected:
328   // Verify constructor invariants.
329   bool verify() const;
330 
331   // Only field when the BoxValue has explicit LEN parameters.
332   // Otherwise, the LEN parameters are in the fir.box.
333   llvm::SmallVector<mlir::Value, 2> explicitParams;
334 };
335 
336 /// Set of variables (addresses) holding the allocatable properties. These may
337 /// be empty in case it is not deemed safe to duplicate the descriptor
338 /// information locally (For instance, a volatile allocatable will always be
339 /// lowered to a descriptor to preserve the integrity of the entity and its
340 /// associated properties. As such, all references to the entity and its
341 /// property will go through the descriptor explicitly.).
342 class MutableProperties {
343 public:
344   bool isEmpty() const { return !addr; }
345   mlir::Value addr;
346   llvm::SmallVector<mlir::Value, 2> extents;
347   llvm::SmallVector<mlir::Value, 2> lbounds;
348   /// Only keep track of the deferred LEN parameters through variables, since
349   /// they are the only ones that can change as per the deferred type parameters
350   /// definition in F2018 standard section 3.147.12.2.
351   /// Non-deferred values are returned by
352   /// MutableBoxValue.nonDeferredLenParams().
353   llvm::SmallVector<mlir::Value, 2> deferredParams;
354 };
355 
356 /// MutableBoxValue is used for entities that are represented by the address of
357 /// a box. This is intended to be used for entities whose base address, shape
358 /// and type are not constant in the entity lifetime (e.g Allocatables and
359 /// Pointers).
360 class MutableBoxValue : public AbstractIrBox {
361 public:
362   /// Create MutableBoxValue given the address \p addr of the box and the non
363   /// deferred LEN parameters \p lenParameters. The non deferred LEN parameters
364   /// must always be provided, even if they are constant and already reflected
365   /// in the address type.
366   MutableBoxValue(mlir::Value addr, mlir::ValueRange lenParameters,
367                   MutableProperties mutableProperties)
368       : AbstractIrBox(addr), lenParams{lenParameters.begin(),
369                                        lenParameters.end()},
370         mutableProperties{mutableProperties} {
371     // Currently only accepts fir.(ref/ptr/heap)<fir.box<type>> mlir::Value for
372     // the address. This may change if we accept
373     // fir.(ref/ptr/heap)<fir.heap<type>> for scalar without LEN parameters.
374     assert(verify() &&
375            "MutableBoxValue requires mem ref to fir.box<fir.[heap|ptr]<type>>");
376   }
377   /// Is this a Fortran pointer ?
378   bool isPointer() const {
379     return mlir::isa<fir::PointerType>(getBoxTy().getEleTy());
380   }
381   /// Is this an allocatable ?
382   bool isAllocatable() const {
383     return mlir::isa<fir::HeapType>(getBoxTy().getEleTy());
384   }
385   // Replace the fir.ref<fir.box>, keeping any non-deferred parameters.
386   MutableBoxValue clone(mlir::Value newBox) const {
387     return {newBox, lenParams, mutableProperties};
388   }
389   /// Does this entity has any non deferred LEN parameters?
390   bool hasNonDeferredLenParams() const { return !lenParams.empty(); }
391   /// Return the non deferred LEN parameters.
392   llvm::ArrayRef<mlir::Value> nonDeferredLenParams() const { return lenParams; }
393   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
394                                        const MutableBoxValue &);
395   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this; }
396 
397   /// Set of variable is used instead of a descriptor to hold the entity
398   /// properties instead of a fir.ref<fir.box<>>.
399   bool isDescribedByVariables() const { return !mutableProperties.isEmpty(); }
400 
401   const MutableProperties &getMutableProperties() const {
402     return mutableProperties;
403   }
404 
405 protected:
406   /// Validate the address type form in the constructor.
407   bool verify() const;
408   /// Hold the non-deferred LEN parameter values  (both for characters and
409   /// derived). Non-deferred LEN parameters cannot change dynamically, as
410   /// opposed to deferred type parameters (3.147.12.2).
411   llvm::SmallVector<mlir::Value, 2> lenParams;
412   /// Set of variables holding the extents, lower bounds and
413   /// base address when it is deemed safe to work with these variables rather
414   /// than directly with a descriptor.
415   MutableProperties mutableProperties;
416 };
417 
418 class ExtendedValue;
419 
420 /// Get the base value of an extended value. Every type of extended value has a
421 /// base value or is null.
422 mlir::Value getBase(const ExtendedValue &exv);
423 
424 /// Get the LEN property value of an extended value. CHARACTER values have a LEN
425 /// property.
426 mlir::Value getLen(const ExtendedValue &exv);
427 
428 /// Pretty-print an extended value.
429 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ExtendedValue &);
430 
431 /// Return a clone of the extended value `exv` with the base value `base`
432 /// substituted.
433 ExtendedValue substBase(const ExtendedValue &exv, mlir::Value base);
434 
435 /// Is the extended value `exv` an array? Note that this returns true for
436 /// assumed-ranks that could actually be scalars at runtime.
437 bool isArray(const ExtendedValue &exv);
438 
439 /// Get the type parameters for `exv`.
440 llvm::SmallVector<mlir::Value> getTypeParams(const ExtendedValue &exv);
441 
442 //===----------------------------------------------------------------------===//
443 // Functions that may generate IR to recover properties from extended values.
444 //===----------------------------------------------------------------------===//
445 namespace factory {
446 
447 /// Generalized function to recover dependent type parameters. This does away
448 /// with the distinction between deferred and non-deferred LEN type parameters
449 /// (Fortran definition), since that categorization is irrelevant when getting
450 /// all type parameters for a value of dependent type.
451 llvm::SmallVector<mlir::Value> getTypeParams(mlir::Location loc,
452                                              FirOpBuilder &builder,
453                                              const ExtendedValue &exv);
454 
455 /// Specialization of get type parameters for an ArrayLoadOp. An array load must
456 /// either have all type parameters given as arguments or be a boxed value.
457 llvm::SmallVector<mlir::Value>
458 getTypeParams(mlir::Location loc, FirOpBuilder &builder, ArrayLoadOp load);
459 
460 // The generalized function to get a vector of extents is
461 /// Get extents from \p box. For fir::BoxValue and
462 /// fir::MutableBoxValue, this will generate code to read the extents.
463 llvm::SmallVector<mlir::Value>
464 getExtents(mlir::Location loc, FirOpBuilder &builder, const ExtendedValue &box);
465 
466 /// Get exactly one extent for any array-like extended value, \p exv. If \p exv
467 /// is not an array or has rank less then \p dim, the result will be a nullptr.
468 mlir::Value getExtentAtDimension(mlir::Location loc, FirOpBuilder &builder,
469                                  const ExtendedValue &exv, unsigned dim);
470 
471 } // namespace factory
472 
473 /// An extended value is a box of values pertaining to a discrete entity. It is
474 /// used in lowering to track all the runtime values related to an entity. For
475 /// example, an entity may have an address in memory that contains its value(s)
476 /// as well as various attribute values that describe the shape and starting
477 /// indices if it is an array entity.
478 class ExtendedValue : public details::matcher<ExtendedValue> {
479 public:
480   using VT =
481       std::variant<UnboxedValue, CharBoxValue, ArrayBoxValue, CharArrayBoxValue,
482                    ProcBoxValue, BoxValue, MutableBoxValue, PolymorphicValue>;
483 
484   ExtendedValue() : box{UnboxedValue{}} {}
485   template <typename A, typename = std::enable_if_t<
486                             !std::is_same_v<std::decay_t<A>, ExtendedValue>>>
487   constexpr ExtendedValue(A &&a) : box{std::forward<A>(a)} {
488     if (const auto *b = getUnboxed()) {
489       if (*b) {
490         auto type = b->getType();
491         if (mlir::isa<fir::BoxCharType>(type))
492           fir::emitFatalError(b->getLoc(), "BoxChar should be unboxed");
493         type = fir::unwrapSequenceType(fir::unwrapRefType(type));
494         if (fir::isa_char(type))
495           fir::emitFatalError(b->getLoc(),
496                               "character buffer should be in CharBoxValue");
497       }
498     }
499   }
500 
501   template <typename A>
502   constexpr const A *getBoxOf() const {
503     return std::get_if<A>(&box);
504   }
505 
506   constexpr const CharBoxValue *getCharBox() const {
507     return getBoxOf<CharBoxValue>();
508   }
509 
510   constexpr const UnboxedValue *getUnboxed() const {
511     return getBoxOf<UnboxedValue>();
512   }
513 
514   unsigned rank() const {
515     return match([](const fir::UnboxedValue &box) -> unsigned { return 0; },
516                  [](const fir::CharBoxValue &box) -> unsigned { return 0; },
517                  [](const fir::ProcBoxValue &box) -> unsigned { return 0; },
518                  [](const fir::PolymorphicValue &box) -> unsigned { return 0; },
519                  [](const auto &box) -> unsigned { return box.rank(); });
520   }
521 
522   bool isPolymorphic() const {
523     return match([](const fir::PolymorphicValue &box) -> bool { return true; },
524                  [](const fir::ArrayBoxValue &box) -> bool {
525                    return box.getSourceBox() ? true : false;
526                  },
527                  [](const auto &box) -> bool { return false; });
528   }
529 
530   bool hasAssumedRank() const {
531     return match(
532         [](const fir::BoxValue &box) -> bool { return box.hasAssumedRank(); },
533         [](const fir::MutableBoxValue &box) -> bool {
534           return box.hasAssumedRank();
535         },
536         [](const auto &box) -> bool { return false; });
537   }
538 
539   /// LLVM style debugging of extended values
540   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this << '\n'; }
541 
542   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &,
543                                        const ExtendedValue &);
544 
545   const VT &matchee() const { return box; }
546 
547 private:
548   VT box;
549 };
550 
551 /// Is the extended value `exv` unboxed and non-null?
552 inline bool isUnboxedValue(const ExtendedValue &exv) {
553   return exv.match(
554       [](const fir::UnboxedValue &box) { return box ? true : false; },
555       [](const auto &) { return false; });
556 }
557 
558 /// Returns the base type of \p exv. This is the type of \p exv
559 /// without any memory or box type. The sequence type, if any, is kept.
560 inline mlir::Type getBaseTypeOf(const ExtendedValue &exv) {
561   return exv.match(
562       [](const fir::MutableBoxValue &box) { return box.getBaseTy(); },
563       [](const fir::BoxValue &box) { return box.getBaseTy(); },
564       [&](const auto &) {
565         return fir::unwrapRefType(fir::getBase(exv).getType());
566       });
567 }
568 
569 /// Return the scalar type of \p exv type. This removes all
570 /// reference, box, or sequence type from \p exv base.
571 inline mlir::Type getElementTypeOf(const ExtendedValue &exv) {
572   return fir::unwrapSequenceType(getBaseTypeOf(exv));
573 }
574 
575 /// Is the extended value `exv` a derived type with LEN parameters?
576 inline bool isDerivedWithLenParameters(const ExtendedValue &exv) {
577   return fir::isRecordWithTypeParameters(getElementTypeOf(exv));
578 }
579 
580 } // namespace fir
581 
582 #endif // FORTRAN_OPTIMIZER_BUILDER_BOXVALUE_H
583