Skip to content

Commit bce7a7e

Browse files
committed
[flang] Check that various variables referenced in I/O statements may be defined
A number of I/O syntax rules involve variables that will be written to, and must therefore be definable. This includes internal file variables, IOSTAT= and IOMSG= specifiers, most INQUIRE statement specifiers, a few other specifiers, and input variables. This patch checks for these violations, and implements several additional I/O TODO constraint checks. Differential Revision: https://reviews.llvm.org/D86557
1 parent e713b0e commit bce7a7e

File tree

9 files changed

+150
-34
lines changed

9 files changed

+150
-34
lines changed

flang/lib/Semantics/check-io.cpp

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ void IoChecker::Enter(const parser::ConnectSpec::CharExpr &spec) {
155155
}
156156
}
157157

158-
void IoChecker::Enter(const parser::ConnectSpec::Newunit &) {
158+
void IoChecker::Enter(const parser::ConnectSpec::Newunit &var) {
159+
CheckForDefinableVariable(var, "NEWUNIT");
159160
SetSpecifier(IoSpecKind::Newunit);
160161
}
161162

@@ -266,10 +267,11 @@ void IoChecker::Enter(const parser::IdExpr &) { SetSpecifier(IoSpecKind::Id); }
266267

267268
void IoChecker::Enter(const parser::IdVariable &spec) {
268269
SetSpecifier(IoSpecKind::Id);
269-
auto expr{GetExpr(spec)};
270+
const auto *expr{GetExpr(spec)};
270271
if (!expr || !expr->GetType()) {
271272
return;
272273
}
274+
CheckForDefinableVariable(spec, "ID");
273275
int kind{expr->GetType()->kind()};
274276
int defaultKind{context_.GetDefaultKind(TypeCategory::Integer)};
275277
if (kind < defaultKind) {
@@ -281,21 +283,18 @@ void IoChecker::Enter(const parser::IdVariable &spec) {
281283

282284
void IoChecker::Enter(const parser::InputItem &spec) {
283285
flags_.set(Flag::DataList);
284-
if (const parser::Variable * var{std::get_if<parser::Variable>(&spec.u)}) {
285-
const parser::Name &name{GetLastName(*var)};
286-
if (name.symbol) {
287-
if (auto *details{name.symbol->detailsIf<ObjectEntityDetails>()}) {
288-
// TODO: Determine if this check is needed at all, and if so, replace
289-
// the false subcondition with a check for a whole array. Otherwise,
290-
// the check incorrectly flags array element and section references.
291-
if (details->IsAssumedSize() && false) {
292-
// This check may be superseded by C928 or C1002.
293-
context_.Say(name.source,
294-
"'%s' must not be a whole assumed size array"_err_en_US,
295-
name.source); // C1231
296-
}
297-
}
298-
}
286+
const parser::Variable *var{std::get_if<parser::Variable>(&spec.u)};
287+
if (!var) {
288+
return;
289+
}
290+
CheckForDefinableVariable(*var, "Input");
291+
const auto &name{GetLastName(*var)};
292+
const auto *expr{GetExpr(*var)};
293+
if (name.symbol && IsAssumedSizeArray(*name.symbol) && expr &&
294+
!evaluate::IsArrayElement(*GetExpr(*var))) {
295+
context_.Say(name.source,
296+
"Whole assumed size array '%s' may not be an input item"_err_en_US,
297+
name.source); // C1231
299298
}
300299
}
301300

@@ -386,6 +385,8 @@ void IoChecker::Enter(const parser::InquireSpec::CharVar &spec) {
386385
specKind = IoSpecKind::Dispose;
387386
break;
388387
}
388+
CheckForDefinableVariable(std::get<parser::ScalarDefaultCharVariable>(spec.t),
389+
parser::ToUpperCaseLetters(common::EnumToString(specKind)));
389390
SetSpecifier(specKind);
390391
}
391392

@@ -412,6 +413,8 @@ void IoChecker::Enter(const parser::InquireSpec::IntVar &spec) {
412413
specKind = IoSpecKind::Size;
413414
break;
414415
}
416+
CheckForDefinableVariable(std::get<parser::ScalarIntVariable>(spec.t),
417+
parser::ToUpperCaseLetters(common::EnumToString(specKind)));
415418
SetSpecifier(specKind);
416419
}
417420

@@ -500,17 +503,23 @@ void IoChecker::Enter(const parser::IoControlSpec::Rec &) {
500503
SetSpecifier(IoSpecKind::Rec);
501504
}
502505

503-
void IoChecker::Enter(const parser::IoControlSpec::Size &) {
506+
void IoChecker::Enter(const parser::IoControlSpec::Size &var) {
507+
CheckForDefinableVariable(var, "SIZE");
504508
SetSpecifier(IoSpecKind::Size);
505509
}
506510

507511
void IoChecker::Enter(const parser::IoUnit &spec) {
508512
if (const parser::Variable * var{std::get_if<parser::Variable>(&spec.u)}) {
509-
// TODO: C1201 - internal file variable must not be an array section ...
510-
if (auto expr{GetExpr(*var)}) {
511-
if (!ExprTypeKindIsDefault(*expr, context_)) {
513+
if (stmt_ == IoStmtKind::Write) {
514+
CheckForDefinableVariable(*var, "Internal file");
515+
}
516+
if (const auto *expr{GetExpr(*var)}) {
517+
if (HasVectorSubscript(*expr)) {
518+
context_.Say(parser::FindSourceLocation(*var), // C1201
519+
"Internal file must not have a vector subscript"_err_en_US);
520+
} else if (!ExprTypeKindIsDefault(*expr, context_)) {
512521
// This may be too restrictive; other kinds may be valid.
513-
context_.Say( // C1202
522+
context_.Say(parser::FindSourceLocation(*var), // C1202
514523
"Invalid character kind for an internal file variable"_err_en_US);
515524
}
516525
}
@@ -522,13 +531,26 @@ void IoChecker::Enter(const parser::IoUnit &spec) {
522531
}
523532
}
524533

525-
void IoChecker::Enter(const parser::MsgVariable &) {
534+
void IoChecker::Enter(const parser::MsgVariable &var) {
535+
if (stmt_ == IoStmtKind::None) {
536+
// allocate, deallocate, image control
537+
CheckForDefinableVariable(var, "ERRMSG");
538+
return;
539+
}
540+
CheckForDefinableVariable(var, "IOMSG");
526541
SetSpecifier(IoSpecKind::Iomsg);
527542
}
528543

529-
void IoChecker::Enter(const parser::OutputItem &) {
544+
void IoChecker::Enter(const parser::OutputItem &item) {
530545
flags_.set(Flag::DataList);
531-
// TODO: C1233 - output item must not be a procedure pointer
546+
if (const auto *x{std::get_if<parser::Expr>(&item.u)}) {
547+
if (const auto *expr{GetExpr(*x)}) {
548+
if (IsProcedurePointer(*expr)) {
549+
context_.Say(parser::FindSourceLocation(*x),
550+
"Output item must not be a procedure pointer"_err_en_US); // C1233
551+
}
552+
}
553+
}
532554
}
533555

534556
void IoChecker::Enter(const parser::StatusExpr &spec) {
@@ -555,12 +577,14 @@ void IoChecker::Enter(const parser::StatusExpr &spec) {
555577
}
556578
}
557579

558-
void IoChecker::Enter(const parser::StatVariable &) {
580+
void IoChecker::Enter(const parser::StatVariable &var) {
559581
if (stmt_ == IoStmtKind::None) {
560-
// ALLOCATE & DEALLOCATE
561-
} else {
562-
SetSpecifier(IoSpecKind::Iostat);
582+
// allocate, deallocate, image control
583+
CheckForDefinableVariable(var, "STAT");
584+
return;
563585
}
586+
CheckForDefinableVariable(var, "IOSTAT");
587+
SetSpecifier(IoSpecKind::Iostat);
564588
}
565589

566590
void IoChecker::Leave(const parser::BackspaceStmt &) {
@@ -808,7 +832,7 @@ void IoChecker::CheckStringValue(IoSpecKind specKind, const std::string &value,
808832

809833
// CheckForRequiredSpecifier and CheckForProhibitedSpecifier functions
810834
// need conditions to check, and string arguments to insert into a message.
811-
// A IoSpecKind provides both an absence/presence condition and a string
835+
// An IoSpecKind provides both an absence/presence condition and a string
812836
// argument (its name). A (condition, string) pair provides an arbitrary
813837
// condition and an arbitrary string.
814838

@@ -893,6 +917,17 @@ void IoChecker::CheckForProhibitedSpecifier(
893917
}
894918
}
895919

920+
template <typename A>
921+
void IoChecker::CheckForDefinableVariable(
922+
const A &var, const std::string &s) const {
923+
const Symbol *sym{
924+
GetFirstName(*parser::Unwrap<parser::Variable>(var)).symbol};
925+
if (WhyNotModifiable(*sym, context_.FindScope(*context_.location()))) {
926+
context_.Say(parser::FindSourceLocation(var),
927+
"%s variable '%s' must be definable"_err_en_US, s, sym->name());
928+
}
929+
}
930+
896931
void IoChecker::CheckForPureSubprogram() const { // C1597
897932
CHECK(context_.location());
898933
if (FindPureProcedureContaining(context_.FindScope(*context_.location()))) {

flang/lib/Semantics/check-io.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ class IoChecker : public virtual BaseChecker {
122122
void CheckForProhibitedSpecifier(IoSpecKind, bool, const std::string &) const;
123123
void CheckForProhibitedSpecifier(bool, const std::string &, IoSpecKind) const;
124124

125+
template <typename A>
126+
void CheckForDefinableVariable(const A &var, const std::string &s) const;
127+
128+
void CheckForPureSubprogram() const;
129+
125130
void Init(IoStmtKind s) {
126131
stmt_ = s;
127132
specifierSet_.reset();
@@ -130,8 +135,6 @@ class IoChecker : public virtual BaseChecker {
130135

131136
void Done() { stmt_ = IoStmtKind::None; }
132137

133-
void CheckForPureSubprogram() const;
134-
135138
SemanticsContext &context_;
136139
IoStmtKind stmt_{IoStmtKind::None};
137140
common::EnumSet<IoSpecKind, common::IoSpecKind_enumSize> specifierSet_;

flang/test/Semantics/deallocate05.f90

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Program deallocatetest
2121

2222
Real :: r
2323
Integer :: s
24+
Integer, Parameter :: const_s = 13
2425
Integer :: e
2526
Integer :: pi
2627
Character(256) :: ee
@@ -56,6 +57,8 @@ Program deallocatetest
5657

5758
!ERROR: STAT may not be duplicated in a DEALLOCATE statement
5859
Deallocate(x, stat=s, stat=s)
60+
!ERROR: STAT variable 'const_s' must be definable
61+
Deallocate(x, stat=const_s)
5962
!ERROR: ERRMSG may not be duplicated in a DEALLOCATE statement
6063
Deallocate(x, errmsg=ee, errmsg=ee)
6164
!ERROR: STAT may not be duplicated in a DEALLOCATE statement

flang/test/Semantics/io01.f90

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
integer :: unit10 = 10
2222
integer :: unit11 = 11
2323
integer :: n = 40
24+
integer, parameter :: const_new_unit = 66
2425

2526
integer(kind=1) :: stat1
2627
integer(kind=2) :: stat2
@@ -73,6 +74,9 @@
7374
!ERROR: If NEWUNIT appears, FILE or STATUS must also appear
7475
open(newunit=n, newunit=nn, iostat=stat4)
7576

77+
!ERROR: NEWUNIT variable 'const_new_unit' must be definable
78+
open(newunit=const_new_unit, status=cc)
79+
7680
!ERROR: Duplicate UNIT specifier
7781
open(unit=100, unit=100)
7882

flang/test/Semantics/io02.f90

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
! RUN: %S/test_errors.sh %s %t %f18
22
integer :: unit10 = 10
33
integer :: unit11 = 11
4+
integer, parameter :: const_stat = 6666
45

56
integer(kind=1) :: stat1
67
integer(kind=8) :: stat8
@@ -28,5 +29,8 @@
2829
!ERROR: Invalid STATUS value 'old'
2930
close(status='old', unit=17)
3031

32+
!ERROR: IOSTAT variable 'const_stat' must be definable
33+
close(14, iostat=const_stat)
34+
3135
9 continue
3236
end

flang/test/Semantics/io03.f90

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22
character(kind=1,len=50) internal_file
33
character(kind=2,len=50) internal_file2
44
character(kind=4,len=50) internal_file4
5+
character(kind=1,len=50) internal_fileA(20)
56
character(kind=1,len=111) msg
67
character(20) advance
8+
character(20) :: cvar;
9+
character, parameter :: const_internal_file = "(I6)"
10+
character, parameter :: const_cvar = "Ceci n'est pas une pipe."
711
integer*1 stat1
812
integer*2 stat2, id2
913
integer*8 stat8
1014
integer :: iunit = 10
11-
integer, parameter :: junit = 11
15+
integer, parameter :: junit = 11, const_size = 13, const_int = 15
16+
integer :: vv(10) = 7
1217

1318
namelist /mmm/ mm1, mm2
1419
namelist /nnn/ nn1, nn2
@@ -29,11 +34,14 @@
2934
read(fmt='(I4)', unit=*) jj
3035
read(iunit, *) jj
3136
read(junit, *) jj
32-
read(10, *) jj
37+
read(10, *) jj, cvar, cvar(7:17)
3338
read(internal_file, *) jj
39+
read(internal_fileA(3), *) jj
40+
read(internal_fileA(4:9), *) jj
3441
read(10, nnn)
3542
read(internal_file, nnn)
3643
read(internal_file, nml=nnn)
44+
read(const_internal_file, *)
3745
read(fmt=*, unit=internal_file)
3846
read(nml=nnn, unit=internal_file)
3947
read(iunit, nnn)
@@ -53,6 +61,21 @@
5361
!ERROR: Invalid character kind for an internal file variable
5462
read(internal_file4, *) jj
5563

64+
!ERROR: Internal file must not have a vector subscript
65+
read(internal_fileA(vv), *) jj
66+
67+
!ERROR: Input variable 'const_int' must be definable
68+
read(11, *) const_int
69+
70+
!ERROR: SIZE variable 'const_size' must be definable
71+
read(11, pos=ipos, size=const_size, end=9)
72+
73+
!ERROR: Input variable 'const_cvar' must be definable
74+
read(11, *) const_cvar
75+
76+
!ERROR: Input variable 'const_cvar' must be definable
77+
read(11, *) const_cvar(3:13)
78+
5679
!ERROR: Duplicate IOSTAT specifier
5780
read(11, pos=ipos, iostat=stat1, iostat=stat2)
5881

@@ -136,3 +159,25 @@
136159

137160
9 continue
138161
end
162+
163+
subroutine s(aa, n)
164+
integer :: aa(5,*)
165+
integer, intent(in) :: n
166+
integer :: bb(10), vv(10)
167+
type tt
168+
real :: x, y, z
169+
end type tt
170+
type(tt) :: qq(20)
171+
172+
vv = 1
173+
174+
read(*, *) aa(n,1)
175+
read(*, *) aa(n:n+2,2)
176+
read(*, *) qq(2:5)%y
177+
178+
!ERROR: Input variable 'n' must be definable
179+
read(*, *) n
180+
181+
!ERROR: Whole assumed size array 'aa' may not be an input item
182+
read(*, *) aa
183+
end

flang/test/Semantics/io04.f90

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
character(kind=1,len=50) internal_file
33
character(kind=1,len=100) msg
44
character(20) sign
5+
character, parameter :: const_internal_file = "(I6)"
56
integer*1 stat1, id1
67
integer*2 stat2
78
integer*4 stat4
89
integer*8 stat8
910
integer :: iunit = 10
1011
integer, parameter :: junit = 11
1112
integer, pointer :: a(:)
13+
integer, parameter :: const_id = 66666
14+
procedure(), pointer :: procptr
1215

1316
namelist /nnn/ nn1, nn2
1417

@@ -66,6 +69,9 @@
6669
!ERROR: If NML appears, a data list must not appear
6770
write(10, nnn, rec=40, fmt=1) 'Ok'
6871

72+
!ERROR: Internal file variable 'const_internal_file' must be definable
73+
write(const_internal_file, fmt=*)
74+
6975
!ERROR: If UNIT=* appears, POS must not appear
7076
write(*, pos=n, nml=nnn)
7177

@@ -118,8 +124,14 @@
118124
!ERROR: ID kind (1) is smaller than default INTEGER kind (4)
119125
write(id=id1, unit=10, asynchronous='Yes') 'Ok'
120126

127+
!ERROR: ID variable 'const_id' must be definable
128+
write(10, *, asynchronous='yes', id=const_id, iostat=stat2) 'Ok'
129+
121130
write(*, '(X)')
122131

132+
!ERROR: Output item must not be a procedure pointer
133+
print*, n1, procptr, n2
134+
123135
1 format (A)
124136
9 continue
125137
end

0 commit comments

Comments
 (0)