Skip to content

Commit 1062c14

Browse files
authored
[flang] Prevent IR name clashes between BIND(C) and external procedures (#66777)
Defining a procedure with a BIND(C, NAME="...") where the binding label matches the assembly name of a non BIND(C) external procedure in the same file causes a failure when generating the LLVM IR because of the assembly symbol name clash. Prevent this crash with a clearer semantic error.
1 parent 515a826 commit 1062c14

File tree

4 files changed

+86
-5
lines changed

4 files changed

+86
-5
lines changed

flang/include/flang/Common/Fortran.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,12 @@ bool AreCompatibleCUDADataAttrs(
114114

115115
static constexpr char blankCommonObjectName[] = "__BLNK__";
116116

117+
// Get the assembly name for a non BIND(C) external symbol other than the blank
118+
// common block.
119+
inline std::string GetExternalAssemblyName(
120+
std::string symbolName, bool underscoring) {
121+
return underscoring ? std::move(symbolName) + "_" : std::move(symbolName);
122+
}
123+
117124
} // namespace Fortran::common
118125
#endif // FORTRAN_COMMON_FORTRAN_H_

flang/lib/Optimizer/Transforms/ExternalNameConversion.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,8 @@ mangleExternalName(const std::pair<fir::NameUniquer::NameKind,
3838
if (result.first == fir::NameUniquer::NameKind::COMMON &&
3939
result.second.name.empty())
4040
return Fortran::common::blankCommonObjectName;
41-
42-
if (appendUnderscore)
43-
return result.second.name + "_";
44-
45-
return result.second.name;
41+
return Fortran::common::GetExternalAssemblyName(result.second.name,
42+
appendUnderscore);
4643
}
4744

4845
/// Update the early outlining parent name

flang/lib/Semantics/check-declarations.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ class CheckHelper {
130130
}
131131
bool IsResultOkToDiffer(const FunctionResult &);
132132
void CheckGlobalName(const Symbol &);
133+
void CheckProcedureAssemblyName(const Symbol &symbol);
133134
void CheckExplicitSave(const Symbol &);
134135
void CheckBindC(const Symbol &);
135136
void CheckBindCFunctionResult(const Symbol &);
@@ -178,6 +179,9 @@ class CheckHelper {
178179
std::map<std::string, SymbolRef> globalNames_;
179180
// Collection of external procedures without global definitions
180181
std::map<std::string, SymbolRef> externalNames_;
182+
// Collection of target dependent assembly names of external and BIND(C)
183+
// procedures.
184+
std::map<std::string, SymbolRef> procedureAssemblyNames_;
181185
};
182186

183187
class DistinguishabilityHelper {
@@ -277,6 +281,7 @@ void CheckHelper::Check(const Symbol &symbol) {
277281
CheckContiguous(symbol);
278282
}
279283
CheckGlobalName(symbol);
284+
CheckProcedureAssemblyName(symbol);
280285
if (symbol.attrs().test(Attr::ASYNCHRONOUS) &&
281286
!evaluate::IsVariable(symbol)) {
282287
messages_.Say(
@@ -2623,6 +2628,43 @@ void CheckHelper::CheckGlobalName(const Symbol &symbol) {
26232628
}
26242629
}
26252630

2631+
void CheckHelper::CheckProcedureAssemblyName(const Symbol &symbol) {
2632+
if (!IsProcedure(symbol) || symbol != symbol.GetUltimate())
2633+
return;
2634+
const std::string *bindName{symbol.GetBindName()};
2635+
const bool hasExplicitBindingLabel{
2636+
symbol.GetIsExplicitBindName() && bindName};
2637+
if (hasExplicitBindingLabel || IsExternal(symbol)) {
2638+
const std::string assemblyName{hasExplicitBindingLabel
2639+
? *bindName
2640+
: common::GetExternalAssemblyName(
2641+
symbol.name().ToString(), context_.underscoring())};
2642+
auto pair{procedureAssemblyNames_.emplace(std::move(assemblyName), symbol)};
2643+
if (!pair.second) {
2644+
const Symbol &other{*pair.first->second};
2645+
const bool otherHasExplicitBindingLabel{
2646+
other.GetIsExplicitBindName() && other.GetBindName()};
2647+
if (otherHasExplicitBindingLabel != hasExplicitBindingLabel) {
2648+
// The BIND(C,NAME="...") binding label is the same as the name that
2649+
// will be used in LLVM IR for an external procedure declared without
2650+
// BIND(C) in the same file. While this is not forbidden by the
2651+
// standard, this name collision would lead to a crash when producing
2652+
// the IR.
2653+
if (auto *msg{messages_.Say(symbol.name(),
2654+
"%s procedure assembly name conflicts with %s procedure assembly name"_err_en_US,
2655+
hasExplicitBindingLabel ? "BIND(C)" : "Non BIND(C)",
2656+
hasExplicitBindingLabel ? "non BIND(C)" : "BIND(C)")}) {
2657+
msg->Attach(other.name(), "Conflicting declaration"_en_US);
2658+
}
2659+
context_.SetError(symbol);
2660+
context_.SetError(other);
2661+
}
2662+
// Otherwise, the global names also match and the conflict is analyzed
2663+
// by CheckGlobalName.
2664+
}
2665+
}
2666+
}
2667+
26262668
void CheckHelper::CheckBindC(const Symbol &symbol) {
26272669
bool isExplicitBindC{symbol.attrs().test(Attr::BIND_C)};
26282670
if (isExplicitBindC) {

flang/test/Semantics/bind-c14.f90

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1 -funderscoring
2+
3+
subroutine conflict1()
4+
end subroutine
5+
6+
!ERROR: BIND(C) procedure assembly name conflicts with non BIND(C) procedure assembly name
7+
subroutine foo(x) bind(c, name="conflict1_")
8+
real :: x
9+
end subroutine
10+
11+
subroutine no_conflict1() bind(c, name="")
12+
end subroutine
13+
subroutine foo2() bind(c, name="conflict2_")
14+
end subroutine
15+
16+
subroutine bar()
17+
interface
18+
subroutine no_conflict1() bind(c, name="")
19+
end subroutine
20+
! ERROR: Non BIND(C) procedure assembly name conflicts with BIND(C) procedure assembly name
21+
subroutine conflict2()
22+
end subroutine
23+
end interface
24+
call no_conflict1()
25+
call conflict2
26+
end subroutine
27+
28+
subroutine no_conflict2() bind(c, name="no_conflict2_")
29+
end subroutine
30+
31+
subroutine _()
32+
end subroutine
33+
34+
subroutine dash_no_conflict() bind(c, name="")
35+
end subroutine

0 commit comments

Comments
 (0)