Skip to content

Commit f61b267

Browse files
committed
[flang] Accept interoperable types without BIND(C)
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 ce7700e commit f61b267

File tree

8 files changed

+197
-110
lines changed

8 files changed

+197
-110
lines changed

flang/docs/Extensions.md

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

124128
## Extensions, deletions, and legacy features supported by default
125129

@@ -351,6 +355,9 @@ end
351355
when necessary to the type of the result.
352356
An `OPTIONAL`, `POINTER`, or `ALLOCATABLE` argument after
353357
the first two cannot be converted, as it may not be present.
358+
* A derived type that meets (most of) the requirements of an interoperable
359+
derived type can be used as such where an interoperable type is
360+
required, with warnings, even if it lacks the BIND(C) attribute.
354361

355362
### Extensions supported when enabled by options
356363

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: 159 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,16 @@ class CheckHelper {
115115
}
116116
return msg;
117117
}
118+
bool InModuleFile() const {
119+
return FindModuleFileContaining(context_.FindScope(messages_.at())) !=
120+
nullptr;
121+
}
118122
template <typename... A> parser::Message *WarnIfNotInModuleFile(A &&...x) {
119-
if (FindModuleFileContaining(context_.FindScope(messages_.at()))) {
123+
if (InModuleFile()) {
120124
return nullptr;
125+
} else {
126+
return messages_.Say(std::forward<A>(x)...);
121127
}
122-
return messages_.Say(std::forward<A>(x)...);
123128
}
124129
template <typename... A>
125130
parser::Message *WarnIfNotInModuleFile(parser::CharBlock source, A &&...x) {
@@ -132,6 +137,7 @@ class CheckHelper {
132137
void CheckGlobalName(const Symbol &);
133138
void CheckProcedureAssemblyName(const Symbol &symbol);
134139
void CheckExplicitSave(const Symbol &);
140+
parser::Messages WhyNotInteroperableDerivedType(const Symbol &);
135141
void CheckBindC(const Symbol &);
136142
void CheckBindCFunctionResult(const Symbol &);
137143
// Check functions for defined I/O procedures
@@ -182,6 +188,8 @@ class CheckHelper {
182188
// Collection of target dependent assembly names of external and BIND(C)
183189
// procedures.
184190
std::map<std::string, SymbolRef> procedureAssemblyNames_;
191+
// Derived types that have been examined by WhyNotInteroperableDerivedType
192+
UnorderedSymbolSet examinedByWhyNotInteroperableDerivedType_;
185193
};
186194

187195
class DistinguishabilityHelper {
@@ -2758,11 +2766,116 @@ void CheckHelper::CheckProcedureAssemblyName(const Symbol &symbol) {
27582766
}
27592767
}
27602768

2769+
parser::Messages CheckHelper::WhyNotInteroperableDerivedType(
2770+
const Symbol &symbol) {
2771+
parser::Messages msgs;
2772+
if (examinedByWhyNotInteroperableDerivedType_.find(symbol) !=
2773+
examinedByWhyNotInteroperableDerivedType_.end()) {
2774+
return msgs;
2775+
}
2776+
examinedByWhyNotInteroperableDerivedType_.insert(symbol);
2777+
if (const auto *derived{symbol.detailsIf<DerivedTypeDetails>()}) {
2778+
if (derived->sequence()) { // C1801
2779+
msgs.Say(symbol.name(),
2780+
"An interoperable derived type cannot have the SEQUENCE attribute"_err_en_US);
2781+
} else if (!derived->paramDecls().empty()) { // C1802
2782+
msgs.Say(symbol.name(),
2783+
"An interoperable derived type cannot have a type parameter"_err_en_US);
2784+
} else if (const auto *parent{
2785+
symbol.scope()->GetDerivedTypeParent()}) { // C1803
2786+
if (symbol.attrs().test(Attr::BIND_C)) {
2787+
msgs.Say(symbol.name(),
2788+
"A derived type with the BIND attribute cannot be an extended derived type"_err_en_US);
2789+
} else {
2790+
bool interoperableParent{true};
2791+
if (parent->symbol()) {
2792+
if (auto bad{WhyNotInteroperableDerivedType(*parent->symbol())};
2793+
bad.AnyFatalError()) {
2794+
auto &msg{msgs.Say(symbol.name(),
2795+
"The parent of an interoperable type is not interoperable"_err_en_US)};
2796+
bad.AttachTo(msg, parser::Severity::None);
2797+
interoperableParent = false;
2798+
}
2799+
}
2800+
if (interoperableParent) {
2801+
msgs.Say(symbol.name(),
2802+
"An interoperable type should not be an extended derived type"_warn_en_US);
2803+
}
2804+
}
2805+
}
2806+
const Symbol *parentComponent{symbol.scope()
2807+
? derived->GetParentComponent(*symbol.scope())
2808+
: nullptr};
2809+
for (const auto &pair : *symbol.scope()) {
2810+
const Symbol &component{*pair.second};
2811+
if (&component == parentComponent) {
2812+
continue; // was checked above
2813+
}
2814+
if (IsProcedure(component)) { // C1804
2815+
msgs.Say(component.name(),
2816+
"An interoperable derived type cannot have a type bound procedure"_err_en_US);
2817+
} else if (IsAllocatableOrPointer(component)) { // C1806
2818+
msgs.Say(component.name(),
2819+
"An interoperable derived type cannot have a pointer or allocatable component"_err_en_US);
2820+
} else if (const auto *type{component.GetType()}) {
2821+
if (const auto *derived{type->AsDerived()}) {
2822+
if (auto bad{WhyNotInteroperableDerivedType(derived->typeSymbol())};
2823+
bad.AnyFatalError()) {
2824+
auto &msg{msgs.Say(component.name(),
2825+
"Component '%s' of an interoperable derived type must have an interoperable type but does not"_err_en_US,
2826+
component.name())};
2827+
bad.AttachTo(msg, parser::Severity::None);
2828+
} else if (!derived->typeSymbol().GetUltimate().attrs().test(
2829+
Attr::BIND_C)) {
2830+
msgs.Say(component.name(),
2831+
"Derived type of component '%s' of an interoperable derived type should have the BIND attribute"_warn_en_US,
2832+
component.name())
2833+
.Attach(derived->typeSymbol().name(),
2834+
"Non-BIND(C) component type"_en_US);
2835+
}
2836+
} else if (!IsInteroperableIntrinsicType(
2837+
*type, context_.languageFeatures())) {
2838+
auto maybeDyType{evaluate::DynamicType::From(*type)};
2839+
if (type->category() == DeclTypeSpec::Logical) {
2840+
if (context_.ShouldWarn(common::UsageWarning::LogicalVsCBool)) {
2841+
msgs.Say(component.name(),
2842+
"A LOGICAL component of an interoperable type should have the interoperable KIND=C_BOOL"_port_en_US);
2843+
}
2844+
} else if (type->category() == DeclTypeSpec::Character &&
2845+
maybeDyType && maybeDyType->kind() == 1) {
2846+
if (context_.ShouldWarn(common::UsageWarning::BindCCharLength)) {
2847+
msgs.Say(component.name(),
2848+
"A CHARACTER component of an interoperable type should have length 1"_port_en_US);
2849+
}
2850+
} else {
2851+
msgs.Say(component.name(),
2852+
"Each component of an interoperable derived type must have an interoperable type"_err_en_US);
2853+
}
2854+
}
2855+
}
2856+
if (auto extents{
2857+
evaluate::GetConstantExtents(foldingContext_, &component)};
2858+
extents && evaluate::GetSize(*extents) == 0) {
2859+
msgs.Say(component.name(),
2860+
"An array component of an interoperable type must have at least one element"_err_en_US);
2861+
}
2862+
}
2863+
if (derived->componentNames().empty()) { // F'2023 C1805
2864+
if (context_.ShouldWarn(common::LanguageFeature::EmptyBindCDerivedType)) {
2865+
msgs.Say(symbol.name(),
2866+
"A derived type with the BIND attribute should not be empty"_port_en_US);
2867+
}
2868+
}
2869+
}
2870+
return msgs;
2871+
}
2872+
27612873
void CheckHelper::CheckBindC(const Symbol &symbol) {
27622874
bool isExplicitBindC{symbol.attrs().test(Attr::BIND_C)};
27632875
if (isExplicitBindC) {
2764-
CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER);
27652876
CheckConflicting(symbol, Attr::BIND_C, Attr::ELEMENTAL);
2877+
CheckConflicting(symbol, Attr::BIND_C, Attr::INTRINSIC);
2878+
CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER);
27662879
} else {
27672880
// symbol must be interoperable (e.g., dummy argument of interoperable
27682881
// procedure interface) but is not itself BIND(C).
@@ -2832,13 +2945,28 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
28322945
}
28332946
if (const auto *type{symbol.GetType()}) {
28342947
const auto *derived{type->AsDerived()};
2835-
if (derived && !derived->typeSymbol().attrs().test(Attr::BIND_C)) {
2836-
if (auto *msg{messages_.Say(symbol.name(),
2837-
"The derived type of a BIND(C) object must also be BIND(C)"_err_en_US)}) {
2838-
msg->Attach(
2839-
derived->typeSymbol().name(), "Non-interoperable type"_en_US);
2948+
if (derived) {
2949+
if (isExplicitBindC) {
2950+
if (!derived->typeSymbol().attrs().test(Attr::BIND_C)) {
2951+
if (auto *msg{messages_.Say(symbol.name(),
2952+
"The derived type of a BIND(C) object must also be BIND(C)"_err_en_US)}) {
2953+
msg->Attach(
2954+
derived->typeSymbol().name(), "Non-interoperable type"_en_US);
2955+
}
2956+
context_.SetError(symbol);
2957+
}
2958+
} else {
2959+
if (auto bad{WhyNotInteroperableDerivedType(derived->typeSymbol())};
2960+
!bad.empty()) {
2961+
if (bad.AnyFatalError()) {
2962+
if (auto *msg{messages_.Say(symbol.name(),
2963+
"The derived type of an interoperable object must be interoperable, but is not"_err_en_US)}) {
2964+
bad.AttachTo(*msg, parser::Severity::None);
2965+
}
2966+
context_.SetError(symbol);
2967+
}
2968+
}
28402969
}
2841-
context_.SetError(symbol);
28422970
}
28432971
if (type->IsAssumedType() || IsAssumedLengthCharacter(symbol)) {
28442972
// ok
@@ -2881,17 +3009,20 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
28813009
"An interoperable pointer must not be CONTIGUOUS"_err_en_US);
28823010
}
28833011
} else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
2884-
if (!proc->procInterface() ||
2885-
!proc->procInterface()->attrs().test(Attr::BIND_C)) {
2886-
if (proc->isDummy()) {
2887-
messages_.Say(symbol.name(),
2888-
"A dummy procedure to an interoperable procedure must also be interoperable"_err_en_US);
2889-
context_.SetError(symbol);
2890-
} else {
2891-
messages_.Say(symbol.name(),
2892-
"An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US);
2893-
context_.SetError(symbol);
3012+
if (!IsBindCProcedure(symbol) && proc->isDummy()) {
3013+
messages_.Say(symbol.name(),
3014+
"A dummy procedure to an interoperable procedure must also be interoperable"_err_en_US);
3015+
context_.SetError(symbol);
3016+
} else if (!proc->procInterface()) {
3017+
if (context_.ShouldWarn(
3018+
common::LanguageFeature::NonBindCInteroperability)) {
3019+
WarnIfNotInModuleFile(symbol.name(),
3020+
"An interface name with BIND attribute should be specified if the BIND attribute is specified in a procedure declaration statement"_warn_en_US);
28943021
}
3022+
} else if (!proc->procInterface()->attrs().test(Attr::BIND_C)) {
3023+
messages_.Say(symbol.name(),
3024+
"An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US);
3025+
context_.SetError(symbol);
28953026
}
28963027
} else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) {
28973028
for (const Symbol *dummy : subp->dummyArgs()) {
@@ -2903,77 +3034,16 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
29033034
context_.SetError(symbol);
29043035
}
29053036
}
2906-
} else if (const auto *derived{symbol.detailsIf<DerivedTypeDetails>()}) {
2907-
if (derived->sequence()) { // C1801
2908-
messages_.Say(symbol.name(),
2909-
"A derived type with the BIND attribute cannot have the SEQUENCE attribute"_err_en_US);
2910-
context_.SetError(symbol);
2911-
} else if (!derived->paramDecls().empty()) { // C1802
2912-
messages_.Say(symbol.name(),
2913-
"A derived type with the BIND attribute has type parameter(s)"_err_en_US);
2914-
context_.SetError(symbol);
2915-
} else if (symbol.scope()->GetDerivedTypeParent()) { // C1803
2916-
messages_.Say(symbol.name(),
2917-
"A derived type with the BIND attribute cannot extend from another derived type"_err_en_US);
2918-
context_.SetError(symbol);
2919-
} else {
2920-
for (const auto &pair : *symbol.scope()) {
2921-
const Symbol *component{&*pair.second};
2922-
if (IsProcedure(*component)) { // C1804
2923-
messages_.Say(component->name(),
2924-
"A derived type with the BIND attribute cannot have a type bound procedure"_err_en_US);
2925-
context_.SetError(symbol);
2926-
}
2927-
if (IsAllocatableOrPointer(*component)) { // C1806
2928-
messages_.Say(component->name(),
2929-
"A derived type with the BIND attribute cannot have a pointer or allocatable component"_err_en_US);
2930-
context_.SetError(symbol);
2931-
}
2932-
if (const auto *type{component->GetType()}) {
2933-
if (const auto *derived{type->AsDerived()}) {
2934-
if (!derived->typeSymbol().attrs().test(Attr::BIND_C)) {
2935-
if (auto *msg{messages_.Say(component->name(),
2936-
"Component '%s' of an interoperable derived type must have the BIND attribute"_err_en_US,
2937-
component->name())}) {
2938-
msg->Attach(derived->typeSymbol().name(),
2939-
"Non-interoperable component type"_en_US);
2940-
}
2941-
context_.SetError(symbol);
2942-
}
2943-
} else if (!IsInteroperableIntrinsicType(
2944-
*type, context_.languageFeatures())) {
2945-
auto maybeDyType{evaluate::DynamicType::From(*type)};
2946-
if (type->category() == DeclTypeSpec::Logical) {
2947-
if (context_.ShouldWarn(common::UsageWarning::LogicalVsCBool)) {
2948-
WarnIfNotInModuleFile(component->name(),
2949-
"A LOGICAL component of a BIND(C) type should have the interoperable KIND=C_BOOL"_port_en_US);
2950-
}
2951-
} else if (type->category() == DeclTypeSpec::Character &&
2952-
maybeDyType && maybeDyType->kind() == 1) {
2953-
if (context_.ShouldWarn(common::UsageWarning::BindCCharLength)) {
2954-
WarnIfNotInModuleFile(component->name(),
2955-
"A CHARACTER component of a BIND(C) type should have length 1"_port_en_US);
2956-
}
2957-
} else {
2958-
messages_.Say(component->name(),
2959-
"Each component of an interoperable derived type must have an interoperable type"_err_en_US);
2960-
context_.SetError(symbol);
2961-
}
2962-
}
2963-
}
2964-
if (auto extents{
2965-
evaluate::GetConstantExtents(foldingContext_, component)};
2966-
extents && evaluate::GetSize(*extents) == 0) {
2967-
messages_.Say(component->name(),
2968-
"An array component of an interoperable type must have at least one element"_err_en_US);
2969-
context_.SetError(symbol);
2970-
}
3037+
} else if (symbol.has<DerivedTypeDetails>()) {
3038+
if (auto msgs{WhyNotInteroperableDerivedType(symbol)}; !msgs.empty()) {
3039+
if (msgs.AnyFatalError()) {
3040+
context_.SetError(symbol);
29713041
}
2972-
}
2973-
if (derived->componentNames().empty()) { // F'2023 C1805
2974-
if (context_.ShouldWarn(common::LanguageFeature::EmptyBindCDerivedType)) {
2975-
WarnIfNotInModuleFile(symbol.name(),
2976-
"A derived type with the BIND attribute is empty"_port_en_US);
3042+
if (msgs.AnyFatalError() ||
3043+
(!InModuleFile() &&
3044+
context_.ShouldWarn(
3045+
common::LanguageFeature::NonBindCInteroperability))) {
3046+
context_.messages().Annex(std::move(msgs));
29773047
}
29783048
}
29793049
}

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)