Skip to content

Commit 7cf1608

Browse files
committed
[flang] Rework handling of non-type-bound user-defined I/O
A fairly recent introduction of runtime I/O APIs called OutputDerivedType() and InputDerivedType() didn't cover NAMELIST I/O's need to access non-type-bound generic interfaces for user-defined derived type I/O when those generic interfaces are defined in some scope other than the one that defines the derived type. The patch adds a new data structure shared between lowering and the runtime that can represent all of the cases that can arise with non-type-bound defined I/O. It can represent scopes in which non-type-bound defined I/O generic interfaces are inaccessible, too, due to IMPORT statements. The data structure is now an operand to OutputDerivedType() and InputDerivedType() as well as a data member in the NamelistGroup structure. Differential Revision: https://reviews.llvm.org/D148257
1 parent 3ece37b commit 7cf1608

28 files changed

+503
-349
lines changed

flang/include/flang/Common/Fortran.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ ENUM_CLASS(IoSpecKind, Access, Action, Advance, Asynchronous, Blank, Decimal,
5757
Dispose, // nonstandard
5858
)
5959

60+
// Defined I/O variants
61+
ENUM_CLASS(
62+
DefinedIo, ReadFormatted, ReadUnformatted, WriteFormatted, WriteUnformatted)
63+
const char *AsFortran(DefinedIo);
64+
6065
// Floating-point rounding modes; these are packed into a byte to save
6166
// room in the runtime's format processing context structure.
6267
enum class RoundingMode : std::uint8_t {
@@ -75,5 +80,6 @@ static constexpr int maxRank{15};
7580

7681
// Fortran names may have up to 63 characters (See Fortran 2018 C601).
7782
static constexpr int maxNameLen{63};
83+
7884
} // namespace Fortran::common
7985
#endif // FORTRAN_COMMON_FORTRAN_H_

flang/include/flang/Runtime/io-api.h

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Descriptor;
2323

2424
namespace Fortran::runtime::io {
2525

26+
class NonTbpDefinedIoTable;
2627
class NamelistGroup;
2728
class IoStatementState;
2829
using Cookie = IoStatementState *;
@@ -275,21 +276,19 @@ bool IONAME(InputLogical)(Cookie, bool &);
275276
bool IONAME(OutputNamelist)(Cookie, const NamelistGroup &);
276277
bool IONAME(InputNamelist)(Cookie, const NamelistGroup &);
277278

278-
// When an I/O list item has a derived type with a specific user-defined
279+
// When an I/O list item has a derived type with a specific defined
279280
// I/O subroutine of the appropriate generic kind for the active
280281
// I/O data transfer statement (read/write, formatted/unformatted)
281-
// and that I/O subroutine is a specific procedure for an explicit
282-
// generic INTERFACE or GENERIC statement that is *not* type-bound,
283-
// this data item transfer API enables the use of that procedure
284-
// for the item. Pass 'true' for 'isPolymorphic' when the first ("dtv")
285-
// dummy argument of the specific procedure is CLASS(t), not TYPE(t).
286-
// If the procedure pointer is null, or when the next edit descriptor for
287-
// formatted I/O is not DT, the procedure will not be called and the
288-
// behavior will be as if (Output/Input)Descriptor had been called.
282+
// that pertains to the type or its components, and those subroutines
283+
// are dynamic or neither type-bound nor defined with interfaces
284+
// in the same scope as the derived type (or an IMPORT statement has
285+
// made such a generic interface inaccessible), these data item transfer
286+
// APIs enable the I/O runtime to make the right calls to defined I/O
287+
// subroutines.
289288
bool IONAME(OutputDerivedType)(
290-
Cookie, const Descriptor &, void (*)(), bool isPolymorphic);
289+
Cookie, const Descriptor &, const NonTbpDefinedIoTable *);
291290
bool IONAME(InputDerivedType)(
292-
Cookie, const Descriptor &, void (*)(), bool isPolymorphic);
291+
Cookie, const Descriptor &, const NonTbpDefinedIoTable *);
293292

294293
// Additional specifier interfaces for the connection-list of
295294
// on OPEN statement (only). SetBlank(), SetDecimal(),

flang/include/flang/Semantics/runtime-type-info.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#define FORTRAN_SEMANTICS_RUNTIME_TYPE_INFO_H_
1616

1717
#include "flang/Common/reference.h"
18+
#include "flang/Semantics/symbol.h"
19+
#include <map>
1820
#include <set>
1921
#include <string>
2022
#include <vector>
@@ -24,12 +26,6 @@ class raw_ostream;
2426
}
2527

2628
namespace Fortran::semantics {
27-
class Scope;
28-
class SemanticsContext;
29-
class Symbol;
30-
31-
using SymbolRef = common::Reference<const Symbol>;
32-
using SymbolVector = std::vector<SymbolRef>;
3329

3430
struct RuntimeDerivedTypeTables {
3531
Scope *schemata{nullptr};
@@ -52,5 +48,14 @@ constexpr char procCompName[]{"proc"};
5248

5349
SymbolVector CollectBindings(const Scope &dtScope);
5450

51+
struct NonTbpDefinedIo {
52+
const Symbol *subroutine;
53+
common::DefinedIo definedIo;
54+
bool isDtvArgPolymorphic;
55+
};
56+
57+
std::multimap<const Symbol *, NonTbpDefinedIo>
58+
CollectNonTbpDefinedIoGenericInterfaces(const Scope &scope);
59+
5560
} // namespace Fortran::semantics
5661
#endif // FORTRAN_SEMANTICS_RUNTIME_TYPE_INFO_H_

flang/include/flang/Semantics/symbol.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,6 @@ class HostAssocDetails {
445445
// defined assignment, intrinsic operator, or defined I/O.
446446
struct GenericKind {
447447
ENUM_CLASS(OtherKind, Name, DefinedOp, Assignment, Concat)
448-
ENUM_CLASS(DefinedIo, // defined io
449-
ReadFormatted, ReadUnformatted, WriteFormatted, WriteUnformatted)
450448
GenericKind() : u{OtherKind::Name} {}
451449
template <typename T> GenericKind(const T &x) { u = x; }
452450
bool IsName() const { return Is(OtherKind::Name); }
@@ -455,9 +453,9 @@ struct GenericKind {
455453
bool IsIntrinsicOperator() const;
456454
bool IsOperator() const;
457455
std::string ToString() const;
458-
static SourceName AsFortran(DefinedIo);
456+
static SourceName AsFortran(common::DefinedIo);
459457
std::variant<OtherKind, common::NumericOperator, common::LogicalOperator,
460-
common::RelationalOperator, DefinedIo>
458+
common::RelationalOperator, common::DefinedIo>
461459
u;
462460

463461
private:

flang/include/flang/Semantics/tools.h

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -620,30 +620,19 @@ std::optional<ArraySpec> ToArraySpec(
620620
std::optional<ArraySpec> ToArraySpec(
621621
evaluate::FoldingContext &, const std::optional<evaluate::Shape> &);
622622

623-
// Searches a derived type and a scope for a particular user defined I/O
624-
// procedure.
623+
// Searches a derived type and a scope for a particular defined I/O procedure.
625624
bool HasDefinedIo(
626-
GenericKind::DefinedIo, const DerivedTypeSpec &, const Scope * = nullptr);
625+
common::DefinedIo, const DerivedTypeSpec &, const Scope * = nullptr);
627626

628627
// Some intrinsic operators have more than one name (e.g. `operator(.eq.)` and
629628
// `operator(==)`). GetAllNames() returns them all, including symbolName.
630629
std::forward_list<std::string> GetAllNames(
631630
const SemanticsContext &, const SourceName &);
632631

633632
// Determines the derived type of a procedure's initial "dtv" dummy argument,
634-
// assuming that the procedure is a specific procedure of a user-defined
635-
// derived type I/O generic interface,
633+
// assuming that the procedure is a specific procedure of a defined I/O
634+
// generic interface,
636635
const DerivedTypeSpec *GetDtvArgDerivedType(const Symbol &);
637636

638-
// Locates a non-type-bound generic interface in the enclosing scopes for a
639-
// given user-defined derived type I/O operation, given a specific derived type
640-
// spec. Intended for use when lowering I/O data list items to identify a remote
641-
// or dynamic non-type-bound UDDTIO subroutine so that it can be passed to the
642-
// I/O runtime's NonTypeBoundDefinedIo() API.
643-
std::pair<const Symbol *, bool /*isPolymorphic*/> FindNonTypeBoundDefinedIo(
644-
const SemanticsContext, const parser::OutputItem &, bool isFormatted);
645-
std::pair<const Symbol *, bool /*isPolymorphic*/> FindNonTypeBoundDefinedIo(
646-
const SemanticsContext, const parser::InputItem &, bool isFormatted);
647-
648637
} // namespace Fortran::semantics
649638
#endif // FORTRAN_SEMANTICS_TOOLS_H_

flang/lib/Common/Fortran.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,18 @@ const char *AsFortran(RelationalOperator opr) {
6060
}
6161
}
6262

63+
const char *AsFortran(DefinedIo x) {
64+
switch (x) {
65+
SWITCH_COVERS_ALL_CASES
66+
case DefinedIo::ReadFormatted:
67+
return "read(formatted)";
68+
case DefinedIo::ReadUnformatted:
69+
return "read(unformatted)";
70+
case DefinedIo::WriteFormatted:
71+
return "write(formatted)";
72+
case DefinedIo::WriteUnformatted:
73+
return "write(unformatted)";
74+
}
75+
}
76+
6377
} // namespace Fortran::common

flang/lib/Semantics/check-declarations.cpp

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -121,30 +121,29 @@ class CheckHelper {
121121
void CheckBindCFunctionResult(const Symbol &);
122122
// Check functions for defined I/O procedures
123123
void CheckDefinedIoProc(
124-
const Symbol &, const GenericDetails &, GenericKind::DefinedIo);
124+
const Symbol &, const GenericDetails &, common::DefinedIo);
125125
bool CheckDioDummyIsData(const Symbol &, const Symbol *, std::size_t);
126-
void CheckDioDummyIsDerived(const Symbol &, const Symbol &,
127-
GenericKind::DefinedIo ioKind, const Symbol &);
126+
void CheckDioDummyIsDerived(
127+
const Symbol &, const Symbol &, common::DefinedIo ioKind, const Symbol &);
128128
void CheckDioDummyIsDefaultInteger(const Symbol &, const Symbol &);
129129
void CheckDioDummyIsScalar(const Symbol &, const Symbol &);
130130
void CheckDioDummyAttrs(const Symbol &, const Symbol &, Attr);
131131
void CheckDioDtvArg(
132-
const Symbol &, const Symbol *, GenericKind::DefinedIo, const Symbol &);
132+
const Symbol &, const Symbol *, common::DefinedIo, const Symbol &);
133133
void CheckGenericVsIntrinsic(const Symbol &, const GenericDetails &);
134134
void CheckDefaultIntegerArg(const Symbol &, const Symbol *, Attr);
135135
void CheckDioAssumedLenCharacterArg(
136136
const Symbol &, const Symbol *, std::size_t, Attr);
137137
void CheckDioVlistArg(const Symbol &, const Symbol *, std::size_t);
138-
void CheckDioArgCount(
139-
const Symbol &, GenericKind::DefinedIo ioKind, std::size_t);
138+
void CheckDioArgCount(const Symbol &, common::DefinedIo ioKind, std::size_t);
140139
struct TypeWithDefinedIo {
141140
const DerivedTypeSpec &type;
142-
GenericKind::DefinedIo ioKind;
141+
common::DefinedIo ioKind;
143142
const Symbol &proc;
144143
const Symbol &generic;
145144
};
146-
void CheckAlreadySeenDefinedIo(const DerivedTypeSpec &,
147-
GenericKind::DefinedIo, const Symbol &, const Symbol &generic);
145+
void CheckAlreadySeenDefinedIo(const DerivedTypeSpec &, common::DefinedIo,
146+
const Symbol &, const Symbol &generic);
148147
void CheckModuleProcedureDef(const Symbol &);
149148

150149
SemanticsContext &context_;
@@ -1426,7 +1425,7 @@ void CheckHelper::CheckGeneric(
14261425
const Symbol &symbol, const GenericDetails &details) {
14271426
CheckSpecifics(symbol, details);
14281427
common::visit(common::visitors{
1429-
[&](const GenericKind::DefinedIo &io) {
1428+
[&](const common::DefinedIo &io) {
14301429
CheckDefinedIoProc(symbol, details, io);
14311430
},
14321431
[&](const GenericKind::OtherKind &other) {
@@ -2498,13 +2497,13 @@ bool CheckHelper::CheckDioDummyIsData(
24982497
}
24992498

25002499
void CheckHelper::CheckAlreadySeenDefinedIo(const DerivedTypeSpec &derivedType,
2501-
GenericKind::DefinedIo ioKind, const Symbol &proc, const Symbol &generic) {
2502-
// Check for conflict between non-type-bound UDDTIO and type-bound generics.
2503-
// It's okay to have two or more distinct derived type I/O procedures
2504-
// for the same type if they're coming from distinct non-type-bound
2505-
// interfaces. (The non-type-bound interfaces would have been merged into
2506-
// a single generic -- with errors where indistinguishable -- if both were
2507-
// visible in the same scope.)
2500+
common::DefinedIo ioKind, const Symbol &proc, const Symbol &generic) {
2501+
// Check for conflict between non-type-bound defined I/O and type-bound
2502+
// generics. It's okay to have two or more distinct defined I/O procedures for
2503+
// the same type if they're coming from distinct non-type-bound interfaces.
2504+
// (The non-type-bound interfaces would have been merged into a single generic
2505+
// -- with errors where indistinguishable -- when both were visible from the
2506+
// same scope.)
25082507
if (generic.owner().IsDerivedType()) {
25092508
return;
25102509
}
@@ -2528,7 +2527,7 @@ void CheckHelper::CheckAlreadySeenDefinedIo(const DerivedTypeSpec &derivedType,
25282527
}
25292528

25302529
void CheckHelper::CheckDioDummyIsDerived(const Symbol &subp, const Symbol &arg,
2531-
GenericKind::DefinedIo ioKind, const Symbol &generic) {
2530+
common::DefinedIo ioKind, const Symbol &generic) {
25322531
if (const DeclTypeSpec *type{arg.GetType()}) {
25332532
if (const DerivedTypeSpec *derivedType{type->AsDerived()}) {
25342533
CheckAlreadySeenDefinedIo(*derivedType, ioKind, subp, generic);
@@ -2573,13 +2572,13 @@ void CheckHelper::CheckDioDummyIsScalar(const Symbol &subp, const Symbol &arg) {
25732572
}
25742573

25752574
void CheckHelper::CheckDioDtvArg(const Symbol &subp, const Symbol *arg,
2576-
GenericKind::DefinedIo ioKind, const Symbol &generic) {
2575+
common::DefinedIo ioKind, const Symbol &generic) {
25772576
// Dtv argument looks like: dtv-type-spec, INTENT(INOUT) :: dtv
25782577
if (CheckDioDummyIsData(subp, arg, 0)) {
25792578
CheckDioDummyIsDerived(subp, *arg, ioKind, generic);
25802579
CheckDioDummyAttrs(subp, *arg,
2581-
ioKind == GenericKind::DefinedIo::ReadFormatted ||
2582-
ioKind == GenericKind::DefinedIo::ReadUnformatted
2580+
ioKind == common::DefinedIo::ReadFormatted ||
2581+
ioKind == common::DefinedIo::ReadUnformatted
25832582
? Attr::INTENT_INOUT
25842583
: Attr::INTENT_IN);
25852584
}
@@ -2668,10 +2667,10 @@ void CheckHelper::CheckDioVlistArg(
26682667
}
26692668

26702669
void CheckHelper::CheckDioArgCount(
2671-
const Symbol &subp, GenericKind::DefinedIo ioKind, std::size_t argCount) {
2670+
const Symbol &subp, common::DefinedIo ioKind, std::size_t argCount) {
26722671
const std::size_t requiredArgCount{
2673-
(std::size_t)(ioKind == GenericKind::DefinedIo::ReadFormatted ||
2674-
ioKind == GenericKind::DefinedIo::WriteFormatted
2672+
(std::size_t)(ioKind == common::DefinedIo::ReadFormatted ||
2673+
ioKind == common::DefinedIo::WriteFormatted
26752674
? 6
26762675
: 4)};
26772676
if (argCount != requiredArgCount) {
@@ -2704,7 +2703,7 @@ void CheckHelper::CheckDioDummyAttrs(
27042703

27052704
// Enforce semantics for defined input/output procedures (12.6.4.8.2) and C777
27062705
void CheckHelper::CheckDefinedIoProc(const Symbol &symbol,
2707-
const GenericDetails &details, GenericKind::DefinedIo ioKind) {
2706+
const GenericDetails &details, common::DefinedIo ioKind) {
27082707
for (auto ref : details.specificProcs()) {
27092708
const Symbol &ultimate{ref->GetUltimate()};
27102709
const auto *binding{ultimate.detailsIf<ProcBindingDetails>()};
@@ -2730,8 +2729,8 @@ void CheckHelper::CheckDefinedIoProc(const Symbol &symbol,
27302729
CheckDefaultIntegerArg(specific, arg, Attr::INTENT_IN);
27312730
break;
27322731
case 2:
2733-
if (ioKind == GenericKind::DefinedIo::ReadFormatted ||
2734-
ioKind == GenericKind::DefinedIo::WriteFormatted) {
2732+
if (ioKind == common::DefinedIo::ReadFormatted ||
2733+
ioKind == common::DefinedIo::WriteFormatted) {
27352734
// CHARACTER (LEN=*), INTENT(IN) :: iotype
27362735
CheckDioAssumedLenCharacterArg(
27372736
specific, arg, argCount, Attr::INTENT_IN);
@@ -2741,8 +2740,8 @@ void CheckHelper::CheckDefinedIoProc(const Symbol &symbol,
27412740
}
27422741
break;
27432742
case 3:
2744-
if (ioKind == GenericKind::DefinedIo::ReadFormatted ||
2745-
ioKind == GenericKind::DefinedIo::WriteFormatted) {
2743+
if (ioKind == common::DefinedIo::ReadFormatted ||
2744+
ioKind == common::DefinedIo::WriteFormatted) {
27462745
// INTEGER, INTENT(IN) :: v_list(:)
27472746
CheckDioVlistArg(specific, arg, argCount);
27482747
} else {

0 commit comments

Comments
 (0)