-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[Clang] Added explanation why is_trivially default_constructible
is false
#152888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[Clang] Added explanation why is_trivially default_constructible
is false
#152888
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-clang Author: Abhijeet (abhijeetsharma200) ChangesExpands on #141911 Full diff: https://github.com/llvm/llvm-project/pull/152888.diff 5 Files Affected:
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
|
Expands on #141911