Skip to content

[Flang] Give a more specific error message for expressions where an IO Unit is expected #126970

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion flang/include/flang/Parser/parse-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -2642,7 +2642,7 @@ WRAPPER_CLASS(FileUnitNumber, ScalarIntExpr);
// symbols are known.
struct IoUnit {
UNION_CLASS_BOILERPLATE(IoUnit);
std::variant<Variable, FileUnitNumber, Star> u;
std::variant<Variable, common::Indirection<Expr>, Star> u;
};

// R1206 file-name-expr -> scalar-default-char-expr
Expand Down
4 changes: 3 additions & 1 deletion flang/lib/Lower/IO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1841,7 +1841,9 @@ static mlir::Value genIOUnit(Fortran::lower::AbstractConverter &converter,
int defaultUnitNumber) {
auto &builder = converter.getFirOpBuilder();
if (iounit)
if (auto *e = std::get_if<Fortran::parser::FileUnitNumber>(&iounit->u))
if (auto *e =
std::get_if<Fortran::common::Indirection<Fortran::parser::Expr>>(
&iounit->u))
return genIOUnitNumber(converter, loc, Fortran::semantics::GetExpr(*e),
ty, csi, stmtCtx);
return builder.create<mlir::arith::ConstantOp>(
Expand Down
6 changes: 5 additions & 1 deletion flang/lib/Parser/io-parsers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ namespace Fortran::parser {
// R905 char-variable -> variable
// "char-variable" is attempted first since it's not type constrained but
// syntactically ambiguous with "file-unit-number", which is constrained.
// Note, "file-unit-number" is replaced by "expr" to allow for better
// error messages.
TYPE_PARSER(construct<IoUnit>(variable / lookAhead(space / ",);\n"_ch)) ||
construct<IoUnit>(fileUnitNumber) || construct<IoUnit>(star))
construct<IoUnit>(
indirect(expr) / (lookAhead(space >> ",)"_ch) || atEndOfStmt)) ||
construct<IoUnit>(star))

// R1202 file-unit-number -> scalar-int-expr
TYPE_PARSER(construct<FileUnitNumber>(
Expand Down
27 changes: 25 additions & 2 deletions flang/lib/Semantics/check-io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include "check-io.h"
#include "definable.h"
#include "flang/Common/format.h"
#include "flang/Common/indirection.h"
#include "flang/Evaluate/tools.h"
#include "flang/Parser/characters.h"
#include "flang/Parser/tools.h"
#include "flang/Semantics/expression.h"
#include "flang/Semantics/tools.h"
Expand Down Expand Up @@ -576,8 +578,9 @@ void IoChecker::Enter(const parser::IoUnit &spec) {
std::move(mutableVar.u))};
newExpr.source = source;
newExpr.typedExpr = std::move(typedExpr);
mutableSpec.u = parser::FileUnitNumber{
parser::ScalarIntExpr{parser::IntExpr{std::move(newExpr)}}};
mutableSpec.u = common::Indirection<parser::Expr>{std::move(newExpr)};
SetSpecifier(IoSpecKind::Unit);
flags_.set(Flag::NumberUnit);
} else if (!dyType || dyType->category() != TypeCategory::Character) {
SetSpecifier(IoSpecKind::Unit);
context_.Say(parser::FindSourceLocation(*var),
Expand All @@ -598,6 +601,26 @@ void IoChecker::Enter(const parser::IoUnit &spec) {
} else if (std::get_if<parser::Star>(&spec.u)) {
SetSpecifier(IoSpecKind::Unit);
flags_.set(Flag::StarUnit);
} else if (const common::Indirection<parser::Expr> *pexpr{
std::get_if<common::Indirection<parser::Expr>>(&spec.u)}) {
const auto *expr{GetExpr(context_, *pexpr)};
std::optional<evaluate::DynamicType> dyType;
if (expr) {
dyType = expr->GetType();
}
if (!expr || !dyType) {
context_.Say(parser::FindSourceLocation(*pexpr),
"I/O unit must be a character variable or scalar integer expression"_err_en_US);
} else if (dyType->category() != TypeCategory::Integer) {
context_.Say(parser::FindSourceLocation(*pexpr),
"I/O unit must be a character variable or a scalar integer expression, but is an expression of type %s"_err_en_US,
parser::ToUpperCaseLetters(dyType->AsFortran()));
} else if (expr->Rank() != 0) {
context_.Say(parser::FindSourceLocation(*pexpr),
"I/O unit number must be scalar"_err_en_US);
}
SetSpecifier(IoSpecKind::Unit);
flags_.set(Flag::NumberUnit);
}
}

Expand Down
16 changes: 16 additions & 0 deletions flang/test/Semantics/io03.f90
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,22 @@
!ERROR: ID kind (2) is smaller than default INTEGER kind (4)
read(10, id=id2, asynchronous='yes') jj

!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
read((msg), *)
!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)
read("a string", *)
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
read(msg//msg, *)
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type LOGICAL(4)
read(.true., *)
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type REAL(4)
read(1.0, *)
read(internal_fileA, *)
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
read((internal_fileA), *)
!ERROR: I/O unit number must be scalar
read([1,2,3], *)

9 continue
end

Expand Down
18 changes: 18 additions & 0 deletions flang/test/Semantics/io04.f90
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
character(kind=1,len=100) msg
character(20) sign
character, parameter :: const_internal_file*(*) = "(I6)"
character(kind=1,len=50) internal_fileA(20)
integer*1 stat1, id1
integer*2 stat2
integer*4 stat4
Expand Down Expand Up @@ -138,6 +139,23 @@

write(*, '(X)')

!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
write((msg), *)
!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)
write("a string", *)
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
write(msg//msg, *)
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type LOGICAL(4)
write(.true., *)
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type REAL(4)
write(1.0, *)
write(internal_fileA, *)
!ERROR: I/O unit must be a character variable or a scalar integer expression, but is an expression of type CHARACTER(1)
write((internal_fileA), *)
!ERROR: I/O unit number must be scalar
write([1,2,3], *)


!ERROR: Output item must not be a procedure
print*, procptr
!ERROR: Output item must not be a procedure
Expand Down
2 changes: 1 addition & 1 deletion flang/test/Semantics/unsigned-errors.f90
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
!ERROR: No intrinsic or user-defined ASSIGNMENT(=) matches operand types INTEGER(4) and UNSIGNED(4)
j = 1u

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

!ERROR: ARITHMETIC IF expression must not be an UNSIGNED expression
Expand Down
Loading