Skip to content

Commit bc2b9e4

Browse files
author
git apple-llvm automerger
committed
Merge commit '5ea0ba2c13af' from llvm.org/main into next
2 parents 1d5defb + 5ea0ba2 commit bc2b9e4

File tree

10 files changed

+269
-67
lines changed

10 files changed

+269
-67
lines changed

flang/include/flang/Semantics/semantics.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,12 @@ class SemanticsContext {
168168
return messages_.Say(std::move(msg));
169169
}
170170
template <typename... A>
171-
void SayWithDecl(const Symbol &symbol, const parser::CharBlock &at,
172-
parser::MessageFixedText &&msg, A &&...args) {
171+
parser::Message &SayWithDecl(const Symbol &symbol,
172+
const parser::CharBlock &at, parser::MessageFixedText &&msg,
173+
A &&...args) {
173174
auto &message{Say(at, std::move(msg), args...)};
174175
evaluate::AttachDeclaration(&message, symbol);
176+
return message;
175177
}
176178

177179
const Scope &FindScope(parser::CharBlock) const;

flang/include/flang/Semantics/tools.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -610,11 +610,6 @@ std::optional<ArraySpec> ToArraySpec(
610610
// procedure.
611611
bool HasDefinedIo(
612612
GenericKind::DefinedIo, const DerivedTypeSpec &, const Scope * = nullptr);
613-
// Seeks out an allocatable or pointer ultimate component that is not
614-
// nested in a nonallocatable/nonpointer component with a specific
615-
// defined I/O procedure.
616-
const Symbol *FindUnsafeIoDirectComponent(
617-
GenericKind::DefinedIo, const DerivedTypeSpec &, const Scope * = nullptr);
618613

619614
// Some intrinsic operators have more than one name (e.g. `operator(.eq.)` and
620615
// `operator(==)`). GetAllNames() returns them all, including symbolName.

flang/lib/Semantics/check-io.cpp

Lines changed: 150 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ void IoChecker::Enter(const parser::InputItem &spec) {
323323
}
324324
CheckForDefinableVariable(*var, "Input");
325325
if (auto expr{AnalyzeExpr(context_, *var)}) {
326-
CheckForBadIoComponent(*expr,
326+
CheckForBadIoType(*expr,
327327
flags_.test(Flag::FmtOrNml) ? GenericKind::DefinedIo::ReadFormatted
328328
: GenericKind::DefinedIo::ReadUnformatted,
329329
var->GetSource());
@@ -616,7 +616,7 @@ void IoChecker::Enter(const parser::OutputItem &item) {
616616
context_.Say(parser::FindSourceLocation(*x),
617617
"Output item must not be a procedure pointer"_err_en_US); // C1233
618618
}
619-
CheckForBadIoComponent(*expr,
619+
CheckForBadIoType(*expr,
620620
flags_.test(Flag::FmtOrNml)
621621
? GenericKind::DefinedIo::WriteFormatted
622622
: GenericKind::DefinedIo::WriteUnformatted,
@@ -738,29 +738,21 @@ void IoChecker::Leave(const parser::PrintStmt &) {
738738
Done();
739739
}
740740

741-
static void CheckForDoVariableInNamelist(const Symbol &namelist,
742-
SemanticsContext &context, parser::CharBlock namelistLocation) {
743-
const auto &details{namelist.GetUltimate().get<NamelistDetails>()};
744-
for (const Symbol &object : details.objects()) {
745-
context.CheckIndexVarRedefine(namelistLocation, object);
746-
}
747-
}
748-
749-
static void CheckForDoVariableInNamelistSpec(
750-
const parser::ReadStmt &readStmt, SemanticsContext &context) {
751-
const std::list<parser::IoControlSpec> &controls{readStmt.controls};
741+
static const parser::Name *FindNamelist(
742+
const std::list<parser::IoControlSpec> &controls) {
752743
for (const auto &control : controls) {
753-
if (const auto *namelist{std::get_if<parser::Name>(&control.u)}) {
754-
if (const Symbol * symbol{namelist->symbol}) {
755-
CheckForDoVariableInNamelist(*symbol, context, namelist->source);
744+
if (const parser::Name * namelist{std::get_if<parser::Name>(&control.u)}) {
745+
if (namelist->symbol &&
746+
namelist->symbol->GetUltimate().has<NamelistDetails>()) {
747+
return namelist;
756748
}
757749
}
758750
}
751+
return nullptr;
759752
}
760753

761754
static void CheckForDoVariable(
762755
const parser::ReadStmt &readStmt, SemanticsContext &context) {
763-
CheckForDoVariableInNamelistSpec(readStmt, context);
764756
const std::list<parser::InputItem> &items{readStmt.items};
765757
for (const auto &item : items) {
766758
if (const parser::Variable *
@@ -774,6 +766,12 @@ void IoChecker::Leave(const parser::ReadStmt &readStmt) {
774766
if (!flags_.test(Flag::InternalUnit)) {
775767
CheckForPureSubprogram();
776768
}
769+
if (const parser::Name * namelist{FindNamelist(readStmt.controls)}) {
770+
if (namelist->symbol) {
771+
CheckNamelist(*namelist->symbol, GenericKind::DefinedIo::ReadFormatted,
772+
namelist->source);
773+
}
774+
}
777775
CheckForDoVariable(readStmt, context_);
778776
if (!flags_.test(Flag::IoControlList)) {
779777
Done();
@@ -807,10 +805,16 @@ void IoChecker::Leave(const parser::WaitStmt &) {
807805
Done();
808806
}
809807

810-
void IoChecker::Leave(const parser::WriteStmt &) {
808+
void IoChecker::Leave(const parser::WriteStmt &writeStmt) {
811809
if (!flags_.test(Flag::InternalUnit)) {
812810
CheckForPureSubprogram();
813811
}
812+
if (const parser::Name * namelist{FindNamelist(writeStmt.controls)}) {
813+
if (namelist->symbol) {
814+
CheckNamelist(*namelist->symbol, GenericKind::DefinedIo::WriteFormatted,
815+
namelist->source);
816+
}
817+
}
814818
LeaveReadWrite();
815819
CheckForProhibitedSpecifier(IoSpecKind::Blank); // C1213
816820
CheckForProhibitedSpecifier(IoSpecKind::End); // C1213
@@ -1030,20 +1034,139 @@ void IoChecker::CheckForPureSubprogram() const { // C1597
10301034
}
10311035
}
10321036

1033-
// Fortran 2018, 12.6.3 paragraph 7
1034-
void IoChecker::CheckForBadIoComponent(const SomeExpr &expr,
1037+
// Seeks out an allocatable or pointer ultimate component that is not
1038+
// nested in a nonallocatable/nonpointer component with a specific
1039+
// defined I/O procedure.
1040+
static const Symbol *FindUnsafeIoDirectComponent(GenericKind::DefinedIo which,
1041+
const DerivedTypeSpec &derived, const Scope &scope) {
1042+
if (HasDefinedIo(which, derived, &scope)) {
1043+
return nullptr;
1044+
}
1045+
if (const Scope * dtScope{derived.scope()}) {
1046+
for (const auto &pair : *dtScope) {
1047+
const Symbol &symbol{*pair.second};
1048+
if (IsAllocatableOrPointer(symbol)) {
1049+
return &symbol;
1050+
}
1051+
if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
1052+
if (const DeclTypeSpec * type{details->type()}) {
1053+
if (type->category() == DeclTypeSpec::Category::TypeDerived) {
1054+
const DerivedTypeSpec &componentDerived{type->derivedTypeSpec()};
1055+
if (const Symbol *
1056+
bad{FindUnsafeIoDirectComponent(
1057+
which, componentDerived, scope)}) {
1058+
return bad;
1059+
}
1060+
}
1061+
}
1062+
}
1063+
}
1064+
}
1065+
return nullptr;
1066+
}
1067+
1068+
// For a type that does not have a defined I/O subroutine, finds a direct
1069+
// component that is a witness to an accessibility violation outside the module
1070+
// in which the type was defined.
1071+
static const Symbol *FindInaccessibleComponent(GenericKind::DefinedIo which,
1072+
const DerivedTypeSpec &derived, const Scope &scope) {
1073+
if (const Scope * dtScope{derived.scope()}) {
1074+
if (const Scope * module{FindModuleContaining(*dtScope)}) {
1075+
for (const auto &pair : *dtScope) {
1076+
const Symbol &symbol{*pair.second};
1077+
if (IsAllocatableOrPointer(symbol)) {
1078+
continue; // already an error
1079+
}
1080+
if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
1081+
const DerivedTypeSpec *componentDerived{nullptr};
1082+
if (const DeclTypeSpec * type{details->type()}) {
1083+
if (type->category() == DeclTypeSpec::Category::TypeDerived) {
1084+
componentDerived = &type->derivedTypeSpec();
1085+
}
1086+
}
1087+
if (componentDerived &&
1088+
HasDefinedIo(which, *componentDerived, &scope)) {
1089+
continue; // this component and its descendents are fine
1090+
}
1091+
if (symbol.attrs().test(Attr::PRIVATE) &&
1092+
!symbol.test(Symbol::Flag::ParentComp)) {
1093+
if (!DoesScopeContain(module, scope)) {
1094+
return &symbol;
1095+
}
1096+
}
1097+
if (componentDerived) {
1098+
if (const Symbol *
1099+
bad{FindInaccessibleComponent(
1100+
which, *componentDerived, scope)}) {
1101+
return bad;
1102+
}
1103+
}
1104+
}
1105+
}
1106+
}
1107+
}
1108+
return nullptr;
1109+
}
1110+
1111+
// Fortran 2018, 12.6.3 paragraphs 5 & 7
1112+
parser::Message *IoChecker::CheckForBadIoType(const evaluate::DynamicType &type,
10351113
GenericKind::DefinedIo which, parser::CharBlock where) const {
1036-
if (auto type{expr.GetType()}) {
1037-
if (type->category() == TypeCategory::Derived &&
1038-
!type->IsUnlimitedPolymorphic()) {
1114+
if (type.IsUnlimitedPolymorphic()) {
1115+
return &context_.Say(
1116+
where, "I/O list item may not be unlimited polymorphic"_err_en_US);
1117+
} else if (type.category() == TypeCategory::Derived) {
1118+
const auto &derived{type.GetDerivedTypeSpec()};
1119+
const Scope &scope{context_.FindScope(where)};
1120+
if (const Symbol *
1121+
bad{FindUnsafeIoDirectComponent(which, derived, scope)}) {
1122+
return &context_.SayWithDecl(*bad, where,
1123+
"Derived type '%s' in I/O cannot have an allocatable or pointer direct component '%s' unless using defined I/O"_err_en_US,
1124+
derived.name(), bad->name());
1125+
}
1126+
if (!HasDefinedIo(which, derived, &scope)) {
1127+
if (type.IsPolymorphic()) {
1128+
return &context_.Say(where,
1129+
"Derived type '%s' in I/O may not be polymorphic unless using defined I/O"_err_en_US,
1130+
derived.name());
1131+
}
10391132
if (const Symbol *
1040-
bad{FindUnsafeIoDirectComponent(
1041-
which, type->GetDerivedTypeSpec(), &context_.FindScope(where))}) {
1042-
context_.SayWithDecl(*bad, where,
1043-
"Derived type in I/O cannot have an allocatable or pointer direct component unless using defined I/O"_err_en_US);
1133+
bad{FindInaccessibleComponent(which, derived, scope)}) {
1134+
return &context_.Say(where,
1135+
"I/O of the derived type '%s' may not be performed without defined I/O in a scope in which a direct component like '%s' is inaccessible"_err_en_US,
1136+
derived.name(), bad->name());
10441137
}
10451138
}
10461139
}
1140+
return nullptr;
1141+
}
1142+
1143+
void IoChecker::CheckForBadIoType(const SomeExpr &expr,
1144+
GenericKind::DefinedIo which, parser::CharBlock where) const {
1145+
if (auto type{expr.GetType()}) {
1146+
CheckForBadIoType(*type, which, where);
1147+
}
1148+
}
1149+
1150+
parser::Message *IoChecker::CheckForBadIoType(const Symbol &symbol,
1151+
GenericKind::DefinedIo which, parser::CharBlock where) const {
1152+
if (auto type{evaluate::DynamicType::From(symbol)}) {
1153+
if (auto *msg{CheckForBadIoType(*type, which, where)}) {
1154+
evaluate::AttachDeclaration(*msg, symbol);
1155+
return msg;
1156+
}
1157+
}
1158+
return nullptr;
1159+
}
1160+
1161+
void IoChecker::CheckNamelist(const Symbol &namelist,
1162+
GenericKind::DefinedIo which, parser::CharBlock namelistLocation) const {
1163+
const auto &details{namelist.GetUltimate().get<NamelistDetails>()};
1164+
for (const Symbol &object : details.objects()) {
1165+
context_.CheckIndexVarRedefine(namelistLocation, object);
1166+
if (auto *msg{CheckForBadIoType(object, which, namelistLocation)}) {
1167+
evaluate::AttachDeclaration(*msg, namelist);
1168+
}
1169+
}
10471170
}
10481171

10491172
} // namespace Fortran::semantics

flang/lib/Semantics/check-io.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,15 @@ class IoChecker : public virtual BaseChecker {
126126

127127
void CheckForPureSubprogram() const;
128128

129-
void CheckForBadIoComponent(
129+
parser::Message *CheckForBadIoType(const evaluate::DynamicType &,
130+
GenericKind::DefinedIo, parser::CharBlock) const;
131+
void CheckForBadIoType(
130132
const SomeExpr &, GenericKind::DefinedIo, parser::CharBlock) const;
133+
parser::Message *CheckForBadIoType(
134+
const Symbol &, GenericKind::DefinedIo, parser::CharBlock) const;
135+
136+
void CheckNamelist(
137+
const Symbol &, GenericKind::DefinedIo, parser::CharBlock) const;
131138

132139
void Init(IoStmtKind s) {
133140
stmt_ = s;

flang/lib/Semantics/resolve-names.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,7 @@ class ConstructVisitor : public virtual DeclarationVisitor {
11941194
// Creates Block scopes with neither symbol name nor symbol details.
11951195
bool Pre(const parser::SelectRankConstruct::RankCase &);
11961196
void Post(const parser::SelectRankConstruct::RankCase &);
1197+
bool Pre(const parser::TypeGuardStmt::Guard &);
11971198
void Post(const parser::TypeGuardStmt::Guard &);
11981199
void Post(const parser::SelectRankCaseStmt::Rank &);
11991200
bool Pre(const parser::ChangeTeamStmt &);
@@ -6407,6 +6408,14 @@ void ConstructVisitor::Post(const parser::SelectRankConstruct::RankCase &) {
64076408
PopScope();
64086409
}
64096410

6411+
bool ConstructVisitor::Pre(const parser::TypeGuardStmt::Guard &x) {
6412+
if (std::holds_alternative<parser::DerivedTypeSpec>(x.u)) {
6413+
// CLASS IS (t)
6414+
SetDeclTypeSpecCategory(DeclTypeSpec::Category::ClassDerived);
6415+
}
6416+
return true;
6417+
}
6418+
64106419
void ConstructVisitor::Post(const parser::TypeGuardStmt::Guard &x) {
64116420
if (auto *symbol{MakeAssocEntity()}) {
64126421
if (std::holds_alternative<parser::Default>(x.u)) {

flang/lib/Semantics/tools.cpp

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,31 +1514,4 @@ bool HasDefinedIo(GenericKind::DefinedIo which, const DerivedTypeSpec &derived,
15141514
return false;
15151515
}
15161516

1517-
const Symbol *FindUnsafeIoDirectComponent(GenericKind::DefinedIo which,
1518-
const DerivedTypeSpec &derived, const Scope *scope) {
1519-
if (HasDefinedIo(which, derived, scope)) {
1520-
return nullptr;
1521-
}
1522-
if (const Scope * dtScope{derived.scope()}) {
1523-
for (const auto &pair : *dtScope) {
1524-
const Symbol &symbol{*pair.second};
1525-
if (IsAllocatableOrPointer(symbol)) {
1526-
return &symbol;
1527-
}
1528-
if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
1529-
if (const DeclTypeSpec * type{details->type()}) {
1530-
if (type->category() == DeclTypeSpec::Category::TypeDerived) {
1531-
if (const Symbol *
1532-
bad{FindUnsafeIoDirectComponent(
1533-
which, type->derivedTypeSpec(), scope)}) {
1534-
return bad;
1535-
}
1536-
}
1537-
}
1538-
}
1539-
}
1540-
}
1541-
return nullptr;
1542-
}
1543-
15441517
} // namespace Fortran::semantics

flang/test/Semantics/io12.f90

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ subroutine test3(u)
5252
type(maybeBad) :: y
5353
type(poison) :: z
5454
write(u) x ! always ok
55-
!ERROR: Derived type in I/O cannot have an allocatable or pointer direct component unless using defined I/O
55+
!ERROR: Derived type 'maybebad' in I/O cannot have an allocatable or pointer direct component 'allocatablecomponent' unless using defined I/O
5656
write(u) y ! bad here
57-
!ERROR: Derived type in I/O cannot have an allocatable or pointer direct component unless using defined I/O
57+
!ERROR: Derived type 'poison' in I/O cannot have an allocatable or pointer direct component 'allocatablecomponent' unless using defined I/O
5858
write(u) z ! bad
5959
end subroutine
6060
end module
@@ -69,7 +69,7 @@ subroutine test4(u)
6969
type(poison) :: z
7070
write(u) x ! always ok
7171
write(u) y ! ok here
72-
!ERROR: Derived type in I/O cannot have an allocatable or pointer direct component unless using defined I/O
72+
!ERROR: Derived type 'poison' in I/O cannot have an allocatable or pointer direct component 'allocatablecomponent' unless using defined I/O
7373
write(u) z ! bad
7474
end subroutine
7575
end module

flang/test/Semantics/io14.f90

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
! Test polymorphic restrictions
3+
module m
4+
type base
5+
end type
6+
type, extends(base) :: t
7+
integer n
8+
contains
9+
procedure :: fwrite
10+
generic :: write(formatted) => fwrite
11+
end type
12+
contains
13+
subroutine fwrite(x, unit, iotype, vlist, iostat, iomsg)
14+
class(t), intent(in) :: x
15+
integer, intent(in) :: unit
16+
character(*), intent(in) :: iotype
17+
integer, intent(in) :: vlist(:)
18+
integer, intent(out) :: iostat
19+
character(*), intent(in out) :: iomsg
20+
write(unit, *, iostat=iostat, iomsg=iomsg) '(', iotype, ':', vlist, ':', x%n, ')'
21+
end subroutine
22+
subroutine subr(x, y, z)
23+
class(t), intent(in) :: x
24+
class(base), intent(in) :: y
25+
class(*), intent(in) :: z
26+
print *, x ! ok
27+
!ERROR: Derived type 'base' in I/O may not be polymorphic unless using defined I/O
28+
print *, y
29+
!ERROR: I/O list item may not be unlimited polymorphic
30+
print *, z
31+
end subroutine
32+
end
33+
34+
program main
35+
use m
36+
call subr(t(123),t(234),t(345))
37+
end

0 commit comments

Comments
 (0)