1 //===-- runtime/descriptor-io.cpp -----------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "descriptor-io.h"
10 #include "flang/Common/restorer.h"
11 #include "flang/Runtime/freestanding-tools.h"
13 namespace Fortran::runtime::io::descr
{
14 RT_OFFLOAD_API_GROUP_BEGIN
16 // Defined formatted I/O (maybe)
17 Fortran::common::optional
<bool> DefinedFormattedIo(IoStatementState
&io
,
18 const Descriptor
&descriptor
, const typeInfo::DerivedType
&derived
,
19 const typeInfo::SpecialBinding
&special
,
20 const SubscriptValue subscripts
[]) {
21 Fortran::common::optional
<DataEdit
> peek
{
22 io
.GetNextDataEdit(0 /*to peek at it*/)};
24 (peek
->descriptor
== DataEdit::DefinedDerivedType
||
25 peek
->descriptor
== DataEdit::ListDirected
)) {
27 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
28 DataEdit edit
{*io
.GetNextDataEdit(1)}; // now consume it; no repeats
29 RUNTIME_CHECK(handler
, edit
.descriptor
== peek
->descriptor
);
30 char ioType
[2 + edit
.maxIoTypeChars
];
31 auto ioTypeLen
{std::size_t{2} /*"DT"*/ + edit
.ioTypeChars
};
32 if (edit
.descriptor
== DataEdit::DefinedDerivedType
) {
35 std::memcpy(ioType
+ 2, edit
.ioType
, edit
.ioTypeChars
);
38 ioType
, io
.mutableModes().inNamelist
? "NAMELIST" : "LISTDIRECTED");
39 ioTypeLen
= runtime::strlen(ioType
);
41 StaticDescriptor
<1, true> vListStatDesc
;
42 Descriptor
&vListDesc
{vListStatDesc
.descriptor()};
43 vListDesc
.Establish(TypeCategory::Integer
, sizeof(int), nullptr, 1);
44 vListDesc
.set_base_addr(edit
.vList
);
45 vListDesc
.GetDimension(0).SetBounds(1, edit
.vListEntries
);
46 vListDesc
.GetDimension(0).SetByteStride(
47 static_cast<SubscriptValue
>(sizeof(int)));
48 ExternalFileUnit
*actualExternal
{io
.GetExternalFileUnit()};
49 ExternalFileUnit
*external
{actualExternal
};
51 // Create a new unit to service defined I/O for an
52 // internal I/O parent.
53 external
= &ExternalFileUnit::NewUnit(handler
, true);
55 ChildIo
&child
{external
->PushChildIo(io
)};
56 // Child formatted I/O is nonadvancing by definition (F'2018 12.6.2.4).
57 auto restorer
{common::ScopedSet(io
.mutableModes().nonAdvancing
, true)};
58 int unit
{external
->unitNumber()};
61 Fortran::common::optional
<std::int64_t> startPos
;
62 if (edit
.descriptor
== DataEdit::DefinedDerivedType
&&
63 special
.which() == typeInfo::SpecialBinding::Which::ReadFormatted
) {
64 // DT is an edit descriptor so everything that the child
65 // I/O subroutine reads counts towards READ(SIZE=).
66 startPos
= io
.InquirePos();
68 if (special
.IsArgDescriptor(0)) {
69 // "dtv" argument is "class(t)", pass a descriptor
70 auto *p
{special
.GetProc
<void (*)(const Descriptor
&, int &, char *,
71 const Descriptor
&, int &, char *, std::size_t, std::size_t)>()};
72 StaticDescriptor
<1, true, 10 /*?*/> elementStatDesc
;
73 Descriptor
&elementDesc
{elementStatDesc
.descriptor()};
74 elementDesc
.Establish(
75 derived
, nullptr, 0, nullptr, CFI_attribute_pointer
);
76 elementDesc
.set_base_addr(descriptor
.Element
<char>(subscripts
));
77 p(elementDesc
, unit
, ioType
, vListDesc
, ioStat
, ioMsg
, ioTypeLen
,
80 // "dtv" argument is "type(t)", pass a raw pointer
81 auto *p
{special
.GetProc
<void (*)(const void *, int &, char *,
82 const Descriptor
&, int &, char *, std::size_t, std::size_t)>()};
83 p(descriptor
.Element
<char>(subscripts
), unit
, ioType
, vListDesc
, ioStat
,
84 ioMsg
, ioTypeLen
, sizeof ioMsg
);
86 handler
.Forward(ioStat
, ioMsg
, sizeof ioMsg
);
87 external
->PopChildIo(child
);
88 if (!actualExternal
) {
89 // Close unit created for internal I/O above.
90 auto *closing
{external
->LookUpForClose(external
->unitNumber())};
91 RUNTIME_CHECK(handler
, external
== closing
);
92 external
->DestroyClosed();
95 io
.GotChar(io
.InquirePos() - *startPos
);
97 return handler
.GetIoStat() == IostatOk
;
99 // There's a defined I/O subroutine, but there's a FORMAT present and
100 // it does not have a DT data edit descriptor, so apply default formatting
101 // to the components of the derived type as usual.
102 return Fortran::common::nullopt
;
106 // Defined unformatted I/O
107 bool DefinedUnformattedIo(IoStatementState
&io
, const Descriptor
&descriptor
,
108 const typeInfo::DerivedType
&derived
,
109 const typeInfo::SpecialBinding
&special
) {
110 // Unformatted I/O must have an external unit (or child thereof).
111 IoErrorHandler
&handler
{io
.GetIoErrorHandler()};
112 ExternalFileUnit
*external
{io
.GetExternalFileUnit()};
113 if (!external
) { // INQUIRE(IOLENGTH=)
114 handler
.SignalError(IostatNonExternalDefinedUnformattedIo
);
117 ChildIo
&child
{external
->PushChildIo(io
)};
118 int unit
{external
->unitNumber()};
119 int ioStat
{IostatOk
};
121 std::size_t numElements
{descriptor
.Elements()};
122 SubscriptValue subscripts
[maxRank
];
123 descriptor
.GetLowerBounds(subscripts
);
124 if (special
.IsArgDescriptor(0)) {
125 // "dtv" argument is "class(t)", pass a descriptor
126 auto *p
{special
.GetProc
<void (*)(
127 const Descriptor
&, int &, int &, char *, std::size_t)>()};
128 StaticDescriptor
<1, true, 10 /*?*/> elementStatDesc
;
129 Descriptor
&elementDesc
{elementStatDesc
.descriptor()};
130 elementDesc
.Establish(derived
, nullptr, 0, nullptr, CFI_attribute_pointer
);
131 for (; numElements
-- > 0; descriptor
.IncrementSubscripts(subscripts
)) {
132 elementDesc
.set_base_addr(descriptor
.Element
<char>(subscripts
));
133 p(elementDesc
, unit
, ioStat
, ioMsg
, sizeof ioMsg
);
134 if (ioStat
!= IostatOk
) {
139 // "dtv" argument is "type(t)", pass a raw pointer
140 auto *p
{special
.GetProc
<void (*)(
141 const void *, int &, int &, char *, std::size_t)>()};
142 for (; numElements
-- > 0; descriptor
.IncrementSubscripts(subscripts
)) {
143 p(descriptor
.Element
<char>(subscripts
), unit
, ioStat
, ioMsg
,
145 if (ioStat
!= IostatOk
) {
150 handler
.Forward(ioStat
, ioMsg
, sizeof ioMsg
);
151 external
->PopChildIo(child
);
152 return handler
.GetIoStat() == IostatOk
;
155 RT_OFFLOAD_API_GROUP_END
156 } // namespace Fortran::runtime::io::descr