Skip to content

Commit 90501be

Browse files
authored
[flang] Accept interoperable types without BIND(C) (#91363)
A derived type that meets (most of) the requirements of an interoperable type but doesn't actually have the BIND(C) attribute can be accepted as an interoperable type, with optional warnings.
1 parent 5ad418b commit 90501be

File tree

8 files changed

+214
-110
lines changed

8 files changed

+214
-110
lines changed

flang/docs/Extensions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ end
114114
appear in real applications, but are still non-conforming under the
115115
incomplete tests in F'2023 15.4.3.4.5.
116116
These cases are compiled with optional portability warnings.
117+
* `PROCEDURE(), BIND(C) :: PROC` is not conforming, as there is no
118+
procedure interface. This compiler accepts it, since there is otherwise
119+
no way to declare an interoperable dummy procedure with an arbitrary
120+
interface like `void (*)()`.
117121

118122
## Extensions, deletions, and legacy features supported by default
119123

@@ -345,6 +349,9 @@ end
345349
when necessary to the type of the result.
346350
An `OPTIONAL`, `POINTER`, or `ALLOCATABLE` argument after
347351
the first two cannot be converted, as it may not be present.
352+
* A derived type that meets (most of) the requirements of an interoperable
353+
derived type can be used as such where an interoperable type is
354+
required, with warnings, even if it lacks the BIND(C) attribute.
348355

349356
### Extensions supported when enabled by options
350357

flang/include/flang/Common/Fortran-features.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
4848
ImpliedDoIndexScope, DistinctCommonSizes, OddIndexVariableRestrictions,
4949
IndistinguishableSpecifics, SubroutineAndFunctionSpecifics,
5050
EmptySequenceType, NonSequenceCrayPointee, BranchIntoConstruct,
51-
BadBranchTarget, ConvertedArgument, HollerithPolymorphic, ListDirectedSize)
51+
BadBranchTarget, ConvertedArgument, HollerithPolymorphic, ListDirectedSize,
52+
NonBindCInteroperability)
5253

5354
// Portability and suspicious usage warnings
5455
ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,

flang/lib/Semantics/check-declarations.cpp

Lines changed: 176 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,16 @@ class CheckHelper {
116116
}
117117
return msg;
118118
}
119+
bool InModuleFile() const {
120+
return FindModuleFileContaining(context_.FindScope(messages_.at())) !=
121+
nullptr;
122+
}
119123
template <typename... A> parser::Message *WarnIfNotInModuleFile(A &&...x) {
120-
if (FindModuleFileContaining(context_.FindScope(messages_.at()))) {
124+
if (InModuleFile()) {
121125
return nullptr;
126+
} else {
127+
return messages_.Say(std::forward<A>(x)...);
122128
}
123-
return messages_.Say(std::forward<A>(x)...);
124129
}
125130
template <typename... A>
126131
parser::Message *WarnIfNotInModuleFile(parser::CharBlock source, A &&...x) {
@@ -133,6 +138,7 @@ class CheckHelper {
133138
void CheckGlobalName(const Symbol &);
134139
void CheckProcedureAssemblyName(const Symbol &symbol);
135140
void CheckExplicitSave(const Symbol &);
141+
parser::Messages WhyNotInteroperableDerivedType(const Symbol &, bool isError);
136142
void CheckBindC(const Symbol &);
137143
void CheckBindCFunctionResult(const Symbol &);
138144
// Check functions for defined I/O procedures
@@ -183,6 +189,8 @@ class CheckHelper {
183189
// Collection of target dependent assembly names of external and BIND(C)
184190
// procedures.
185191
std::map<std::string, SymbolRef> procedureAssemblyNames_;
192+
// Derived types that have been examined by WhyNotInteroperableDerivedType
193+
UnorderedSymbolSet examinedByWhyNotInteroperableDerivedType_;
186194
};
187195

188196
class DistinguishabilityHelper {
@@ -2822,11 +2830,129 @@ void CheckHelper::CheckProcedureAssemblyName(const Symbol &symbol) {
28222830
}
28232831
}
28242832

2833+
parser::Messages CheckHelper::WhyNotInteroperableDerivedType(
2834+
const Symbol &symbol, bool isError) {
2835+
parser::Messages msgs;
2836+
if (examinedByWhyNotInteroperableDerivedType_.find(symbol) !=
2837+
examinedByWhyNotInteroperableDerivedType_.end()) {
2838+
return msgs;
2839+
}
2840+
isError |= symbol.attrs().test(Attr::BIND_C);
2841+
examinedByWhyNotInteroperableDerivedType_.insert(symbol);
2842+
if (const auto *derived{symbol.detailsIf<DerivedTypeDetails>()}) {
2843+
if (derived->sequence()) { // C1801
2844+
msgs.Say(symbol.name(),
2845+
"An interoperable derived type cannot have the SEQUENCE attribute"_err_en_US);
2846+
} else if (!derived->paramDecls().empty()) { // C1802
2847+
msgs.Say(symbol.name(),
2848+
"An interoperable derived type cannot have a type parameter"_err_en_US);
2849+
} else if (const auto *parent{
2850+
symbol.scope()->GetDerivedTypeParent()}) { // C1803
2851+
if (isError) {
2852+
msgs.Say(symbol.name(),
2853+
"A derived type with the BIND attribute cannot be an extended derived type"_err_en_US);
2854+
} else {
2855+
bool interoperableParent{true};
2856+
if (parent->symbol()) {
2857+
auto bad{WhyNotInteroperableDerivedType(*parent->symbol(), false)};
2858+
if (bad.AnyFatalError()) {
2859+
auto &msg{msgs.Say(symbol.name(),
2860+
"The parent of an interoperable type is not interoperable"_err_en_US)};
2861+
bad.AttachTo(msg, parser::Severity::None);
2862+
interoperableParent = false;
2863+
}
2864+
}
2865+
if (interoperableParent) {
2866+
msgs.Say(symbol.name(),
2867+
"An interoperable type should not be an extended derived type"_warn_en_US);
2868+
}
2869+
}
2870+
}
2871+
const Symbol *parentComponent{symbol.scope()
2872+
? derived->GetParentComponent(*symbol.scope())
2873+
: nullptr};
2874+
for (const auto &pair : *symbol.scope()) {
2875+
const Symbol &component{*pair.second};
2876+
if (&component == parentComponent) {
2877+
continue; // was checked above
2878+
}
2879+
if (IsProcedure(component)) { // C1804
2880+
msgs.Say(component.name(),
2881+
"An interoperable derived type cannot have a type bound procedure"_err_en_US);
2882+
} else if (IsAllocatableOrPointer(component)) { // C1806
2883+
msgs.Say(component.name(),
2884+
"An interoperable derived type cannot have a pointer or allocatable component"_err_en_US);
2885+
} else if (const auto *type{component.GetType()}) {
2886+
if (const auto *derived{type->AsDerived()}) {
2887+
auto bad{
2888+
WhyNotInteroperableDerivedType(derived->typeSymbol(), isError)};
2889+
if (bad.AnyFatalError()) {
2890+
auto &msg{msgs.Say(component.name(),
2891+
"Component '%s' of an interoperable derived type must have an interoperable type but does not"_err_en_US,
2892+
component.name())};
2893+
bad.AttachTo(msg, parser::Severity::None);
2894+
} else if (!derived->typeSymbol().GetUltimate().attrs().test(
2895+
Attr::BIND_C)) {
2896+
auto &msg{
2897+
msgs.Say(component.name(),
2898+
"Derived type of component '%s' of an interoperable derived type should have the BIND attribute"_warn_en_US,
2899+
component.name())
2900+
.Attach(derived->typeSymbol().name(),
2901+
"Non-BIND(C) component type"_en_US)};
2902+
bad.AttachTo(msg, parser::Severity::None);
2903+
} else {
2904+
msgs.Annex(std::move(bad));
2905+
}
2906+
} else if (!IsInteroperableIntrinsicType(
2907+
*type, context_.languageFeatures())) {
2908+
auto maybeDyType{evaluate::DynamicType::From(*type)};
2909+
if (type->category() == DeclTypeSpec::Logical) {
2910+
if (context_.ShouldWarn(common::UsageWarning::LogicalVsCBool)) {
2911+
msgs.Say(component.name(),
2912+
"A LOGICAL component of an interoperable type should have the interoperable KIND=C_BOOL"_port_en_US);
2913+
}
2914+
} else if (type->category() == DeclTypeSpec::Character &&
2915+
maybeDyType && maybeDyType->kind() == 1) {
2916+
if (context_.ShouldWarn(common::UsageWarning::BindCCharLength)) {
2917+
msgs.Say(component.name(),
2918+
"A CHARACTER component of an interoperable type should have length 1"_port_en_US);
2919+
}
2920+
} else {
2921+
msgs.Say(component.name(),
2922+
"Each component of an interoperable derived type must have an interoperable type"_err_en_US);
2923+
}
2924+
}
2925+
}
2926+
if (auto extents{
2927+
evaluate::GetConstantExtents(foldingContext_, &component)};
2928+
extents && evaluate::GetSize(*extents) == 0) {
2929+
msgs.Say(component.name(),
2930+
"An array component of an interoperable type must have at least one element"_err_en_US);
2931+
}
2932+
}
2933+
if (derived->componentNames().empty()) { // F'2023 C1805
2934+
if (context_.ShouldWarn(common::LanguageFeature::EmptyBindCDerivedType)) {
2935+
msgs.Say(symbol.name(),
2936+
"A derived type with the BIND attribute should not be empty"_port_en_US);
2937+
}
2938+
}
2939+
}
2940+
if (isError) {
2941+
for (auto &m : msgs.messages()) {
2942+
if (!m.IsFatal()) {
2943+
m.set_severity(parser::Severity::Error);
2944+
}
2945+
}
2946+
}
2947+
return msgs;
2948+
}
2949+
28252950
void CheckHelper::CheckBindC(const Symbol &symbol) {
28262951
bool isExplicitBindC{symbol.attrs().test(Attr::BIND_C)};
28272952
if (isExplicitBindC) {
2828-
CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER);
28292953
CheckConflicting(symbol, Attr::BIND_C, Attr::ELEMENTAL);
2954+
CheckConflicting(symbol, Attr::BIND_C, Attr::INTRINSIC);
2955+
CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER);
28302956
} else {
28312957
// symbol must be interoperable (e.g., dummy argument of interoperable
28322958
// procedure interface) but is not itself BIND(C).
@@ -2896,13 +3022,30 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
28963022
}
28973023
if (const auto *type{symbol.GetType()}) {
28983024
const auto *derived{type->AsDerived()};
2899-
if (derived && !derived->typeSymbol().attrs().test(Attr::BIND_C)) {
2900-
if (auto *msg{messages_.Say(symbol.name(),
2901-
"The derived type of a BIND(C) object must also be BIND(C)"_err_en_US)}) {
2902-
msg->Attach(
2903-
derived->typeSymbol().name(), "Non-interoperable type"_en_US);
3025+
if (derived) {
3026+
if (derived->typeSymbol().attrs().test(Attr::BIND_C)) {
3027+
} else if (isExplicitBindC) {
3028+
if (auto *msg{messages_.Say(symbol.name(),
3029+
"The derived type of a BIND(C) object must also be BIND(C)"_err_en_US)}) {
3030+
msg->Attach(derived->typeSymbol().name(), "Non-BIND(C) type"_en_US);
3031+
}
3032+
context_.SetError(symbol);
3033+
} else if (auto bad{WhyNotInteroperableDerivedType(
3034+
derived->typeSymbol(), false)};
3035+
!bad.empty()) {
3036+
if (auto *msg{messages_.Say(symbol.name(),
3037+
"The derived type of an interoperable object must be interoperable, but is not"_err_en_US)}) {
3038+
msg->Attach(
3039+
derived->typeSymbol().name(), "Non-interoperable type"_en_US);
3040+
bad.AttachTo(*msg, parser::Severity::None);
3041+
}
3042+
context_.SetError(symbol);
3043+
} else {
3044+
if (auto *msg{messages_.Say(symbol.name(),
3045+
"The derived type of an interoperable object should be BIND(C)"_warn_en_US)}) {
3046+
msg->Attach(derived->typeSymbol().name(), "Non-BIND(C) type"_en_US);
3047+
}
29043048
}
2905-
context_.SetError(symbol);
29063049
}
29073050
if (type->IsAssumedType() || IsAssumedLengthCharacter(symbol)) {
29083051
// ok
@@ -2945,17 +3088,20 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
29453088
"An interoperable pointer must not be CONTIGUOUS"_err_en_US);
29463089
}
29473090
} else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
2948-
if (!proc->procInterface() ||
2949-
!proc->procInterface()->attrs().test(Attr::BIND_C)) {
2950-
if (proc->isDummy()) {
2951-
messages_.Say(symbol.name(),
2952-
"A dummy procedure to an interoperable procedure must also be interoperable"_err_en_US);
2953-
context_.SetError(symbol);
2954-
} else {
2955-
messages_.Say(symbol.name(),
2956-
"An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US);
2957-
context_.SetError(symbol);
3091+
if (!IsBindCProcedure(symbol) && proc->isDummy()) {
3092+
messages_.Say(symbol.name(),
3093+
"A dummy procedure to an interoperable procedure must also be interoperable"_err_en_US);
3094+
context_.SetError(symbol);
3095+
} else if (!proc->procInterface()) {
3096+
if (context_.ShouldWarn(
3097+
common::LanguageFeature::NonBindCInteroperability)) {
3098+
WarnIfNotInModuleFile(symbol.name(),
3099+
"An interface name with BIND attribute should be specified if the BIND attribute is specified in a procedure declaration statement"_warn_en_US);
29583100
}
3101+
} else if (!proc->procInterface()->attrs().test(Attr::BIND_C)) {
3102+
messages_.Say(symbol.name(),
3103+
"An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US);
3104+
context_.SetError(symbol);
29593105
}
29603106
} else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) {
29613107
for (const Symbol *dummy : subp->dummyArgs()) {
@@ -2967,77 +3113,18 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
29673113
context_.SetError(symbol);
29683114
}
29693115
}
2970-
} else if (const auto *derived{symbol.detailsIf<DerivedTypeDetails>()}) {
2971-
if (derived->sequence()) { // C1801
2972-
messages_.Say(symbol.name(),
2973-
"A derived type with the BIND attribute cannot have the SEQUENCE attribute"_err_en_US);
2974-
context_.SetError(symbol);
2975-
} else if (!derived->paramDecls().empty()) { // C1802
2976-
messages_.Say(symbol.name(),
2977-
"A derived type with the BIND attribute has type parameter(s)"_err_en_US);
2978-
context_.SetError(symbol);
2979-
} else if (symbol.scope()->GetDerivedTypeParent()) { // C1803
2980-
messages_.Say(symbol.name(),
2981-
"A derived type with the BIND attribute cannot extend from another derived type"_err_en_US);
2982-
context_.SetError(symbol);
2983-
} else {
2984-
for (const auto &pair : *symbol.scope()) {
2985-
const Symbol *component{&*pair.second};
2986-
if (IsProcedure(*component)) { // C1804
2987-
messages_.Say(component->name(),
2988-
"A derived type with the BIND attribute cannot have a type bound procedure"_err_en_US);
2989-
context_.SetError(symbol);
2990-
}
2991-
if (IsAllocatableOrPointer(*component)) { // C1806
2992-
messages_.Say(component->name(),
2993-
"A derived type with the BIND attribute cannot have a pointer or allocatable component"_err_en_US);
2994-
context_.SetError(symbol);
2995-
}
2996-
if (const auto *type{component->GetType()}) {
2997-
if (const auto *derived{type->AsDerived()}) {
2998-
if (!derived->typeSymbol().attrs().test(Attr::BIND_C)) {
2999-
if (auto *msg{messages_.Say(component->name(),
3000-
"Component '%s' of an interoperable derived type must have the BIND attribute"_err_en_US,
3001-
component->name())}) {
3002-
msg->Attach(derived->typeSymbol().name(),
3003-
"Non-interoperable component type"_en_US);
3004-
}
3005-
context_.SetError(symbol);
3006-
}
3007-
} else if (!IsInteroperableIntrinsicType(
3008-
*type, context_.languageFeatures())) {
3009-
auto maybeDyType{evaluate::DynamicType::From(*type)};
3010-
if (type->category() == DeclTypeSpec::Logical) {
3011-
if (context_.ShouldWarn(common::UsageWarning::LogicalVsCBool)) {
3012-
WarnIfNotInModuleFile(component->name(),
3013-
"A LOGICAL component of a BIND(C) type should have the interoperable KIND=C_BOOL"_port_en_US);
3014-
}
3015-
} else if (type->category() == DeclTypeSpec::Character &&
3016-
maybeDyType && maybeDyType->kind() == 1) {
3017-
if (context_.ShouldWarn(common::UsageWarning::BindCCharLength)) {
3018-
WarnIfNotInModuleFile(component->name(),
3019-
"A CHARACTER component of a BIND(C) type should have length 1"_port_en_US);
3020-
}
3021-
} else {
3022-
messages_.Say(component->name(),
3023-
"Each component of an interoperable derived type must have an interoperable type"_err_en_US);
3024-
context_.SetError(symbol);
3025-
}
3026-
}
3027-
}
3028-
if (auto extents{
3029-
evaluate::GetConstantExtents(foldingContext_, component)};
3030-
extents && evaluate::GetSize(*extents) == 0) {
3031-
messages_.Say(component->name(),
3032-
"An array component of an interoperable type must have at least one element"_err_en_US);
3033-
context_.SetError(symbol);
3034-
}
3116+
} else if (symbol.has<DerivedTypeDetails>()) {
3117+
if (auto msgs{WhyNotInteroperableDerivedType(symbol, false)};
3118+
!msgs.empty()) {
3119+
bool anyFatal{msgs.AnyFatalError()};
3120+
if (msgs.AnyFatalError() ||
3121+
(!InModuleFile() &&
3122+
context_.ShouldWarn(
3123+
common::LanguageFeature::NonBindCInteroperability))) {
3124+
context_.messages().Annex(std::move(msgs));
30353125
}
3036-
}
3037-
if (derived->componentNames().empty()) { // F'2023 C1805
3038-
if (context_.ShouldWarn(common::LanguageFeature::EmptyBindCDerivedType)) {
3039-
WarnIfNotInModuleFile(symbol.name(),
3040-
"A derived type with the BIND attribute is empty"_port_en_US);
3126+
if (anyFatal) {
3127+
context_.SetError(symbol);
30413128
}
30423129
}
30433130
}

flang/test/Semantics/bind-c03.f90

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
! RUN: %python %S/test_errors.py %s %flang_fc1
1+
! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic
22
! Check for C1521
33
! If proc-language-binding-spec (bind(c)) is specified, the proc-interface
44
! shall appear, it shall be an interface-name, and interface-name shall be
@@ -24,7 +24,10 @@ subroutine proc3() bind(c)
2424
!ERROR: An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement
2525
procedure(proc2), bind(c) :: pc2
2626

27-
!ERROR: An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement
27+
!WARNING: An interface name with BIND attribute should be specified if the BIND attribute is specified in a procedure declaration statement
2828
procedure(integer), bind(c) :: pc3
2929

30+
!WARNING: An interface name with BIND attribute should be specified if the BIND attribute is specified in a procedure declaration statement
31+
procedure(), bind(c) :: pc5
32+
3033
end

0 commit comments

Comments
 (0)