Skip to content

Commit 17537db

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 17537db

File tree

8 files changed

+201
-110
lines changed

8 files changed

+201
-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: 163 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,8 @@ class CheckHelper {
132137
void CheckGlobalName(const Symbol &);
133138
void CheckProcedureAssemblyName(const Symbol &symbol);
134139
void CheckExplicitSave(const Symbol &);
140+
parser::Messages WhyNotInteroperableDerivedType(
141+
const Symbol &, UnorderedSymbolSet &checked);
135142
void CheckBindC(const Symbol &);
136143
void CheckBindCFunctionResult(const Symbol &);
137144
// Check functions for defined I/O procedures
@@ -2758,11 +2765,117 @@ void CheckHelper::CheckProcedureAssemblyName(const Symbol &symbol) {
27582765
}
27592766
}
27602767

2768+
parser::Messages CheckHelper::WhyNotInteroperableDerivedType(
2769+
const Symbol &symbol, UnorderedSymbolSet &checked) {
2770+
parser::Messages msgs;
2771+
if (checked.find(symbol) != checked.end()) {
2772+
return msgs;
2773+
}
2774+
checked.insert(symbol);
2775+
if (const auto *derived{symbol.detailsIf<DerivedTypeDetails>()}) {
2776+
if (derived->sequence()) { // C1801
2777+
msgs.Say(symbol.name(),
2778+
"An interoperable derived type cannot not have the SEQUENCE attribute"_err_en_US);
2779+
} else if (!derived->paramDecls().empty()) { // C1802
2780+
msgs.Say(symbol.name(),
2781+
"An interoperable derived type cannot have a type parameter"_err_en_US);
2782+
} else if (const auto *parent{
2783+
symbol.scope()->GetDerivedTypeParent()}) { // C1803
2784+
if (symbol.attrs().test(Attr::BIND_C)) {
2785+
msgs.Say(symbol.name(),
2786+
"A derived type with the BIND attribute cannot be an extended derived type"_err_en_US);
2787+
} else {
2788+
bool interoperableParent{true};
2789+
if (parent->symbol()) {
2790+
if (auto bad{
2791+
WhyNotInteroperableDerivedType(*parent->symbol(), checked)};
2792+
bad.AnyFatalError()) {
2793+
auto &msg{msgs.Say(symbol.name(),
2794+
"The parent of an interoperable type is not interoperable"_err_en_US)};
2795+
bad.AttachTo(msg, parser::Severity::None);
2796+
interoperableParent = false;
2797+
}
2798+
}
2799+
if (interoperableParent) {
2800+
msgs.Say(symbol.name(),
2801+
"An interoperable type should not be an extended derived type"_warn_en_US);
2802+
}
2803+
}
2804+
}
2805+
const Symbol *parentComponent{symbol.scope()
2806+
? derived->GetParentComponent(*symbol.scope())
2807+
: nullptr};
2808+
for (const auto &pair : *symbol.scope()) {
2809+
const Symbol &component{*pair.second};
2810+
if (&component == parentComponent) {
2811+
continue; // was checked above
2812+
}
2813+
if (IsProcedure(component)) { // C1804
2814+
msgs.Say(component.name(),
2815+
"An interoperable derived type cannot have a type bound procedure"_err_en_US);
2816+
} else if (IsAllocatableOrPointer(component)) { // C1806
2817+
msgs.Say(component.name(),
2818+
"An interoperable derived type cannot have a pointer or allocatable component"_err_en_US);
2819+
} else if (const auto *type{component.GetType()}) {
2820+
if (const auto *derived{type->AsDerived()}) {
2821+
if (auto bad{WhyNotInteroperableDerivedType(
2822+
derived->typeSymbol(), checked)};
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,30 @@ 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+
UnorderedSymbolSet checked;
2960+
if (auto bad{WhyNotInteroperableDerivedType(
2961+
derived->typeSymbol(), checked)};
2962+
!bad.empty()) {
2963+
if (bad.AnyFatalError()) {
2964+
if (auto *msg{messages_.Say(symbol.name(),
2965+
"The derived type of an interoperable object must be interoperable, but is not"_err_en_US)}) {
2966+
bad.AttachTo(*msg, parser::Severity::None);
2967+
}
2968+
context_.SetError(symbol);
2969+
}
2970+
}
28402971
}
2841-
context_.SetError(symbol);
28422972
}
28432973
if (type->IsAssumedType() || IsAssumedLengthCharacter(symbol)) {
28442974
// ok
@@ -2881,17 +3011,20 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
28813011
"An interoperable pointer must not be CONTIGUOUS"_err_en_US);
28823012
}
28833013
} 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);
3014+
if (!IsBindCProcedure(symbol) && proc->isDummy()) {
3015+
messages_.Say(symbol.name(),
3016+
"A dummy procedure to an interoperable procedure must also be interoperable"_err_en_US);
3017+
context_.SetError(symbol);
3018+
} else if (!proc->procInterface()) {
3019+
if (context_.ShouldWarn(
3020+
common::LanguageFeature::NonBindCInteroperability)) {
3021+
WarnIfNotInModuleFile(symbol.name(),
3022+
"An interface name with BIND attribute should be specified if the BIND attribute is specified in a procedure declaration statement"_warn_en_US);
28943023
}
3024+
} else if (!proc->procInterface()->attrs().test(Attr::BIND_C)) {
3025+
messages_.Say(symbol.name(),
3026+
"An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US);
3027+
context_.SetError(symbol);
28953028
}
28963029
} else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) {
28973030
for (const Symbol *dummy : subp->dummyArgs()) {
@@ -2903,77 +3036,18 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
29033036
context_.SetError(symbol);
29043037
}
29053038
}
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-
}
3039+
} else if (symbol.has<DerivedTypeDetails>()) {
3040+
UnorderedSymbolSet checked;
3041+
if (auto msgs{WhyNotInteroperableDerivedType(symbol, checked)};
3042+
!msgs.empty()) {
3043+
if (msgs.AnyFatalError()) {
3044+
context_.SetError(symbol);
29713045
}
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);
3046+
if (msgs.AnyFatalError() ||
3047+
(!InModuleFile() &&
3048+
context_.ShouldWarn(
3049+
common::LanguageFeature::NonBindCInteroperability))) {
3050+
context_.messages().Annex(std::move(msgs));
29773051
}
29783052
}
29793053
}

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)