[clang] [Clang] Emit error for duplicate mangled names within a lambda (PR #107581)

Kerry McLaughlin via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 13 06:57:51 PDT 2024


https://github.com/kmclaughlin-arm updated https://github.com/llvm/llvm-project/pull/107581

>From 1e6f25c517d8d1adeeaf59f826141efdcad8f05a Mon Sep 17 00:00:00 2001
From: Kerry McLaughlin <kerry.mclaughlin at arm.com>
Date: Fri, 6 Sep 2024 10:13:33 +0000
Subject: [PATCH 1/2] [Clang] Emit error for duplicate mangled names within a
 lambda

When functions are passed as arguments to a lambda, it's possible for
the mangled names of these functions to be the same despite the prototypes
being different. For example:

  int non_streaming_fn(int);
  int streaming_fn(int) __arm_streaming;

  auto lambda_fn = [](const auto &f) { return f(); };
  return lambda_fn(non_streaming_fn) + lambda_fn(streaming_fn);

Only one function will be generated for the lambda above and the __arm_streaming
attribute from streaming_fn will be incorrectly applied to non_streaming_fn.
With this change, an error will be emitted when a duplicate mangled name is
found but the function prototypes do not match.
---
 clang/lib/CodeGen/CodeGenModule.cpp           | 35 ++++++++++++++-----
 .../aarch64-sme-lambda-attributes.cpp         | 28 +++++++++++++++
 2 files changed, 54 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index df4c13c9ad97aa..b07090e3de6dfd 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4638,8 +4638,8 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
   // Lookup the entry, lazily creating it if necessary.
   llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
   if (Entry) {
+    const FunctionDecl *FD = cast_or_null<FunctionDecl>(D);
     if (WeakRefReferences.erase(Entry)) {
-      const FunctionDecl *FD = cast_or_null<FunctionDecl>(D);
       if (FD && !FD->hasAttr<WeakAttr>())
         Entry->setLinkage(llvm::Function::ExternalLinkage);
     }
@@ -4652,18 +4652,35 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
 
     // If there are two attempts to define the same mangled name, issue an
     // error.
-    if (IsForDefinition && !Entry->isDeclaration()) {
-      GlobalDecl OtherGD;
-      // Check that GD is not yet in DiagnosedConflictingDefinitions is required
-      // to make sure that we issue an error only once.
-      if (lookupRepresentativeDecl(MangledName, OtherGD) &&
-          (GD.getCanonicalDecl().getDecl() !=
-           OtherGD.getCanonicalDecl().getDecl()) &&
-          DiagnosedConflictingDefinitions.insert(GD).second) {
+    GlobalDecl OtherGD;
+    // Check that GD is not yet in DiagnosedConflictingDefinitions is required
+    // to make sure that we issue an error only once.
+    if (GD && lookupRepresentativeDecl(MangledName, OtherGD) &&
+        (GD.getCanonicalDecl().getDecl() !=
+         OtherGD.getCanonicalDecl().getDecl()) &&
+        DiagnosedConflictingDefinitions.insert(GD).second) {
+      if (IsForDefinition && !Entry->isDeclaration()) {
         getDiags().Report(D->getLocation(), diag::err_duplicate_mangled_name)
             << MangledName;
         getDiags().Report(OtherGD.getDecl()->getLocation(),
                           diag::note_previous_definition);
+      } else {
+        // For lambdas, it's possible to create the same mangled name from
+        // different function prototypes. For example, two FPTs may have
+        // identical types but incompatible function attributes which we should
+        // not allow.
+        auto *MD = dyn_cast<CXXMethodDecl>(D);
+        if (MD && MD->getParent()->isLambda()) {
+          const FunctionDecl *OtherFD =
+              cast_or_null<FunctionDecl>(OtherGD.getDecl());
+          if (FD && FD->hasPrototype() && OtherFD && OtherFD->hasPrototype()) {
+            if (FD->getType()->getAs<FunctionProtoType>() !=
+                OtherFD->getType()->getAs<FunctionProtoType>())
+              getDiags().Report(D->getLocation(),
+                                diag::err_duplicate_mangled_name)
+                  << MangledName;
+          }
+        }
       }
     }
 
diff --git a/clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp b/clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp
new file mode 100644
index 00000000000000..bce3e657001679
--- /dev/null
+++ b/clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp
@@ -0,0 +1,28 @@
+// REQUIRES: aarch64-registered-target
+
+// RUN: %clang_cc1 -triple aarch64 -target-feature +sme -target-feature +sme2 -emit-llvm -o - %s -verify -DTEST1
+// RUN: %clang_cc1 -triple aarch64 -target-feature +sme -target-feature +sme2 -emit-llvm -o - %s -verify -DTEST2
+
+int normal_fn(int);
+int streaming_fn(int) __arm_streaming;
+int streaming_compatible_fn(int) __arm_streaming_compatible;
+
+#ifdef TEST1
+
+// expected-error at +2 {{definition with same mangled name '_ZZ32function_params_normal_streamingvENK3$_0clIFiiEEEDaRT_' as another definition}}
+int function_params_normal_streaming() {
+  auto a = [](auto &fn) { return fn(42); };
+  return a(normal_fn) + a(streaming_fn);
+}
+
+#endif
+
+#ifdef TEST2
+
+// expected-error at +2 {{definition with same mangled name '_ZZ36function_params_streaming_compatiblevENK3$_0clIFiiEEEDaRT_' as another definition}}
+int function_params_streaming_compatible() {
+  auto a = [](auto &fn) { return fn(42); };
+  return a(streaming_fn) + a(streaming_compatible_fn);
+}
+
+#endif

>From fc1394eec345702eea7f3c664e5fc33ce3c525d0 Mon Sep 17 00:00:00 2001
From: Kerry McLaughlin <kerry.mclaughlin at arm.com>
Date: Fri, 13 Sep 2024 08:58:02 +0000
Subject: [PATCH 2/2] - Rewrite changes to GetOrCreateLLVMFunction

---
 clang/lib/CodeGen/CodeGenModule.cpp           | 37 ++++++-------------
 .../aarch64-sme-lambda-attributes.cpp         |  6 ++-
 2 files changed, 15 insertions(+), 28 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index b07090e3de6dfd..e0cfbdf57534c7 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4638,8 +4638,8 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
   // Lookup the entry, lazily creating it if necessary.
   llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
   if (Entry) {
-    const FunctionDecl *FD = cast_or_null<FunctionDecl>(D);
     if (WeakRefReferences.erase(Entry)) {
+      const FunctionDecl *FD = cast_or_null<FunctionDecl>(D);
       if (FD && !FD->hasAttr<WeakAttr>())
         Entry->setLinkage(llvm::Function::ExternalLinkage);
     }
@@ -4652,35 +4652,20 @@ llvm::Constant *CodeGenModule::GetOrCreateLLVMFunction(
 
     // If there are two attempts to define the same mangled name, issue an
     // error.
-    GlobalDecl OtherGD;
-    // Check that GD is not yet in DiagnosedConflictingDefinitions is required
-    // to make sure that we issue an error only once.
-    if (GD && lookupRepresentativeDecl(MangledName, OtherGD) &&
-        (GD.getCanonicalDecl().getDecl() !=
-         OtherGD.getCanonicalDecl().getDecl()) &&
-        DiagnosedConflictingDefinitions.insert(GD).second) {
-      if (IsForDefinition && !Entry->isDeclaration()) {
+    auto *MD = dyn_cast_or_null<CXXMethodDecl>(D);
+    bool IsLambda = MD && MD->getParent()->isLambda();
+    if (IsForDefinition && (!Entry->isDeclaration() || IsLambda)) {
+      GlobalDecl OtherGD;
+      // Check that GD is not yet in DiagnosedConflictingDefinitions is required
+      // to make sure that we issue an error only once.
+      if (lookupRepresentativeDecl(MangledName, OtherGD) &&
+          (GD.getCanonicalDecl().getDecl() !=
+           OtherGD.getCanonicalDecl().getDecl()) &&
+          DiagnosedConflictingDefinitions.insert(GD).second) {
         getDiags().Report(D->getLocation(), diag::err_duplicate_mangled_name)
             << MangledName;
         getDiags().Report(OtherGD.getDecl()->getLocation(),
                           diag::note_previous_definition);
-      } else {
-        // For lambdas, it's possible to create the same mangled name from
-        // different function prototypes. For example, two FPTs may have
-        // identical types but incompatible function attributes which we should
-        // not allow.
-        auto *MD = dyn_cast<CXXMethodDecl>(D);
-        if (MD && MD->getParent()->isLambda()) {
-          const FunctionDecl *OtherFD =
-              cast_or_null<FunctionDecl>(OtherGD.getDecl());
-          if (FD && FD->hasPrototype() && OtherFD && OtherFD->hasPrototype()) {
-            if (FD->getType()->getAs<FunctionProtoType>() !=
-                OtherFD->getType()->getAs<FunctionProtoType>())
-              getDiags().Report(D->getLocation(),
-                                diag::err_duplicate_mangled_name)
-                  << MangledName;
-          }
-        }
       }
     }
 
diff --git a/clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp b/clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp
index bce3e657001679..d7667bf3343e75 100644
--- a/clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp
+++ b/clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp
@@ -9,7 +9,8 @@ int streaming_compatible_fn(int) __arm_streaming_compatible;
 
 #ifdef TEST1
 
-// expected-error at +2 {{definition with same mangled name '_ZZ32function_params_normal_streamingvENK3$_0clIFiiEEEDaRT_' as another definition}}
+// expected-error at +3 {{definition with same mangled name '_ZZ32function_params_normal_streamingvENK3$_0clIFiiEEEDaRT_' as another definition}}
+// expected-note at +2 {{previous definition is here}}
 int function_params_normal_streaming() {
   auto a = [](auto &fn) { return fn(42); };
   return a(normal_fn) + a(streaming_fn);
@@ -19,7 +20,8 @@ int function_params_normal_streaming() {
 
 #ifdef TEST2
 
-// expected-error at +2 {{definition with same mangled name '_ZZ36function_params_streaming_compatiblevENK3$_0clIFiiEEEDaRT_' as another definition}}
+// expected-error at +3 {{definition with same mangled name '_ZZ36function_params_streaming_compatiblevENK3$_0clIFiiEEEDaRT_' as another definition}}
+// expected-note at +2 {{previous definition is here}}
 int function_params_streaming_compatible() {
   auto a = [](auto &fn) { return fn(42); };
   return a(streaming_fn) + a(streaming_compatible_fn);



More information about the cfe-commits mailing list