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