3 A polymorphic entity is a data entity that can be of different type during the
4 execution of a program.
6 This document aims to give insights at the representation of polymorphic
7 entities in FIR and how polymorphic related constructs and features are lowered
12 Here is a list of the sections and constraints of the Fortran standard involved
13 for polymorphic entities.
15 - 7.3.2.1 - 7.3.2.2: TYPE specifier (TYPE(*))
20 - 7.3.2.3: CLASS specifier
21 - 7.5.4.5: The passed-object dummy argument
23 - 9.7.1: ALLOCATE statement
25 - 9.7.2: NULLIFY statement
26 - When a NULLIFY statement is applied to a polymorphic pointer (7.3.2.3),
27 its dynamic type becomes the same as its declared type.
28 - 10.2.2.3: Data pointer assignment
29 - 11.1.3: ASSOCIATE construct
30 - 11.1.11: SELECT TYPE construct
40 - 16.9.76 EXTENDS_TYPE_OF (A, MOLD)
41 - 16.9.165 SAME_TYPE_AS (A, B)
42 - 16.9.184 STORAGE_SIZE (A [, KIND])
43 - C.10.5 Polymorphic Argument Association (15.5.2.9)
47 ## Representation in FIR
49 ### Polymorphic entities `CLASS(type1)`
51 A polymorphic entity is represented as a class type in FIR. In the example below
52 the dummy argument `p` is passed to the subroutine `foo` as a polymorphic entity
53 with the extensible type `point`. The type information captured in the class is
54 the best statically available at compile time.
55 `!fir.class` is a new type introduced for polymorphic entities. It's similar to
56 a box type but allows the distinction between a monomorphic and a polymorphic
58 `!fir.class` and `!fir.box` are based on a same `BaseBoxType` similar to the
59 `BaseMemRefType` done for MemRef.
67 type, extends(point) :: point_3d
73 ! code of the subroutine
79 func.func @foo(%p : !fir.class<!fir.type<_QTpoint{x:f32,y:f32}>>)
82 ### Unlimited polymorphic entities `CLASS(*)`
84 The unlimited polymorphic entity is represented as a class type with `none` as
91 ! code of the subroutine
97 func.func @bar(%x : !fir.class<none>)
100 ### Assumed-type `TYPE(*)`
102 Assumed type is added in Fortran 2018 and it is available only for dummy
103 arguments. It's mainly used for interfaces to non-Fortran code and is similar
105 An entity that is declared using the `TYPE(*)` type specifier is assumed-type
106 and is an unlimited polymorphic entity. It is not declared to have a type, and
107 is not considered to have the same declared type as any other entity,
108 including another unlimited polymorphic entity. Its dynamic type and type
109 parameters are assumed from its effective argument (7.3.2.2 - 3).
111 Assumed-type is represented in FIR as `!fir.box<none>`.
113 ### SELECT TYPE construct
115 The `SELECT TYPE` construct select for execution at most one of its constituent
116 block. The selection is based on the dynamic type of the selector.
123 type, extends(point) :: point_3d
126 type, extends(point) :: color_point
130 type(point), target :: p
131 type(point_3d), target :: p3
132 type(color_point), target :: c
133 class(point), pointer :: p_or_c
135 select type ( a => p_or_c )
139 print*, a%x, a%y, a%z
145 From the Fortran standard:
146 > A `TYPE IS` type guard statement matches the selector if the dynamic type
147 and kind type parameter values of the selector are the same as those specified
148 by the statement. A `CLASS IS` type guard statement matches the selector if the
149 dynamic type of the selector is an extension of the type specified by the
150 statement and the kind type parameter values specified by the statement are the
151 same as the corresponding type parameter values of the dynamic type of the
154 In the example above the `CLASS IS` type guard is matched.
156 The construct is lowered to a specific FIR operation `fir.select_type`. It is
157 similar to other FIR "select" operations such as `fir.select` and
158 `fir.select_rank`. The dynamic type of the selector value is matched against a
159 list of type descriptor. The `TYPE IS` type guard statement is represented by a
160 `#fir.type_is` attribute and the `CLASS IS` type guard statement is represented
161 by a `#fir.class_is` attribute.
162 The `CLASS DEFAULT` type guard statement is represented by a `unit` attribute.
166 fir.select_type %6 : !fir.class<!fir.ptr<!fir.type<_QFTpoint{x:f32,y:f32}>>> [
167 #fir.class_is<!fir.type<_QFTpoint{x:f32,y:f32}>>, ^bb1,
168 #fir.type_is<!fir.type<_QFTpoint_3d{x:f32,y:f32,z:f32}>>, ^bb2,
172 Lowering of the `fir.select_type` operation will produce a if-then-else ladder.
173 The testing of the dynamic type of the selector is done by calling runtime
176 The runtime has two functions to compare dynamic types. Note that these two
177 functions _ignore_ the values of `KIND` type parameters.
179 The functions for the `EXTENDS_TYPE_OF` and `SAME_TYPE_AS`
180 intrinsics (`flang/include/flang/Runtime/derived-api.h`).
182 // Perform the test of the SAME_TYPE_AS intrinsic.
183 bool RTNAME(SameTypeAs)(const Descriptor &, const Descriptor &);
185 // Perform the test of the EXTENDS_TYPE_OF intrinsic.
186 bool RTNAME(ExtendsTypeOf)(const Descriptor &, const Descriptor &);
189 For the `SELECT TYPE` construct, the `KIND` type parameter is not ignored. The
190 `TYPE IS` type guard statement is lowered to an inlined comparison. The
191 `CLASS IS` type guard statement is lowered to a runtime function call.
193 The function `ClassIs` implements the dynamic type comparison.
194 (`flang/include/flang/Runtime/derived-api.h`).
196 // Perform the test of the CLASS IS type guard statement of the SELECT TYPE
198 bool RTNAME(ClassIs)(const Descriptor &, const typeInfo::DerivedType &);
201 **FIR** (lower level FIR/MLIR after conversion to an if-then-else ladder)
204 func @f(%arg0: !fir.class<!fir.ptr<!fir.type<_QFTpoint{x:f32,y:f32}>>>) -> () {
205 // TYPE IS comparison done inlined.
206 %0 = fir.address_of(@_QFE.dt.point_3d) : !fir.ref<!fir type<_QM__fortran_type_infoTderivedtype{}>>
207 %1 = fir.box_tdesc %arg0 : (!fir.class<!fir.ptr<!fir.type<_QFTpoint{x:f32,y:f32}>>>) -> !fir.tdesc<none>
208 %2 = fir.convert %0 : (!fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>) -> index
209 %3 = fir.convert %1 : (!fir.tdesc<none>) -> index
210 %4 = arith.cmpi eq, %2, %3 : index
211 cf.cond_br %4, ^bb4, ^bb3
218 // CLASS IS comparison done with a runtime function call.
219 %24 = fir.address_of(@_QFE.dt.point) : !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>
220 %25 = fir.convert %24 : (!fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>) -> !fir.ref<none>
221 %26 = fir.convert %6 : (!fir.class<!fir.ptr<!fir.type<_QFTpoint{x:f32,y:f32}>>>) -> !fir.box<none>
222 %27 = fir.call @_FortranAClassIs(%26, %25) : (!fir.box<none>, !fir.ref<none>) -> i1
223 cf.cond_br %27, ^bb2, ^bb1
228 // CLASS DEFAULT block.
230 ^bb6: // 3 preds: ^bb2, ^bb4, ^bb5
233 func.func private @_FortranAClassIs(!fir.box<none>, !fir.ref<none>) -> i1
237 Dynamic type comparisons are inlined for performance whenever possible.
238 Dynamic type comparison for the `TYPE IS` type guard is inlined and
239 intrinsic types comparison when dealing with unlimited polymorphic entities are
247 %i32typecode = arith.constant 9 : i8
248 %typecode = fir.box_typecode %selector : (!fir.class<none>) -> i8
249 %isi32 = arith.cmpi eq, %typecode, %i32typecode : i8
256 Dynamic dispatch is the process of selecting which implementation of a
257 polymorphic procedure to call at runtime. The runtime already has information
258 to be used in this process (more information can be found here:
259 [RuntimeTypeInfo.md](RuntimeTypeInfo.md)).
261 The declaration of the data structures are present in
262 `flang/runtime/type-info.h`.
264 In the example below, there is a basic type `shape` with two type extensions
265 `triangle` and `rectangle`.
266 The two type extensions override the `get_area` type-bound procedure.
271 |---------------------|
273 |---------------------|
275 | + isFilled:logical |
276 |---------------------|
278 | + get_area():real |
279 |---------------------|
283 |---------------------------------------------------|
286 |---------------------| |---------------------|
287 | triangle | | rectangle |
288 |---------------------| |---------------------|
289 | + base:real | | + length:real |
290 | + height:real | | + width:real |
291 |---------------------| |---------------------|
292 | + get_area():real | | + get_area():real |
293 |---------------------| |---------------------|
304 procedure :: get_area => get_area_shape
305 procedure :: init => init_shape
308 type, extends(shape) :: triangle
312 procedure :: get_area => get_area_triangle
315 type, extends(shape) :: rectangle
319 procedure :: get_area => get_area_rectangle
323 class(shape), allocatable :: item
328 function get_area_shape(this)
329 real :: get_area_shape
334 subroutine init_shape(this, color)
338 this%isFilled = .false.
341 function get_area_triangle(this)
342 real :: get_area_triangle
343 class(triangle) :: this
344 get_area_triangle = (this%base * this%height) / 2
347 function get_area_rectangle(this)
348 real :: get_area_rectangle
349 class(rectangle) :: this
350 get_area_rectangle = this%length * this%width
353 function get_all_area(shapes)
355 type(shape_array) :: shapes(:)
361 do i = 1, size(shapes)
362 get_all_area = get_all_area + shapes(i)%item%get_area()
366 subroutine set_base_values(sh, v1, v2)
368 real, intent(in) :: v1, v2
378 print*,'Cannot set values'
389 type(shape_array), dimension(2) :: shapes
391 allocate (triangle::shapes(1)%item)
392 allocate (rectangle::shapes(2)%item)
394 do i = 1, size(shapes)
395 call shapes(i)%item%init(i)
398 call set_base_values(shapes(1)%item, 2.0, 1.5)
399 call set_base_values(shapes(2)%item, 5.0, 4.5)
401 area = get_all_area(shapes)
405 deallocate(shapes(1)%item)
406 deallocate(shapes(2)%item)
410 The `fir.dispatch` operation is used to perform a dynamic dispatch. This
411 operation is comparable to the `fir.call` operation but for polymorphic
413 Call to `NON_OVERRIDABLE` type-bound procedure are resolved at compile time and
414 a `fir.call` operation is emitted instead of a `fir.dispatch`.
415 When the type of a polymorphic entity can be fully determined at compile
416 time, a `fir.dispatch` op can even be converted to a `fir.call` op. This will
417 be discussed in more detailed later in the document in the devirtualization
421 Here is simple example of the `fir.dispatch` operation. The operation specify
422 the binding name of the type-bound procedure to be called and pass the
423 descriptor as argument. If the `NOPASS` attribute is set then the descriptor is
424 not passed as argument when lowered. If `PASS(arg-name)` is specified, the
425 `fir.pass` attribute is added to point to the PASS argument in the
426 `fir.dispatch` operation. `fir.nopass` attribute is added for the `NOPASS`. The
427 descriptor still need to be present in the `fir.dispatch` operation for the
428 dynamic dispatch. The CodeGen will then omit the descriptor in the argument
429 of the generated call.
431 The dispatch explanation focus only on the call to `get_area()` as seen in the
436 get_all_area = get_all_area + shapes(i)%item%get_area()
441 %1 = fir.convert %0 : !fir.ref<!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>>
442 %2 = fir.dispatch "get_area"(%1 : !fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>) -> f32
445 The type information is stored in the `f18Addendum` of the descriptor. The
446 format is defined in `flang/runtime/type-info.h` and part of its representation
447 in LLVM IR is shown below. The binding is comparable to a vtable. Each derived
448 type has a complete type-bound procedure table in which all of the bindings of
449 its ancestor types appear first.
453 Representation of the derived type information with the bindings.
455 %_QM__fortran_type_infoTderivedtype = type { { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8 }, i64, { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, i32, i8, i8, i8, i8, [4 x i8] }
456 %_QM__fortran_type_infoTbinding = type { %_QM__fortran_builtinsT__builtin_c_funptr, { ptr, i64, i32, i8, i8, i8, i8 } }
457 %_QM__fortran_builtinsT__builtin_c_funptr = type { i64 }
460 The `fir.dispatch` is lowered to FIR operations by the `PolymorphicOpConversion`
461 pass. It uses the runtime information to extract the
462 correct function from the vtable and to perform the actual call. Here is
463 what it can look like in pseudo LLVM IR code.
467 %2 = fir.box_tdesc %arg0 : (!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>) -> !fir.tdesc<none>
468 %3 = fir.box_tdesc %arg0 : (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>) -> !fir.tdesc<none>
469 %4 = fir.convert %3 : (!fir.tdesc<none>) -> !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>
470 %5 = fir.field_index binding, !fir.type<_QM__fortran_type_infoTderivedtype{}>
471 %6 = fir.coordinate_of %4, %5 : (!fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>>>>
472 %7 = fir.load %6 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>>>>
473 %8 = fir.box_addr %7 : (!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>>>) -> !fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>>
474 %c0 = arith.constant 0 : index
475 %9 = fir.coordinate_of %8, %c0 : (!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding{}>>
476 %10 = fir.field_index proc, !fir.type<_QM__fortran_type_infoTbinding{proc:!fir.type<_QM__fortran_builtinsT__builtin_c_funptr{__address:i64}>,name:!fir.box<!fir.ptr<!fir.char<1,?>>>}>
477 %11 = fir.coordinate_of %9, %10 : (!fir.ref<!fir.type<_QM__fortran_type_infoTbinding{}>>, !fir.field) -> !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_funptr{__address:i64}>>
478 %12 = fir.field_index __address, !fir.type<_QM__fortran_builtinsT__builtin_c_funptr{__address:i64}>
479 %13 = fir.coordinate_of %11, %12 : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_funptr{__address:i64}>>, !fir.field) -> !fir.ref<i64>
480 %14 = fir.load %13 : !fir.ref<i64>
481 %15 = fir.convert %14 : (i64) -> ((!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>) -> ())
482 fir.call %15(%arg0) : (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>) -> ()
487 // Retrieve the derived type runtime information and the vtable.
488 %14 = getelementptr %_QM__fortran_type_infoTderivedtype, ptr %13, i32 0, i32 0
489 %15 = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %14
490 store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] } %15, ptr %8
491 %16 = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %8, i32 0, i32 0
492 %17 = load ptr, ptr %16
493 %18 = getelementptr %_QM__fortran_type_infoTbinding, ptr %17, i64 0
494 %19 = getelementptr %_QM__fortran_type_infoTbinding, ptr %18, i32 0, i32 0
495 %20 = getelementptr %_QM__fortran_builtinsT__builtin_c_funptr, ptr %19, i32 0, i32 0
497 %21 = load i64, ptr %20
498 // Cast to func pointer
499 %22 = inttoptr i64 %21 to ptr
500 // Perform the actual function call
501 call void %22(ptr %0)
504 ### Passing polymorphic entities as argument
510 TYPE, EXTENDS(t1) :: t2
514 1) Dummy argument is fixed type and actual argument is fixed type.
515 - `TYPE(t1)` to `TYPE(t1)`: Nothing special to take into consideration.
516 2) Dummy argument is polymorphic and actual argument is fixed type. In these
517 cases, the actual argument need to be boxed to be passed to the
518 subroutine/function since those are expecting a descriptor.
520 func.func @_QMmod1Ps(%arg0: !fir.class<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>)
521 func.func @_QQmain() {
522 %0 = fir.alloca !fir.type<_QMmod1Tshape{x:i32,y:i32}> {uniq_name = "_QFEsh"}
523 %1 = fir.embox %0 : (!fir.ref<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>) -> !fir.class<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>
524 fir.call @_QMmod1Ps(%1) : (!fir.class<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>) -> ()
528 - `TYPE(t1)` to `CLASS(t1)`
529 - `TYPE(t2)` to `CLASS(t1)`
530 - `TYPE(t1)` to `CLASS(t2)` - Invalid
531 - `TYPE(t2)` to `CLASS(t2)`
532 3) Actual argument is polymorphic and dummy argument is fixed type. These case
533 are restricted to the declared type of the polymorphic entities.
534 - The simple case is when the actual argument is a scalar
535 polymorphic entity passed to a non-PDT. The caller just extract the
536 base address from the descriptor and pass it to the function.
537 - In other cases, the caller needs to perform a copyin/copyout since it
538 cannot just extract the base address of the `CLASS(T)` because it is
539 likely not contiguous.
540 - `CLASS(t1)` to `TYPE(t1)`
541 - `CLASS(t2)` to `TYPE(t1)` - Invalid
542 - `CLASS(t1)` to `TYPE(t2)` - Invalid
543 - `CLASS(t2)` to `TYPE(t2)`
544 4) Both actual and dummy arguments are polymorphic. These particular cases are
545 straight forward. The function expect polymorphic entities already.
546 The boxed type is passed without change.
547 - `CLASS(t1)` to `CLASS(t1)`
548 - `CLASS(t2)` to `CLASS(t1)`
549 - `CLASS(t1)` to `CLASS(t2)` - Invalid
550 - `CLASS(t2)` to `CLASS(t2)`
552 ### User-Defined Derived Type Input/Output
554 User-Defined Derived Type Input/Output allows to define how a derived-type
555 is read or written from/to a file.
557 There are 4 basic subroutines that can be defined:
563 Here are their respective interfaces:
567 subroutine read_formatted(dtv, unit, iotype, v_list, iostat, iomsg)
568 subroutine write_formatted(dtv, unit, iotype, v_list, iostat, iomsg)
569 subroutine read_unformatted(dtv, unit, iotype, v_list, iostat, iomsg)
570 subroutine write_unformatted(dtv, unit, iotype, v_list, iostat, iomsg)
573 When defined on a derived-type, these specific type-bound procedures are stored
574 as special bindings in the type descriptor (see `SpecialBinding` in
575 `flang/runtime/type-info.h`).
577 With a derived-type the function call to `@_FortranAioOutputDescriptor` from IO
578 runtime will be emitted in lowering.
588 %5 = fir.call @_FortranAioBeginUnformattedOutput(%c10_i32, %4, %c56_i32) : (i32, !fir.ref<i8>, i32) -> !fir.ref<i8>
589 %6 = fir.embox %2 : (!fir.ref<!fir.type<_QTt>>) -> !fir.class<!fir.type<_QTt>>
590 %7 = fir.convert %6 : (!fir.class<!fir.type<_QTt>>) -> !fir.box<none>
591 %8 = fir.call @_FortranAioOutputDescriptor(%5, %7) : (!fir.ref<i8>, !fir.box<none>) -> i1
592 %9 = fir.call @_FortranAioEndIoStatement(%5) : (!fir.ref<i8>) -> i32
595 When dealing with polymorphic entities the call to IO runtime can stay
596 unchanged. The runtime function `OutputDescriptor` can make the dynamic dispatch
597 to the correct binding stored in the descriptor.
601 The `FINAL` specifies a final subroutine that might be executed when a data
602 entity of that type is finalized. Section 7.5.6.3 defines when finalization
605 Final subroutines like User-Defined Derived Type Input/Output are stored as
606 special bindings in the type descriptor. The runtime is able to handle the
607 finalization with a call the the `@_FortranADestroy` function
608 (`flang/include/flang/Runtime/derived-api.h`).
612 %5 = fir.call @_FortranADestroy(%desc) : (!fir.box<none>) -> none
615 The `@_FortranADestroy` function will take care to call the final subroutines
616 and the ones from the parent type.
618 Appropriate call to finalization have to be lowered at the right places (7.5.6.3
619 When finalization occurs).
623 Sometimes there is enough information at compile-time to avoid going through
624 a dynamic dispatch for a type-bound procedure call on a polymorphic entity. To
625 be able to perform this optimization directly in FIR the dispatch table is also
626 present statically with the `fir.dispatch_table` and `fir.dt_entry` operations.
628 Here is an example of these operations representing the dispatch tables for the
629 same example than for the dynamic dispatch.
633 fir.dispatch_table @_QMgeometryE.dt.shape {
634 fir.dt_entry init, @_QMgeometryPinit_shape
635 fir.dt_entry get_area, @_QMgeometryPget_area_shape
638 fir.dispatch_table @_QMgeometryE.dt.rectangle {
639 fir.dt_entry init, @_QMgeometryPinit_shape
640 fir.dt_entry get_area, @_QMgeometryPget_area_rectangle
643 fir.dispatch_table @_QMgeometryE.dt.triangle {
644 fir.dt_entry init, @_QMgeometryPinit_shape
645 fir.dt_entry get_area, @_QMgeometryPget_area_triangle
649 With this information, an optimization pass can replace `fir.dispatch`
650 operations with `fir.call` operations to the correct functions when the type is
651 know at compile time.
653 This is the case in a `type is` type-guard block as illustrated below.
657 subroutine get_only_triangle_area(sh)
673 The call to `get_area` in the `type is (triangle)` guard can be replaced.
675 %3 = fir.dispatch "get_area"(%desc)
677 %3 = fir.call @get_area_triangle(%desc)
680 Another example would be the one below. In this case as well, a dynamic dispatch
681 is not necessary and a `fir.call` can be emitted instead.
686 class(shape), pointer :: sh
687 type(triangle), target :: tr
694 Note that the frontend is already replacing some of the dynamic dispatch calls
695 with the correct static ones. The optimization pass is useful for cases not
696 handled by the frontend and especially cases showing up after some other
697 optimizations are applied.
699 ### `ALLOCATE`/`DEALLOCATE` statements
701 The allocation and deallocation of polymorphic entities are delegated to the
703 The corresponding function signatures can be found in
704 `flang/include/flang/Runtime/allocatable.h` and in
705 `flang/include/flang/Runtime/pointer.h` for pointer allocation.
709 The `ALLOCATE` statement is lowered to runtime calls as shown in the example
714 allocate(triangle::shapes(1)%item)
715 allocate(rectangle::shapes(2)%item)
720 %0 = fir.address_of(@_QMgeometryE.dt.triangle) : !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype>>
721 %1 = fir.convert %item1 : (!fir.ref<!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>>) -> !fir.ref<!fir.box<none>>
722 %2 = fir.call @_FortranAAllocatableInitDerived(%1, %0)
723 %3 = fir.call @_FortranAAllocatableAllocate(%1, ...)
725 %4 = fir.address_of(@_QMgeometryE.dt.rectangle) : !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype>>
726 %5 = fir.convert %item2 : (!fir.ref<!fir.class<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32}>>>) -> !fir.ref<!fir.box<none>>
727 %6 = fir.call @_FortranAAllocatableInitDerived(%5, %4)
728 %7 = fir.call @_FortranAAllocatableAllocate(%5, ...)
731 For pointer allocation, the `PointerAllocate` function is used.
735 The `DEALLOCATE` statement is lowered to a runtime call to
736 `AllocatableDeallocate` and `PointerDeallocate` for pointers.
740 deallocate(shapes(1)%item)
741 deallocate(shapes(2)%item)
746 %8 = fir.call @_FortranAAllocatableDeallocate(%desc1)
747 %9 = fir.call @_FortranAAllocatableDeallocate(%desc2)
750 ### `EXTENDS_TYPE_OF`/`SAME_TYPE_AS` intrinsics
752 `EXTENDS_TYPE_OF` and `SAME_TYPE_AS` intrinsics have implementation in the
753 runtime. Respectively `SameTypeAs` and `ExtendsTypeOf` in
754 `flang/include/flang/Evaluate/type.h`.
756 Both intrinsic functions are lowered to their respective runtime calls.
758 ### Assignment / Pointer assignment
760 Intrinsic assignment of an object to another is already implemented in the
761 runtime. The function `@_FortranAAsssign` performs the correct operations.
763 Available in `flang/include/flang/Runtime/assign.h`.
765 ### User defined assignment and operator
772 procedure :: assign_t1
773 generic :: assignment(=) => assign_t1
776 type, extends(t1) :: t2
781 subroutine assign_t1(to, from)
782 class(t1), intent(inout) :: to
783 class(t1), intent(in) :: from
784 ! Custom code for the assignment
787 subroutine assign_t2(to, from)
788 class(t2), intent(inout) :: to
789 class(t2), intent(in) :: from
790 ! Custom code for the assignment
798 class(t1), allocatable :: v1
799 class(t1), allocatable :: v2
809 In the example above the assignment `v2 = v1` is done by a call to `assign_t1`.
810 This is resolved at compile time since `t2` could not have a generic type-bound
811 procedure for assignment with an interface that is not distinguishable. This
812 is the same for user defined operators.
816 When a `NULLIFY` statement is applied to a polymorphic pointer (7.3.2.3), its
817 dynamic type becomes the same as its declared type.
819 The `NULLIFY` statement is lowered to a call to the corresponding runtime
820 function `PointerNullifyDerived` in `flang/include/flang/Runtime/pointer.h`.
822 ### Impact on existing FIR operations dealing with descriptors
824 Currently, FIR has a couple of operations taking descriptors as inputs or
825 producing descriptors as outputs. These operations might need to deal with the
826 dynamic type of polymorphic entities.
828 - `fir.load`/`fir.store`
829 - Currently a `fir.load` of a `fir.box` is a special case. In the code
830 generation no copy is made. This could be problematic with polymorphic
831 entities. When a `fir.load` is performed on a `fir.class` type, the dynamic
837 class(shape), pointer :: a
839 subroutine sub1(a, b)
848 In the example above, the dynamic type of `a` and `b` might be different. The
849 dynamic type of `a` must be copied when it is associated on `b`.
853 // fir.load must copy the dynamic type from the pointer `a`
854 %0 = fir.address_of(@_QMmod1Ea) : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>>>
855 %1 = fir.load %0 : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>>>
859 - The embox operation is used to create a descriptor from a reference. With
860 polymorphic entities, it is used to create a polymorphic descriptor from
861 a derived type. The declared type of the descriptor and the derived type
862 are identical. The dynamic type of the descriptor must be set when it is
863 created. This is already handled by lowering.
866 - The rebox operation is used to create a new descriptor from a another
867 descriptor with new optional dimension. If the original descriptor is a
868 polymorphic entities its dynamic type must be propagated to the new
871 %0 = fir.slice %c10, %c33, %c2 : (index, index, index) -> !fir.slice<1>
872 %1 = fir.shift %c0 : (index) -> !fir.shift<1>
873 %2 = fir.rebox %x(%1)[%0] : (!fir.class<!fir.array<?x!fir.type<>>>, !fir.shift<1>, !fir.slice<1>) -> !fir.class<!fir.array<?x!fir.type<>>>
879 - Lowering part is tested with LIT tests in tree
880 - Polymorphic entities involved a lot of runtime information so executable
881 tests will be useful for full testing.
886 Current list of TODOs in lowering:
887 - `flang/lib/Lower/Bridge.cpp:448` not yet implemented: create polymorphic host associated copy
888 - `flang/lib/Lower/CallInterface.cpp:795` not yet implemented: support for polymorphic types
889 - `flang/lib/Lower/ConvertType.cpp:237` not yet implemented: support for polymorphic types
894 - [1] https://www.pgroup.com/blogs/posts/f03-oop-part1.htm
895 - [2] https://www.pgroup.com/blogs/posts/f03-oop-part2.htm
896 - [3] https://www.pgroup.com/blogs/posts/f03-oop-part3.htm
897 - [4] https://www.pgroup.com/blogs/posts/f03-oop-part4.htm
898 - [5] Modern Fortran explained