https://github.com/love1angel updated 
https://github.com/llvm/llvm-project/pull/185886

>From 6b435c9b2296217570596c015ca17dd10520cd80 Mon Sep 17 00:00:00 2001
From: Peng Xie <[email protected]>
Date: Wed, 11 Mar 2026 14:45:40 +0800
Subject: [PATCH 1/6] [Clang] Implement P3074R7: trivial unions (C++26)

P3074R7 makes union special member functions trivial by default in
C++26, regardless of variant member triviality. A defaulted destructor
for a union is now defined as deleted only if its default constructor
is user-provided (or deleted/ambiguous), or if a variant member has
both a default member initializer and a non-trivial destructor.

The pre-existing rule that a deleted or inaccessible member destructor
causes the union destructor to be deleted (p7.2) is preserved.

This patch does not implement the implicit-lifetime-start semantics
from P3074R7 p4, which was reworked by P3726R1 into
__builtin_start_lifetime.

Defines __cpp_trivial_union=202502L (bumped to 202602L by P3726R1).
---
 .../clang/AST/CXXRecordDeclDefinitionBits.def |   5 +
 clang/include/clang/AST/DeclCXX.h             |   7 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   6 +
 clang/lib/AST/DeclCXX.cpp                     |  46 +++-
 clang/lib/Frontend/InitPreprocessor.cpp       |   4 +
 clang/lib/Sema/SemaDeclCXX.cpp                | 112 +++++++-
 clang/test/CXX/drs/cwg14xx.cpp                |  68 +++--
 clang/test/CXX/special/class.ctor/p5-0x.cpp   |  16 +-
 clang/test/CXX/special/class.ctor/p6-0x.cpp   |  31 +--
 clang/test/CXX/special/class.dtor/p5-0x.cpp   |  33 +--
 clang/test/SemaCXX/cxx26-trivial-union.cpp    | 253 ++++++++++++++++++
 11 files changed, 512 insertions(+), 69 deletions(-)
 create mode 100644 clang/test/SemaCXX/cxx26-trivial-union.cpp

diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def 
b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index 7e6e2147a448d..0fd4a86de6f5a 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -119,6 +119,11 @@ FIELD(HasInitMethod, 1, NO_MERGE)
 /// within anonymous unions or structs.
 FIELD(HasInClassInitializer, 1, NO_MERGE)
 
+/// True if any field with an in-class initializer has a type (or base
+/// element type) with a non-trivial destructor. Used for C++26 union
+/// destructor deletion check ([class.dtor]p7 bullet 2).
+FIELD(HasDMIWithNonTrivialDtor, 1, NO_MERGE)
+
 /// True if any field is of reference type, and does not have an
 /// in-class initializer.
 ///
diff --git a/clang/include/clang/AST/DeclCXX.h 
b/clang/include/clang/AST/DeclCXX.h
index 2af396f025c93..912691683fcb7 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1147,6 +1147,13 @@ class CXXRecordDecl : public RecordDecl {
   /// structs).
   bool hasInClassInitializer() const { return data().HasInClassInitializer; }
 
+  /// Whether this class has any in-class initializer for a non-static data
+  /// member whose type (or base element type) has a non-trivial destructor.
+  /// Used for C++26 union destructor deletion ([class.dtor]p7 bullet 2).
+  bool hasDMIWithNonTrivialDtor() const {
+    return data().HasDMIWithNonTrivialDtor;
+  }
+
   /// Whether this class or any of its subobjects has any members of
   /// reference type which would make value-initialization ill-formed.
   ///
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6d2fae551566f..a6f9a4d92f511 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6429,6 +6429,12 @@ def note_enforce_read_only_placement : Note<"type was 
declared read-only here">;
 
 def note_deleted_dtor_no_operator_delete : Note<
   "virtual destructor requires an unambiguous, accessible 'operator delete'">;
+def note_deleted_dtor_default_ctor : Note<
+  "destructor of union %0 is implicitly deleted because "
+  "%select{"
+  "%select{it has no default constructor|its default constructor is a deleted 
function}2|"
+  "overload resolution to default-initialize it is ambiguous|"
+  "its default constructor is not trivial}1">;
 def note_deleted_special_member_class_subobject : Note<
   "%select{default constructor of|copy constructor of|move constructor of|"
   "copy assignment operator of|move assignment operator of|destructor of|"
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 3b9d888bb2c0a..32ae2131beae1 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -81,6 +81,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl 
*D)
       HasPrivateFields(false), HasProtectedFields(false),
       HasPublicFields(false), HasMutableFields(false), 
HasVariantMembers(false),
       HasOnlyCMembers(true), HasInitMethod(false), 
HasInClassInitializer(false),
+      HasDMIWithNonTrivialDtor(false),
       HasUninitializedReferenceMember(false), HasUninitializedFields(false),
       HasInheritedConstructor(false), HasInheritedDefaultConstructor(false),
       HasInheritedAssignment(false),
@@ -1177,10 +1178,29 @@ void CXXRecordDecl::addedMember(Decl *D) {
          Field->getType()->getAsCXXRecordDecl()->hasInClassInitializer())) {
       data().HasInClassInitializer = true;
 
+      // C++26 [class.dtor]p7 bullet 2: track whether any field with a DMI
+      // has a type with a non-trivial destructor, for union dtor deletion.
+      if (Field->hasInClassInitializer()) {
+        QualType FT =
+            getASTContext().getBaseElementType(Field->getType());
+        if (const auto *FR = FT->getAsCXXRecordDecl();
+            FR && FR->hasNonTrivialDestructor())
+          data().HasDMIWithNonTrivialDtor = true;
+      } else if (Field->isAnonymousStructOrUnion()) {
+        if (Field->getType()->getAsCXXRecordDecl()->data()
+                .HasDMIWithNonTrivialDtor)
+          data().HasDMIWithNonTrivialDtor = true;
+      }
+
       // C++11 [class]p5:
       //   A default constructor is trivial if [...] no non-static data member
       //   of its class has a brace-or-equal-initializer.
-      data().HasTrivialSpecialMembers &= ~SMF_DefaultConstructor;
+      // C++26 [class.default.ctor]p3:
+      //   A default constructor is trivial if [...] either X is a union or
+      //   no non-static data member of its class has a
+      //   brace-or-equal-initializer.
+      if (!(isUnion() && getASTContext().getLangOpts().CPlusPlus26))
+        data().HasTrivialSpecialMembers &= ~SMF_DefaultConstructor;
 
       // C++11 [dcl.init.aggr]p1:
       //   An aggregate is a [...] class with [...] no
@@ -1239,7 +1259,11 @@ void CXXRecordDecl::addedMember(Decl *D) {
           if (FieldRec->hasNonTrivialMoveAssignment())
             data().DefaultedMoveAssignmentIsDeleted = true;
           if (FieldRec->hasNonTrivialDestructor()) {
-            data().DefaultedDestructorIsDeleted = true;
+            // In C++26, the destructor of a union is not deleted merely
+            // because a variant member has a non-trivial destructor.
+            // Deletion is determined later by Sema (see C++26 [class.dtor]p7).
+            if (!Context.getLangOpts().CPlusPlus26)
+              data().DefaultedDestructorIsDeleted = true;
             // C++20 [dcl.constexpr]p5:
             //   The definition of a constexpr destructor whose function-body 
is
             //   not = delete shall additionally satisfy...
@@ -1267,7 +1291,13 @@ void CXXRecordDecl::addedMember(Decl *D) {
         //    -- for all the non-static data members of its class that are of
         //       class type (or array thereof), each such class has a trivial
         //       default constructor.
-        if (!FieldRec->hasTrivialDefaultConstructor())
+        // C++26 [class.default.ctor]p3:
+        //   A default constructor is trivial if [...] either X is a union
+        //   or for all the non-static data members of its class that are
+        //   of class type [...] each such class has a trivial default
+        //   constructor.
+        if (!FieldRec->hasTrivialDefaultConstructor() &&
+            !(isUnion() && Context.getLangOpts().CPlusPlus26))
           data().HasTrivialSpecialMembers &= ~SMF_DefaultConstructor;
 
         // C++0x [class.copy]p13:
@@ -1305,9 +1335,15 @@ void CXXRecordDecl::addedMember(Decl *D) {
         if (!FieldRec->hasTrivialMoveAssignment())
           data().HasTrivialSpecialMembers &= ~SMF_MoveAssignment;
 
-        if (!FieldRec->hasTrivialDestructor())
+        // C++26 [class.dtor]p8:
+        //   A destructor is trivial if [...] either X is a union or for
+        //   all the non-static data members of its class that are of class
+        //   type [...] each such class has a trivial destructor.
+        if (!FieldRec->hasTrivialDestructor() &&
+            !(isUnion() && Context.getLangOpts().CPlusPlus26))
           data().HasTrivialSpecialMembers &= ~SMF_Destructor;
-        if (!FieldRec->hasTrivialDestructorForCall())
+        if (!FieldRec->hasTrivialDestructorForCall() &&
+            !(isUnion() && Context.getLangOpts().CPlusPlus26))
           data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor;
         if (!FieldRec->hasIrrelevantDestructor())
           data().HasIrrelevantDestructor = false;
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp 
b/clang/lib/Frontend/InitPreprocessor.cpp
index 1ccd74314f373..a00b34625510b 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -746,6 +746,10 @@ static void InitializeCPlusPlusFeatureTestMacros(const 
LangOptions &LangOpts,
   Builder.defineMacro("__cpp_variadic_friend", "202403L");
   Builder.defineMacro("__cpp_trivial_relocatability", "202502L");
 
+  // C++26 features.
+  if (LangOpts.CPlusPlus26)
+    Builder.defineMacro("__cpp_trivial_union", "202502L");
+
   if (LangOpts.Char8)
     Builder.defineMacro("__cpp_char8_t", "202207L");
   Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index c1d3960e65ef6..50659188c4244 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9631,6 +9631,11 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
   if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::NoMemberOrDeleted) {
     if (CSM == CXXSpecialMemberKind::DefaultConstructor && Field &&
         Field->getParent()->isUnion()) {
+      // In C++26, a union's defaulted default constructor is never
+      // deleted due to a variant member with a non-trivial default
+      // constructor (see C++26 [class.default.ctor]p3).
+      if (S.getLangOpts().CPlusPlus26)
+        return false;
       // [class.default.ctor]p2:
       //   A defaulted default constructor for class X is defined as deleted if
       //   - X is a union that has a variant member with a non-trivial default
@@ -9653,6 +9658,11 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
     // destructor is never actually called, but is semantically checked as
     // if it were.
     if (CSM == CXXSpecialMemberKind::DefaultConstructor) {
+      // In C++26, a union's defaulted default constructor is never
+      // deleted due to a variant member with a non-trivial default
+      // constructor (see C++26 [class.default.ctor]p3).
+      if (S.getLangOpts().CPlusPlus26)
+        return false;
       // [class.default.ctor]p2:
       //   A defaulted default constructor for class X is defined as deleted if
       //   - X is a union that has a variant member with a non-trivial default
@@ -9661,6 +9671,12 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
       const auto *RD = cast<CXXRecordDecl>(Field->getParent());
       if (!RD->hasInClassInitializer())
         DiagKind = NonTrivialDecl;
+    } else if (CSM == CXXSpecialMemberKind::Destructor &&
+               S.getLangOpts().CPlusPlus26) {
+      // In C++26, a union's destructor is not deleted merely because a
+      // variant member has a non-trivial destructor. Deletion is determined
+      // by the new union-specific rules (see C++26 [class.dtor]p7).
+      return false;
     } else {
       DiagKind = NonTrivialDecl;
     }
@@ -9822,6 +9838,13 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
   if (inUnion() && shouldDeleteForVariantPtrAuthMember(FD))
     return true;
 
+  // In C++26, a union's defaulted default constructor is trivially defined
+  // and never deleted due to variant member properties
+  // (see C++26 [class.default.ctor]p3).
+  if (inUnion() && S.getLangOpts().CPlusPlus26 &&
+      CSM == CXXSpecialMemberKind::DefaultConstructor)
+    return false;
+
   if (CSM == CXXSpecialMemberKind::DefaultConstructor) {
     // For a default constructor, all references must be initialized in-class
     // and, if a union, it must have a non-const member.
@@ -9900,7 +9923,8 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
 
       // At least one member in each anonymous union must be non-const
       if (CSM == CXXSpecialMemberKind::DefaultConstructor &&
-          AllVariantFieldsAreConst && !FieldRecord->field_empty()) {
+          AllVariantFieldsAreConst && !FieldRecord->field_empty() &&
+          !S.getLangOpts().CPlusPlus26) {
         if (Diagnose)
           S.Diag(FieldRecord->getLocation(),
                  diag::note_deleted_default_ctor_all_const)
@@ -9930,6 +9954,8 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() {
   // default constructor. Don't do that.
   if (CSM == CXXSpecialMemberKind::DefaultConstructor && inUnion() &&
       AllFieldsAreConst) {
+    if (S.getLangOpts().CPlusPlus26)
+      return false;
     bool AnyFields = false;
     for (auto *F : MD->getParent()->fields())
       if ((AnyFields = !F->isUnnamedBitField()))
@@ -9945,6 +9971,72 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() {
   return false;
 }
 
+/// C++26 [class.dtor]p7: Check union-specific destructor deletion rules.
+/// Returns true if the destructor should be deleted.
+static bool ShouldDeleteUnionDestructorInCpp26(Sema &S, CXXRecordDecl *RD,
+                                               CXXMethodDecl *MD,
+                                               bool Diagnose) {
+  // Bullet 1: overload resolution for default initialization.
+  Sema::SpecialMemberOverloadResult SMOR = S.LookupSpecialMember(
+      RD, CXXSpecialMemberKind::DefaultConstructor,
+      /*ConstArg=*/false, /*VolatileArg=*/false, /*RValueThis=*/false,
+      /*ConstThis=*/false, /*VolatileThis=*/false);
+  bool CtorOK = false;
+  if (SMOR.getKind() == Sema::SpecialMemberOverloadResult::Success) {
+    auto *Ctor = cast<CXXConstructorDecl>(SMOR.getMethod());
+    // Use !isUserProvided() rather than isTrivial() because the triviality
+    // flag may not be set yet at the point DeclareImplicitDestructor runs.
+    CtorOK = !Ctor->isDeleted() && !Ctor->isUserProvided();
+  } else if (SMOR.getKind() ==
+                 Sema::SpecialMemberOverloadResult::NoMemberOrDeleted &&
+             !SMOR.getMethod()) {
+    CtorOK = true;
+  }
+  if (!CtorOK) {
+    if (Diagnose) {
+      auto *Ctor = SMOR.getMethod();
+      S.Diag(RD->getLocation(), diag::note_deleted_dtor_default_ctor)
+          << RD << SMOR.getKind() << (Ctor && Ctor->isDeleted());
+    }
+    return true;
+  }
+
+  // Bullet 2: variant member with DMI and non-trivial dtor.
+  if (!RD->hasDMIWithNonTrivialDtor())
+    return false;
+
+  if (!Diagnose)
+    return true;
+
+  // Walk fields to find the specific field for the diagnostic.
+  auto FindDMIWithNonTrivialDtor = [&](const CXXRecordDecl *Record,
+                                       auto &Self) -> bool {
+    for (const FieldDecl *FD : Record->fields()) {
+      if (FD->hasInClassInitializer()) {
+        QualType FT = S.Context.getBaseElementType(FD->getType());
+        if (const auto *FR = FT->getAsCXXRecordDecl();
+            FR && FR->hasNonTrivialDestructor()) {
+          S.Diag(FD->getLocation(),
+                 diag::note_deleted_special_member_class_subobject)
+              << S.getSpecialMember(MD) << RD << /*IsField=*/true << FD
+              << /*NonTrivialDecl=*/4 << /*IsDtorCallInCtor=*/false
+              << /*IsObjCPtr=*/false;
+          return true;
+        }
+      }
+      if (FD->isAnonymousStructOrUnion()) {
+        if (const auto *SubRD = FD->getType()->getAsCXXRecordDecl()) {
+          if (Self(SubRD, Self))
+            return true;
+        }
+      }
+    }
+    return false;
+  };
+  FindDMIWithNonTrivialDtor(RD, FindDMIWithNonTrivialDtor);
+  return true;
+}
+
 /// Determine whether a defaulted special member function should be defined as
 /// deleted, as specified in C++11 [class.ctor]p5, C++11 [class.copy]p11,
 /// C++11 [class.copy]p23, and C++11 [class.dtor]p5.
@@ -10050,6 +10142,13 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
 
   SpecialMemberDeletionInfo SMI(*this, MD, CSM, ICI, Diagnose);
 
+  // C++26 [class.dtor]p7: union-specific destructor deletion rules.
+  if (getLangOpts().CPlusPlus26 && RD->isUnion() &&
+      CSM == CXXSpecialMemberKind::Destructor) {
+    if (ShouldDeleteUnionDestructorInCpp26(*this, RD, MD, Diagnose))
+      return true;
+  }
+
   // Per DR1611, do not consider virtual bases of constructors of abstract
   // classes, since we are not going to construct them.
   // Per DR1658, do not consider virtual bases of destructors of abstract
@@ -10483,7 +10582,16 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, 
CXXSpecialMemberKind CSM,
   //    -- for all of the non-static data members of its class that are of 
class
   //       type (or array thereof), each such class has a trivial [default
   //       constructor or destructor]
-  if (!checkTrivialClassMembers(*this, RD, CSM, ConstArg, TAH, Diagnose))
+  //
+  // C++26 [class.default.ctor]p3, [class.dtor]p8:
+  //   A default constructor [destructor] is trivial if [...] either X is
+  //   a union or for all the non-static data members [...] each such class
+  //   has a trivial default constructor [destructor].
+  if (RD->isUnion() && getLangOpts().CPlusPlus26 &&
+      (CSM == CXXSpecialMemberKind::DefaultConstructor ||
+       CSM == CXXSpecialMemberKind::Destructor)) {
+    // Skip member triviality checks for unions in C++26.
+  } else if (!checkTrivialClassMembers(*this, RD, CSM, ConstArg, TAH, 
Diagnose))
     return false;
 
   // C++11 [class.dtor]p5:
diff --git a/clang/test/CXX/drs/cwg14xx.cpp b/clang/test/CXX/drs/cwg14xx.cpp
index afa4bc9d8179a..4db8e38d9a5e5 100644
--- a/clang/test/CXX/drs/cwg14xx.cpp
+++ b/clang/test/CXX/drs/cwg14xx.cpp
@@ -1,18 +1,18 @@
 // RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives -verify=expected
-// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives -verify=expected,cxx11-17,since-cxx11
-// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14
-// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14
-// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20
-// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20
-// RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20
+// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx11-17,since-cxx11,precxx26
+// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,precxx26
+// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,precxx26
+// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,precxx26
+// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,precxx26
+// RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,since-cxx26
 
 // RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected
-// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx11-17,since-cxx11
-// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14
-// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14
-// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20
-// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20
-// RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20
+// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx11-17,since-cxx11,precxx26
+// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,precxx26
+// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,precxx26
+// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,precxx26
+// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,precxx26
+// RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,since-cxx26
 
 namespace cwg1413 { // cwg1413: 12
   template<int> struct Check {
@@ -128,7 +128,7 @@ struct A {
 namespace cwg1460 { // cwg1460: 3.5
 #if __cplusplus >= 201103L
   namespace DRExample {
-    union A {
+    union A { // #cwg1460-DRExample-A
       union {};
       // since-cxx11-error@-1 {{declaration does not declare anything}}
       union {};
@@ -136,6 +136,8 @@ namespace cwg1460 { // cwg1460: 3.5
       constexpr A() {}
     };
     constexpr A a = A();
+    // since-cxx26-error@-1 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-DRExample-A {{destructor of union 'A' is 
implicitly deleted because its default constructor is not trivial}}
 
     union B {
       union {};
@@ -277,7 +279,7 @@ namespace cwg1460 { // cwg1460: 3.5
     };
     static_assert(A().a == 1 && A().b == 2 && A().c == 3, "");
 
-    union B {
+    union B { // #cwg1460-Overriding-B
       int a, b = 2, c;
       constexpr B() : a(1) {}
       constexpr B(char) : b(4) {}
@@ -285,24 +287,42 @@ namespace cwg1460 { // cwg1460: 3.5
       constexpr B(const char*) {}
     };
     static_assert(B().a == 1, "");
+    // since-cxx26-error@-1 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B().b == 2, "");
-    // since-cxx11-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   since-cxx11-note@-2 {{read of member 'b' of union with active member 
'a' is not allowed in a constant expression}}
+    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   precxx26-note@-2 {{read of member 'b' of union with active member 'a' 
is not allowed in a constant expression}}
+    // since-cxx26-error@-3 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B('x').a == 0, "");
-    // since-cxx11-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   since-cxx11-note@-2 {{read of member 'a' of union with active member 
'b' is not allowed in a constant expression}}
+    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   precxx26-note@-2 {{read of member 'a' of union with active member 'b' 
is not allowed in a constant expression}}
+    // since-cxx26-error@-3 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B('x').b == 4, "");
+    // since-cxx26-error@-1 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B(123).b == 2, "");
-    // since-cxx11-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   since-cxx11-note@-2 {{read of member 'b' of union with active member 
'c' is not allowed in a constant expression}}
+    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   precxx26-note@-2 {{read of member 'b' of union with active member 'c' 
is not allowed in a constant expression}}
+    // since-cxx26-error@-3 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B(123).c == 3, "");
+    // since-cxx26-error@-1 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B("").a == 1, "");
-    // since-cxx11-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   since-cxx11-note@-2 {{read of member 'a' of union with active member 
'b' is not allowed in a constant expression}}
+    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   precxx26-note@-2 {{read of member 'a' of union with active member 'b' 
is not allowed in a constant expression}}
+    // since-cxx26-error@-3 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B("").b == 2, "");
+    // since-cxx26-error@-1 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B("").c == 3, "");
-    // since-cxx11-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   since-cxx11-note@-2 {{read of member 'c' of union with active member 
'b' is not allowed in a constant expression}}
+    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   precxx26-note@-2 {{read of member 'c' of union with active member 'b' 
is not allowed in a constant expression}}
+    // since-cxx26-error@-3 {{attempt to use a deleted function}}
+    //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
 
     struct C {
       union { int a, b = 2, c; };
diff --git a/clang/test/CXX/special/class.ctor/p5-0x.cpp 
b/clang/test/CXX/special/class.ctor/p5-0x.cpp
index e0c53058f892b..f00ec4ddc6350 100644
--- a/clang/test/CXX/special/class.ctor/p5-0x.cpp
+++ b/clang/test/CXX/special/class.ctor/p5-0x.cpp
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 
-Wno-deprecated-builtins -Wno-defaulted-function-deleted
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11-23 %s -std=c++11 
-Wno-deprecated-builtins -Wno-defaulted-function-deleted
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11-23 %s -std=c++23 
-Wno-deprecated-builtins -Wno-defaulted-function-deleted
+// RUN: %clang_cc1 -fsyntax-only -verify=expected %s -std=c++26 
-Wno-deprecated-builtins -Wno-defaulted-function-deleted
 
 struct DefaultedDefCtor1 {};
 struct DefaultedDefCtor2 { DefaultedDefCtor2() = default; };
@@ -23,8 +25,8 @@ int n;
 
 // - X is a union-like class that has a variant member with a non-trivial
 // default constructor,
-union Deleted1a { UserProvidedDefCtor u; }; // expected-note {{default 
constructor of 'Deleted1a' is implicitly deleted because variant field 'u' has 
a non-trivial default constructor}}
-Deleted1a d1a; // expected-error {{implicitly-deleted default constructor}}
+union Deleted1a { UserProvidedDefCtor u; }; // cxx11-23-note {{default 
constructor of 'Deleted1a' is implicitly deleted because variant field 'u' has 
a non-trivial default constructor}}
+Deleted1a d1a; // cxx11-23-error {{implicitly-deleted default constructor}}
 union NotDeleted1a { DefaultedDefCtor1 nu; };
 NotDeleted1a nd1a;
 union NotDeleted1b { DefaultedDefCtor2 nu; };
@@ -86,19 +88,19 @@ NotDeleted3i nd3i;
 union Deleted4a {
   const int a;
   const int b;
-  const UserProvidedDefCtor c; // expected-note {{because variant field 'c' 
has a non-trivial default constructor}}
+  const UserProvidedDefCtor c; // cxx11-23-note {{because variant field 'c' 
has a non-trivial default constructor}}
 };
-Deleted4a d4a; // expected-error {{implicitly-deleted default constructor}}
+Deleted4a d4a; // cxx11-23-error {{implicitly-deleted default constructor}}
 union NotDeleted4a { const int a; int b; };
 NotDeleted4a nd4a;
 
 // - X is a non-union class and all members of any anonymous union member are 
of
 // const-qualified type (or array thereof),
 struct Deleted5a {
-  union { const int a; }; // expected-note {{because all data members of an 
anonymous union member are const-qualified}}
+  union { const int a; }; // cxx11-23-note {{because all data members of an 
anonymous union member are const-qualified}}
   union { int b; };
 };
-Deleted5a d5a; // expected-error {{implicitly-deleted default constructor}}
+Deleted5a d5a; // cxx11-23-error {{implicitly-deleted default constructor}}
 struct NotDeleted5a { union { const int a; int b; }; union { const int c; int 
d; }; };
 NotDeleted5a nd5a;
 
diff --git a/clang/test/CXX/special/class.ctor/p6-0x.cpp 
b/clang/test/CXX/special/class.ctor/p6-0x.cpp
index 156a2b20c6b52..474e7aad91207 100644
--- a/clang/test/CXX/special/class.ctor/p6-0x.cpp
+++ b/clang/test/CXX/special/class.ctor/p6-0x.cpp
@@ -1,11 +1,12 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx11-23 %s -std=c++11
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx26 %s -std=c++26
 
 // Implicitly-defined default constructors are constexpr if the implicit
 // definition would be.
-struct NonConstexpr1 { // expected-note {{here}}
+struct NonConstexpr1 { // cxx11-23-note {{here}} cxx26-note {{previous 
declaration is here}}
   int a;
 };
-struct NonConstexpr2 { // expected-note {{here}}
+struct NonConstexpr2 { // cxx11-23-note {{here}} cxx26-note {{previous 
declaration is here}}
   NonConstexpr1 nl;
 };
 struct NonConstexpr2a : NonConstexpr1 { };
@@ -15,8 +16,8 @@ constexpr NonConstexpr2a nc2a = NonConstexpr2a(); // ok, does 
not call construct
 constexpr int nc2_a = NonConstexpr2().nl.a; // ok
 constexpr int nc2a_a = NonConstexpr2a().a; // ok
 struct Helper {
-  friend constexpr NonConstexpr1::NonConstexpr1(); // expected-error {{follows 
non-constexpr declaration}}
-  friend constexpr NonConstexpr2::NonConstexpr2(); // expected-error {{follows 
non-constexpr declaration}}
+  friend constexpr NonConstexpr1::NonConstexpr1(); // cxx11-23-error {{follows 
non-constexpr declaration}} cxx26-error {{missing exception specification}}
+  friend constexpr NonConstexpr2::NonConstexpr2(); // cxx11-23-error {{follows 
non-constexpr declaration}} cxx26-error {{missing exception specification}}
 };
 
 struct Constexpr1 {};
@@ -31,14 +32,14 @@ constexpr Constexpr2 c2 = Constexpr2(); // ok
 
 int n;
 struct Member {
-  Member() : a(n) {}
+  Member() : a(n) {} // cxx26-note {{here}}
   constexpr Member(int&a) : a(a) {}
   int &a;
 };
-struct NonConstexpr4 { // expected-note {{here}}
+struct NonConstexpr4 { // cxx11-23-note {{here}} cxx26-note {{non-constexpr 
constructor}}
   Member m;
 };
-constexpr NonConstexpr4 nc4 = NonConstexpr4(); // expected-error {{constant 
expression}} expected-note {{non-constexpr constructor 'NonConstexpr4'}}
+constexpr NonConstexpr4 nc4 = NonConstexpr4(); // expected-error {{constant 
expression}} cxx11-23-note {{non-constexpr constructor 'NonConstexpr4'}} 
cxx26-note {{in call to}}
 struct Constexpr3 {
   constexpr Constexpr3() : m(n) {}
   Member m;
@@ -53,11 +54,11 @@ constexpr Constexpr4 c4 = Constexpr4(); // ok
 // This rule breaks some legal C++98 programs!
 struct A {}; // expected-note {{here}}
 struct B {
-  friend A::A(); // expected-error {{non-constexpr declaration of 'A' follows 
constexpr declaration}}
+  friend A::A(); // cxx11-23-error {{non-constexpr declaration of 'A' follows 
constexpr declaration}} cxx26-error {{missing exception specification}}
 };
 
 namespace UnionCtors {
-  union A { // expected-note {{here}}
+  union A { // cxx11-23-note {{here}}
     int a;
     int b;
   };
@@ -79,7 +80,7 @@ namespace UnionCtors {
       int d = 5;
     };
   };
-  struct E { // expected-note {{here}}
+  struct E { // cxx11-23-note {{here}}
     union {
       int a;
       int b;
@@ -87,11 +88,11 @@ namespace UnionCtors {
   };
 
   struct Test {
-    friend constexpr A::A() noexcept; // expected-error {{follows 
non-constexpr declaration}}
+    friend constexpr A::A() noexcept; // cxx11-23-error {{follows 
non-constexpr declaration}}
     friend constexpr B::B() noexcept;
     friend constexpr C::C() noexcept;
     friend constexpr D::D() noexcept;
-    friend constexpr E::E() noexcept; // expected-error {{follows 
non-constexpr declaration}}
+    friend constexpr E::E() noexcept; // cxx11-23-error {{follows 
non-constexpr declaration}}
   };
 }
 
@@ -122,6 +123,6 @@ namespace PR48763 {
 
   struct G { G(); };
   struct H : D { using D::D; H(int); G g; };
-  union V { H h; }; // expected-note {{field 'h' has a non-trivial default 
constructor}}
-  V v; // expected-error {{deleted}}
+  union V { H h; }; // cxx11-23-note {{field 'h' has a non-trivial default 
constructor}}
+  V v; // cxx11-23-error {{deleted}}
 }
diff --git a/clang/test/CXX/special/class.dtor/p5-0x.cpp 
b/clang/test/CXX/special/class.dtor/p5-0x.cpp
index ae14dcdaf102a..286e980710009 100644
--- a/clang/test/CXX/special/class.dtor/p5-0x.cpp
+++ b/clang/test/CXX/special/class.dtor/p5-0x.cpp
@@ -1,10 +1,11 @@
-// RUN: %clang_cc1 -verify -std=c++11 %s -Wno-defaulted-function-deleted 
-triple x86_64-linux-gnu
+// RUN: %clang_cc1 -verify=expected,precxx26 -std=c++11 %s 
-Wno-defaulted-function-deleted -triple x86_64-linux-gnu
+// RUN: %clang_cc1 -verify=expected,cxx26 -std=c++26 %s 
-Wno-defaulted-function-deleted -triple x86_64-linux-gnu
 
 struct NonTrivDtor {
   ~NonTrivDtor();
 };
 struct DeletedDtor {
-  ~DeletedDtor() = delete; // expected-note 5 {{deleted here}}
+  ~DeletedDtor() = delete; // expected-note 4 {{deleted here}} precxx26-note 
{{deleted here}}
 };
 class InaccessibleDtor {
   ~InaccessibleDtor() = default;
@@ -14,30 +15,30 @@ class InaccessibleDtor {
 
 // -- X is a union-like class that has a variant member with a non-trivial
 // destructor.
-union A1 {
+union A1 { // cxx26-note {{not trivial}}
   A1();
-  NonTrivDtor n; // expected-note {{destructor of 'A1' is implicitly deleted 
because variant field 'n' has a non-trivial destructor}}
+  NonTrivDtor n; // precxx26-note {{destructor of 'A1' is implicitly deleted 
because variant field 'n' has a non-trivial destructor}}
 };
 A1 a1; // expected-error {{deleted function}}
 struct A2 {
   A2();
   union {
-    NonTrivDtor n; // expected-note {{because variant field 'n' has a 
non-trivial destructor}}
+    NonTrivDtor n; // precxx26-note {{because variant field 'n' has a 
non-trivial destructor}}
   };
 };
-A2 a2; // expected-error {{deleted function}}
-union A3 {
+A2 a2; // precxx26-error {{deleted function}}
+union A3 { // cxx26-note {{not trivial}}
   A3();
-  NonTrivDtor n[3]; // expected-note {{because variant field 'n' has a 
non-trivial destructor}}
+  NonTrivDtor n[3]; // precxx26-note {{because variant field 'n' has a 
non-trivial destructor}}
 };
 A3 a3; // expected-error {{deleted function}}
 struct A4 {
   A4();
   union {
-    NonTrivDtor n[3]; // expected-note {{because variant field 'n' has a 
non-trivial destructor}}
+    NonTrivDtor n[3]; // precxx26-note {{because variant field 'n' has a 
non-trivial destructor}}
   };
 };
-A4 a4; // expected-error {{deleted function}}
+A4 a4; // precxx26-error {{deleted function}}
 
 // -- any of the non-static data members has class type M (or array thereof) 
and
 // M has a deleted or inaccessible destructor.
@@ -61,17 +62,17 @@ struct B4 {
   InaccessibleDtor a[4]; // expected-note {{because field 'a' has an 
inaccessible destructor}}
 };
 B4 b4; // expected-error {{deleted function}}
-union B5 {
+union B5 { // cxx26-note {{not trivial}}
   B5();
-  union { // expected-note-re {{because field 'B5::(anonymous union at 
{{.+}})' has a deleted destructor}}
-    DeletedDtor a; // expected-note {{because field 'a' has a deleted 
destructor}}
+  union { // precxx26-note-re {{because field 'B5::(anonymous union at 
{{.+}})' has a deleted destructor}}
+    DeletedDtor a; // precxx26-note {{because field 'a' has a deleted 
destructor}}
   };
 };
 B5 b5; // expected-error {{deleted function}}
-union B6 {
+union B6 { // cxx26-note {{not trivial}}
   B6();
-  union { // expected-note-re {{because field 'B6::(anonymous union at 
{{.+}})' has a deleted destructor}}
-    InaccessibleDtor a; // expected-note {{because field 'a' has an 
inaccessible destructor}}
+  union { // precxx26-note-re {{because field 'B6::(anonymous union at 
{{.+}})' has a deleted destructor}}
+    InaccessibleDtor a; // precxx26-note {{because field 'a' has an 
inaccessible destructor}}
   };
 };
 B6 b6; // expected-error {{deleted function}}
diff --git a/clang/test/SemaCXX/cxx26-trivial-union.cpp 
b/clang/test/SemaCXX/cxx26-trivial-union.cpp
new file mode 100644
index 0000000000000..8265c4f4d7670
--- /dev/null
+++ b/clang/test/SemaCXX/cxx26-trivial-union.cpp
@@ -0,0 +1,253 @@
+// RUN: %clang_cc1 -std=c++26 -fsyntax-only -verify=cxx26 %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=precxx26 %s
+
+// P3074R7: trivial unions
+
+struct NonTrivial {
+  NonTrivial();
+  NonTrivial(const NonTrivial&);
+  NonTrivial& operator=(const NonTrivial&);
+  ~NonTrivial();
+};
+
+struct NonTrivialDtor {
+  ~NonTrivialDtor();
+};
+
+// ===== Test 1: Basic union with non-trivial member =====
+// P3074: default ctor and dtor should be trivial, not deleted.
+union U1 {
+  NonTrivial nt; // precxx26-note 2{{non-trivial}}
+};
+
+#if __cplusplus > 202302L
+static_assert(__is_trivially_constructible(U1));
+static_assert(__is_trivially_destructible(U1));
+U1 test_u1;
+#else
+U1 test_u1_pre; // precxx26-error {{deleted}}
+void destroy_u1(U1 *p) { p->~U1(); } // precxx26-error {{deleted}}
+#endif
+
+// ===== Test 2: Union with non-trivial member and int =====
+union U2 {
+  NonTrivial nt;
+  int k;
+};
+
+#if __cplusplus > 202302L
+static_assert(__is_trivially_constructible(U2));
+static_assert(__is_trivially_destructible(U2));
+#endif
+
+// ===== Test 3: Union with DMI on member with non-trivial dtor =====
+// P3074: dtor is deleted because DMI + non-trivial dtor on same member.
+union U3_deleted_dtor {
+  NonTrivialDtor ntd = {}; // cxx26-note {{non-trivial}} precxx26-note 
{{non-trivial}}
+};
+
+void test_u3_destroy(U3_deleted_dtor *p) {
+  p->~U3_deleted_dtor(); // cxx26-error {{deleted}} precxx26-error {{deleted}}
+}
+
+// ===== Test 4: Union with DMI on non-class member =====
+// DMI on int, but NonTrivial has no DMI => dtor should NOT be deleted.
+union U4 {
+  NonTrivial nt; // precxx26-note {{non-trivial}}
+  int k = 42;
+};
+
+#if __cplusplus > 202302L
+// Despite non-trivial default ctor (due to DMI), destructor is NOT deleted
+// because the member with DMI (k) is int (trivially destructible).
+static_assert(__is_trivially_destructible(U4));
+#else
+void destroy_u4(U4 *p) { p->~U4(); } // precxx26-error {{deleted}}
+#endif
+
+// ===== Test 5: Union with user-provided default constructor =====
+// Dtor is deleted: user-provided default ctor is non-trivial
+// (see C++26 [class.dtor]p7).
+union U5 { // cxx26-note {{not trivial}}
+  U5() : nt() {}
+  NonTrivialDtor nt; // precxx26-note {{non-trivial}}
+};
+
+void test_u5_destroy(U5 *p) { p->~U5(); } // cxx26-error {{deleted}} 
precxx26-error {{deleted}}
+
+// ===== Test 6: Feature test macro =====
+#if __cplusplus > 202302L
+static_assert(__cpp_trivial_union >= 202502L);
+#else
+#ifdef __cpp_trivial_union
+#error "should not have __cpp_trivial_union in C++23"
+#endif
+#endif
+
+// ===== Test 7: Trivial union (no change from status quo) =====
+union U7 {
+  int a;
+  float b;
+};
+
+static_assert(__is_trivially_constructible(U7));
+static_assert(__is_trivially_destructible(U7));
+
+// ===== Test 8: Array member in union =====
+union U8 {
+  NonTrivial arr[4];
+};
+
+#if __cplusplus > 202302L
+static_assert(__is_trivially_constructible(U8));
+static_assert(__is_trivially_destructible(U8));
+#endif
+
+// ===== Test 9: Paper example - string with DMI =====
+struct FakeString {
+  FakeString(const char*);
+  FakeString(const FakeString&);
+  FakeString& operator=(const FakeString&);
+  ~FakeString();
+};
+
+union PaperU2 {
+  FakeString s = "hello"; // cxx26-note {{non-trivial}} precxx26-note 
{{non-trivial}}
+};
+
+void test_paper_u2(PaperU2 *p) {
+  p->~PaperU2(); // cxx26-error {{deleted}} precxx26-error {{deleted}}
+}
+
+// ===== Test 10: Paper example U4 - DMI on pointer, non-trivial string =====
+union PaperU4 {
+  FakeString s; // precxx26-note {{non-trivial}}
+  PaperU4 *next = nullptr;
+};
+
+#if __cplusplus > 202302L
+static_assert(__is_trivially_destructible(PaperU4));
+#else
+void destroy_paper_u4(PaperU4 *p) { p->~PaperU4(); } // precxx26-error 
{{deleted}}
+#endif
+
+// ===== Test 11: No default ctor (suppressed by user-declared ctor) =====
+// When the implicit default ctor is suppressed (not deleted), the
+// [class.dtor]p7 bullet 1 rule does not apply. The union can't be
+// default-initialized, so no risk of indeterminate active member.
+// Destructor is trivial (see C++26 [class.dtor]p7).
+#if __cplusplus > 202302L
+union U11 {
+  U11(int);
+  NonTrivialDtor nt;
+};
+static_assert(__is_trivially_destructible(U11));
+U11 u11(1);
+
+// ===== Test 12: Deleted default ctor (dtor deleted per [class.dtor]p7) =====
+union U12 { // cxx26-note {{deleted function}}
+  U12() = delete;
+  U12(int);
+  NonTrivialDtor nt;
+};
+U12 u12(1); // cxx26-error {{deleted}}
+
+// ===== Test 13: Defaulted ctor => trivial, dtor NOT deleted =====
+union U13 {
+  U13() = default;
+  NonTrivialDtor nt;
+  U13 *next = nullptr;
+};
+U13 u13;
+
+// ===== Test 14: Array member with DMI + non-trivial dtor ([class.dtor]p7 
bullet 2) =====
+struct NonTrivialInt {
+  int i;
+  constexpr NonTrivialInt(int i) : i(i) {}
+  constexpr ~NonTrivialInt() {}
+};
+
+union U14 {
+  NonTrivialInt arr[2] = {1, 2}; // cxx26-note {{non-trivial}}
+};
+U14 u14; // cxx26-error {{deleted}}
+
+// ===== Test 15: Anonymous struct in union, no DMI => NOT deleted =====
+union U15 {
+  struct {
+    NonTrivialDtor x;
+  };
+};
+U15 u15;
+
+// ===== Test 16: Anonymous struct in union, with DMI => deleted =====
+union U16 {
+  struct {
+    NonTrivialInt x = 1; // cxx26-note {{non-trivial}}
+  };
+};
+U16 u16; // cxx26-error {{deleted}}
+
+// ===== Test 17: struct containing anonymous union =====
+struct S17 {
+  union {
+    NonTrivialDtor x;
+  };
+};
+S17 s17;
+
+// ===== Test 18: Deeply nested union-struct-union-struct-union =====
+union U18 {
+  struct {
+    union {
+      struct {
+        union {
+          NonTrivialDtor x;
+        };
+      };
+    };
+  };
+};
+U18 u18;
+
+// ===== Test 19: struct-union-struct-union-struct nesting =====
+struct S19 {
+  union {
+    struct {
+      union {
+        struct {
+          NonTrivialDtor x;
+        };
+      };
+    };
+  };
+};
+S19 s19;
+
+// ===== Test 20: Anonymous union inside union =====
+union U20 {
+  union {
+    NonTrivialDtor x;
+  };
+};
+U20 u20;
+
+// ===== Test 21: Deleted destructor member (p7.2, not union-specific) =====
+// p7.2 still applies: deleted/inaccessible member dtor => union dtor deleted.
+struct DeletedDtor {
+  ~DeletedDtor() = delete; // cxx26-note {{deleted here}} precxx26-note 
{{deleted here}}
+};
+
+union U21 {
+  DeletedDtor a; // cxx26-note {{deleted destructor}} precxx26-note {{deleted 
destructor}}
+};
+void test_u21(U21 *p) { p->~U21(); } // cxx26-error {{deleted}} precxx26-error 
{{deleted}}
+
+// ===== Test 22: Constexpr evaluation of trivial union =====
+constexpr int constexpr_test() {
+  U2 u;
+  u.k = 42;
+  return u.k;
+}
+static_assert(constexpr_test() == 42);
+#endif

>From 3faefdb97098efd7614510c1bb2ba9d642a86a79 Mon Sep 17 00:00:00 2001
From: Peng Xie <[email protected]>
Date: Fri, 10 Apr 2026 15:33:40 +0800
Subject: [PATCH 2/6] Address P3074 review feedback

---
 clang/lib/AST/DeclCXX.cpp      | 19 ++++++++++---------
 clang/lib/Sema/SemaDeclCXX.cpp | 15 +++++++--------
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 32ae2131beae1..91d9dc49c362a 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -81,10 +81,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl 
*D)
       HasPrivateFields(false), HasProtectedFields(false),
       HasPublicFields(false), HasMutableFields(false), 
HasVariantMembers(false),
       HasOnlyCMembers(true), HasInitMethod(false), 
HasInClassInitializer(false),
-      HasDMIWithNonTrivialDtor(false),
-      HasUninitializedReferenceMember(false), HasUninitializedFields(false),
-      HasInheritedConstructor(false), HasInheritedDefaultConstructor(false),
-      HasInheritedAssignment(false),
+      HasDMIWithNonTrivialDtor(false), HasUninitializedReferenceMember(false),
+      HasUninitializedFields(false), HasInheritedConstructor(false),
+      HasInheritedDefaultConstructor(false), HasInheritedAssignment(false),
       NeedOverloadResolutionForCopyConstructor(false),
       NeedOverloadResolutionForMoveConstructor(false),
       NeedOverloadResolutionForCopyAssignment(false),
@@ -1178,16 +1177,18 @@ void CXXRecordDecl::addedMember(Decl *D) {
          Field->getType()->getAsCXXRecordDecl()->hasInClassInitializer())) {
       data().HasInClassInitializer = true;
 
-      // C++26 [class.dtor]p7 bullet 2: track whether any field with a DMI
-      // has a type with a non-trivial destructor, for union dtor deletion.
+      // C++26 [class.dtor]p7 bullet 2: track whether any field with a
+      // default member initializer (DMI) has a type with a non-trivial
+      // destructor, for union destructor deletion.
       if (Field->hasInClassInitializer()) {
-        QualType FT =
-            getASTContext().getBaseElementType(Field->getType());
+        QualType FT = getASTContext().getBaseElementType(Field->getType());
         if (const auto *FR = FT->getAsCXXRecordDecl();
             FR && FR->hasNonTrivialDestructor())
           data().HasDMIWithNonTrivialDtor = true;
       } else if (Field->isAnonymousStructOrUnion()) {
-        if (Field->getType()->getAsCXXRecordDecl()->data()
+        if (Field->getType()
+                ->getAsCXXRecordDecl()
+                ->data()
                 .HasDMIWithNonTrivialDtor)
           data().HasDMIWithNonTrivialDtor = true;
       }
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 50659188c4244..5adddbcc3e28d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9673,9 +9673,8 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
         DiagKind = NonTrivialDecl;
     } else if (CSM == CXXSpecialMemberKind::Destructor &&
                S.getLangOpts().CPlusPlus26) {
-      // In C++26, a union's destructor is not deleted merely because a
-      // variant member has a non-trivial destructor. Deletion is determined
-      // by the new union-specific rules (see C++26 [class.dtor]p7).
+      // C++26 [class.dtor]p7: union destructor deletion is determined
+      // by the union-specific rules, not variant member triviality.
       return false;
     } else {
       DiagKind = NonTrivialDecl;
@@ -9838,9 +9837,8 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
   if (inUnion() && shouldDeleteForVariantPtrAuthMember(FD))
     return true;
 
-  // In C++26, a union's defaulted default constructor is trivially defined
-  // and never deleted due to variant member properties
-  // (see C++26 [class.default.ctor]p3).
+  // C++26 [class.default.ctor]p3: a union's defaulted default constructor
+  // is trivially defined regardless of variant members.
   if (inUnion() && S.getLangOpts().CPlusPlus26 &&
       CSM == CXXSpecialMemberKind::DefaultConstructor)
     return false;
@@ -10001,7 +9999,8 @@ static bool ShouldDeleteUnionDestructorInCpp26(Sema &S, 
CXXRecordDecl *RD,
     return true;
   }
 
-  // Bullet 2: variant member with DMI and non-trivial dtor.
+  // Bullet 2: variant member with default member initializer (DMI)
+  // and non-trivial destructor.
   if (!RD->hasDMIWithNonTrivialDtor())
     return false;
 
@@ -10590,7 +10589,7 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, 
CXXSpecialMemberKind CSM,
   if (RD->isUnion() && getLangOpts().CPlusPlus26 &&
       (CSM == CXXSpecialMemberKind::DefaultConstructor ||
        CSM == CXXSpecialMemberKind::Destructor)) {
-    // Skip member triviality checks for unions in C++26.
+    // Union member triviality doesn't affect the union's own triviality.
   } else if (!checkTrivialClassMembers(*this, RD, CSM, ConstArg, TAH, 
Diagnose))
     return false;
 

>From 09fed545c9136bf6fd7a7f9a6000b5aa32d6634f Mon Sep 17 00:00:00 2001
From: Peng Xie <[email protected]>
Date: Thu, 16 Apr 2026 15:42:09 +0800
Subject: [PATCH 3/6] Update release notes and cxx_status for P3074R7

---
 clang/docs/ReleaseNotes.rst | 3 +++
 clang/www/cxx_status.html   | 2 +-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2da7175b51ea3..58d3487707902 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -127,6 +127,9 @@ C++ Language Changes
 C++2c Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
+- Implemented `P3074R7: Trivial unions <https://wg21.link/P3074R7>`_
+  (``__cpp_trivial_union``).
+
 C++23 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 2c834b07f9a8f..72c62f8424585 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -259,7 +259,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Trivial unions</td>
   <td><a href="https://wg21.link/P3074";>P3074R7</a></td>
-  <td class="none" align="center">No</td>
+  <td class="unreleased" align="center">Clang 23</td>
  </tr>
  <tr>
   <td>Partial program correctness</td>

>From 5227113bf1beb9987328237f3bb803f153fb4b33 Mon Sep 17 00:00:00 2001
From: Peng Xie <[email protected]>
Date: Fri, 17 Apr 2026 14:24:29 +0800
Subject: [PATCH 4/6] Address P3074 review feedback (round 2)

---
 clang/lib/Sema/SemaDeclCXX.cpp | 17 ++++++++-------
 clang/test/CXX/drs/cwg14xx.cpp | 40 +++++++++++++++++-----------------
 2 files changed, 29 insertions(+), 28 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5adddbcc3e28d..f6c1b5cc0930b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -9971,9 +9971,12 @@ bool 
SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() {
 
 /// C++26 [class.dtor]p7: Check union-specific destructor deletion rules.
 /// Returns true if the destructor should be deleted.
-static bool ShouldDeleteUnionDestructorInCpp26(Sema &S, CXXRecordDecl *RD,
-                                               CXXMethodDecl *MD,
-                                               bool Diagnose) {
+static bool ShouldDeleteNonTrivialUnionDestructor(Sema &S, CXXRecordDecl *RD,
+                                                  CXXMethodDecl *MD,
+                                                  bool Diagnose) {
+  if (!S.getLangOpts().CPlusPlus26 || !RD->isUnion())
+    return false;
+
   // Bullet 1: overload resolution for default initialization.
   Sema::SpecialMemberOverloadResult SMOR = S.LookupSpecialMember(
       RD, CXXSpecialMemberKind::DefaultConstructor,
@@ -10142,11 +10145,9 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD,
   SpecialMemberDeletionInfo SMI(*this, MD, CSM, ICI, Diagnose);
 
   // C++26 [class.dtor]p7: union-specific destructor deletion rules.
-  if (getLangOpts().CPlusPlus26 && RD->isUnion() &&
-      CSM == CXXSpecialMemberKind::Destructor) {
-    if (ShouldDeleteUnionDestructorInCpp26(*this, RD, MD, Diagnose))
-      return true;
-  }
+  if (CSM == CXXSpecialMemberKind::Destructor &&
+      ShouldDeleteNonTrivialUnionDestructor(*this, RD, MD, Diagnose))
+    return true;
 
   // Per DR1611, do not consider virtual bases of constructors of abstract
   // classes, since we are not going to construct them.
diff --git a/clang/test/CXX/drs/cwg14xx.cpp b/clang/test/CXX/drs/cwg14xx.cpp
index 4db8e38d9a5e5..f804fb70ccd98 100644
--- a/clang/test/CXX/drs/cwg14xx.cpp
+++ b/clang/test/CXX/drs/cwg14xx.cpp
@@ -1,17 +1,17 @@
 // RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives -verify=expected
-// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx11-17,since-cxx11,precxx26
-// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,precxx26
-// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,precxx26
-// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,precxx26
-// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,precxx26
+// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx11-17,since-cxx11,cxx11-23
+// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,cxx11-23
+// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,cxx11-23
+// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,cxx11-23
+// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,cxx11-23
 // RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions 
-pedantic-errors -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,since-cxx26
 
 // RUN: %clang_cc1 -std=c++98 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected
-// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx11-17,since-cxx11,precxx26
-// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,precxx26
-// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,precxx26
-// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,precxx26
-// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,precxx26
+// RUN: %clang_cc1 -std=c++11 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx11-17,since-cxx11,cxx11-23
+// RUN: %clang_cc1 -std=c++14 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,cxx11-23
+// RUN: %clang_cc1 -std=c++17 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,cxx14-17,cxx11-17,since-cxx11,since-cxx14,cxx11-23
+// RUN: %clang_cc1 -std=c++20 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,cxx11-23
+// RUN: %clang_cc1 -std=c++23 %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,cxx11-23
 // RUN: %clang_cc1 -std=c++2c %s -fexceptions -fcxx-exceptions 
-pedantic-errors -fexperimental-new-constant-interpreter -verify-directives 
-verify=expected,since-cxx11,since-cxx14,since-cxx20,since-cxx26
 
 namespace cwg1413 { // cwg1413: 12
@@ -290,37 +290,37 @@ namespace cwg1460 { // cwg1460: 3.5
     // since-cxx26-error@-1 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B().b == 2, "");
-    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   precxx26-note@-2 {{read of member 'b' of union with active member 'a' 
is not allowed in a constant expression}}
+    // cxx11-23-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   cxx11-23-note@-2 {{read of member 'b' of union with active member 'a' 
is not allowed in a constant expression}}
     // since-cxx26-error@-3 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B('x').a == 0, "");
-    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   precxx26-note@-2 {{read of member 'a' of union with active member 'b' 
is not allowed in a constant expression}}
+    // cxx11-23-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   cxx11-23-note@-2 {{read of member 'a' of union with active member 'b' 
is not allowed in a constant expression}}
     // since-cxx26-error@-3 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B('x').b == 4, "");
     // since-cxx26-error@-1 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B(123).b == 2, "");
-    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   precxx26-note@-2 {{read of member 'b' of union with active member 'c' 
is not allowed in a constant expression}}
+    // cxx11-23-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   cxx11-23-note@-2 {{read of member 'b' of union with active member 'c' 
is not allowed in a constant expression}}
     // since-cxx26-error@-3 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B(123).c == 3, "");
     // since-cxx26-error@-1 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B("").a == 1, "");
-    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   precxx26-note@-2 {{read of member 'a' of union with active member 'b' 
is not allowed in a constant expression}}
+    // cxx11-23-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   cxx11-23-note@-2 {{read of member 'a' of union with active member 'b' 
is not allowed in a constant expression}}
     // since-cxx26-error@-3 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B("").b == 2, "");
     // since-cxx26-error@-1 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
     static_assert(B("").c == 3, "");
-    // precxx26-error@-1 {{static assertion expression is not an integral 
constant expression}}
-    //   precxx26-note@-2 {{read of member 'c' of union with active member 'b' 
is not allowed in a constant expression}}
+    // cxx11-23-error@-1 {{static assertion expression is not an integral 
constant expression}}
+    //   cxx11-23-note@-2 {{read of member 'c' of union with active member 'b' 
is not allowed in a constant expression}}
     // since-cxx26-error@-3 {{attempt to use a deleted function}}
     //   since-cxx26-note@#cwg1460-Overriding-B {{destructor of union 'B' is 
implicitly deleted because its default constructor is not trivial}}
 

>From d7fed4233a3d3a63e826d4cce92037aaba189638 Mon Sep 17 00:00:00 2001
From: Peng Xie <[email protected]>
Date: Wed, 13 May 2026 10:54:46 +0800
Subject: [PATCH 5/6] Add test for ambiguous default ctor diagnostic in trivial
 union

---
 clang/test/SemaCXX/cxx26-trivial-union.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/clang/test/SemaCXX/cxx26-trivial-union.cpp 
b/clang/test/SemaCXX/cxx26-trivial-union.cpp
index 8265c4f4d7670..e2f3121479ad0 100644
--- a/clang/test/SemaCXX/cxx26-trivial-union.cpp
+++ b/clang/test/SemaCXX/cxx26-trivial-union.cpp
@@ -250,4 +250,14 @@ constexpr int constexpr_test() {
   return u.k;
 }
 static_assert(constexpr_test() == 42);
+
+// ===== Test 23: Ambiguous default ctor => dtor deleted =====
+// Multiple constructors with default arguments make default-initialization
+// ambiguous, so dtor is deleted per [class.dtor]p7 bullet 1.
+union U23 { // cxx26-note {{ambiguous}}
+  U23(int = 0);
+  U23(double = 0.0);
+  NonTrivialDtor nt;
+};
+U23 u23(42); // cxx26-error {{deleted}}
 #endif

>From 584a94d205b5429fbc3d42bca79b789c49c3d737 Mon Sep 17 00:00:00 2001
From: Peng Xie <[email protected]>
Date: Wed, 13 May 2026 16:46:54 +0800
Subject: [PATCH 6/6] Add ptrauth union triviality tests for P3074

---
 clang/test/SemaCXX/ptrauth-triviality.cpp | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/clang/test/SemaCXX/ptrauth-triviality.cpp 
b/clang/test/SemaCXX/ptrauth-triviality.cpp
index 900c5293bcd3b..712dab2a4e025 100644
--- a/clang/test/SemaCXX/ptrauth-triviality.cpp
+++ b/clang/test/SemaCXX/ptrauth-triviality.cpp
@@ -192,3 +192,20 @@ template <class... Types> static const bool 
inheritance_relocatability_matches_b
 static_assert(multiple_inheritance_is_relocatable<S4, S5> == 
multiple_inheritance_is_relocatable<S5, S4>);
 static_assert(inheritance_relocatability_matches_bases_v<S4, S5>);
 static_assert(inheritance_relocatability_matches_bases_v<S5, S4>);
+
+// P3074: ptrauth-qualified pointer members in unions.
+// These should be trivially constructible and destructible — they're just
+// unions of pointers regardless of signing scheme.
+union PtrauthCanBeIrksome {
+  int *p;
+  int *__ptrauth(1,0,1) ptrauth_forever;
+};
+static_assert(__is_trivially_constructible(PtrauthCanBeIrksome));
+static_assert(__is_trivially_destructible(PtrauthCanBeIrksome));
+
+union PtrauthCanBeIrksomeEspWithAddressDiscrimination {
+  int *p;
+  int *__ptrauth(1,1,1) ptrauth_forever;
+};
+static_assert(__is_trivially_constructible(PtrauthCanBeIrksomeEspWithAddressDiscrimination));
+static_assert(__is_trivially_destructible(PtrauthCanBeIrksomeEspWithAddressDiscrimination));

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

Reply via email to