Skip to content

Commit 7ff9064

Browse files
committed
[flang] Delay parse tree rewriting for I/O UNIT=func()
When an I/O statement's UNIT= specifier is a variable that is a function reference, parse tree rewriting may determine the wrong type of the result because generic resolution has not yet been performed. So move this bit of parse tree rewriting into I/O semantic checking so that the right handling (integer -> external file unit number, character pointer -> internal I/O) applies. Differential Revision: https://reviews.llvm.org/D135210
1 parent e2eabb7 commit 7ff9064

File tree

4 files changed

+92
-30
lines changed

4 files changed

+92
-30
lines changed

flang/lib/Semantics/check-io.cpp

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -542,17 +542,50 @@ void IoChecker::Enter(const parser::IoControlSpec::Size &var) {
542542

543543
void IoChecker::Enter(const parser::IoUnit &spec) {
544544
if (const parser::Variable * var{std::get_if<parser::Variable>(&spec.u)}) {
545-
if (stmt_ == IoStmtKind::Write) {
546-
CheckForDefinableVariable(*var, "Internal file");
545+
// Only now after generic resolution can it be known whether a function
546+
// call appearing as UNIT=f() is an integer scalar external unit number
547+
// or a character pointer for internal I/O.
548+
const auto *expr{GetExpr(context_, *var)};
549+
std::optional<evaluate::DynamicType> dyType;
550+
if (expr) {
551+
dyType = expr->GetType();
547552
}
548-
if (const auto *expr{GetExpr(context_, *var)}) {
553+
if (dyType && dyType->category() == TypeCategory::Integer) {
554+
if (expr->Rank() != 0) {
555+
context_.Say(parser::FindSourceLocation(*var),
556+
"I/O unit number must be scalar"_err_en_US);
557+
}
558+
// In the case of an integer unit number variable, rewrite the parse
559+
// tree as if the unit had been parsed as a FileUnitNumber in order
560+
// to ease lowering.
561+
auto &mutableSpec{const_cast<parser::IoUnit &>(spec)};
562+
auto &mutableVar{std::get<parser::Variable>(mutableSpec.u)};
563+
auto source{mutableVar.GetSource()};
564+
auto typedExpr{std::move(mutableVar.typedExpr)};
565+
auto newExpr{common::visit(
566+
[](auto &&indirection) {
567+
return parser::Expr{std::move(indirection)};
568+
},
569+
std::move(mutableVar.u))};
570+
newExpr.source = source;
571+
newExpr.typedExpr = std::move(typedExpr);
572+
mutableSpec.u = parser::FileUnitNumber{
573+
parser::ScalarIntExpr{parser::IntExpr{std::move(newExpr)}}};
574+
} else if (!dyType || dyType->category() != TypeCategory::Character) {
575+
SetSpecifier(IoSpecKind::Unit);
576+
context_.Say(parser::FindSourceLocation(*var),
577+
"I/O unit must be a character variable or a scalar integer expression"_err_en_US);
578+
} else { // CHARACTER variable (internal I/O)
579+
if (stmt_ == IoStmtKind::Write) {
580+
CheckForDefinableVariable(*var, "Internal file");
581+
}
549582
if (HasVectorSubscript(*expr)) {
550583
context_.Say(parser::FindSourceLocation(*var), // C1201
551584
"Internal file must not have a vector subscript"_err_en_US);
552585
}
586+
SetSpecifier(IoSpecKind::Unit);
587+
flags_.set(Flag::InternalUnit);
553588
}
554-
SetSpecifier(IoSpecKind::Unit);
555-
flags_.set(Flag::InternalUnit);
556589
} else if (std::get_if<parser::Star>(&spec.u)) {
557590
SetSpecifier(IoSpecKind::Unit);
558591
flags_.set(Flag::StarUnit);

flang/lib/Semantics/rewrite-parse-tree.cpp

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class RewriteMutator {
4141
void Post(parser::Name &);
4242
void Post(parser::SpecificationPart &);
4343
bool Pre(parser::ExecutionPart &);
44-
void Post(parser::IoUnit &);
4544
void Post(parser::ReadStmt &);
4645
void Post(parser::WriteStmt &);
4746

@@ -130,29 +129,6 @@ bool RewriteMutator::Pre(parser::ExecutionPart &x) {
130129
return true;
131130
}
132131

133-
// Convert a syntactically ambiguous io-unit internal-file-variable to a
134-
// file-unit-number.
135-
void RewriteMutator::Post(parser::IoUnit &x) {
136-
if (auto *var{std::get_if<parser::Variable>(&x.u)}) {
137-
const parser::Name &last{parser::GetLastName(*var)};
138-
DeclTypeSpec *type{last.symbol ? last.symbol->GetType() : nullptr};
139-
if (!type || type->category() != DeclTypeSpec::Character) {
140-
// If the Variable is not known to be character (any kind), transform
141-
// the I/O unit in situ to a FileUnitNumber so that automatic expression
142-
// constraint checking will be applied.
143-
auto source{var->GetSource()};
144-
auto expr{common::visit(
145-
[](auto &&indirection) {
146-
return parser::Expr{std::move(indirection)};
147-
},
148-
std::move(var->u))};
149-
expr.source = source;
150-
x.u = parser::FileUnitNumber{
151-
parser::ScalarIntExpr{parser::IntExpr{std::move(expr)}}};
152-
}
153-
}
154-
}
155-
156132
// When a namelist group name appears (without NML=) in a READ or WRITE
157133
// statement in such a way that it can be misparsed as a format expression,
158134
// rewrite the I/O statement's parse tree node as if the namelist group

flang/test/Semantics/io04.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
!ERROR: If UNIT=* appears, REC must not appear
8888
write(*, rec=13) 'Ok'
8989

90-
!ERROR: Must have INTEGER type, but is REAL(4)
90+
!ERROR: I/O unit must be a character variable or a scalar integer expression
9191
write(unit, *) 'Ok'
9292

9393
!ERROR: If ADVANCE appears, UNIT=internal-file must not appear

flang/test/Semantics/io13.f90

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
! Tests for UNIT=function()
3+
module m1
4+
integer, target :: itarget
5+
character(20), target :: ctarget
6+
logical, target :: ltarget
7+
interface gf
8+
module procedure :: intf, pintf, pchf, logf, plogf
9+
end interface
10+
contains
11+
integer function intf(n)
12+
integer(1), intent(in) :: n
13+
intf = n
14+
end function
15+
function pintf(n)
16+
integer(2), intent(in) :: n
17+
integer, pointer :: pintf
18+
pintf => itarget
19+
pintf = n
20+
end function
21+
function pchf(n)
22+
integer(4), intent(in) :: n
23+
character(:), pointer :: pchf
24+
pchf => ctarget
25+
end function
26+
logical function logf(n)
27+
integer(8), intent(in) :: n
28+
logf = .true.
29+
end function
30+
function plogf(n)
31+
integer(16), intent(in) :: n
32+
logical, pointer :: plf
33+
plf => ltarget
34+
end function
35+
subroutine test
36+
write(intf(6_1),"('hi')")
37+
write(pintf(6_2),"('hi')")
38+
write(pchf(123_4),"('hi')")
39+
write(gf(6_1),"('hi')")
40+
write(gf(6_2),"('hi')")
41+
write(gf(666_4),"('hi')")
42+
!ERROR: I/O unit must be a character variable or a scalar integer expression
43+
write(logf(666_8),"('hi')")
44+
!ERROR: I/O unit must be a character variable or a scalar integer expression
45+
write(plogf(666_16),"('hi')")
46+
!ERROR: I/O unit must be a character variable or a scalar integer expression
47+
write(gf(666_8),"('hi')")
48+
!ERROR: I/O unit must be a character variable or a scalar integer expression
49+
write(gf(666_16),"('hi')")
50+
!ERROR: I/O unit must be a character variable or a scalar integer expression
51+
write(null(),"('hi')")
52+
end subroutine
53+
end module

0 commit comments

Comments
 (0)