[clang] [OpenMP] Fix OpenMP reduction segfault with non-copyable types and user initializers. (PR #219265)

Alexey Bataev via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 11 04:27:00 PDT 2026


================
@@ -24377,6 +24378,60 @@ VarDecl *SemaOpenMP::ActOnOpenMPDeclareReductionInitializerStart(Scope *S,
 void SemaOpenMP::ActOnOpenMPDeclareReductionInitializerEnd(
     Decl *D, Expr *Initializer, VarDecl *OmpPrivParm) {
   auto *DRD = cast<OMPDeclareReductionDecl>(D);
+
+  // For non-trivial types with user initializers, build an AST that
+  // includes default construction first to initialize members before user
+  // initializer. This must be done BEFORE popping contexts.
+  if (Initializer && !DRD->getDeclContext()->isDependentContext()) {
+    QualType ReductionType = DRD->getType();
+    if (const auto *RD = ReductionType->getAsCXXRecordDecl()) {
+      CXXConstructorDecl *DefaultCtor =
+          SemaRef.LookupDefaultConstructor(const_cast<CXXRecordDecl *>(RD));
+      if (DefaultCtor && !DefaultCtor->isDeleted() &&
+          !DefaultCtor->isTrivial()) {
+        // Build default arguments for constructor parameters.
+        SmallVector<Expr *, 4> CtorArgs;
+        for (unsigned I : llvm::seq(DefaultCtor->getNumParams())) {
+          const ParmVarDecl *Param = DefaultCtor->getParamDecl(I);
+          if (Param->hasDefaultArg()) {
+            ExprResult DefArg = SemaRef.BuildCXXDefaultArgExpr(
+                D->getLocation(), DefaultCtor,
+                const_cast<ParmVarDecl *>(Param));
+            if (DefArg.isUsable())
+              CtorArgs.push_back(DefArg.get());
+          }
+        }
+
+        // Build constructor expression targeting omp_priv.
+        ExprResult CtorCall = SemaRef.BuildCXXConstructExpr(
+            D->getLocation(), ReductionType, DefaultCtor,
+            /*Elidable=*/false, CtorArgs,
+            /*HadMultipleCandidates=*/false,
+            /*IsListInitialization=*/false,
+            /*IsStdInitListInitialization=*/false,
+            /*RequiresZeroInit=*/false, CXXConstructionKind::Complete,
+            SourceRange());
+
+        if (CtorCall.isUsable()) {
+          // Wrap constructor and user initializer in StmtExpr.
+          // Create CompoundStmt directly since we don't have an active
+          // scope.
+          SmallVector<Stmt *, 2> Stmts;
+          Stmts.push_back(CtorCall.get());
+          Stmts.push_back(Initializer);
+
+          CompoundStmt *CS =
+              CompoundStmt::Create(SemaRef.Context, Stmts, FPOptionsOverride(),
+                                   D->getLocation(), D->getLocation());
+
+          Initializer = new (SemaRef.Context) StmtExpr(
+              CS, SemaRef.Context.VoidTy, D->getLocation(), D->getLocation(),
+              /*TemplateDepth=*/0);
+        }
+      }
+    }
+  }
----------------
alexey-bataev wrote:

I would not do this, it is unsafe and unsound, use standard functions, like AddInitializer or something like AddefaultInitializer(?), already used in this file, do not try to build constructor call yourself, you're missing so many corner cases/generate incorrect code

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


More information about the cfe-commits mailing list