File: ParameterizedDerivedTypes.md

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (1027 lines) | stat: -rw-r--r-- 37,258 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
# Parameterized Derived Types (PDTs)

Derived types can be parameterized with type parameters. A type parameter is
either a kind type parameter or a length type parameter. Both kind and length
type parameters are of integer type.

This document aims to give insights at the representation of PDTs in FIR and how
PDTs related constructs and features are lowered to FIR.

# Fortran standard

Here is a list of the sections and constraints of the Fortran standard involved
for parameterized derived types.

- 7.2 Type parameters
  - C701
  - C702
- 9.4.5: Type parameter inquiry
- 9.7.1: ALLOCATE statement
- 9.7.2: NULLIFY
- 9.7.3: DEALLOCATE

The constraints are implemented and tested in flang.

### PDT with kind type parameter

PDTs with kind type parameter are already implemented in flang. Since the kind
type parameter shall be a constant expression, it can be determined at
compile-time and is folded in the type itself. Kind type parameters also play
a role in determining a specific type instance according to the Fortran
standard.

**Fortran**
```fortran
type t(k)
  integer, kind :: k
end type

type(t(1)) :: tk1
type(t(2)) :: tk2
```

In the example above, `tk1` and `tk2` have distinct types.

Lowering makes the distinction between the two types by giving them different
names `@_QFE.kp.t.1` and `@_QFE.kp.t.2`. More information about the unique names
can be found here: `flang/docs/BijectiveInternalNameUniquing.md`

### PDT with length type parameter

Two PDTs with the same derived type and the same kind type parameters but
different length type parameters are not distinct types. Unlike the kind type
parameter, the length type parameters do not play a role in determining a
specific type instance.
PDTs with length type parameter can be seen as dependent types[1].

In the example below, `tk1` and `tk2` have the same type but may have different
layout in memory. They have different value for the length type parameter `l`.
`tk1` and `tk2` are not convertible unlike `CHARACTER` types.
Assigning `tk2` to `tk1` is not a valid program.

**Fortran**
```fortran
type t(k,l)
  integer, kind :: k
  integer, len :: l
end type

type(t(1, i+1)) :: tk1
type(t(1, i+2)) :: tk2

! This is invalid
tk2 = tk1
```

Components with length type parameters cannot be folded into the type at
compile-time like the one with kind type parameters since their size is not
known. There are multiple ways to implement length type parameters and here are
two possibilities.

1. Directly encapsulate the components in the derived type. This will be referred
   as the "inlined" solution in the rest of the document. The size of the
   descriptor will not be fixed and be computed at runtime. Size, offset need
   to be computed at runtime as well.

2. Use a level of indirection for the components outside of the descriptor. This
   will be referred as the "outlined" solution in the rest of the document.
   The descriptor size will then remain the same.

These solutions have pros and cons and more details are given in the next few
sections.

#### Implementing PDT with inlined components

In case of `len_type1`, the size, offset, etc. of `fld1` and `fld2` depend on
the runtime values of `i` and `j` when the components are inlined into the
derived type. At runtime, this information needs to be computed to be retrieved.
While lowering the PDT, compiler generated functions can be created in order to
compute this information. 

Note: The type description tables generated by semantics and used throughout the
runtime have component offsets as constants. Inlining component would require
this representation to be extended.

**Fortran**
```fortran
! PDT with one level of inlined components.
type len_type1(i, j)
  integer, len :: i, j
  character(i+j) :: fld1
  character(j-i+2) :: fld2
end type
```

#### Implementing PDT with outlined components

A level of indirection can be used and `fld1` and `fld2` are then outlined
as shown in `len_type2`. _compiler_allocatable_ is here only to show which
components have an indirection.

**Fortran**
```fortran
! PDT with one level of indirection.
type len_type2(i, j)
  integer, len :: i, j
  ! The two following components are not directly stored in the type but
  ! allocatable components managed by the compiler. The
  ! `compiler_managed_allocatable` is not a proper keyword but just added here
  ! to have a better understanding.
  character(i+j), compiler_managed_allocatable :: fld1 
  character(j-i+2), compiler_managed_allocatable :: fld2
end type
```

This solution has performance drawback because of the added indirections. It
also has to deal with compiler managed allocation/deallocation of the components
pointed by the indirections.

These indirections are more problematic when we deal with array slice of derived
types as it could require temporaries depending how the memory is allocated.

The outlined solution is also problematic for unformatted I/O as the
indirections need to be followed correctly when reading or writing records.

#### Example of nested PDTs

PDTs can be nested. Here are some example used later in the document.

**Fortran**
```fortran
! PDT with second level of inlined components.
type len_type3(i, j)
  integer, len :: i, j
  character(2*j) :: name
  type(len_type1(i*2, j+4)) :: field
end type

! PDT with second level of indirection
type len_type4(i, j)
  integer, len :: i, j
  character(2*j), compiler_allocatable :: name
  type(len_type2(i-1, 2**j)), compiler_allocatable :: field
end type
```

#### Example with array slice

Let's take an example with an array slice to see the advantages and
disadvantages of the two solutions.

For all derived types that do not have LEN type parameter (only have
compile-time constants) a standard descriptor can be set with the correct offset
and strides such that `array%field%fld2` can be encoded in the descriptor, is
not contiguous, and does not require a copy. This is what is implemented in
flang.

**Fortran**
```fortran
! Declare arrays of PDTs
type(len_type3(exp1,exp2)) :: pdt_inlined_array(exp3)
type(len_type4(exp1,exp2)) :: pdt_outlined_array(exp3)

! Passing/accessing a slice of PDTs array
pdt_inlined_array%field%fld2
```

For a derived type with length type parameters inlined the expression
`pdt_inlined_array%field%fld2` can be encoded in the standard descriptor because
the components of `pdt_inlined_array` are inlined such that the array is laid
out with all its subcomponents in a contiguous range of memory.

For the `pdt_outlined_array` array, the implementation has to insert several
level of indirections and therefore cannot be encoded in the standard
descriptor.
The different indirections levels break the property of the large contiguous
block in memory if the allocation is done for each components. This would make
the `pdt_outlined_array` a ragged array. The memory can also be allocated for
components with length type parameters while allocating the base object (in this
case the `pdt_outlined_array`).

For each non-allocatable/non-pointer leaf automatic component of a PDT base
entity (`pdt_outlined_array` here) or a base entity containing PDTs, the
initialization will allocate a single block in memory for all the leaf
components reachable in the base entity (`pdt_outlined_array(i)%field%fld1`).
The size of this block will be `N * sizeof(leaf-component)` where `N` is the
multiplication of the size of each part-ref from the base entity to the leaf
component. The descriptor for each leaf component can then point to the correct
location in the block `block[i*sizeof(leaf-component)]`.

Outlining the components has the advantage that the size of the PDTs are
compile-time constant as each field is encoded as a descriptor pointing to the
data. It has a disadvantage to require non-standard descriptors and comes with
additional runtime cost.

With components inlining, the size of the PDTs are not compile-time constant.
This solution has the advantage to not add a performance drawback with
additional indirections but requires to compute the size of the descriptor
at runtime.
The size of the PDTs need to be computed at runtime. This is already the case
for dynamic allocation sizes since it is possible for arrays to have dynamic
shapes, etc.

### Support of PDTs in other compilers

1) Nested PDTs
2) Array of PDTs
3) Allocatable array of PDTs
4) Pointer to array section
5) Formatted I/O
6) Unformatted I/O
7) User-defined I/O
8) FINAL subroutine
9) ELEMENTAL FINAL subroutine

| Compiler  |   1   |   2   |   3   |   4   |   5   |   6   |   7   |   8   |   9   |
| --------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| gfortran  | crash |   ok  | crash |   ok  |   ok  |   ok  |   no  |   no  |   no  |
| nag       |   ok  |   ok  |   ok  | crash |   ok  |   ok  |   ok  |   no  |   no  |
| nvfortran | crash |   ok  |   ok  |   ok  |   ok  |   ok  |   ok  |   ok  |   no  |
| xlf       |   ok  |   ok  |   ok  |   ok  | wrong |   ok  | wrong |   no  |   no  |
| ifort     |   ok  |   ok  |   ok  |   ok  |   ok  |   ok  |   ok  | crash | crash |

_Legends of results in the table_
```
ok = compile + run + good result
wrong = compile + run + wrong result
crash = compiler crash or runtime crash
no = doesn't compile with no crash
```

#### Field inlining in lowering

A PDT with length type parameters has a list of 1 or more type parameters that
are runtime values. These length type parameter values can be present in
specification of other type parameters, array bounds expressions, etc.
All these expressions are integer specifications expressions and can be
evaluated at any given point with the length type parameters value of the PDT
instance. This is possible because constraints C750 and C754 from Fortran 2018
standard that restrict what can appear in the specification expression.

_note: C750 and C754 are partially enforced in the semantic at the moment._

These expressions can be lowered into small simple functions. For example,
the offset of `fld1` in `len_type1` could be 0; its size would be computed as
`sizeof(char) * (i+j)`. `size` can be lowered into a compiler generated
function.

**FIR**
```c
// Example of compiler generated functions to compute offsets, size, etc.
// This is just an example and actual implementation might have more functions.

// name field offset.
func.func @_len_type3.offset.name() -> index {
  %0 = arith.constant 0 : index
  return %0 : index
}

// size for `name`: sizeof(char) * (2 * i) + padding
func.func @_len_type3.memsize.name(%i: index, %j: index) -> index {
  %0 = arith.constant 2 : index
  %1 = arith.constant 8 : index
  %2 = arith.muli %0, %i : index
  %3 = arith.muli %1, %2 : index
  // padding not added here
  return %3 : index
}

// `fld` field offset.
func.func @_len_type3.offset.field(%i: index, %j: index) -> index {
  %0 = call @_len_type3.offset.name() : () -> index
  %1 = call @_len_type3.memsize.name(%i, %j) : (index, index) -> index
  %2 = arith.addi %0, %1 : index
  return %2 : index
}

// 1st type parameter used for field `fld`: i*2
func.func @_len_type3.field.typeparam.1(%i : index, %j : index) -> index {
  %0 = arith.constant 2 : index
  %1 = arith.muli %0, %i : index
  return %1 : index
}

// 2nd type parameter used for field `fld`: j+4
func.func @_len_type3.field.typeparam.2(%i : index, %j : index) -> index {
  %0 = arith.constant 4 : index
  %1 = arith.addi %j, %0 : index
  return %1 : index
}

// `fld1` offset in `len_type1`.
func.func @_len_type1.offset.fld1() -> index {
  %0 = arith.constant 0 : index
  return %0 : index
}

// size for `fld1`.
func.func @_len_type1.memsize.fld1(%i : index, %j : index) -> index {
  %0 = arith.constant 8 : index
  %1 = arith.addi %i, %j : index
  %2 = arith.muli %0, %1 : index
  return %2 : index
}

// `fld2` offset in `len_type1`.
func.func @_len_type1.offset.fld2(%i : index, %j : index) -> index {
  %0 = call @_len_type1.offset.fld1() : () -> index
  %1 = call @_len_type1.memsize.fld1(%i, %j) : (index, index) -> index
  %2 = arith.addi %0, %1 : index
  return %2 : index
}
```

Access a field
```fortran
pdt_inlined_array(1)%field%fld2
```

Example of offset computation in the PDTs.
```c
%0 = call @_len_type3.field.typeparam.1(%i, %j) : (index, index) -> index
%1 = call @_len_type3.field.typeparam.2(%i, %j) : (index, index) -> index
%2 = call @_len_type3.offset.fld(%i, %j) : (index, index) -> index
%3 = call @_len_type1.offset.fld2(%0, %1) : (index, index) -> index
%offset_of_1st_element = arith.addi %2, %3 : index
// Use the value computed offset_of_1st_element
```

In the case where the length type parameters values `(i,j)` are compile-time
constants then function inlining and constant folding will transform these
dependent types into statically defined types with no runtime cost.

**Fortran**
```fortran
type t(l)
  integer, len :: l
  integer :: i(l)
end type

type(t(n)), target :: a(10)
integer, pointer :: p(:)
p => a(:)%i(5)
```

When making a new descriptor like for pointer association, the `field_index`
operation can take the length type parameters needed for size/offset
computation.

**FIR**
```c
%5 = fir.field_index i, !fir.type<_QMmod1Tt{l:i32,i:!fir.array<?xi32>}>(%n : i32)
```

### Length type parameter with expression

The component of a PDT can be defined with expressions including the length
type parameters.

**Fortran**
```fortran
type t1(n, m)
  integer, len :: n = 2
  integer, len :: m = 4
  real :: data(n*m)
end type
```

The idea would be to replace the expression with an extra length type parameter
with a compiler generated name and a default value of `n*m`. All instance of the
expression would then reference the new name.

**Fortran**
```fortran
type t1(n, m)
  integer, len :: n = 2
  integer, len :: m = 4
  integer, len :: _t1_n_m = 8 ! hidden extra length type parameter
  real :: data(_t1_n_m)
end type
```

At any place where the a PDT is initialized, the lowering would make the
evaluation and their values saved in the addendum and pointed to by the
descriptor.

### `ALLOCATE`/`DEALLOCATE` statements

The allocation and deallocation of PDTs are delegated to the runtime.

The corresponding function can be found in
`flang/include/flang/Runtime/allocatable.h` and
`flang/include/flang/Runtime/pointer.h` for pointer allocation.

`ALLOCATE`

The `ALLOCATE` statement is lowered to a sequence of function calls as shown in
the example below.

**Fortran**
```fortran
type t1(i)
  integer, len :: i = 4
  character(i) :: c
end type

type(t1), allocatable :: t
type(t1), pointer :: p

allocate(t1(2)::t)
allocate(t1(2)::p)
```

**FIR**
```c
// For allocatable
%5 = fir.call @_FortranAAllocatableInitDerived(%desc, %type) : (!fir.box<none>, ) -> ()
// The AllocatableSetDerivedLength functions is called for each length type parameters.
%6 = fir.call @_FortranAAllocatableSetDerivedLength(%desc, %pos, %value) : (!fir.box<none>, i32, i64) -> ()
%7 = fir.call @_FortranAAllocatableAllocate(%3) : (!fir.box<none>) -> ()

// For pointer
%5 = fir.call @_FortranAPointerNullifyDerived(%desc, %type) : (!fir.box<none>, ) -> ()
// The PointerSetDerivedLength functions is called for each length type parameters.
%6 = fir.call @_FortranAPointerSetDerivedLength(%desc, %pos, %value) : (!fir.box<none>, i32, i64) -> ()
%7 = fir.call @_FortranAPointerAllocate(%3) : (!fir.box<none>) -> ()
```

`DEALLOCATE`

The `DEALLOCATE` statement is lowered to a runtime call to
`AllocatableDeallocate` and `PointerDeallocate` for pointers.

**Fortran**
```fortran
deallocate(pdt1)
```

**FIR** 
```c
// For allocatable
%8 = fir.call @_FortranAAllocatableDeallocate(%desc1) : (!fir.box<none>) -> (i32)

// For pointer
%8 = fir.call @_FortranAPointerDeallocate(%desc1) : (!fir.box<none>) -> (i32)
```

### `NULLIFY`

The `NULLIFY` statement is lowered to a call to the corresponding runtime
function `PointerNullifyDerived` in `flang/include/flang/Runtime/pointer.h`.

**Fortran**
```fortran
NULLIFY(p)
```

**FIR**
```c
%0 = fir.call @_FortranAPointerNullifyDerived(%desc, %type) : (!fir.box<none>, !fir.tdesc) -> ()
```

### Formatted I/O

The I/O runtime internals are described in this file:
`flang/docs/IORuntimeInternals.md`.

When an I/O statement with a derived-type is encountered in lowering, the
derived-type is emboxed in a descriptor if it is not already and a call to the
runtime library is issued with the descriptor (as shown in the example below).
The function is `_FortranAioOutputDescriptor`. The call make a call to
`FormattedDerivedTypeIO` in `flang/runtime/descriptor-io.h` for derived-type.
This function will need to be updated to support the chosen solution for PDTs.

**Fortran**
```fortran
type t
  integer, len :: l
  integer :: i(l) = 42
end type

! ...

subroutine print_pdt
  type(t(10)) :: x
  print*, x
end subroutine
```

**FIR**
```c
func.func @_QMpdtPprint_pdt() {
  %l = arith.constant = 10
  %0 = fir.alloca !fir.type<_QMpdtTt{l:i32,i:!fir.array<?xi32>}> (%l : i32) {bindc_name = "x", uniq_name = "_QMpdt_initFlocalEx"}
  %1 = fir.embox %0 : (!fir.ref<!fir.type<_QMpdtTt{l:i32,i:!fir.array<?xi32>}>>) (typeparams %l : i32) -> !fir.box<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<2xi32>}>> 
  %2 = fir.address_of(@_QQcl.2E2F6669725F7064745F6578616D706C652E66393000) : !fir.ref<!fir.char<1,22>>
  %c8_i32 = arith.constant 8 : i32
  %3 = fir.convert %1 : (!fir.box<!fir.type<_QMpdtTt{l:i32,i:!fir.array<?xi32>}>>) -> !fir.box<none>
  %4 = fir.convert %2 : (!fir.ref<!fir.char<1,22>>) -> !fir.ref<i8>
  %5 = fir.call @_FortranAInitialize(%3, %4, %c8_i32) : (!fir.box<none>, !fir.ref<i8>, i32) -> none
  %c-1_i32 = arith.constant -1 : i32
  %6 = fir.address_of(@_QQcl.2E2F6669725F7064745F6578616D706C652E66393000) : !fir.ref<!fir.char<1,22>>
  %7 = fir.convert %6 : (!fir.ref<!fir.char<1,22>>) -> !fir.ref<i8>
  %c10_i32 = arith.constant 10 : i32
  %8 = fir.call @_FortranAioBeginExternalListOutput(%c-1_i32, %7, %c10_i32) : (i32, !fir.ref<i8>, i32) -> !fir.ref<i8>
  %9 = fir.embox %0 : (!fir.ref<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>) (typeparams %l : i32) -> !fir.box<!fir.type<_QMpdtTt{l:i32,i:!fir.array<?xi32>}>>
  %10 = fir.convert %9 : (!fir.box<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>) -> !fir.box<none>
  %11 = fir.call @_FortranAioOutputDescriptor(%8, %10) : (!fir.ref<i8>, !fir.box<none>) -> i1
  %12 = fir.call @_FortranAioEndIoStatement(%8) : (!fir.ref<i8>) -> i32
  return
}
```

### Unformatted I/O

The entry point in the runtime for unformatted I/O is similar than the one for
formatted I/O. A call to `_FortranAioOutputDescriptor` with the correct
descriptor is also issued by the lowering. For unformatted I/O, the runtime is
calling `UnformattedDescriptorIO` from `flang/runtime/descriptor-io.h`.
This function will need to be updated to support the chosen solution for PDTs.

### Default component initialization of local variables

Default initializers for components with length type parameters need to be
processed as the derived type instance is created.
The length parameters block must also be created and attached to the addendum.
See _New f18addendum_ section for more information.

### Assignment

As mentioned in 10.2.1.2 (8), for an assignment, each length type parameter of
the variable shall have the same value as the corresponding type parameter
unless the lhs is allocatable.

**Fortran**
```fortran
type t(l)
  integer, len :: l
  integer :: i(l)
end type

! ...

type(t(10)) :: a, b
type(t(20)) :: c
type(t(:)), allocatable :: d
a = b ! Legal assignment
c = b ! Illegal assignment because `c` does not have the same length type 
      ! parameter value than `b`.
d = c ! Legal because `d` is allocatable
```

A simple intrinsic assignment without allocatable or pointer follows the same
path than the traditional derived-type (addressing of component is different)
since the length type parameter values are identical and do not need to be
copied or reallocated. The length type parameters values are retrieved when
copying the data.

Assignment of PDTs with allocatable or pointer components are done with the help
of the runtime. A call to `_FortranAAssign` is done with the lhs and rhs
descriptors. The length type parameters are available in the descriptors.

For allocatable PDTs, if the rhs side has different length type parameters than
the lhs, it is deallocated first and allocated with the rhs length type
parameters information (F'2018 10.2.1.3(3)). There is code in the runtime to
handle this already. It will need to be updated for the new f18addendum.

### Finalization

A final subroutine is called for a PDT if the subroutine has the same kind type
parameters and rank as the entity to be finalized. The final subroutine is
called with the entity as the actual argument.
If there is an elemental final subroutine whose dummy argument has the same kind
type parameters as the entity to be finalized, or a final subroutine whose dummy
argument is assumed-rank with the same kind type parameters as the entity to be
finalized, the subroutine is called with the entity as the actual argument.
Otherwise, no subroutine is called.

**Example from the F2018 standard**
```fortran
module m

  type t(k)
    integer, kind :: k
    real(k), pointer :: vector(:) => NULL()
  contains
    final :: finalize_t1s, finalize_t1v, finalize_t2e
  end type

contains
  
  subroutine finalize_t1s(x)
    type(t(kind(0.0))) x
    if (associated(x%vector)) deallocate(x%vector)
  END subroutine
  
  subroutine finalize_t1v(x)
    type(t(kind(0.0))) x(:)
    do i = lbound(x,1), ubound(x,1)
      if (associated(x(i)%vector)) deallocate(x(i)%vector)
    end do
  end subroutine

  elemental subroutine finalize_t2e(x)
    type(t(kind(0.0d0))), intent(inout) :: x
    if (associated(x%vector)) deallocate(x%vector)
  end subroutine
end module

subroutine example(n)
use m

type(t(kind(0.0))) a, b(10), c(n,2)
type(t(kind(0.0d0))) d(n,n)
...
! Returning from this subroutine will effectively do
!    call finalize_t1s(a)
!    call finalize_t1v(b)
!    call finalize_t2e(d)
! No final subroutine will be called for variable C because the user
! omitted to define a suitable specific procedure for it.
end subroutine
```

### Type parameter inquiry

Type parameter inquiry is used to get the value of a type parameter in a PDT.

**Fortran**
```fortran
module t
type t1(i, j)
  integer, len :: i = 4
  integer, len :: j = 2
  character(i*j) :: c
end type
end

program main
use t
type(t1(2, 2)) :: ti
print*, ti%c%len
print*, ti%i
print*, ti%j
end

! Should print:
! 4
! 2
! 2
```

These values are present in the `f18Addendum` and can be retrieved from it with
the correct index. If the length type parameter for a field is an expression,
a compiler generated function is used to computed its value.
The length type parameters are indexed in declaration order; i.e., 0 is the
first length type parameter in the deepest base type.

### PDTs and polymorphism

In some cases with polymorphic entities, it is necessary to copy the length
type parameters from a descriptor to another. With the current design this is
not possible since the descriptor cannot be reallocated and the addendum is
allocated with a fixed number of length type parameters.

**Fortran**
```fortran
! The example below illustrates a case where the number of length type
! parameters are different and need to be copied to an existing descriptor
! addendum.
module m1
type t1
  integer :: i
end type

! This type could be defined in another compilation unit.
type, extends(t1) :: t2(l1, l2)
  integer, len :: l1, l2
end type

contains

subroutine reallocate(x)
  class(t1), allocatable :: x
  allocate(t2(l1=1, l2=2):: x)
end subroutine

end module

program p
  use m1

  class(t1), allocatable :: x

  call reallocate(x)
  ! The new length type parameters need to be propagated at this point.

  ! rest of code using `x`
end program
```

The proposed solution is to add indirection in the `f18Addendum` and store the
length type parameters in a separate block instead of directly in the addendum.
At the moment the storage for the length type parameters is allocated once as
a `std::int64_t` array.

**New f18Addendum**
```cpp
{*derivedType_, *lenParamValues_}
```

Adding the indirection in the descriptor's addendum requires to manage the
lifetime of the block holding the length type parameter values.

Here are some thoughts of how to manage it:
- For allocatables, the space for the LEN parameters can be allocated as part of
  the same malloc as the payload data.
- For automatics, same thing, if we implement automatics as allocatables.
- For monomorphic local variables, the LEN parameters would be in a little array
  on the stack. Or we could treat any variable or component with LEN parameters
  as being automatic even when it's monomorphic.
- For pointers and dummy arguments, we can just copy the pointer in the addendum
  from the target to the pointer or dummy descriptor.
- For dynamically allocated descriptors, the LEN parameter values could just
  follow the addendum in the same malloc.

The addendum of an array sections/sub-objects would point to the same block than
the base object.

In some special cases, a descriptor needs to be passed between the caller and
the callee. This includes array of PDTs and derived-type with PDT components.
The example describe one of the corner case where the length type parameter
would be lost if the descriptor is not passed.

### Example that require a descriptor

Because of the length type parameters store in the addendum, it is required in
some case to pass the PDT with a descriptor to preserve the length type
parameters information. The example below illustrates such a case.

**Fortran**
```fortran
module m
type t
 integer :: i
end type

type, extends(t) :: t2(l)
  integer, len :: l
  real :: x(l)
end type

type base
  type(t2(20)) :: pdt_component
end type

class(t), pointer :: p(:)

contains

subroutine foo(x, n)
  integer :: n
  type(base), target :: x(n)
  ! Without descriptor, the actual argument is a zero-sized array. The length
  ! type parameters of `x(n)%pdt_component` are not propagated from the caller.

  ! A descriptor local to this function is created to pass the array section
  ! in bar. 
  call bar(x%pdt_component)
end subroutine

subroutine bar(x)
  type(t2(*)), target :: x(:)
  p => x
end subroutine

subroutine test()
  type(base), target :: x(100)
  call foo(x(1:-1:1), 0)
  select type (p)
   type is (t2(*))
    ! This type parameters of x(1:60:3) in foo must still live here
    print *, p%l
   class default
     print *, "something else"
  end select
end subroutine
end module

  use m
  call test()
end
```

Because of the use case described above, PDTs, array of PDTs or derived-type
with PDT components will be passed by descriptor.

## FIR operations with length type parameters

Couple of operations have length type parameters as operands already in their
design. For some operations, length type parameters are likely needed with
the two proposed solution. Some other operation like the array operations, the
operands are not needed when dealing with a descriptor since the length type
parameters are in it.

The operations will be updated if needed during the implementation of the
chosen solution.

#### `fir.alloca`

This primitive operation is used to allocate an object on the stack. When
allocating a PDT, the length type parameters are passed to the
operation so its size can be computed accordingly.

**FIR**
```c
%i = arith.constant 10 : i32
%0 = fir.alloca !fir.type<_QMmod1Tpdt{i:i32,data:!fir.array<?xf32>}> (%i : i32)
// %i is the ssa value of the length type parameter
```

#### `fir.allocmem`

This operation is used to create a heap memory reference suitable for storing a
value of the given type. When creating a PDT, the length type parameters are
passed so the size can be computed accordingly.

**FIR**
```c
%i = arith.constant 10 : i32
%0 = fir.alloca !fir.type<_QMmod1Tpdt{i:i32,data:!fir.array<?xf32>}> (%i : i32)
// ...
fir.freemem %0 : !fir.type<_QMmod1Tpdt{i:i32,data:!fir.array<?xf32>}>
```

#### `fir.embox`

The `fir.embox` operation create a boxed reference value. In the case of PDTs
the length type parameters can be passed as well to the operation.

**Fortran**
```fortran
subroutine local()
  type(t(2)) :: x ! simple local PDT
  ! ...
end subroutine
```

**FIR**
```c
func.func @_QMpdt_initPlocal() {
  %c2_i32 = arith.constant 2 : i32
  %0 = fir.alloca !fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}> (%c2 : i32)
       {bindc_name = "x", uniq_name = "_QMpdt_initFlocalEx"}
  // The fir.embox operation is responsible to place the provided length type
  // parameters in the descriptor addendum so they are available to the runtime
  // call later.
  %1 = fir.embox %0 : (!fir.ref<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>) (typeparams %c2 : i32)
       -> !fir.box<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>
  %2 = fir.address_of(@_QQcl.2E2F6669725F7064745F6578616D706C652E66393000) : !fir.ref<!fir.char<1,22>>
  %c8_i32 = arith.constant 8 : i32
  %3 = fir.convert %1 : (!fir.box<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>) -> !fir.box<none>
  %4 = fir.convert %2 : (!fir.ref<!fir.char<1,22>>) -> !fir.ref<i8>
  %5 = fir.call @_FortranAInitialize(%3, %4, %c8_i32) : (!fir.box<none>, !fir.ref<i8>, i32) -> none
  return
}
```

#### `fir.field_index`

The `fir.field_index` operation is used to generate a field offset value from
a field identifier in a derived-type. The operation takes length type parameter
values with a PDT so it can compute a correct offset.

**FIR**
```c
%l = arith.constant 10 : i32
%1 = fir.field_index i, !fir.type<_QMpdt_initTt{l:i32,i:i32}> (%l : i32)
%2 = fir.coordinate_of %ref, %1 : (!fir.type<_QMpdt_initTt{l:i32,i:i32}>, !fir.field) -> !fir.ref<i32>
%3 = fir.load %2 : !fir.ref<i32>
return %3
```

#### `fir.len_param_index`

This operation is used to get the length type parameter offset in from a PDT.

**FIR**
```c
func.func @_QPpdt_len_value(%arg0: !fir.box<!fir.type<t1{l:i32,!fir.array<?xi32>}>>) -> i32 {
  %0 = fir.len_param_index l, !fir.box<!fir.type<t1{l:i32,!fir.array<?xi32>}>>
  %1 = fir.coordinate_of %arg0, %0 : (!fir.box<!fir.type<t1{l:i32,!fir.array<?xi32>}>>, !fir.len) -> !fir.ref<i32>
  %2 = fir.load %1 : !fir.ref<i32>
  return %2 : i32
}
```

#### `fir.save_result`

Save the result of a function returning an array, box, or record type value into
a memory location given the shape and LEN parameters of the result. Length type
parameters is passed if the PDT is not boxed.

**FIR**
```c
func.func @return_pdt(%buffer: !fir.ref<!fir.type<t2(l1:i32,l2:i32){x:f32}>>) {
  %l1 = arith.constant 3 : i32
  %l2 = arith.constant 5 : i32
  %res = fir.call @foo() : () -> !fir.type<t2(l1:i32,l2:i32){x:f32}>
  fir.save_result %res to %buffer typeparams %l1, %l2 : !fir.type<t2(l1:i32,l2:i32){x:f32}>, !fir.ref<!fir.type<t2(l1:i32,l2:i32){x:f32}>>, i32, i32
  return
}
```

#### `fir.array_*` operations

The current design of the different `fir.array_*` operations include length type
parameters operands. This is designed to use PDT without descriptor directly in
FIR.

**FIR**
```c
// Operation used with a boxed PDT does not need the length type parameters as
// they are directly retrieved from the box.
%0 = fir.array_coor %boxed_pdt, %i, %j  (fir.box<fir.array<?x?xfir.type<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>>>, index, index) -> !fir.ref<fir.type<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>>

// In case the PDT would not be boxed, the length type parameters are needed to
// compute the correct addressing.
%0 = fir.array_coor %pdt_base, %i, %j typeparams %l  (fir.ref<fir.array<?x?xfir.type<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>>>, index, index, index) -> !fir.ref<fir.type<PDT>>
```

---

## Implementation choice

While both solutions have pros and cons, we want to implement the outlined
solution.
- The runtime was implemented with this solution in mind.
- The size of the descriptor does not need to be computed at runtime.

---

# Testing

- Lowering part is tested with LIT tests in tree
- PDTs involved a lot of runtime information so executable
  tests will be useful for full testing.

---

# Current TODOs
Current list of TODOs in lowering:
- `flang/lib/Lower/Allocatable.cpp:461` not yet implement: derived type length parameters in allocate
- `flang/lib/Lower/Allocatable.cpp:645` not yet implement: deferred length type parameters
- `flang/lib/Lower/Bridge.cpp:454` not yet implemented: get length parameters from derived type BoxValue
- `flang/lib/Lower/ConvertExpr.cpp:341` not yet implemented: copy derived type with length parameters
- `flang/lib/Lower/ConvertExpr.cpp:993` not yet implemented: component with length parameters in structure constructor
- `flang/lib/Lower/ConvertExpr.cpp:1063` not yet implemented: component with length parameters in structure constructor
- `flang/lib/Lower/ConvertExpr.cpp:1146` not yet implemented: type parameter inquiry
- `flang/lib/Lower/ConvertExpr.cpp:2424` not yet implemented: creating temporary for derived type with length parameters
- `flang/lib/Lower/ConvertExpr.cpp:3742` not yet implemented: gather rhs LEN parameters in assignment to allocatable
- `flang/lib/Lower/ConvertExpr.cpp:4725` not yet implemented: derived type array expression temp with LEN parameters
- `flang/lib/Lower/ConvertExpr.cpp:6400` not yet implemented: PDT size
- `flang/lib/Lower/ConvertExpr.cpp:6419` not yet implemented: PDT offset
- `flang/lib/Lower/ConvertExpr.cpp:6679` not yet implemented: array expr type parameter inquiry
- `flang/lib/Lower/ConvertExpr.cpp:7135` not yet implemented: need to adjust type parameter(s) to reflect the final component
- `flang/lib/Lower/ConvertType.cpp:334` not yet implemented: parameterized derived types
- `flang/lib/Lower/ConvertType.cpp:370` not yet implemented: derived type length parameters
- `flang/lib/Lower/ConvertVariable.cpp:169` not yet implemented: initial-data-target with derived type length parameters
- `flang/lib/Lower/ConvertVariable.cpp:197` not yet implemented: initial-data-target with derived type length parameters
- `flang/lib/Lower/VectorSubscripts.cpp:121` not yet implemented: threading length parameters in field index op
- `flang/lib/Optimizer/Builder/BoxValue.cpp:60` not yet implemented: box value is missing type parameters
- `flang/lib/Optimizer/Builder/BoxValue.cpp:67` not yet implemented: mutable box value is missing type parameters
- `flang/lib/Optimizer/Builder/FIRBuilder.cpp:688` not yet implemented: read fir.box with length parameters
- `flang/lib/Optimizer/Builder/FIRBuilder.cpp:746` not yet implemented: generate code to get LEN type parameters
- `flang/lib/Optimizer/Builder/FIRBuilder.cpp:779` not yet implemented: derived type with type parameters
- `flang/lib/Optimizer/Builder/FIRBuilder.cpp:905` not yet implemented: allocatable and pointer components non deferred length parameters
- `flang/lib/Optimizer/Builder/FIRBuilder.cpp:917` not yet implemented: array component shape depending on length parameters
- `flang/lib/Optimizer/Builder/FIRBuilder.cpp:924` not yet implemented: get character component length from length type parameters
- `flang/lib/Optimizer/Builder/FIRBuilder.cpp:934` not yet implemented: lower component ref that is a derived type with length parameter
- `flang/lib/Optimizer/Builder/FIRBuilder.cpp:956` not yet implemented: get length parameters from derived type BoxValue
- `flang/lib/Optimizer/Builder/MutableBox.cpp:70` not yet implemented: updating mutablebox of derived type with length parameters
- `flang/lib/Optimizer/Builder/MutableBox.cpp:168` not yet implemented: read allocatable or pointer derived type LEN parameters
- `flang/lib/Optimizer/Builder/MutableBox.cpp:310` not yet implemented: update allocatable derived type length parameters
- `flang/lib/Optimizer/Builder/MutableBox.cpp:505` not yet implemented: pointer assignment to derived with length parameters
- `flang/lib/Optimizer/Builder/MutableBox.cpp:597` not yet implemented: pointer assignment to derived with length parameters
- `flang/lib/Optimizer/Builder/MutableBox.cpp:740` not yet implemented: reallocation of derived type entities with length parameters


Current list of TODOs in code generation:

- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:1034` not yet implemented: fir.allocmem codegen of derived type with length parameters
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:1581` not yet implemented: generate call to calculate size of PDT
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:1708` not yet implemented: fir.embox codegen of derived with length parameters
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:1749` not yet implemented: reboxing descriptor of derived type with length parameters
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:2229` not yet implemented: derived type with type parameters
- `flang/lib/Optimizer/CodeGen/CodeGen.cpp:2256` not yet implemented: compute size of derived type with type parameters
- `flang/lib/Optimizer/CodeGen/TypeConverter.h:257` not yet implemented: extended descriptor derived with length parameters

Current list of TODOs in optimizations:

- `flang/lib/Optimizer/Transforms/ArrayValueCopy.cpp:1007` not yet implemented: unhandled dynamic type parameters

---

Resources:
- [0] Fortran standard
- [1] https://en.wikipedia.org/wiki/Dependent_type