Skip to content

[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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 "
Expand Down
111 changes: 110 additions & 1 deletion clang/lib/Sema/SemaTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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;
Expand Down
45 changes: 45 additions & 0 deletions clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
62 changes: 61 additions & 1 deletion clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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...);
Expand Down Expand Up @@ -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...);
Expand Down Expand Up @@ -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)> {};

Expand Down Expand Up @@ -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}}
}


Expand All @@ -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

Expand Down Expand Up @@ -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}}
}
}

Expand Down
Loading