https://github.com/abhijeetsharma200 updated https://github.com/llvm/llvm-project/pull/152888
>From 48d71f870d21a45c0a197d1853788a4aec80018b Mon Sep 17 00:00:00 2001 From: Abhijeet Sharma <abhijeetsharma2...@gmail.com> Date: Sun, 10 Aug 2025 04:49:15 +0200 Subject: [PATCH 1/2] Added explain is trivially default constructible --- .../clang/Basic/DiagnosticSemaKinds.td | 5 +- clang/lib/Sema/SemaTypeTraits.cpp | 106 ++++++++++++++++ ...overload-resolution-deferred-templates.cpp | 45 +++++++ .../type-traits-unsatisfied-diags-std.cpp | 62 ++++++++- .../SemaCXX/type-traits-unsatisfied-diags.cpp | 119 ++++++++++++++++++ 5 files changed, 335 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 116341f4b66d5..37cfcb706030a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1772,7 +1772,8 @@ def note_unsatisfied_trait "%Replaceable{replaceable}|" "%TriviallyCopyable{trivially copyable}|" "%Empty{empty}|" - "%StandardLayout{standard-layout}" + "%StandardLayout{standard-layout}|" + "%TriviallyDefaultConstructible{trivially default constructible}" "}1">; def note_unsatisfied_trait_reason @@ -1807,6 +1808,8 @@ def note_unsatisfied_trait_reason "constructor}|" "%DeletedCtr{has a deleted %select{copy|move}1 " "constructor}|" + "%NTDCBase{has a non-trivially-default-constructible base %1}|" + "%NTDCField{has a non-trivially-default-constructible member %1 of type %2}|" "%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 9b9dd172003a0..79f6b5ff3e380 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -1963,6 +1963,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) { .Case("is_assignable", TypeTrait::BTT_IsAssignable) .Case("is_empty", TypeTrait::UTT_IsEmpty) .Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout) + .Case("is_trivially_default_constructible", TypeTrait::UTT_HasTrivialDefaultConstructor) .Case("is_constructible", TypeTrait::TT_IsConstructible) .Default(std::nullopt); } @@ -2592,6 +2593,108 @@ static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc, SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D; } +static void DiagnoseNonTriviallyDefaultConstructibleReason(Sema &SemaRef, + SourceLocation Loc, + const CXXRecordDecl *D) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait) + << D << diag::TraitName::TriviallyDefaultConstructible; + + // Check if the class has a user-provided constructor + if (D->hasUserDeclaredConstructor()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::UserProvidedCtr << /*Copy*/ 0 + << D->getLocation(); + } + + // Check if the class has a deleted constructor + if (D->hasDeletedDefaultConstructor()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::DeletedCtr << /*Copy*/ 0 + << D->getLocation(); + } + + // Check for virtual functions and virtual base classes + if (D->isPolymorphic()) { + // Find a virtual function to point to + for (const CXXMethodDecl *Method : D->methods()) { + if (Method->isVirtual()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::VirtualFunction << Method + << Method->getSourceRange(); + break; + } + } + } + + // Check base classes + for (const CXXBaseSpecifier &Base : D->bases()) { + if (Base.isVirtual()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::VBase << Base.getType() + << Base.getSourceRange(); + } + if (!Base.getType()->isTriviallyDefaultConstructibleType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::NTDCBase << Base.getType() + << Base.getSourceRange(); + } + } + + // Check non-static data members + for (const FieldDecl *Field : D->fields()) { + if (!Field->getType()->isTriviallyDefaultConstructibleType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::NTDCField << Field + << Field->getType() << Field->getSourceRange(); + } + } + + // Check if it's a union with non-trivial constructor members + if (D->isUnion()) { + bool HasNonTrivialMember = false; + for (const FieldDecl *Field : D->fields()) { + if (!Field->getType()->isTriviallyDefaultConstructibleType()) { + HasNonTrivialMember = true; + break; + } + } + if (HasNonTrivialMember) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::UnionWithUserDeclaredSMF << /*Constructor*/ 0 + << D->getSourceRange(); + } + } + + SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D; +} + +static void DiagnoseNonTriviallyDefaultConstructibleReason(Sema &SemaRef, + SourceLocation Loc, + QualType T) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait) + << T << diag::TraitName::TriviallyDefaultConstructible; + + // Check type-level exclusion first. + if (T->isVariablyModifiedType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::VLA; + return; + } + + if (T->isReferenceType()) { + SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) + << diag::TraitNotSatisfiedReason::Ref; + return; + } + T = T.getNonReferenceType(); + const CXXRecordDecl *D = T->getAsCXXRecordDecl(); + if (!D || D->isInvalidDecl()) + return; + + if (D->hasDefinition()) + DiagnoseNonTriviallyDefaultConstructibleReason(SemaRef, Loc, D); +} + void Sema::DiagnoseTypeTraitDetails(const Expr *E) { E = E->IgnoreParenImpCasts(); if (E->containsErrors()) @@ -2621,6 +2724,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) { case UTT_IsStandardLayout: DiagnoseNonStandardLayoutReason(*this, E->getBeginLoc(), Args[0]); break; + case UTT_HasTrivialDefaultConstructor: + DiagnoseNonTriviallyDefaultConstructibleReason(*this, E->getBeginLoc(), Args[0]); + break; case TT_IsConstructible: DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args); break; diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp index 135865c8450f5..c70deca4e7abc 100644 --- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp +++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp @@ -105,6 +105,51 @@ static_assert(__is_constructible(Movable, int)); // expected-note@#err-self-constraint-1 4{{}} // expected-note@#Movable {{'Movable' defined here}} +// Test trivially default constructible type traits +struct TriviallyDefaultConstructible { + // Trivial default constructor +}; + +struct NonTriviallyDefaultConstructible { + NonTriviallyDefaultConstructible() {} // User-provided constructor +}; + +struct VirtualBase { + virtual ~VirtualBase() {} // Virtual destructor +}; + +struct UnionWithNonTrivial { + UnionWithNonTrivial() {} // User-provided constructor +}; + +union UnionType { + int i; + UnionWithNonTrivial u; // Non-trivial member +}; + +static_assert(__is_trivially_default_constructible(TriviallyDefaultConstructible)); +static_assert(!__is_trivially_default_constructible(NonTriviallyDefaultConstructible)); +static_assert(!__is_trivially_default_constructible(VirtualBase)); +static_assert(!__is_trivially_default_constructible(UnionType)); + +// Test in template constraints +struct TriviallyDefaultConstructibleWrapper { + template <typename T> + requires __is_trivially_default_constructible(T) + explicit TriviallyDefaultConstructibleWrapper(T op) noexcept; // #1 + TriviallyDefaultConstructibleWrapper() noexcept = default; // #2 +}; + +static_assert(__is_trivially_default_constructible(TriviallyDefaultConstructibleWrapper)); + +struct NonTriviallyDefaultConstructibleWrapper { + template <typename T> + requires __is_trivially_default_constructible(T) + explicit NonTriviallyDefaultConstructibleWrapper(T op) = delete; // #1 +}; + +static_assert(!__is_trivially_default_constructible(NonTriviallyDefaultConstructibleWrapper)); + template <typename T> struct Members { constexpr auto f(auto) { diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp index f3ddbbfe15bdc..aeb54f631c1aa 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp @@ -43,6 +43,14 @@ static constexpr bool value = __is_standard_layout(T); template <typename T> constexpr bool is_standard_layout_v = __is_standard_layout(T); +template <typename T> +struct is_trivially_default_constructible { + static constexpr bool value = __is_trivially_default_constructible(T); +}; + +template <typename T> +constexpr bool is_trivially_default_constructible_v = __is_trivially_default_constructible(T); + template <typename... Args> struct is_constructible { static constexpr bool value = __is_constructible(Args...); @@ -106,6 +114,17 @@ using is_standard_layout = __details_is_standard_layout<T>; template <typename T> constexpr bool is_standard_layout_v = __is_standard_layout(T); +template <typename T> +struct __details_is_trivially_default_constructible { + static constexpr bool value = __is_trivially_default_constructible(T); +}; + +template <typename T> +using is_trivially_default_constructible = __details_is_trivially_default_constructible<T>; + +template <typename T> +constexpr bool is_trivially_default_constructible_v = __is_trivially_default_constructible(T); + template <typename... Args> struct __details_is_constructible{ static constexpr bool value = __is_constructible(Args...); @@ -146,6 +165,15 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>; template <typename T> constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value; +template <typename T> +struct __details_is_trivially_default_constructible : bool_constant<__is_trivially_default_constructible(T)> {}; + +template <typename T> +using is_trivially_default_constructible = __details_is_trivially_default_constructible<T>; + +template <typename T> +constexpr bool is_trivially_default_constructible_v = is_trivially_default_constructible<T>::value; + template <typename T, typename U> struct __details_is_assignable : bool_constant<__is_assignable(T, U)> {}; @@ -300,6 +328,15 @@ namespace test_namespace { static_assert(is_constructible_v<void>); // expected-error@-1 {{static assertion failed due to requirement 'is_constructible_v<void>'}} \ // expected-note@-1 {{because it is a cv void type}} + + static_assert(is_trivially_default_constructible<int&>::value); + // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_trivially_default_constructible<int &>::value'}} \ + // expected-note@-1 {{'int &' is not trivially default constructible}} \ + // expected-note@-1 {{because it is a reference type}} + static_assert(is_trivially_default_constructible_v<int&>); + // expected-error@-1 {{static assertion failed due to requirement 'is_trivially_default_constructible_v<int &>'}} \ + // expected-note@-1 {{'int &' is not trivially default constructible}} \ + // expected-note@-1 {{because it is a reference type}} } @@ -320,6 +357,14 @@ concept C2 = std::is_trivially_copyable_v<T>; // #concept4 template <C2 T> void g2(); // #cand4 +template <typename T> +requires std::is_trivially_default_constructible<T>::value void f5(); // #cand5 + +template <typename T> +concept C5 = std::is_trivially_default_constructible_v<T>; // #concept6 + +template <C5 T> void g5(); // #cand6 + template <typename T, typename U> requires std::is_assignable<T, U>::value void f4(); // #cand7 @@ -390,9 +435,24 @@ void test() { g3<void>(); // expected-error@-1 {{no matching function for call to 'g3'}} \ // expected-note@#cand6 {{candidate template ignored: constraints not satisfied [with T = void]}} \ - // expected-note@#cand6 {{because 'void' does not satisfy 'C3'}} \ + // expected-note@#concept6 {{because 'void' does not satisfy 'C3'}} \ // expected-note@#concept6 {{because 'std::is_constructible_v<void>' evaluated to false}} \ // expected-note@#concept6 {{because it is a cv void type}} + + f5<void>(); + // expected-error@-1 {{no matching function for call to 'f5'}} \ + // expected-note@#cand5 {{candidate template ignored: constraints not satisfied [with T = void]}} \ + // expected-note-re@#cand5 {{because '{{.*}}is_trivially_default_constructible<void>::value' evaluated to false}} \ + // expected-note@#cand5 {{'void' is not trivially default constructible}} \ + // expected-note@#cand5 {{because it is a cv void type}} + + g5<void>(); + // expected-error@-1 {{no matching function for call to 'g5'}} \ + // expected-note@#cand6 {{candidate template ignored: constraints not satisfied [with T = void]}} \ + // expected-note@#cand6 {{because 'void' does not satisfy 'C5'}} \ + // expected-note@#concept6 {{because 'std::is_trivially_default_constructible_v<void>' evaluated to false}} \ + // expected-note@#cand6 {{'void' is not trivially default constructible}} \ + // expected-note@#cand6 {{because it is a cv void type}} } } diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp index 54806a93ddf80..0822948abffc5 100644 --- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp +++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp @@ -489,6 +489,125 @@ static_assert(__is_trivially_copyable(S12)); // expected-note@#tc-S12 {{'S12' defined here}} } +namespace trivially_default_constructible { + +struct TrivialBase { + // Trivial default constructor +}; + +struct NonTrivialBase { + NonTrivialBase() {} // User-provided constructor +}; + +struct VirtualBase { + virtual ~VirtualBase() {} // Virtual destructor +}; + +struct UnionWithNonTrivial { + UnionWithNonTrivial() {} // User-provided constructor +}; + +union UnionType { + int i; + UnionWithNonTrivial u; // Non-trivial member +}; + +struct S1 { // #tdc-S1 + S1() {} // User-provided constructor +}; +static_assert(__is_trivially_default_constructible(S1)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(trivially_default_constructible::S1)'}} \ +// expected-note@-1 {{'S1' is not trivially default constructible}} \ +// expected-note@-1 {{because it has a user-provided constructor}} \ +// expected-note@#tdc-S1 {{'S1' defined here}} + +struct S2 { // #tdc-S2 + S2() = delete; // Deleted constructor +}; +static_assert(__is_trivially_default_constructible(S2)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(trivially_default_constructible::S2)'}} \ +// expected-note@-1 {{'S2' is not trivially default constructible}} \ +// expected-note@-1 {{because it has a deleted constructor}} \ +// expected-note@#tdc-S2 {{'S2' defined here}} + +struct S3 : NonTrivialBase { // #tdc-S3 + // Inherits non-trivial base +}; +static_assert(__is_trivially_default_constructible(S3)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(trivially_default_constructible::S3)'}} \ +// expected-note@-1 {{'S3' is not trivially default constructible}} \ +// expected-note@-1 {{because it has a non-trivially-default-constructible base 'NonTrivialBase'}} \ +// expected-note@#tdc-S3 {{'S3' defined here}} + +struct S4 { // #tdc-S4 + NonTrivialBase member; // Non-trivial member +}; +static_assert(__is_trivially_default_constructible(S4)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(trivially_default_constructible::S4)'}} \ +// expected-note@-1 {{'S4' is not trivially default constructible}} \ +// expected-note@-1 {{because it has a non-trivially-default-constructible member 'member' of type 'NonTrivialBase'}} \ +// expected-note@#tdc-S4 {{'S4' defined here}} + +struct S5 : VirtualBase { // #tdc-S5 + // Has virtual base class +}; +static_assert(__is_trivially_default_constructible(S5)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(trivially_default_constructible::S5)'}} \ +// expected-note@-1 {{'S5' is not trivially default constructible}} \ +// expected-note@-1 {{because it has a virtual base 'VirtualBase'}} \ +// expected-note@#tdc-S5 {{'S5' defined here}} + +struct S6 { // #tdc-S6 + UnionType member; // Union with non-trivial member +}; +static_assert(__is_trivially_default_constructible(S6)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(trivially_default_constructible::S6)'}} \ +// expected-note@-1 {{'S6' is not trivially default constructible}} \ +// expected-note@-1 {{because it has a non-trivially-default-constructible member 'member' of type 'UnionType'}} \ +// expected-note@#tdc-S6 {{'S6' defined here}} + +struct S7 { // #tdc-S7 + virtual void f() {} // Virtual function +}; +static_assert(__is_trivially_default_constructible(S7)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(trivially_default_constructible::S7)'}} \ +// expected-note@-1 {{'S7' is not trivially default constructible}} \ +// expected-note@-1 {{because it has a virtual function 'f'}} \ +// expected-note@#tdc-S7 {{'S7' defined here}} + +// Test reference types +static_assert(__is_trivially_default_constructible(int&)); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(int &)'}} \ +// expected-note@-1 {{'int &' is not trivially default constructible}} \ +// expected-note@-1 {{because it is a reference type}} + +// Test variably modified types +extern int vla_size; +static_assert(__is_trivially_default_constructible(int[vla_size])); +// expected-error@-1 {{static assertion failed due to requirement '__is_trivially_default_constructible(int[vla_size])'}} \ +// expected-note@-1 {{'int[vla_size]' is not trivially default constructible}} \ +// expected-note@-1 {{because it is a variably-modified type}} + +// Test incomplete types +struct S8; // expected-note {{forward declaration of 'trivially_default_constructible::S8'}} +static_assert(__is_trivially_default_constructible(S8)); +// expected-error@-1 {{incomplete type 'S8' used in type trait expression}} + +// Test valid cases +static_assert(__is_trivially_default_constructible(int)); +static_assert(__is_trivially_default_constructible(TrivialBase)); +static_assert(__is_trivially_default_constructible(int[5])); +static_assert(__is_trivially_default_constructible(int*)); + +struct ValidClass { + int x; + // No user-provided constructor, no virtual functions, no virtual bases + // All members are trivially default constructible +}; +static_assert(__is_trivially_default_constructible(ValidClass)); + +} + namespace constructible { struct S1 { // #c-S1 >From b6dbd1dca8ff809375b54a05be7efec794b75386 Mon Sep 17 00:00:00 2001 From: Abhijeet Sharma <abhijeetsharma2...@gmail.com> Date: Sun, 10 Aug 2025 06:45:32 +0200 Subject: [PATCH 2/2] Fixed constructor method --- clang/lib/Sema/SemaTypeTraits.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 79f6b5ff3e380..ac1a000d66c2d 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -2519,7 +2519,7 @@ static void DiagnoseNonStandardLayoutReason(Sema &SemaRef, SourceLocation Loc, << diag::TraitNotSatisfiedReason::MultipleDataBase; } if (D->isPolymorphic()) { - // Find the best location to point “defined here” at. + // Find the best location to point "defined here" at. const CXXMethodDecl *VirtualMD = nullptr; // First, look for a virtual method. for (const auto *M : D->methods()) { @@ -2607,7 +2607,7 @@ static void DiagnoseNonTriviallyDefaultConstructibleReason(Sema &SemaRef, } // Check if the class has a deleted constructor - if (D->hasDeletedDefaultConstructor()) { + if (D->hasDefaultConstructor() && !D->hasTrivialDefaultConstructor()) { SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) << diag::TraitNotSatisfiedReason::DeletedCtr << /*Copy*/ 0 << D->getLocation(); @@ -2633,7 +2633,8 @@ static void DiagnoseNonTriviallyDefaultConstructibleReason(Sema &SemaRef, << diag::TraitNotSatisfiedReason::VBase << Base.getType() << Base.getSourceRange(); } - if (!Base.getType()->isTriviallyDefaultConstructibleType()) { + const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); + if (BaseDecl && !BaseDecl->hasTrivialDefaultConstructor()) { SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) << diag::TraitNotSatisfiedReason::NTDCBase << Base.getType() << Base.getSourceRange(); @@ -2642,7 +2643,8 @@ static void DiagnoseNonTriviallyDefaultConstructibleReason(Sema &SemaRef, // Check non-static data members for (const FieldDecl *Field : D->fields()) { - if (!Field->getType()->isTriviallyDefaultConstructibleType()) { + const CXXRecordDecl *FieldDecl = Field->getType()->getAsCXXRecordDecl(); + if (FieldDecl && !FieldDecl->hasTrivialDefaultConstructor()) { SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) << diag::TraitNotSatisfiedReason::NTDCField << Field << Field->getType() << Field->getSourceRange(); @@ -2653,7 +2655,8 @@ static void DiagnoseNonTriviallyDefaultConstructibleReason(Sema &SemaRef, if (D->isUnion()) { bool HasNonTrivialMember = false; for (const FieldDecl *Field : D->fields()) { - if (!Field->getType()->isTriviallyDefaultConstructibleType()) { + const CXXRecordDecl *FieldDecl = Field->getType()->getAsCXXRecordDecl(); + if (FieldDecl && !FieldDecl->hasTrivialDefaultConstructor()) { HasNonTrivialMember = true; break; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits