[clang] [Clang] Added explanation why is_trivially_copy_constructible evaluated to false (PR #225487)
Darshan N via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 22 11:57:22 PDT 2026
https://github.com/pesdarshan created https://github.com/llvm/llvm-project/pull/225487
Part of https://github.com/llvm/llvm-project/issues/141911
When std::is_trivially_copy_constructible<T> fails, Clang now explains why (user-provided copy ctor, deleted, inaccessible, non-trivial base/member, etc.), same as it already does for is_trivially_copyable.
There's no dedicated builtin for this trait it's implemented as __is_trivially_constructible(T, const T&), so I had to detect that shape specifically rather than relying on an existing name mapping.
>From f4f5367f1f3c305a9588f1516ccf5d900e8234ad Mon Sep 17 00:00:00 2001
From: Darshan N <Darshan.N3 at ibm.com>
Date: Wed, 16 Sep 2026 11:31:47 +0530
Subject: [PATCH] Added explain is trivially copy constructible
---
.../clang/Basic/DiagnosticSemaKinds.td | 5 +
clang/lib/Sema/SemaTypeTraits.cpp | 149 +++++++++++++++---
.../type-traits-unsatisfied-diags-std.cpp | 79 ++++++++++
.../SemaCXX/type-traits-unsatisfied-diags.cpp | 138 ++++++++++++++++
4 files changed, 352 insertions(+), 19 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fca68f292f6671..94d6a4cf9c5024 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1845,6 +1845,7 @@ def note_unsatisfied_trait
: Note<"%0 is not %enum_select<TraitName>{"
"%TriviallyRelocatable{trivially relocatable}|"
"%TriviallyCopyable{trivially copyable}|"
+ "%TriviallyCopyConstructible{trivially copy constructible}|"
"%Empty{empty}|"
"%StandardLayout{standard-layout}|"
"%Aggregate{aggregate}|"
@@ -1867,6 +1868,8 @@ def note_unsatisfied_trait_reason
"%NTRField{has a non-trivially-relocatable member %1 of type %2}|"
"%NTCBase{has a non-trivially-copyable base %1}|"
"%NTCField{has a non-trivially-copyable member %1 of type %2}|"
+ "%NTCCBase{has a non-trivially-copy-constructible base %1}|"
+ "%NTCCField{has a non-trivially-copy-constructible member %1 of type %2}|"
"%NonEmptyMember{has a non-static data member %1 of type %2}|"
"%PolymorphicType{is a polymorphic type}|"
"%VirtualFunction{has a virtual function %1}|"
@@ -1885,6 +1888,8 @@ def note_unsatisfied_trait_reason
"%InheritedCtr{has an inherited constructor}|"
"%DeletedCtr{has a deleted %select{copy|move}1 "
"constructor}|"
+ "%InaccessibleCtr{has an inaccessible %select{copy|move}1 "
+ "constructor}|"
"%UserProvidedAssign{has a user provided %select{copy|move}1 "
"assignment operator}|"
"%DeletedAssign{has a deleted %select{copy|move}1 "
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index cb30ff9fd02da9..2804c6fd1483f0 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -2016,6 +2016,22 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
using ExtractedTypeTraitInfo =
std::optional<std::pair<TypeTrait, llvm::SmallVector<QualType, 1>>>;
+// Synthesize args for std::is_trivially_copy_constructible<T>, which wraps
+// __is_trivially_constructible but is not in the StdName table because it
+// fixes its own argument list.
+static ExtractedTypeTraitInfo
+SynthesizeTriviallyConstructibleArgs(ASTContext &Ctx, StringRef Name,
+ ArrayRef<QualType> TemplateArgs) {
+ if (TemplateArgs.size() != 1)
+ return std::nullopt;
+ if (Name != "is_trivially_copy_constructible")
+ return std::nullopt;
+ // std::is_trivially_copy_constructible<T> == __is_trivially_constructible(T, const T&)
+ QualType T = TemplateArgs[0];
+ QualType ConstTRef = Ctx.getLValueReferenceType(T.withConst());
+ return {{TT_IsTriviallyConstructible, {T, ConstTRef}}};
+}
+
// Recognize type traits that are builting type traits, or known standard
// type traits in <type_traits>. Note that at this point we assume the
// trait evaluated to false, so we need only to recognize the shape of the
@@ -2024,6 +2040,21 @@ static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) {
llvm::SmallVector<QualType, 1> Args;
std::optional<TypeTrait> Trait;
+ // Collect type arguments from a template argument list into Args.
+ // Packs are flattened; non-type arguments are silently skipped (they can
+ // appear in library templates whose argument lists we don't control).
+ auto CollectTypeArgs = [&Args](auto Range) {
+ for (const auto &Arg : Range) {
+ if (Arg.getKind() == TemplateArgument::ArgKind::Pack) {
+ for (const auto &Inner : Arg.pack_elements())
+ if (Inner.getKind() == TemplateArgument::ArgKind::Type)
+ Args.push_back(Inner.getAsType());
+ } else if (Arg.getKind() == TemplateArgument::ArgKind::Type) {
+ Args.push_back(Arg.getAsType());
+ }
+ }
+ };
+
// builtins
if (const auto *TraitExpr = dyn_cast<TypeTraitExpr>(E)) {
Trait = TraitExpr->getTrait();
@@ -2043,20 +2074,14 @@ static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) {
StringRef Name = VD->getIdentifier()->getName();
if (!Name.consume_back("_v"))
return std::nullopt;
+ // First try the direct StdName mapping.
Trait = StdNameToTypeTrait(Name);
- if (!Trait)
- return std::nullopt;
- for (const auto &Arg : VD->getTemplateArgs().asArray()) {
- if (Arg.getKind() == TemplateArgument::ArgKind::Pack) {
- for (const auto &InnerArg : Arg.pack_elements())
- Args.push_back(InnerArg.getAsType());
- } else if (Arg.getKind() == TemplateArgument::ArgKind::Type) {
- Args.push_back(Arg.getAsType());
- } else {
- llvm_unreachable("Unexpected kind");
- }
- }
- return {{Trait.value(), std::move(Args)}};
+ CollectTypeArgs(VD->getTemplateArgs().asArray());
+ if (Trait)
+ return {{Trait.value(), std::move(Args)}};
+ // Fall back to traits that synthesize their own arg list.
+ return SynthesizeTriviallyConstructibleArgs(
+ VD->getASTContext(), Name, Args);
}
// std::is_xxx<>::value
@@ -2071,12 +2096,15 @@ static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) {
const TemplateDecl *D = Ts->getTemplateName().getAsTemplateDecl();
if (!D || !D->isInStdNamespace())
return std::nullopt;
- Trait = StdNameToTypeTrait(D->getIdentifier()->getName());
- if (!Trait)
- return std::nullopt;
- for (const auto &Arg : Ts->template_arguments())
- Args.push_back(Arg.getAsType());
- return {{Trait.value(), std::move(Args)}};
+ StringRef Name = D->getIdentifier()->getName();
+ // First try the direct StdName mapping.
+ Trait = StdNameToTypeTrait(Name);
+ CollectTypeArgs(Ts->template_arguments());
+ if (Trait)
+ return {{Trait.value(), std::move(Args)}};
+ // Fall back to traits that synthesize their own arg list.
+ return SynthesizeTriviallyConstructibleArgs(
+ D->getASTContext(), Name, Args);
}
return std::nullopt;
}
@@ -2312,6 +2340,78 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}
+static void DiagnoseNonTriviallyCopyConstructibleReason(Sema &SemaRef,
+ SourceLocation Loc,
+ const CXXRecordDecl *D) {
+ for (const CXXBaseSpecifier &B : D->bases()) {
+ if (B.isVirtual())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::VBase << B.getType()
+ << B.getSourceRange();
+ const CXXRecordDecl *BD = B.getType()->getAsCXXRecordDecl();
+ if (BD && BD->hasNonTrivialCopyConstructor())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NTCCBase << B.getType()
+ << B.getSourceRange();
+ }
+
+ for (const FieldDecl *F : D->fields()) {
+ // Use getBaseElementType to see through array types (e.g. NTBase arr[2]).
+ // Print the declared field type so the note matches the source.
+ QualType ElemTy = SemaRef.Context.getBaseElementType(F->getType());
+ const CXXRecordDecl *FD = ElemTy->getAsCXXRecordDecl();
+ if (FD && FD->hasNonTrivialCopyConstructor())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NTCCField << F << F->getType()
+ << F->getSourceRange();
+ }
+
+ if (D->isPolymorphic())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::PolymorphicType;
+
+ for (const CXXConstructorDecl *Ctor : D->ctors()) {
+ if (!Ctor->isCopyConstructor())
+ continue;
+ if (Ctor->isDeleted())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::DeletedCtr << /*copy*/ 0
+ << Ctor->getSourceRange();
+ else if (Ctor->isUserProvided())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::UserProvidedCtr << /*copy*/ 0
+ << Ctor->getSourceRange();
+ else if (Ctor->getAccess() != AS_public)
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::InaccessibleCtr << /*copy*/ 0
+ << Ctor->getSourceRange();
+ }
+}
+
+static void DiagnoseNonTriviallyCopyConstructibleReason(Sema &SemaRef,
+ SourceLocation Loc,
+ QualType T) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
+ << T << diag::TraitName::TriviallyCopyConstructible;
+
+ if (T->isVoidType())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::CVVoidType;
+ else if (T->isFunctionType())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::FunctionType;
+ else if (T->isIncompleteArrayType())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::IncompleteArrayType;
+
+ const CXXRecordDecl *D = T->getAsCXXRecordDecl();
+ if (!D || D->isInvalidDecl() || !D->hasDefinition())
+ return;
+
+ DiagnoseNonTriviallyCopyConstructibleReason(SemaRef, Loc, D);
+ SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
+}
+
static void DiagnoseNonAssignableReason(Sema &SemaRef, SourceLocation Loc,
QualType T, QualType U) {
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
@@ -2794,6 +2894,17 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case UTT_IsAbstract:
DiagnoseNonAbstractReason(*this, E->getBeginLoc(), Args[0]);
break;
+ case TT_IsTriviallyConstructible:
+ if (Args.size() == 2) {
+ // Check that the second arg is `cv T &` (copy-construction shape).
+ // Args[1] is the source type as it appears in __is_trivially_constructible.
+ if (const auto *LVRef = Args[1]->getAs<LValueReferenceType>()) {
+ if (Context.hasSameUnqualifiedType(Args[0], LVRef->getPointeeType()))
+ DiagnoseNonTriviallyCopyConstructibleReason(*this, E->getBeginLoc(),
+ Args[0]);
+ }
+ }
+ break;
default:
break;
}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
index 8a9f3bc400d206..dc052adfe3b892 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -73,6 +73,14 @@ struct is_abstract {
template <typename T>
constexpr bool is_abstract_v = __is_abstract(T);
+template <typename T>
+struct is_trivially_copy_constructible {
+ static constexpr bool value = __is_trivially_constructible(T, const T&);
+};
+template <typename T>
+constexpr bool is_trivially_copy_constructible_v =
+ __is_trivially_constructible(T, const T&);
+
#endif
#ifdef STD2
@@ -167,6 +175,16 @@ using is_abstract = __details_is_abstract<T>;
template <typename T>
constexpr bool is_abstract_v = __is_abstract(T);
+template <typename T>
+struct __details_is_trivially_copy_constructible {
+ static constexpr bool value = __is_trivially_constructible(T, const T&);
+};
+template <typename T>
+using is_trivially_copy_constructible = __details_is_trivially_copy_constructible<T>;
+template <typename T>
+constexpr bool is_trivially_copy_constructible_v =
+ __is_trivially_constructible(T, const T&);
+
#endif
@@ -252,6 +270,16 @@ using is_abstract = __details_is_abstract<T>;
template <typename T>
constexpr bool is_abstract_v = is_abstract<T>::value;
+template <typename T>
+struct __details_is_trivially_copy_constructible
+ : bool_constant<__is_trivially_constructible(T, const T&)> {};
+template <typename T>
+using is_trivially_copy_constructible =
+ __details_is_trivially_copy_constructible<T>;
+template <typename T>
+constexpr bool is_trivially_copy_constructible_v =
+ is_trivially_copy_constructible<T>::value;
+
#endif
}
@@ -277,6 +305,32 @@ static_assert(std::is_trivially_copyable_v<int&>);
// expected-note at -1 {{'int &' is not trivially copyable}} \
// expected-note at -1 {{because it is a reference type}}
+struct UserProvidedCopyCtor { // #tcc-std-UserProvided
+ UserProvidedCopyCtor(const UserProvidedCopyCtor&) {}
+};
+
+// std::is_trivially_copy_constructible<T>::value path
+static_assert(std::is_trivially_copy_constructible<int>::value);
+
+static_assert(std::is_trivially_copy_constructible<UserProvidedCopyCtor>::value);
+// expected-error-re at -1 {{static assertion failed due to requirement 'std::{{.*}}is_trivially_copy_constructible<{{.*}}UserProvidedCopyCtor>::value'}} \
+// expected-note at -1 {{'UserProvidedCopyCtor' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a user provided copy constructor}} \
+// expected-note@#tcc-std-UserProvided {{'UserProvidedCopyCtor' defined here}}
+
+// std::is_trivially_copy_constructible_v<T> path
+static_assert(std::is_trivially_copy_constructible_v<int>);
+
+static_assert(std::is_trivially_copy_constructible_v<UserProvidedCopyCtor>);
+// expected-error at -1 {{static assertion failed due to requirement 'std::is_trivially_copy_constructible_v<UserProvidedCopyCtor>'}} \
+// expected-note at -1 {{'UserProvidedCopyCtor' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a user provided copy constructor}} \
+// expected-note@#tcc-std-UserProvided {{'UserProvidedCopyCtor' defined here}}
+
+
+// Positive: references are trivially copy constructible (must produce no diagnostic)
+static_assert(std::is_trivially_copy_constructible<int&>::value);
+static_assert(std::is_trivially_copy_constructible_v<int&>);
// Direct tests
static_assert(std::is_standard_layout<int>::value);
@@ -493,6 +547,14 @@ concept C2 = std::is_trivially_copyable_v<T>; // #concept4
template <C2 T> void g2(); // #cand4
+template <typename T>
+requires std::is_trivially_copy_constructible<T>::value void f6(); // #cand11
+
+template <typename T>
+concept C6 = std::is_trivially_copy_constructible_v<T>; // #concept11
+
+template <C6 T> void g6(); // #cand12
+
template <typename T, typename U>
requires std::is_assignable<T, U>::value void f4(); // #cand7
@@ -549,6 +611,23 @@ void test() {
// expected-note@#concept4 {{'int &' is not trivially copyable}} \
// expected-note@#concept4 {{because it is a reference type}}
+ f6<UserProvidedCopyCtor>();
+ // expected-error at -1 {{no matching function for call to 'f6'}} \
+ // expected-note@#cand11 {{candidate template ignored: constraints not satisfied [with T = UserProvidedCopyCtor]}} \
+ // expected-note-re@#cand11 {{because '{{.*}}is_trivially_copy_constructible<{{.*}}UserProvidedCopyCtor>::value' evaluated to false}} \
+ // expected-note@#cand11 {{'UserProvidedCopyCtor' is not trivially copy constructible}} \
+ // expected-note@#cand11 {{because it has a user provided copy constructor}} \
+ // expected-note@#tcc-std-UserProvided {{'UserProvidedCopyCtor' defined here}}
+
+ g6<UserProvidedCopyCtor>();
+ // expected-error at -1 {{no matching function for call to 'g6'}} \
+ // expected-note@#cand12 {{candidate template ignored: constraints not satisfied [with T = UserProvidedCopyCtor]}} \
+ // expected-note@#cand12 {{because 'UserProvidedCopyCtor' does not satisfy 'C6'}} \
+ // expected-note@#concept11 {{because 'std::is_trivially_copy_constructible_v<UserProvidedCopyCtor>' evaluated to false}} \
+ // expected-note@#concept11 {{'UserProvidedCopyCtor' is not trivially copy constructible}} \
+ // expected-note@#concept11 {{because it has a user provided copy constructor}} \
+ // expected-note@#tcc-std-UserProvided {{'UserProvidedCopyCtor' defined here}}
+
f4<int&, void>();
// expected-error at -1 {{no matching function for call to 'f4'}} \
// expected-note@#cand7 {{candidate template ignored: constraints not satisfied [with T = int &, U = void]}} \
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index 3bde5b06a3aba4..c0271291ada770 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -892,3 +892,141 @@ static_assert(__is_abstract(U));
// expected-note at -1 {{because it is not a struct or class type}}
}
+
+namespace trivially_copy_constructible {
+
+// Case 1: user-provided copy constructor
+struct UserProvided { // #tcc-UserProvided
+ UserProvided(const UserProvided&) {}
+};
+static_assert(__is_trivially_constructible(UserProvided, const UserProvided&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::UserProvided, const trivially_copy_constructible::UserProvided &)'}} \
+// expected-note at -1 {{'UserProvided' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a user provided copy constructor}} \
+// expected-note@#tcc-UserProvided {{'UserProvided' defined here}}
+
+// Case 2: deleted copy constructor
+struct DeletedCopy { // #tcc-DeletedCopy
+ DeletedCopy(const DeletedCopy&) = delete;
+};
+static_assert(__is_trivially_constructible(DeletedCopy, const DeletedCopy&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::DeletedCopy, const trivially_copy_constructible::DeletedCopy &)'}} \
+// expected-note at -1 {{'DeletedCopy' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a deleted copy constructor}} \
+// expected-note@#tcc-DeletedCopy {{'DeletedCopy' defined here}}
+
+// Case 3: polymorphic type (virtual function introduces non-trivial copy)
+struct Polymorphic { // #tcc-Polymorphic
+ virtual void f();
+};
+static_assert(__is_trivially_constructible(Polymorphic, const Polymorphic&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::Polymorphic, const trivially_copy_constructible::Polymorphic &)'}} \
+// expected-note at -1 {{'Polymorphic' is not trivially copy constructible}} \
+// expected-note at -1 {{because it is a polymorphic type}} \
+// expected-note@#tcc-Polymorphic {{'Polymorphic' defined here}}
+
+// Case 4: virtual base
+struct VBase {}; // #tcc-VBase
+struct WithVBase : virtual VBase { // #tcc-WithVBase
+};
+static_assert(__is_trivially_constructible(WithVBase, const WithVBase&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::WithVBase, const trivially_copy_constructible::WithVBase &)'}} \
+// expected-note at -1 {{'WithVBase' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a virtual base 'VBase'}} \
+// expected-note@#tcc-WithVBase {{'WithVBase' defined here}}
+
+// Case 5: base with non-trivial copy constructor
+struct NTBase { // #tcc-NTBase
+ NTBase(const NTBase&) {}
+};
+struct WithNTBase : NTBase { // #tcc-WithNTBase
+};
+static_assert(__is_trivially_constructible(WithNTBase, const WithNTBase&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::WithNTBase, const trivially_copy_constructible::WithNTBase &)'}} \
+// expected-note at -1 {{'WithNTBase' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a non-trivially-copy-constructible base 'NTBase'}} \
+// expected-note@#tcc-WithNTBase {{'WithNTBase' defined here}}
+
+// Case 6: member with non-trivial copy constructor
+struct WithNTMember { // #tcc-WithNTMember
+ NTBase m;
+};
+static_assert(__is_trivially_constructible(WithNTMember, const WithNTMember&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::WithNTMember, const trivially_copy_constructible::WithNTMember &)'}} \
+// expected-note at -1 {{'WithNTMember' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a non-trivially-copy-constructible member 'm' of type 'NTBase'}} \
+// expected-note@#tcc-WithNTMember {{'WithNTMember' defined here}}
+
+// Case 7: array member with non-trivial copy constructor
+struct WithArrayMember { // #tcc-WithArrayMember
+ NTBase arr[2];
+};
+static_assert(__is_trivially_constructible(WithArrayMember, const WithArrayMember&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::WithArrayMember, const trivially_copy_constructible::WithArrayMember &)'}} \
+// expected-note at -1 {{'WithArrayMember' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a non-trivially-copy-constructible member 'arr' of type 'NTBase[2]'}} \
+// expected-note@#tcc-WithArrayMember {{'WithArrayMember' defined here}}
+
+// Case 8: inaccessible (private) copy constructor
+struct PrivateCopy { // #tcc-PrivateCopy
+private:
+ PrivateCopy(const PrivateCopy&) = default;
+};
+static_assert(__is_trivially_constructible(PrivateCopy, const PrivateCopy&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::PrivateCopy, const trivially_copy_constructible::PrivateCopy &)'}} \
+// expected-note at -1 {{'PrivateCopy' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has an inaccessible copy constructor}} \
+// expected-note@#tcc-PrivateCopy {{'PrivateCopy' defined here}}
+
+// Case 9: non-const lvalue reference shape — also recognised as copy construction
+struct NonConstRef { // #tcc-NonConstRef
+ NonConstRef(NonConstRef&) {}
+};
+static_assert(__is_trivially_constructible(NonConstRef, NonConstRef&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::NonConstRef, trivially_copy_constructible::NonConstRef &)'}} \
+// expected-note at -1 {{'NonConstRef' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a user provided copy constructor}} \
+// expected-note@#tcc-NonConstRef {{'NonConstRef' defined here}}
+
+// Case 10: multiple reasons at once
+struct Multi : virtual VBase, NTBase { // #tcc-Multi
+ NTBase m;
+};
+static_assert(__is_trivially_constructible(Multi, const Multi&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::Multi, const trivially_copy_constructible::Multi &)'}} \
+// expected-note at -1 {{'Multi' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a virtual base 'VBase'}} \
+// expected-note at -1 {{because it has a non-trivially-copy-constructible base 'NTBase'}} \
+// expected-note at -1 {{because it has a non-trivially-copy-constructible member 'm' of type 'NTBase'}} \
+// expected-note@#tcc-Multi {{'Multi' defined here}}
+
+// Case 11: presence of a non-copy constructor must not confuse the walk
+struct MultiCtor { // #tcc-MultiCtor
+ MultiCtor(int) {}
+ MultiCtor(const MultiCtor&) = delete;
+};
+static_assert(__is_trivially_constructible(MultiCtor, const MultiCtor&));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::MultiCtor, const trivially_copy_constructible::MultiCtor &)'}} \
+// expected-note at -1 {{'MultiCtor' is not trivially copy constructible}} \
+// expected-note at -1 {{because it has a deleted copy constructor}} \
+// expected-note@#tcc-MultiCtor {{'MultiCtor' defined here}}
+
+// Positive cases — must produce no diagnostic
+struct Trivial { int x; };
+static_assert(__is_trivially_constructible(Trivial, const Trivial&));
+static_assert(__is_trivially_constructible(int, const int&));
+// References are trivially copy constructible
+static_assert(__is_trivially_constructible(int&, int&));
+
+// Misclassification guard: 2-arg non-copy shape must NOT trigger copy diagnostic
+struct HasIntCtor { HasIntCtor(int) {} };
+static_assert(__is_trivially_constructible(HasIntCtor, int));
+// expected-error at -1 {{static assertion failed due to requirement '__is_trivially_constructible(trivially_copy_constructible::HasIntCtor, int)'}}
+
+// Dependent context — must not crash
+template <typename T>
+void dependent() {
+ static_assert(__is_trivially_constructible(T, const T&));
+}
+
+}
More information about the cfe-commits
mailing list