Author: Akash Manna
Date: 2026-10-03T18:48:21Z
New Revision: 9b2ad1942cb13925325c5a8e458aac88767e2c44

URL: 
https://github.com/llvm/llvm-project/commit/9b2ad1942cb13925325c5a8e458aac88767e2c44
DIFF: 
https://github.com/llvm/llvm-project/commit/9b2ad1942cb13925325c5a8e458aac88767e2c44.diff

LOG: [Clang][OpenMP] Fix crash on `copyprivate` of a data member or structured 
binding (#227699)

Fixes #217893

`copyprivate` needs its list items to be threadprivate or private in the
enclosing context. For a data member we only checked that it wasn't
`shared`, and then assumed an enclosing construct had already made a
private copy of it. That doesn't hold in a `task` or `taskloop` region,
where a member that is private outside is implicitly firstprivate but
gets no copy, or under `default(none)`, where it has no attribute at
all. With no copy to refer to we hit `assert(VD ||
isOpenMPCapturedDecl(D))`, and without assertions a null address could
end up being passed to `__kmpc_copyprivate`. A structured binding in the
clause hit the same assertion.

A data member with no private copy is now diagnosed with the existing
"copyprivate variable must be threadprivate or private in the enclosing
context" error. In a dependent context the copies aren't built until
instantiation, so the clause keeps the list item as written there and is
checked when instantiated, like the other data-sharing clauses; that
also fixes the crash for a data member in `copyprivate` inside a member
function template. A structured binding stays as written too, which is
how the other clauses already treat it.

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/Sema/SemaOpenMP.cpp
    clang/test/OpenMP/single_copyprivate_messages.cpp
    clang/test/OpenMP/structured-bindings-messages.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index d6c40a2920cf5..bf194e87f76a6 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -578,6 +578,9 @@ features cannot lower the translation-unit ABI level;
 - Fixed a bug where a stray closing curley brace in an OpenMP/OpenACC pragma 
could cause pragma parsing issues when inside of a member function. (#GH214195)
 - Fixed a bug where preprocessor directives following comments were not 
correctly recognized when using -C. (#GH48361)
 - Fixed a crash when declaring a member template within a local class inside 
an OpenMP region. (#GH216052)
+- Fixed a crash when an OpenMP `copyprivate` clause names a non-static data 
member that is not private in the
+  enclosing context, which is now diagnosed, a data member inside a member 
function template, or a structured
+  binding. (#GH217893)
 - Fixed an assertion failure when a variable implicitly mapped by an OpenMP 
`target` directive has a class type
   (such as `std::map`) whose mapper lookup instantiates a class template 
specialization. (#GH154704)
 - Fixed a bug where repeated #imports of modular headers in non-modular 
compilation were translated to #pragma clang module import. (#GH216924)

diff  --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 315f869e0b72e..efc570a8933f1 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -23296,11 +23296,16 @@ OMPClause 
*SemaOpenMP::ActOnOpenMPCopyprivateClause(ArrayRef<Expr *> VarList,
       //  threadprivate or private in the enclosing context.
       if (DVar.CKind == OMPC_unknown) {
         DVar = DSAStack->getImplicitDSA(D, false);
-        if (DVar.CKind == OMPC_shared) {
+        // A data member is private only if an enclosing construct captured it.
+        const bool IsShared = DVar.CKind == OMPC_shared;
+        if (IsShared ||
+            (isa<FieldDecl>(D) && !SemaRef.CurContext->isDependentContext() &&
+             !isOpenMPCapturedDecl(D))) {
           Diag(ELoc, diag::err_omp_required_access)
               << getOpenMPClauseNameForDiag(OMPC_copyprivate)
               << "threadprivate or private in the enclosing context";
-          reportOriginalDsa(SemaRef, DSAStack, D, DVar);
+          if (IsShared)
+            reportOriginalDsa(SemaRef, DSAStack, D, DVar);
           continue;
         }
       }
@@ -23347,10 +23352,13 @@ OMPClause 
*SemaOpenMP::ActOnOpenMPCopyprivateClause(ArrayRef<Expr *> VarList,
 
     // No need to mark vars as copyprivate, they are already threadprivate or
     // implicitly private.
-    assert(VD || isOpenMPCapturedDecl(D));
+    const bool IsBindingDecl = isa<BindingDecl>(D);
+    assert(VD || IsBindingDecl || SemaRef.CurContext->isDependentContext() ||
+           isOpenMPCapturedDecl(D));
     Vars.push_back(
-        VD ? RefExpr->IgnoreParens()
-           : buildCapture(SemaRef, D, SimpleRefExpr, /*WithInit=*/false));
+        (VD || IsBindingDecl || SemaRef.CurContext->isDependentContext())
+            ? RefExpr->IgnoreParens()
+            : buildCapture(SemaRef, D, SimpleRefExpr, /*WithInit=*/false));
     SrcExprs.push_back(PseudoSrcExpr);
     DstExprs.push_back(PseudoDstExpr);
     AssignmentOps.push_back(AssignmentOp.get());

diff  --git a/clang/test/OpenMP/single_copyprivate_messages.cpp 
b/clang/test/OpenMP/single_copyprivate_messages.cpp
index 1dc508b01bd74..734c48f863b6d 100644
--- a/clang/test/OpenMP/single_copyprivate_messages.cpp
+++ b/clang/test/OpenMP/single_copyprivate_messages.cpp
@@ -206,3 +206,44 @@ int parallel() {
 
   return 0;
 }
+
+namespace GH217893 {
+struct S {
+  int a;
+  S() {
+#pragma omp parallel firstprivate(a)
+#pragma omp taskloop
+#pragma omp single copyprivate(a) // expected-error {{copyprivate variable 
must be threadprivate or private in the enclosing context}} expected-error 
{{region cannot be closely nested inside 'taskloop' region; perhaps you forget 
to enclose 'omp single' directive into a parallel region?}}
+  }; // expected-error {{expected statement}}
+
+  void nested() {
+#pragma omp parallel firstprivate(a)
+#pragma omp taskloop
+    for (int i = 0; i < 2; ++i) {
+#pragma omp single copyprivate(a) // expected-error {{copyprivate variable 
must be threadprivate or private in the enclosing context}} expected-error 
{{region cannot be closely nested inside 'taskloop' region; perhaps you forget 
to enclose 'omp single' directive into a parallel region?}}
+      foo();
+    }
+#pragma omp parallel default(none)
+#pragma omp single copyprivate(a) // expected-error {{copyprivate variable 
must be threadprivate or private in the enclosing context}}
+    foo();
+#pragma omp parallel private(a)
+#pragma omp single copyprivate(a)
+    foo();
+  }
+
+  template <class T> void dependent() {
+#pragma omp parallel private(a)
+#pragma omp single copyprivate(a)
+    foo();
+#pragma omp parallel private(a)
+#pragma omp task
+#pragma omp taskgroup
+#pragma omp single copyprivate(a) // expected-error {{copyprivate variable 
must be threadprivate or private in the enclosing context}}
+    foo();
+  }
+};
+
+void instantiate(S &s) {
+  s.dependent<int>(); // expected-note {{in instantiation of function template 
specialization 'GH217893::S::dependent<int>' requested here}}
+}
+}

diff  --git a/clang/test/OpenMP/structured-bindings-messages.cpp 
b/clang/test/OpenMP/structured-bindings-messages.cpp
index 647de0055068c..88316d317ef98 100644
--- a/clang/test/OpenMP/structured-bindings-messages.cpp
+++ b/clang/test/OpenMP/structured-bindings-messages.cpp
@@ -563,3 +563,45 @@ void test_bindings_only_orig_not_dereferenced() {
     c = c + 30;
   }
 }
+
+void test_copyprivate_orphaned() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp single copyprivate(a, b)
+  ;
+}
+
+void test_copyprivate_private() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel private(a, b)
+#pragma omp single copyprivate(a, b)
+  ;
+}
+
+void test_copyprivate_firstprivate() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel firstprivate(a)
+#pragma omp single copyprivate(a)
+  ;
+}
+
+void test_copyprivate_shared() {
+  Point p{1, 2};
+  auto [a, b] = p;
+#pragma omp parallel // expected-note {{implicitly determined as shared}}
+#pragma omp single copyprivate(a) // expected-error {{copyprivate variable 
must be threadprivate or private in the enclosing context}}
+  ;
+}
+
+template <typename T> void test_copyprivate_template(T p) {
+  auto [a, b] = p;
+#pragma omp parallel private(a)
+#pragma omp single copyprivate(a)
+  ;
+}
+
+void instantiate_copyprivate_template() {
+  test_copyprivate_template(Point{1, 2});
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to