Skip to content

Commit 374c470

Browse files
authored
[Flang] Give a more specific error message for expressions where an IO Unit is expected (#126970)
This PR fixes #125446 by specializing the error message that is generated when an arbitrary expression is used as an IO Unit. See the tests for specific examples, but the general gist is that if you use a non-variable expression as the IO unit argument to a read or write, you now get a more specific error message indicating the the expression could have been a scalar integer expression or character variable.
1 parent 236fa50 commit 374c470

File tree

7 files changed

+69
-6
lines changed

7 files changed

+69
-6
lines changed

flang/include/flang/Parser/parse-tree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2642,7 +2642,7 @@ WRAPPER_CLASS(FileUnitNumber, ScalarIntExpr);
26422642
// symbols are known.
26432643
struct IoUnit {
26442644
UNION_CLASS_BOILERPLATE(IoUnit);
2645-
std::variant<Variable, FileUnitNumber, Star> u;
2645+
std::variant<Variable, common::Indirection<Expr>, Star> u;
26462646
};
26472647

26482648
// R1206 file-name-expr -> scalar-default-char-expr

flang/lib/Lower/IO.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1841,7 +1841,9 @@ static mlir::Value genIOUnit(Fortran::lower::AbstractConverter &converter,
18411841
int defaultUnitNumber) {
18421842
auto &builder = converter.getFirOpBuilder();
18431843
if (iounit)
1844-
if (auto *e = std::get_if<Fortran::parser::FileUnitNumber>(&iounit->u))
1844+
if (auto *e =
1845+
std::get_if<Fortran::common::Indirection<Fortran::parser::Expr>>(
1846+
&iounit->u))
18451847
return genIOUnitNumber(converter, loc, Fortran::semantics::GetExpr(*e),
18461848
ty, csi, stmtCtx);
18471849
return builder.create<mlir::arith::ConstantOp>(

flang/lib/Parser/io-parsers.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ namespace Fortran::parser {
2323
// R905 char-variable -> variable
2424
// "char-variable" is attempted first since it's not type constrained but
2525
// syntactically ambiguous with "file-unit-number", which is constrained.
26+
// Note, "file-unit-number" is replaced by "expr" to allow for better
27+
// error messages.
2628
TYPE_PARSER(construct<IoUnit>(variable / lookAhead(space / ",);\n"_ch)) ||
27-
construct<IoUnit>(fileUnitNumber) || construct<IoUnit>(star))
29+
construct<IoUnit>(
30+
indirect(expr) / (lookAhead(space >> ",)"_ch) || atEndOfStmt)) ||
31+
construct<IoUnit>(star))
2832

2933
// R1202 file-unit-number -> scalar-int-expr
3034
TYPE_PARSER(construct<FileUnitNumber>(

flang/lib/Semantics/check-io.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#include "check-io.h"
1010
#include "definable.h"
1111
#include "flang/Common/format.h"
12+
#include "flang/Common/indirection.h"
1213
#include "flang/Evaluate/tools.h"
14+
#include "flang/Parser/characters.h"
1315
#include "flang/Parser/tools.h"
1416
#include "flang/Semantics/expression.h"
1517
#include "flang/Semantics/tools.h"
@@ -576,8 +578,9 @@ void IoChecker::Enter(const parser::IoUnit &spec) {
576578
std::move(mutableVar.u))};
577579
newExpr.source = source;
578580
newExpr.typedExpr = std::move(typedExpr);
579-
mutableSpec.u = parser::FileUnitNumber{
580-
parser::ScalarIntExpr{parser::IntExpr{std::move(newExpr)}}};
581+
mutableSpec.u = common::Indirection<parser::Expr>{std::move(newExpr)};
582+
SetSpecifier(IoSpecKind::Unit);
583+
flags_.set(Flag::NumberUnit);
581584
} else if (!dyType || dyType->category() != TypeCategory::Character) {
582585
SetSpecifier(IoSpecKind::Unit);
583586
context_.Say(parser::FindSourceLocation(*var),
@@ -598,6 +601,26 @@ void IoChecker::Enter(const parser::IoUnit &spec) {
598601
} else if (std::get_if<parser::Star>(&spec.u)) {
599602
SetSpecifier(IoSpecKind::Unit);
600603
flags_.set(Flag::StarUnit);
604+
} else if (const common::Indirection<parser::Expr> *pexpr{
605+
std::get_if<common::Indirection<parser::Expr>>(&spec.u)}) {
606+
const auto *expr{GetExpr(context_, *pexpr)};
607+
std::optional<evaluate::DynamicType> dyType;
608+
if (expr) {
609+
dyType = expr->GetType();
610+
}
611+
if (!expr || !dyType) {
612+
context_.Say(parser::FindSourceLocation(*pexpr),
613+
"I/O unit must be a character variable or scalar integer expression"_err_en_US);
614+
} else if (dyType->category() != TypeCategory::Integer) {
615+
context_.Say(parser::FindSourceLocation(*pexpr),
616+
"I/O unit must be a character variable or a scalar integer expression, but is an expression of type %s"_err_en_US,
617+
parser::ToUpperCaseLetters(dyType->AsFortran()));
618+
} else if (expr->Rank() != 0) {
619+
context_.Say(parser::FindSourceLocation(*pexpr),
620+
"I/O unit number must be scalar"_err_en_US);
621+
}
622+
SetSpecifier(IoSpecKind::Unit);
623+
flags_.set(Flag::NumberUnit);
601624
}
602625
}
603626

flang/test/Semantics/io03.f90

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,22 @@
171171
!ERROR: ID kind (2) is smaller than default INTEGER kind (4)
172172
read(10, id=id2, asynchronous='yes') jj
173173

174+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
175+
read((msg), *)
176+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(KIND=1,LEN=8_8)
177+
read("a string", *)
178+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
179+
read(msg//msg, *)
180+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type LOGICAL(4)
181+
read(.true., *)
182+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type REAL(4)
183+
read(1.0, *)
184+
read(internal_fileA, *)
185+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
186+
read((internal_fileA), *)
187+
!ERROR: I/O unit number must be scalar
188+
read([1,2,3], *)
189+
174190
9 continue
175191
end
176192

flang/test/Semantics/io04.f90

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
character(kind=1,len=100) msg
44
character(20) sign
55
character, parameter :: const_internal_file*(*) = "(I6)"
6+
character(kind=1,len=50) internal_fileA(20)
67
integer*1 stat1, id1
78
integer*2 stat2
89
integer*4 stat4
@@ -138,6 +139,23 @@
138139

139140
write(*, '(X)')
140141

142+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
143+
write((msg), *)
144+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(KIND=1,LEN=8_8)
145+
write("a string", *)
146+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
147+
write(msg//msg, *)
148+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type LOGICAL(4)
149+
write(.true., *)
150+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type REAL(4)
151+
write(1.0, *)
152+
write(internal_fileA, *)
153+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
154+
write((internal_fileA), *)
155+
!ERROR: I/O unit number must be scalar
156+
write([1,2,3], *)
157+
158+
141159
!ERROR: Output item must not be a procedure
142160
print*, procptr
143161
!ERROR: Output item must not be a procedure

flang/test/Semantics/unsigned-errors.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types INTEGER(4) and UNSIGNED(4)
6565
j = 1u
6666

67-
!ERROR: Must have INTEGER type, but is UNSIGNED(4)
67+
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type UNSIGNED(4)
6868
write(6u,*) 'hi'
6969

7070
!ERROR: ARITHMETIC IF expression must not be an UNSIGNED expression

0 commit comments

Comments
 (0)