[flang] Add checks for misuse of formatted I/O APIs in unformatted I/O statement
Add checking to I/O statement APIs to catch cases where the formatted I/O data item transfer routines like OutputInteger64 are being incorrectly used for unformatted I/O, which should use the unformatted block or descriptor-based data item interfaces. Differential revision: https://reviews.llvm.org/D88672
This commit is contained in:
parent
17640c5aac
commit
cdfb95ad58
|
@ -922,14 +922,16 @@ bool IONAME(InputUnformattedBlock)(
|
|||
}
|
||||
|
||||
bool IONAME(OutputInteger64)(Cookie cookie, std::int64_t n) {
|
||||
cookie->CheckFormattedStmtType<Direction::Output>("OutputInteger64");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(
|
||||
TypeCategory::Integer, 8, reinterpret_cast<void *>(&n), 0);
|
||||
TypeCategory::Integer, sizeof n, reinterpret_cast<void *>(&n), 0);
|
||||
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
|
||||
}
|
||||
|
||||
bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
|
||||
cookie->CheckFormattedStmtType<Direction::Input>("InputInteger");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(
|
||||
|
@ -938,6 +940,7 @@ bool IONAME(InputInteger)(Cookie cookie, std::int64_t &n, int kind) {
|
|||
}
|
||||
|
||||
bool IONAME(OutputReal32)(Cookie cookie, float x) {
|
||||
cookie->CheckFormattedStmtType<Direction::Output>("OutputReal32");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
|
||||
|
@ -945,6 +948,7 @@ bool IONAME(OutputReal32)(Cookie cookie, float x) {
|
|||
}
|
||||
|
||||
bool IONAME(OutputReal64)(Cookie cookie, double x) {
|
||||
cookie->CheckFormattedStmtType<Direction::Output>("OutputReal64");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
|
||||
|
@ -952,6 +956,7 @@ bool IONAME(OutputReal64)(Cookie cookie, double x) {
|
|||
}
|
||||
|
||||
bool IONAME(InputReal32)(Cookie cookie, float &x) {
|
||||
cookie->CheckFormattedStmtType<Direction::Input>("InputReal32");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(TypeCategory::Real, 4, reinterpret_cast<void *>(&x), 0);
|
||||
|
@ -959,6 +964,7 @@ bool IONAME(InputReal32)(Cookie cookie, float &x) {
|
|||
}
|
||||
|
||||
bool IONAME(InputReal64)(Cookie cookie, double &x) {
|
||||
cookie->CheckFormattedStmtType<Direction::Input>("InputReal64");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(TypeCategory::Real, 8, reinterpret_cast<void *>(&x), 0);
|
||||
|
@ -966,6 +972,7 @@ bool IONAME(InputReal64)(Cookie cookie, double &x) {
|
|||
}
|
||||
|
||||
bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) {
|
||||
cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex32");
|
||||
float z[2]{r, i};
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
|
@ -975,6 +982,7 @@ bool IONAME(OutputComplex32)(Cookie cookie, float r, float i) {
|
|||
}
|
||||
|
||||
bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) {
|
||||
cookie->CheckFormattedStmtType<Direction::Output>("OutputComplex64");
|
||||
double z[2]{r, i};
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
|
@ -984,6 +992,7 @@ bool IONAME(OutputComplex64)(Cookie cookie, double r, double i) {
|
|||
}
|
||||
|
||||
bool IONAME(InputComplex32)(Cookie cookie, float z[2]) {
|
||||
cookie->CheckFormattedStmtType<Direction::Input>("InputComplex32");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(
|
||||
|
@ -992,6 +1001,7 @@ bool IONAME(InputComplex32)(Cookie cookie, float z[2]) {
|
|||
}
|
||||
|
||||
bool IONAME(InputComplex64)(Cookie cookie, double z[2]) {
|
||||
cookie->CheckFormattedStmtType<Direction::Input>("InputComplex64");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(
|
||||
|
@ -999,34 +1009,48 @@ bool IONAME(InputComplex64)(Cookie cookie, double z[2]) {
|
|||
return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
|
||||
}
|
||||
|
||||
bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) {
|
||||
bool IONAME(OutputCharacter)(
|
||||
Cookie cookie, const char *x, std::size_t length, int kind) {
|
||||
cookie->CheckFormattedStmtType<Direction::Output>("OutputCharacter");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(
|
||||
1, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0);
|
||||
kind, length, reinterpret_cast<void *>(const_cast<char *>(x)), 0);
|
||||
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
|
||||
}
|
||||
|
||||
bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) {
|
||||
bool IONAME(OutputAscii)(Cookie cookie, const char *x, std::size_t length) {
|
||||
return IONAME(OutputCharacter(cookie, x, length, 1));
|
||||
}
|
||||
|
||||
bool IONAME(InputCharacter)(
|
||||
Cookie cookie, char *x, std::size_t length, int kind) {
|
||||
cookie->CheckFormattedStmtType<Direction::Input>("InputCharacter");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(1, length, reinterpret_cast<void *>(x), 0);
|
||||
descriptor.Establish(kind, length, reinterpret_cast<void *>(x), 0);
|
||||
return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
|
||||
}
|
||||
|
||||
bool IONAME(InputAscii)(Cookie cookie, char *x, std::size_t length) {
|
||||
return IONAME(InputCharacter(cookie, x, length, 1));
|
||||
}
|
||||
|
||||
bool IONAME(OutputLogical)(Cookie cookie, bool truth) {
|
||||
cookie->CheckFormattedStmtType<Direction::Output>("OutputLogical");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(
|
||||
TypeCategory::Logical, 1, reinterpret_cast<void *>(&truth), 0);
|
||||
TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
|
||||
return descr::DescriptorIO<Direction::Output>(*cookie, descriptor);
|
||||
}
|
||||
|
||||
bool IONAME(InputLogical)(Cookie cookie, bool &truth) {
|
||||
cookie->CheckFormattedStmtType<Direction::Input>("InputLogical");
|
||||
StaticDescriptor staticDescriptor;
|
||||
Descriptor &descriptor{staticDescriptor.descriptor()};
|
||||
descriptor.Establish(
|
||||
TypeCategory::Logical, 1, reinterpret_cast<void *>(&truth), 0);
|
||||
TypeCategory::Logical, sizeof truth, reinterpret_cast<void *>(&truth), 0);
|
||||
return descr::DescriptorIO<Direction::Input>(*cookie, descriptor);
|
||||
}
|
||||
|
||||
|
|
|
@ -231,10 +231,12 @@ bool IONAME(SetSign)(Cookie, const char *, std::size_t);
|
|||
// and avoid the following items when they might crash.
|
||||
bool IONAME(OutputDescriptor)(Cookie, const Descriptor &);
|
||||
bool IONAME(InputDescriptor)(Cookie, const Descriptor &);
|
||||
// Contiguous transfers for unformatted I/O
|
||||
bool IONAME(OutputUnformattedBlock)(
|
||||
Cookie, const char *, std::size_t, std::size_t elementBytes);
|
||||
bool IONAME(InputUnformattedBlock)(
|
||||
Cookie, char *, std::size_t, std::size_t elementBytes);
|
||||
// Formatted (including list directed) I/O data items
|
||||
bool IONAME(OutputInteger64)(Cookie, std::int64_t);
|
||||
bool IONAME(InputInteger)(Cookie, std::int64_t &, int kind = 8);
|
||||
bool IONAME(OutputReal32)(Cookie, float);
|
||||
|
@ -245,7 +247,9 @@ bool IONAME(OutputComplex32)(Cookie, float, float);
|
|||
bool IONAME(InputComplex32)(Cookie, float[2]);
|
||||
bool IONAME(OutputComplex64)(Cookie, double, double);
|
||||
bool IONAME(InputComplex64)(Cookie, double[2]);
|
||||
bool IONAME(OutputCharacter)(Cookie, const char *, std::size_t, int kind = 1);
|
||||
bool IONAME(OutputAscii)(Cookie, const char *, std::size_t);
|
||||
bool IONAME(InputCharacter)(Cookie, char *, std::size_t, int kind = 1);
|
||||
bool IONAME(InputAscii)(Cookie, char *, std::size_t);
|
||||
bool IONAME(OutputLogical)(Cookie, bool);
|
||||
bool IONAME(InputLogical)(Cookie, bool &);
|
||||
|
|
|
@ -43,6 +43,13 @@ class ExternalFormattedIoStatementState;
|
|||
template <Direction> class ExternalListIoStatementState;
|
||||
template <Direction> class UnformattedIoStatementState;
|
||||
|
||||
struct InputStatementState {};
|
||||
struct OutputStatementState {};
|
||||
template <Direction D>
|
||||
using IoDirectionState = std::conditional_t<D == Direction::Input,
|
||||
InputStatementState, OutputStatementState>;
|
||||
struct FormattedIoStatementState {};
|
||||
|
||||
// The Cookie type in the I/O API is a pointer (for C) to this class.
|
||||
class IoStatementState {
|
||||
public:
|
||||
|
@ -90,6 +97,15 @@ public:
|
|||
std::optional<char32_t> NextInField(std::optional<int> &remaining);
|
||||
std::optional<char32_t> GetNextNonBlank(); // can advance record
|
||||
|
||||
template <Direction D> void CheckFormattedStmtType(const char *name) {
|
||||
if (!get_if<FormattedIoStatementState>() ||
|
||||
!get_if<IoDirectionState<D>>()) {
|
||||
GetIoErrorHandler().Crash(
|
||||
"%s called for I/O statement that is not formatted %s", name,
|
||||
D == Direction::Output ? "output" : "input");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<std::reference_wrapper<OpenStatementState>,
|
||||
std::reference_wrapper<CloseStatementState>,
|
||||
|
@ -132,17 +148,11 @@ struct IoStatementBase : public DefaultFormatControlCallbacks {
|
|||
void BadInquiryKeywordHashCrash(InquiryKeywordHash);
|
||||
};
|
||||
|
||||
struct InputStatementState {};
|
||||
struct OutputStatementState {};
|
||||
template <Direction D>
|
||||
using IoDirectionState = std::conditional_t<D == Direction::Input,
|
||||
InputStatementState, OutputStatementState>;
|
||||
|
||||
struct FormattedStatementState {};
|
||||
|
||||
// Common state for list-directed internal & external I/O
|
||||
template <Direction> struct ListDirectedStatementState {};
|
||||
template <> struct ListDirectedStatementState<Direction::Output> {
|
||||
template <Direction> struct ListDirectedStatementState;
|
||||
template <>
|
||||
struct ListDirectedStatementState<Direction::Output>
|
||||
: public FormattedIoStatementState {
|
||||
static std::size_t RemainingSpaceInRecord(const ConnectionState &);
|
||||
bool NeedAdvance(const ConnectionState &, std::size_t) const;
|
||||
bool EmitLeadingSpaceOrAdvance(
|
||||
|
@ -151,7 +161,9 @@ template <> struct ListDirectedStatementState<Direction::Output> {
|
|||
IoStatementState &, int maxRepeat = 1);
|
||||
bool lastWasUndelimitedCharacter{false};
|
||||
};
|
||||
template <> class ListDirectedStatementState<Direction::Input> {
|
||||
template <>
|
||||
class ListDirectedStatementState<Direction::Input>
|
||||
: public FormattedIoStatementState {
|
||||
public:
|
||||
// Skips value separators, handles repetition and null values.
|
||||
// Vacant when '/' appears; present with descriptor == ListDirectedNullValue
|
||||
|
@ -199,7 +211,7 @@ protected:
|
|||
template <Direction DIR, typename CHAR>
|
||||
class InternalFormattedIoStatementState
|
||||
: public InternalIoStatementState<DIR, CHAR>,
|
||||
public FormattedStatementState {
|
||||
public FormattedIoStatementState {
|
||||
public:
|
||||
using CharType = CHAR;
|
||||
using typename InternalIoStatementState<DIR, CharType>::Buffer;
|
||||
|
@ -275,7 +287,7 @@ public:
|
|||
|
||||
template <Direction DIR, typename CHAR>
|
||||
class ExternalFormattedIoStatementState : public ExternalIoStatementState<DIR>,
|
||||
public FormattedStatementState {
|
||||
public FormattedIoStatementState {
|
||||
public:
|
||||
using CharType = CHAR;
|
||||
ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format,
|
||||
|
|
|
@ -78,13 +78,13 @@ TypeCode::TypeCode(TypeCategory f, int kind) {
|
|||
raw_ = CFI_type_Bool;
|
||||
break;
|
||||
case 2:
|
||||
raw_ = CFI_type_int16_t;
|
||||
raw_ = CFI_type_int_fast16_t;
|
||||
break;
|
||||
case 4:
|
||||
raw_ = CFI_type_int32_t;
|
||||
raw_ = CFI_type_int_fast32_t;
|
||||
break;
|
||||
case 8:
|
||||
raw_ = CFI_type_int64_t;
|
||||
raw_ = CFI_type_int_fast64_t;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -290,7 +290,7 @@ bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
|
|||
furthestPositionInRecord = furthestAfter;
|
||||
return true;
|
||||
} else {
|
||||
handler.SignalEnd();
|
||||
// EOF or error: can be handled & has been signaled
|
||||
endfileRecordNumber = currentRecordNumber;
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue