diff --git a/flang/include/flang/Evaluate/type.h b/flang/include/flang/Evaluate/type.h --- a/flang/include/flang/Evaluate/type.h +++ b/flang/include/flang/Evaluate/type.h @@ -52,6 +52,7 @@ using CInteger = Type; using LogicalResult = Type; using LargestReal = Type; +using DefaultCharacter = Type; // A predicate that is true when a kind value is a kind that could possibly // be supported for an intrinsic type category on some target instruction diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -60,7 +60,18 @@ private: }; -class SubprogramDetails { +class WithBindName { +public: + const std::string *bindName() const { + return bindName_ ? &*bindName_ : nullptr; + } + void set_bindName(std::string &&name) { bindName_ = std::move(name); } + +private: + std::optional bindName_; +}; + +class SubprogramDetails : public WithBindName { public: bool isFunction() const { return result_ != nullptr; } bool isInterface() const { return isInterface_; } @@ -68,8 +79,6 @@ Scope *entryScope() { return entryScope_; } const Scope *entryScope() const { return entryScope_; } void set_entryScope(Scope &scope) { entryScope_ = &scope; } - MaybeExpr bindName() const { return bindName_; } - void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); } const Symbol &result() const { CHECK(isFunction()); return *result_; @@ -86,7 +95,6 @@ private: bool isInterface_{false}; // true if this represents an interface-body - MaybeExpr bindName_; std::vector dummyArgs_; // nullptr -> alternate return indicator Symbol *result_{nullptr}; Scope *entryScope_{nullptr}; // if ENTRY, points to subprogram's scope @@ -117,7 +125,7 @@ }; // A name from an entity-decl -- could be object or function. -class EntityDetails { +class EntityDetails : public WithBindName { public: explicit EntityDetails(bool isDummy = false) : isDummy_{isDummy} {} const DeclTypeSpec *type() const { return type_; } @@ -127,14 +135,11 @@ void set_isDummy(bool value = true) { isDummy_ = value; } bool isFuncResult() const { return isFuncResult_; } void set_funcResult(bool x) { isFuncResult_ = x; } - MaybeExpr bindName() const { return bindName_; } - void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); } private: bool isDummy_{false}; bool isFuncResult_{false}; const DeclTypeSpec *type_{nullptr}; - MaybeExpr bindName_; friend llvm::raw_ostream &operator<<( llvm::raw_ostream &, const EntityDetails &); }; @@ -310,19 +315,16 @@ SymbolVector objects_; }; -class CommonBlockDetails { +class CommonBlockDetails : public WithBindName { public: MutableSymbolVector &objects() { return objects_; } const MutableSymbolVector &objects() const { return objects_; } void add_object(Symbol &object) { objects_.emplace_back(object); } - MaybeExpr bindName() const { return bindName_; } - void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); } std::size_t alignment() const { return alignment_; } void set_alignment(std::size_t alignment) { alignment_ = alignment; } private: MutableSymbolVector objects_; - MaybeExpr bindName_; std::size_t alignment_{0}; // required alignment in bytes }; @@ -565,8 +567,10 @@ inline DeclTypeSpec *GetType(); inline const DeclTypeSpec *GetType() const; - void SetType(const DeclTypeSpec &); + + const std::string *GetBindName() const; + void SetBindName(std::string &&); bool IsFuncResult() const; bool IsObjectArray() const; bool IsSubprogram() const; diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt --- a/flang/lib/Semantics/CMakeLists.txt +++ b/flang/lib/Semantics/CMakeLists.txt @@ -1,4 +1,3 @@ - add_flang_library(FortranSemantics assignment.cpp attr.cpp diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -1687,24 +1687,23 @@ : "Module subprogram '%s' does not have NON_RECURSIVE prefix but " "the corresponding interface body does"_err_en_US); } - MaybeExpr bindName1{details1.bindName()}; - MaybeExpr bindName2{details2.bindName()}; - if (bindName1.has_value() != bindName2.has_value()) { + const std::string *bindName1{details1.bindName()}; + const std::string *bindName2{details2.bindName()}; + if (!bindName1 && !bindName2) { + // OK - neither has a binding label + } else if (!bindName1) { Say(symbol1, symbol2, - bindName1.has_value() - ? "Module subprogram '%s' has a binding label but the corresponding" - " interface body does not"_err_en_US - : "Module subprogram '%s' does not have a binding label but the" - " corresponding interface body does"_err_en_US); - } else if (bindName1) { - std::string string1{bindName1->AsFortran()}; - std::string string2{bindName2->AsFortran()}; - if (string1 != string2) { - Say(symbol1, symbol2, - "Module subprogram '%s' has binding label %s but the corresponding" - " interface body has %s"_err_en_US, - string1, string2); - } + "Module subprogram '%s' does not have a binding label but the" + " corresponding interface body does"_err_en_US); + } else if (!bindName2) { + Say(symbol1, symbol2, + "Module subprogram '%s' has a binding label but the" + " corresponding interface body does not"_err_en_US); + } else if (*bindName1 != *bindName2) { + Say(symbol1, symbol2, + "Module subprogram '%s' has binding label '%s' but the corresponding" + " interface body has '%s'"_err_en_US, + *details1.bindName(), *details2.bindName()); } const Procedure *proc1{checkHelper.Characterize(symbol1)}; const Procedure *proc2{checkHelper.Characterize(symbol2)}; diff --git a/flang/lib/Semantics/check-io.h b/flang/lib/Semantics/check-io.h --- a/flang/lib/Semantics/check-io.h +++ b/flang/lib/Semantics/check-io.h @@ -86,13 +86,11 @@ StatusReplace, StatusScratch, DataList) template std::optional GetConstExpr(const T &x) { - using DefaultCharConstantType = - evaluate::Type; if (const SomeExpr * expr{GetExpr(x)}) { const auto foldExpr{ evaluate::Fold(context_.foldingContext(), common::Clone(*expr))}; if constexpr (std::is_same_v) { - return evaluate::GetScalarConstantValue( + return evaluate::GetScalarConstantValue( foldExpr); } else { static_assert(std::is_same_v, "unexpected type"); diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp --- a/flang/lib/Semantics/mod-file.cpp +++ b/flang/lib/Semantics/mod-file.cpp @@ -54,8 +54,8 @@ static void PutInit(llvm::raw_ostream &, const Symbol &, const MaybeExpr &); static void PutInit(llvm::raw_ostream &, const MaybeIntExpr &); static void PutBound(llvm::raw_ostream &, const Bound &); -static llvm::raw_ostream &PutAttrs(llvm::raw_ostream &, Attrs, - const MaybeExpr & = std::nullopt, std::string before = ","s, +llvm::raw_ostream &PutAttrs(llvm::raw_ostream &, Attrs, + const std::string * = nullptr, std::string before = ","s, std::string after = ""s); static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr); @@ -346,7 +346,7 @@ if (isInterface) { os << (isAbstract ? "abstract " : "") << "interface\n"; } - PutAttrs(os, prefixAttrs, std::nullopt, ""s, " "s); + PutAttrs(os, prefixAttrs, nullptr, ""s, " "s); os << (details.isFunction() ? "function " : "subroutine "); os << symbol.name() << '('; int n = 0; @@ -636,26 +636,18 @@ void PutEntity(llvm::raw_ostream &os, const Symbol &symbol, std::function writeType, Attrs attrs) { writeType(); - MaybeExpr bindName; - std::visit(common::visitors{ - [&](const SubprogramDetails &x) { bindName = x.bindName(); }, - [&](const ObjectEntityDetails &x) { bindName = x.bindName(); }, - [&](const ProcEntityDetails &x) { bindName = x.bindName(); }, - [&](const auto &) {}, - }, - symbol.details()); - PutAttrs(os, attrs, bindName); + PutAttrs(os, attrs, symbol.GetBindName()); os << "::" << symbol.name(); } // Put out each attribute to os, surrounded by `before` and `after` and // mapped to lower case. llvm::raw_ostream &PutAttrs(llvm::raw_ostream &os, Attrs attrs, - const MaybeExpr &bindName, std::string before, std::string after) { + const std::string *bindName, std::string before, std::string after) { attrs.set(Attr::PUBLIC, false); // no need to write PUBLIC attrs.set(Attr::EXTERNAL, false); // no need to write EXTERNAL if (bindName) { - bindName->AsFortran(os << before << "bind(c, name=") << ')' << after; + os << before << "bind(c, name=\"" << *bindName << "\")" << after; attrs.set(Attr::BIND_C, false); } for (std::size_t i{0}; i < Attr_enumSize; ++i) { diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -1528,19 +1528,26 @@ } bool AttrsVisitor::SetBindNameOn(Symbol &symbol) { - if (!bindName_) { + if (!attrs_ || !attrs_->test(Attr::BIND_C)) { return false; } - std::visit( - common::visitors{ - [&](EntityDetails &x) { x.set_bindName(std::move(bindName_)); }, - [&](ObjectEntityDetails &x) { x.set_bindName(std::move(bindName_)); }, - [&](ProcEntityDetails &x) { x.set_bindName(std::move(bindName_)); }, - [&](SubprogramDetails &x) { x.set_bindName(std::move(bindName_)); }, - [&](CommonBlockDetails &x) { x.set_bindName(std::move(bindName_)); }, - [](auto &) { common::die("unexpected bind name"); }, - }, - symbol.details()); + std::optional label{ + evaluate::GetScalarConstantValue(bindName_)}; + // 18.9.2(2): discard leading and trailing blanks, ignore if all blank + if (label) { + auto first{label->find_first_not_of(" ")}; + auto last{label->find_last_not_of(" ")}; + if (first == std::string::npos) { + Say(currStmtSource().value(), "Blank binding label ignored"_en_US); + label.reset(); + } else { + *label = label->substr(first, last - first + 1); + } + } + if (!label) { + *label = parser::ToLowerCaseLetters(symbol.name().ToString()); + } + symbol.SetBindName(std::move(*label)); return true; } diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -14,6 +14,7 @@ #include "flang/Semantics/tools.h" #include "llvm/Support/raw_ostream.h" #include +#include namespace Fortran::semantics { @@ -84,7 +85,7 @@ llvm::raw_ostream &operator<<( llvm::raw_ostream &os, const SubprogramDetails &x) { DumpBool(os, "isInterface", x.isInterface_); - DumpExpr(os, "bindName", x.bindName_); + DumpOptional(os, "bindName", x.bindName()); if (x.result_) { DumpType(os << " result:", x.result()); os << x.result_->name(); @@ -290,6 +291,33 @@ details_); } +template +constexpr bool HasBindName{std::is_convertible_v}; + +const std::string *Symbol::GetBindName() const { + return std::visit( + [&](auto &x) -> const std::string * { + if constexpr (HasBindName) { + return x.bindName(); + } else { + return nullptr; + } + }, + details_); +} + +void Symbol::SetBindName(std::string &&name) { + std::visit( + [&](auto &x) { + if constexpr (HasBindName) { + x.set_bindName(std::move(name)); + } else { + DIE("bind name not allowed on this kind of symbol"); + } + }, + details_); +} + bool Symbol::IsFuncResult() const { return std::visit( common::visitors{[](const EntityDetails &x) { return x.isFuncResult(); }, @@ -331,7 +359,7 @@ if (x.type()) { os << " type: " << *x.type(); } - DumpExpr(os, "bindName", x.bindName_); + DumpOptional(os, "bindName", x.bindName()); return os; } @@ -361,7 +389,7 @@ } else { DumpType(os, x.interface_.type()); } - DumpExpr(os, "bindName", x.bindName()); + DumpOptional(os, "bindName", x.bindName()); DumpOptional(os, "passName", x.passName()); if (x.init()) { if (const Symbol * target{*x.init()}) { @@ -448,6 +476,7 @@ DumpSymbolVector(os, x.objects()); }, [&](const CommonBlockDetails &x) { + DumpOptional(os, "bindName", x.bindName()); if (x.alignment()) { os << " alignment=" << x.alignment(); } diff --git a/flang/test/Semantics/modfile04.f90 b/flang/test/Semantics/modfile04.f90 --- a/flang/test/Semantics/modfile04.f90 +++ b/flang/test/Semantics/modfile04.f90 @@ -6,7 +6,7 @@ end type contains - pure subroutine s(x, y) bind(c) + pure subroutine Ss(x, y) bind(c) logical x intent(inout) y intent(in) x @@ -53,7 +53,7 @@ !type::t !end type !contains -!pure subroutine s(x,y) bind(c) +!pure subroutine ss(x,y) bind(c, name="ss") !logical(4),intent(in)::x !real(4),intent(inout)::y !end diff --git a/flang/test/Semantics/modfile21.f90 b/flang/test/Semantics/modfile21.f90 --- a/flang/test/Semantics/modfile21.f90 +++ b/flang/test/Semantics/modfile21.f90 @@ -29,7 +29,7 @@ ! common/cb/x,y,z ! bind(c, name="CB")::/cb/ ! common/cb2/a,b,c -! bind(c)::/cb2/ +! bind(c, name="cb2")::/cb2/ ! common/b/cb ! common//t,w,u,v !end diff --git a/flang/test/Semantics/separate-mp02.f90 b/flang/test/Semantics/separate-mp02.f90 --- a/flang/test/Semantics/separate-mp02.f90 +++ b/flang/test/Semantics/separate-mp02.f90 @@ -136,6 +136,12 @@ end module subroutine s3() bind(c, name="s3") end + module subroutine s4() bind(c, name=" s4") + end + module subroutine s5() bind(c) + end + module subroutine s6() bind(c) + end end interface end @@ -148,9 +154,16 @@ !ERROR: Module subprogram 's2' does not have a binding label but the corresponding interface body does module subroutine s2() end - !ERROR: Module subprogram 's3' has binding label "s3_xxx" but the corresponding interface body has "s3" + !ERROR: Module subprogram 's3' has binding label 's3_xxx' but the corresponding interface body has 's3' module subroutine s3() bind(c, name="s3" // suffix) end + module subroutine s4() bind(c, name="s4 ") + end + module subroutine s5() bind(c, name=" s5") + end + !ERROR: Module subprogram 's6' has binding label 'not_s6' but the corresponding interface body has 's6' + module subroutine s6() bind(c, name="not_s6") + end end