Skip to content

Commit deef049

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 46435ac commit deef049

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
@@ -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: 176 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 &, bool isError);
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,129 @@ void CheckHelper::CheckProcedureAssemblyName(const Symbol &symbol) {
27582766
}
27592767
}
27602768

2769+
parser::Messages CheckHelper::WhyNotInteroperableDerivedType(
2770+
const Symbol &symbol, bool isError) {
2771+
parser::Messages msgs;
2772+
if (examinedByWhyNotInteroperableDerivedType_.find(symbol) !=
2773+
examinedByWhyNotInteroperableDerivedType_.end()) {
2774+
return msgs;
2775+
}
2776+
isError |= symbol.attrs().test(Attr::BIND_C);
2777+
examinedByWhyNotInteroperableDerivedType_.insert(symbol);
2778+
if (const auto *derived{symbol.detailsIf<DerivedTypeDetails>()}) {
2779+
if (derived->sequence()) { // C1801
2780+
msgs.Say(symbol.name(),
2781+
"An interoperable derived type cannot have the SEQUENCE attribute"_err_en_US);
2782+
} else if (!derived->paramDecls().empty()) { // C1802
2783+
msgs.Say(symbol.name(),
2784+
"An interoperable derived type cannot have a type parameter"_err_en_US);
2785+
} else if (const auto *parent{
2786+
symbol.scope()->GetDerivedTypeParent()}) { // C1803
2787+
if (isError) {
2788+
msgs.Say(symbol.name(),
2789+
"A derived type with the BIND attribute cannot be an extended derived type"_err_en_US);
2790+
} else {
2791+
bool interoperableParent{true};
2792+
if (parent->symbol()) {
2793+
auto bad{WhyNotInteroperableDerivedType(*parent->symbol(), false)};
2794+
if (bad.AnyFatalError()) {
2795+
auto &msg{msgs.Say(symbol.name(),
2796+
"The parent of an interoperable type is not interoperable"_err_en_US)};
2797+
bad.AttachTo(msg, parser::Severity::None);
2798+
interoperableParent = false;
2799+
}
2800+
}
2801+
if (interoperableParent) {
2802+
msgs.Say(symbol.name(),
2803+
"An interoperable type should not be an extended derived type"_warn_en_US);
2804+
}
2805+
}
2806+
}
2807+
const Symbol *parentComponent{symbol.scope()
2808+
? derived->GetParentComponent(*symbol.scope())
2809+
: nullptr};
2810+
for (const auto &pair : *symbol.scope()) {
2811+
const Symbol &component{*pair.second};
2812+
if (&component == parentComponent) {
2813+
continue; // was checked above
2814+
}
2815+
if (IsProcedure(component)) { // C1804
2816+
msgs.Say(component.name(),
2817+
"An interoperable derived type cannot have a type bound procedure"_err_en_US);
2818+
} else if (IsAllocatableOrPointer(component)) { // C1806
2819+
msgs.Say(component.name(),
2820+
"An interoperable derived type cannot have a pointer or allocatable component"_err_en_US);
2821+
} else if (const auto *type{component.GetType()}) {
2822+
if (const auto *derived{type->AsDerived()}) {
2823+
auto bad{
2824+
WhyNotInteroperableDerivedType(derived->typeSymbol(), isError)};
2825+
if (bad.AnyFatalError()) {
2826+
auto &msg{msgs.Say(component.name(),
2827+
"Component '%s' of an interoperable derived type must have an interoperable type but does not"_err_en_US,
2828+
component.name())};
2829+
bad.AttachTo(msg, parser::Severity::None);
2830+
} else if (!derived->typeSymbol().GetUltimate().attrs().test(
2831+
Attr::BIND_C)) {
2832+
auto &msg{
2833+
msgs.Say(component.name(),
2834+
"Derived type of component '%s' of an interoperable derived type should have the BIND attribute"_warn_en_US,
2835+
component.name())
2836+
.Attach(derived->typeSymbol().name(),
2837+
"Non-BIND(C) component type"_en_US)};
2838+
bad.AttachTo(msg, parser::Severity::None);
2839+
} else {
2840+
msgs.Annex(std::move(bad));
2841+
}
2842+
} else if (!IsInteroperableIntrinsicType(
2843+
*type, context_.languageFeatures())) {
2844+
auto maybeDyType{evaluate::DynamicType::From(*type)};
2845+
if (type->category() == DeclTypeSpec::Logical) {
2846+
if (context_.ShouldWarn(common::UsageWarning::LogicalVsCBool)) {
2847+
msgs.Say(component.name(),
2848+
"A LOGICAL component of an interoperable type should have the interoperable KIND=C_BOOL"_port_en_US);
2849+
}
2850+
} else if (type->category() == DeclTypeSpec::Character &&
2851+
maybeDyType && maybeDyType->kind() == 1) {
2852+
if (context_.ShouldWarn(common::UsageWarning::BindCCharLength)) {
2853+
msgs.Say(component.name(),
2854+
"A CHARACTER component of an interoperable type should have length 1"_port_en_US);
2855+
}
2856+
} else {
2857+
msgs.Say(component.name(),
2858+
"Each component of an interoperable derived type must have an interoperable type"_err_en_US);
2859+
}
2860+
}
2861+
}
2862+
if (auto extents{
2863+
evaluate::GetConstantExtents(foldingContext_, &component)};
2864+
extents && evaluate::GetSize(*extents) == 0) {
2865+
msgs.Say(component.name(),
2866+
"An array component of an interoperable type must have at least one element"_err_en_US);
2867+
}
2868+
}
2869+
if (derived->componentNames().empty()) { // F'2023 C1805
2870+
if (context_.ShouldWarn(common::LanguageFeature::EmptyBindCDerivedType)) {
2871+
msgs.Say(symbol.name(),
2872+
"A derived type with the BIND attribute should not be empty"_port_en_US);
2873+
}
2874+
}
2875+
}
2876+
if (isError) {
2877+
for (auto &m : msgs.messages()) {
2878+
if (!m.IsFatal()) {
2879+
m.set_severity(parser::Severity::Error);
2880+
}
2881+
}
2882+
}
2883+
return msgs;
2884+
}
2885+
27612886
void CheckHelper::CheckBindC(const Symbol &symbol) {
27622887
bool isExplicitBindC{symbol.attrs().test(Attr::BIND_C)};
27632888
if (isExplicitBindC) {
2764-
CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER);
27652889
CheckConflicting(symbol, Attr::BIND_C, Attr::ELEMENTAL);
2890+
CheckConflicting(symbol, Attr::BIND_C, Attr::INTRINSIC);
2891+
CheckConflicting(symbol, Attr::BIND_C, Attr::PARAMETER);
27662892
} else {
27672893
// symbol must be interoperable (e.g., dummy argument of interoperable
27682894
// procedure interface) but is not itself BIND(C).
@@ -2832,13 +2958,30 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
28322958
}
28332959
if (const auto *type{symbol.GetType()}) {
28342960
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);
2961+
if (derived) {
2962+
if (derived->typeSymbol().attrs().test(Attr::BIND_C)) {
2963+
} else if (isExplicitBindC) {
2964+
if (auto *msg{messages_.Say(symbol.name(),
2965+
"The derived type of a BIND(C) object must also be BIND(C)"_err_en_US)}) {
2966+
msg->Attach(derived->typeSymbol().name(), "Non-BIND(C) type"_en_US);
2967+
}
2968+
context_.SetError(symbol);
2969+
} else if (auto bad{WhyNotInteroperableDerivedType(
2970+
derived->typeSymbol(), false)};
2971+
!bad.empty()) {
2972+
if (auto *msg{messages_.Say(symbol.name(),
2973+
"The derived type of an interoperable object must be interoperable, but is not"_err_en_US)}) {
2974+
msg->Attach(
2975+
derived->typeSymbol().name(), "Non-interoperable type"_en_US);
2976+
bad.AttachTo(*msg, parser::Severity::None);
2977+
}
2978+
context_.SetError(symbol);
2979+
} else {
2980+
if (auto *msg{messages_.Say(symbol.name(),
2981+
"The derived type of an interoperable object should be BIND(C)"_warn_en_US)}) {
2982+
msg->Attach(derived->typeSymbol().name(), "Non-BIND(C) type"_en_US);
2983+
}
28402984
}
2841-
context_.SetError(symbol);
28422985
}
28432986
if (type->IsAssumedType() || IsAssumedLengthCharacter(symbol)) {
28442987
// ok
@@ -2881,17 +3024,20 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
28813024
"An interoperable pointer must not be CONTIGUOUS"_err_en_US);
28823025
}
28833026
} 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);
3027+
if (!IsBindCProcedure(symbol) && proc->isDummy()) {
3028+
messages_.Say(symbol.name(),
3029+
"A dummy procedure to an interoperable procedure must also be interoperable"_err_en_US);
3030+
context_.SetError(symbol);
3031+
} else if (!proc->procInterface()) {
3032+
if (context_.ShouldWarn(
3033+
common::LanguageFeature::NonBindCInteroperability)) {
3034+
WarnIfNotInModuleFile(symbol.name(),
3035+
"An interface name with BIND attribute should be specified if the BIND attribute is specified in a procedure declaration statement"_warn_en_US);
28943036
}
3037+
} else if (!proc->procInterface()->attrs().test(Attr::BIND_C)) {
3038+
messages_.Say(symbol.name(),
3039+
"An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US);
3040+
context_.SetError(symbol);
28953041
}
28963042
} else if (const auto *subp{symbol.detailsIf<SubprogramDetails>()}) {
28973043
for (const Symbol *dummy : subp->dummyArgs()) {
@@ -2903,77 +3049,18 @@ void CheckHelper::CheckBindC(const Symbol &symbol) {
29033049
context_.SetError(symbol);
29043050
}
29053051
}
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-
}
3052+
} else if (symbol.has<DerivedTypeDetails>()) {
3053+
if (auto msgs{WhyNotInteroperableDerivedType(symbol, false)};
3054+
!msgs.empty()) {
3055+
bool anyFatal{msgs.AnyFatalError()};
3056+
if (msgs.AnyFatalError() ||
3057+
(!InModuleFile() &&
3058+
context_.ShouldWarn(
3059+
common::LanguageFeature::NonBindCInteroperability))) {
3060+
context_.messages().Annex(std::move(msgs));
29713061
}
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);
3062+
if (anyFatal) {
3063+
context_.SetError(symbol);
29773064
}
29783065
}
29793066
}

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)