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..ac1a000d66c2d 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -1963,6 +1963,7 @@ static std::optional 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); } @@ -2518,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()) { @@ -2592,6 +2593,111 @@ 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->hasDefaultConstructor() && !D->hasTrivialDefaultConstructor()) { + 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(); + } + 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(); + } + } + + // Check non-static data members + for (const FieldDecl *Field : D->fields()) { + 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(); + } + } + + // Check if it's a union with non-trivial constructor members + if (D->isUnion()) { + bool HasNonTrivialMember = false; + for (const FieldDecl *Field : D->fields()) { + const CXXRecordDecl *FieldDecl = Field->getType()->getAsCXXRecordDecl(); + if (FieldDecl && !FieldDecl->hasTrivialDefaultConstructor()) { + 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 +2727,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 + 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 + requires __is_trivially_default_constructible(T) + explicit NonTriviallyDefaultConstructibleWrapper(T op) = delete; // #1 +}; + +static_assert(!__is_trivially_default_constructible(NonTriviallyDefaultConstructibleWrapper)); + template 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 constexpr bool is_standard_layout_v = __is_standard_layout(T); +template +struct is_trivially_default_constructible { + static constexpr bool value = __is_trivially_default_constructible(T); +}; + +template +constexpr bool is_trivially_default_constructible_v = __is_trivially_default_constructible(T); + template struct is_constructible { static constexpr bool value = __is_constructible(Args...); @@ -106,6 +114,17 @@ using is_standard_layout = __details_is_standard_layout; template constexpr bool is_standard_layout_v = __is_standard_layout(T); +template +struct __details_is_trivially_default_constructible { + static constexpr bool value = __is_trivially_default_constructible(T); +}; + +template +using is_trivially_default_constructible = __details_is_trivially_default_constructible; + +template +constexpr bool is_trivially_default_constructible_v = __is_trivially_default_constructible(T); + template struct __details_is_constructible{ static constexpr bool value = __is_constructible(Args...); @@ -146,6 +165,15 @@ using is_trivially_copyable = __details_is_trivially_copyable; template constexpr bool is_trivially_copyable_v = is_trivially_copyable::value; +template +struct __details_is_trivially_default_constructible : bool_constant<__is_trivially_default_constructible(T)> {}; + +template +using is_trivially_default_constructible = __details_is_trivially_default_constructible; + +template +constexpr bool is_trivially_default_constructible_v = is_trivially_default_constructible::value; + template struct __details_is_assignable : bool_constant<__is_assignable(T, U)> {}; @@ -300,6 +328,15 @@ namespace test_namespace { static_assert(is_constructible_v); // expected-error@-1 {{static assertion failed due to requirement 'is_constructible_v'}} \ // expected-note@-1 {{because it is a cv void type}} + + static_assert(is_trivially_default_constructible::value); + // expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_trivially_default_constructible::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); + // expected-error@-1 {{static assertion failed due to requirement 'is_trivially_default_constructible_v'}} \ + // 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; // #concept4 template void g2(); // #cand4 +template +requires std::is_trivially_default_constructible::value void f5(); // #cand5 + +template +concept C5 = std::is_trivially_default_constructible_v; // #concept6 + +template void g5(); // #cand6 + template requires std::is_assignable::value void f4(); // #cand7 @@ -390,9 +435,24 @@ void test() { g3(); // 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' evaluated to false}} \ // expected-note@#concept6 {{because it is a cv void type}} + + f5(); + // 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::value' evaluated to false}} \ + // expected-note@#cand5 {{'void' is not trivially default constructible}} \ + // expected-note@#cand5 {{because it is a cv void type}} + + g5(); + // 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' 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