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

via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 6 06:09:00 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Kerry McLaughlin (kmclaughlin-arm)

<details>
<summary>Changes</summary>

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 the non_streaming_fn call.
With this change, an error will be emitted when a duplicate mangled name is found
but the function prototypes do not match.

---
Full diff: https://github.com/llvm/llvm-project/pull/107581.diff


2 Files Affected:

- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+26-9) 
- (added) clang/test/CodeGenCXX/aarch64-sme-lambda-attributes.cpp (+28) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/107581


More information about the cfe-commits mailing list