@@ -323,7 +323,7 @@ void IoChecker::Enter(const parser::InputItem &spec) {
323
323
}
324
324
CheckForDefinableVariable (*var, " Input" );
325
325
if (auto expr{AnalyzeExpr (context_, *var)}) {
326
- CheckForBadIoComponent (*expr,
326
+ CheckForBadIoType (*expr,
327
327
flags_.test (Flag::FmtOrNml) ? GenericKind::DefinedIo::ReadFormatted
328
328
: GenericKind::DefinedIo::ReadUnformatted,
329
329
var->GetSource ());
@@ -616,7 +616,7 @@ void IoChecker::Enter(const parser::OutputItem &item) {
616
616
context_.Say (parser::FindSourceLocation (*x),
617
617
" Output item must not be a procedure pointer" _err_en_US); // C1233
618
618
}
619
- CheckForBadIoComponent (*expr,
619
+ CheckForBadIoType (*expr,
620
620
flags_.test (Flag::FmtOrNml)
621
621
? GenericKind::DefinedIo::WriteFormatted
622
622
: GenericKind::DefinedIo::WriteUnformatted,
@@ -738,29 +738,21 @@ void IoChecker::Leave(const parser::PrintStmt &) {
738
738
Done ();
739
739
}
740
740
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) {
752
743
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;
756
748
}
757
749
}
758
750
}
751
+ return nullptr ;
759
752
}
760
753
761
754
static void CheckForDoVariable (
762
755
const parser::ReadStmt &readStmt, SemanticsContext &context) {
763
- CheckForDoVariableInNamelistSpec (readStmt, context);
764
756
const std::list<parser::InputItem> &items{readStmt.items };
765
757
for (const auto &item : items) {
766
758
if (const parser::Variable *
@@ -774,6 +766,12 @@ void IoChecker::Leave(const parser::ReadStmt &readStmt) {
774
766
if (!flags_.test (Flag::InternalUnit)) {
775
767
CheckForPureSubprogram ();
776
768
}
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
+ }
777
775
CheckForDoVariable (readStmt, context_);
778
776
if (!flags_.test (Flag::IoControlList)) {
779
777
Done ();
@@ -807,10 +805,16 @@ void IoChecker::Leave(const parser::WaitStmt &) {
807
805
Done ();
808
806
}
809
807
810
- void IoChecker::Leave (const parser::WriteStmt &) {
808
+ void IoChecker::Leave (const parser::WriteStmt &writeStmt ) {
811
809
if (!flags_.test (Flag::InternalUnit)) {
812
810
CheckForPureSubprogram ();
813
811
}
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
+ }
814
818
LeaveReadWrite ();
815
819
CheckForProhibitedSpecifier (IoSpecKind::Blank); // C1213
816
820
CheckForProhibitedSpecifier (IoSpecKind::End); // C1213
@@ -1030,20 +1034,139 @@ void IoChecker::CheckForPureSubprogram() const { // C1597
1030
1034
}
1031
1035
}
1032
1036
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,
1035
1113
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
+ }
1039
1132
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 () );
1044
1137
}
1045
1138
}
1046
1139
}
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
+ }
1047
1170
}
1048
1171
1049
1172
} // namespace Fortran::semantics
0 commit comments