[flang] Support BIND(C, NAME="...", CDEFINED) extension (#94402)

This CDEFINED keyword extension to a language-binding-spec signifies
that static storage for an interoperable variable will be allocated
outside of Fortran, probably by a C/C++ external object definition.
This commit is contained in:
Peter Klausler 2024-06-11 16:56:30 -07:00 committed by GitHub
parent 0f286f8a36
commit f3c227b797
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 58 additions and 10 deletions

View File

@ -374,6 +374,9 @@ end
required, with warnings, even if it lacks the BIND(C) attribute. required, with warnings, even if it lacks the BIND(C) attribute.
* A "mult-operand" in an expression can be preceded by a unary * A "mult-operand" in an expression can be preceded by a unary
`+` or `-` operator. `+` or `-` operator.
* `BIND(C, NAME="...", CDEFINED)` signifies that the storage for an
interoperable variable will be allocated outside of Fortran,
probably by a C or C++ external definition.
### Extensions supported when enabled by options ### Extensions supported when enabled by options

View File

@ -1296,10 +1296,13 @@ struct AcImpliedDo {
}; };
// R808 language-binding-spec -> // R808 language-binding-spec ->
// BIND ( C [, NAME = scalar-default-char-constant-expr] ) // BIND ( C [, NAME = scalar-default-char-constant-expr ]
// [, CDEFINED ] )
// R1528 proc-language-binding-spec -> language-binding-spec // R1528 proc-language-binding-spec -> language-binding-spec
WRAPPER_CLASS( struct LanguageBindingSpec {
LanguageBindingSpec, std::optional<ScalarDefaultCharConstantExpr>); TUPLE_CLASS_BOILERPLATE(LanguageBindingSpec);
std::tuple<std::optional<ScalarDefaultCharConstantExpr>, bool> t;
};
// R852 named-constant-def -> named-constant = constant-expr // R852 named-constant-def -> named-constant = constant-expr
struct NamedConstantDef { struct NamedConstantDef {

View File

@ -115,10 +115,13 @@ public:
bool isExplicitBindName() const { return isExplicitBindName_; } bool isExplicitBindName() const { return isExplicitBindName_; }
void set_bindName(std::string &&name) { bindName_ = std::move(name); } void set_bindName(std::string &&name) { bindName_ = std::move(name); }
void set_isExplicitBindName(bool yes) { isExplicitBindName_ = yes; } void set_isExplicitBindName(bool yes) { isExplicitBindName_ = yes; }
bool isCDefined() const { return isCDefined_; }
void set_isCDefined(bool yes) { isCDefined_ = yes; }
private: private:
std::optional<std::string> bindName_; std::optional<std::string> bindName_;
bool isExplicitBindName_{false}; bool isExplicitBindName_{false};
bool isCDefined_{false};
}; };
// Device type specific OpenACC routine information // Device type specific OpenACC routine information
@ -814,6 +817,7 @@ public:
void SetBindName(std::string &&); void SetBindName(std::string &&);
bool GetIsExplicitBindName() const; bool GetIsExplicitBindName() const;
void SetIsExplicitBindName(bool); void SetIsExplicitBindName(bool);
void SetIsCDefined(bool);
bool IsFuncResult() const; bool IsFuncResult() const;
bool IsObjectArray() const; bool IsObjectArray() const;
const ArraySpec *GetShape() const; const ArraySpec *GetShape() const;

View File

@ -735,7 +735,8 @@ TYPE_PARSER(construct<AccessSpec>("PUBLIC" >> pure(AccessSpec::Kind::Public)) ||
// BIND ( C [, NAME = scalar-default-char-constant-expr] ) // BIND ( C [, NAME = scalar-default-char-constant-expr] )
// R1528 proc-language-binding-spec -> language-binding-spec // R1528 proc-language-binding-spec -> language-binding-spec
TYPE_PARSER(construct<LanguageBindingSpec>( TYPE_PARSER(construct<LanguageBindingSpec>(
"BIND ( C" >> maybe(", NAME =" >> scalarDefaultCharConstantExpr) / ")")) "BIND ( C" >> maybe(", NAME =" >> scalarDefaultCharConstantExpr),
(", CDEFINED" >> pure(true) || pure(false)) / ")"))
// R809 coarray-spec -> deferred-coshape-spec-list | explicit-coshape-spec // R809 coarray-spec -> deferred-coshape-spec-list | explicit-coshape-spec
// N.B. Bracketed here rather than around references, for consistency with // N.B. Bracketed here rather than around references, for consistency with

View File

@ -524,7 +524,13 @@ public:
Word("NULL()"); Word("NULL()");
} }
void Unparse(const LanguageBindingSpec &x) { // R808 & R1528 void Unparse(const LanguageBindingSpec &x) { // R808 & R1528
Word("BIND(C"), Walk(", NAME=", x.v), Put(')'); Word("BIND(C");
Walk(
", NAME=", std::get<std::optional<ScalarDefaultCharConstantExpr>>(x.t));
if (std::get<bool>(x.t)) {
Word(", CDEFINED");
}
Put(')');
} }
void Unparse(const CoarraySpec &x) { // R809 void Unparse(const CoarraySpec &x) { // R809
common::visit(common::visitors{ common::visit(common::visitors{

View File

@ -315,6 +315,7 @@ private:
bool IsConflictingAttr(Attr); bool IsConflictingAttr(Attr);
MaybeExpr bindName_; // from BIND(C, NAME="...") MaybeExpr bindName_; // from BIND(C, NAME="...")
bool isCDefined_{false}; // BIND(C, NAME="...", CDEFINED) extension
std::optional<SourceName> passName_; // from PASS(...) std::optional<SourceName> passName_; // from PASS(...)
}; };
@ -1762,6 +1763,7 @@ Attrs AttrsVisitor::EndAttrs() {
cudaDataAttr_.reset(); cudaDataAttr_.reset();
passName_ = std::nullopt; passName_ = std::nullopt;
bindName_.reset(); bindName_.reset();
isCDefined_ = false;
return result; return result;
} }
@ -1783,6 +1785,7 @@ void AttrsVisitor::SetBindNameOn(Symbol &symbol) {
!symbol.attrs().test(Attr::BIND_C)) { !symbol.attrs().test(Attr::BIND_C)) {
return; return;
} }
symbol.SetIsCDefined(isCDefined_);
std::optional<std::string> label{ std::optional<std::string> label{
evaluate::GetScalarConstantValue<evaluate::Ascii>(bindName_)}; evaluate::GetScalarConstantValue<evaluate::Ascii>(bindName_)};
// 18.9.2(2): discard leading and trailing blanks // 18.9.2(2): discard leading and trailing blanks
@ -1820,9 +1823,12 @@ void AttrsVisitor::SetBindNameOn(Symbol &symbol) {
void AttrsVisitor::Post(const parser::LanguageBindingSpec &x) { void AttrsVisitor::Post(const parser::LanguageBindingSpec &x) {
if (CheckAndSet(Attr::BIND_C)) { if (CheckAndSet(Attr::BIND_C)) {
if (x.v) { if (const auto &name{
bindName_ = EvaluateExpr(*x.v); std::get<std::optional<parser::ScalarDefaultCharConstantExpr>>(
x.t)}) {
bindName_ = EvaluateExpr(*name);
} }
isCDefined_ = std::get<bool>(x.t);
} }
} }
bool AttrsVisitor::Pre(const parser::IntentSpec &x) { bool AttrsVisitor::Pre(const parser::IntentSpec &x) {
@ -4056,7 +4062,9 @@ void SubprogramVisitor::CreateEntry(
Attrs attrs; Attrs attrs;
const auto &suffix{std::get<std::optional<parser::Suffix>>(stmt.t)}; const auto &suffix{std::get<std::optional<parser::Suffix>>(stmt.t)};
bool hasGlobalBindingName{outer.IsGlobal() && suffix && suffix->binding && bool hasGlobalBindingName{outer.IsGlobal() && suffix && suffix->binding &&
suffix->binding->v.has_value()}; std::get<std::optional<parser::ScalarDefaultCharConstantExpr>>(
suffix->binding->t)
.has_value()};
if (!hasGlobalBindingName) { if (!hasGlobalBindingName) {
if (Symbol * extant{FindSymbol(outer, entryName)}) { if (Symbol * extant{FindSymbol(outer, entryName)}) {
if (!HandlePreviousCalls(entryName, *extant, subpFlag)) { if (!HandlePreviousCalls(entryName, *extant, subpFlag)) {
@ -4440,7 +4448,10 @@ Symbol &SubprogramVisitor::PushSubprogramScope(const parser::Name &name,
bool hasModulePrefix) { bool hasModulePrefix) {
Symbol *symbol{GetSpecificFromGeneric(name)}; Symbol *symbol{GetSpecificFromGeneric(name)};
if (!symbol) { if (!symbol) {
if (bindingSpec && currScope().IsGlobal() && bindingSpec->v) { if (bindingSpec && currScope().IsGlobal() &&
std::get<std::optional<parser::ScalarDefaultCharConstantExpr>>(
bindingSpec->t)
.has_value()) {
// Create this new top-level subprogram with a binding label // Create this new top-level subprogram with a binding label
// in a new global scope, so that its symbol's name won't clash // in a new global scope, so that its symbol's name won't clash
// with another symbol that has a distinct binding label. // with another symbol that has a distinct binding label.
@ -5670,7 +5681,9 @@ bool DeclarationVisitor::Pre(const parser::ProcedureDeclarationStmt &x) {
const auto &procAttrSpec{std::get<std::list<parser::ProcAttrSpec>>(x.t)}; const auto &procAttrSpec{std::get<std::list<parser::ProcAttrSpec>>(x.t)};
for (const parser::ProcAttrSpec &procAttr : procAttrSpec) { for (const parser::ProcAttrSpec &procAttr : procAttrSpec) {
if (auto *bindC{std::get_if<parser::LanguageBindingSpec>(&procAttr.u)}) { if (auto *bindC{std::get_if<parser::LanguageBindingSpec>(&procAttr.u)}) {
if (bindC->v.has_value()) { if (std::get<std::optional<parser::ScalarDefaultCharConstantExpr>>(
bindC->t)
.has_value()) {
if (std::get<std::list<parser::ProcDecl>>(x.t).size() > 1) { if (std::get<std::list<parser::ProcDecl>>(x.t).size() > 1) {
Say(context().location().value(), Say(context().location().value(),
"A procedure declaration statement with a binding name may not declare multiple procedures"_err_en_US); "A procedure declaration statement with a binding name may not declare multiple procedures"_err_en_US);

View File

@ -375,6 +375,18 @@ void Symbol::SetIsExplicitBindName(bool yes) {
details_); details_);
} }
void Symbol::SetIsCDefined(bool yes) {
common::visit(
[&](auto &x) {
if constexpr (HasBindName<decltype(&x)>) {
x.set_isCDefined(yes);
} else {
DIE("CDEFINED not allowed on this kind of symbol");
}
},
details_);
}
bool Symbol::IsFuncResult() const { bool Symbol::IsFuncResult() const {
return common::visit( return common::visit(
common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); }, common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); },
@ -422,6 +434,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const EntityDetails &x) {
os << " type: " << *x.type(); os << " type: " << *x.type();
} }
DumpOptional(os, "bindName", x.bindName()); DumpOptional(os, "bindName", x.bindName());
DumpBool(os, "CDEFINED", x.isCDefined());
return os; return os;
} }

View File

@ -84,3 +84,8 @@ module m3
end end
end interface end interface
end end
!CHECK: cdef01, BIND(C), PUBLIC size=4 offset=0: ObjectEntity type: REAL(4) bindName:cDef01 CDEFINED
module m4
real, bind(c, name='cDef01', cdefined) :: cdef01
end