Skip to content

[libc++][Clang] Added explanation why is_constructible evaluated to false. Updated the diagnostics checks in libc++ tests. #144220

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 8 commits into
base: main
Choose a base branch
from
Open
5 changes: 4 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1797,7 +1797,10 @@ def note_unsatisfied_trait_reason
"%DeletedAssign{has a deleted %select{copy|move}1 "
"assignment operator}|"
"%UnionWithUserDeclaredSMF{is a union with a user-declared "
"%sub{select_special_member_kind}1}"
"%sub{select_special_member_kind}1}|"
"%FunctionType{is a function type}|"
"%CVVoidType{is a cv void type}|"
"%IncompleteArrayType{is an incomplete array type}"
"}0">;

def warn_consteval_if_always_true : Warning<
Expand Down
72 changes: 70 additions & 2 deletions clang/lib/Sema/SemaTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
//===----------------------------------------------------------------------===//

#include "clang/AST/DeclCXX.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/TypeTraits.h"
Expand Down Expand Up @@ -1947,6 +1949,7 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
TypeTrait::UTT_IsCppTriviallyRelocatable)
.Case("is_replaceable", TypeTrait::UTT_IsReplaceable)
.Case("is_trivially_copyable", TypeTrait::UTT_IsTriviallyCopyable)
.Case("is_constructible", TypeTrait::TT_IsConstructible)
.Default(std::nullopt);
}

Expand Down Expand Up @@ -1983,8 +1986,16 @@ static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) {
Trait = StdNameToTypeTrait(Name);
if (!Trait)
return std::nullopt;
for (const auto &Arg : VD->getTemplateArgs().asArray())
Args.push_back(Arg.getAsType());
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)}};
}

Expand Down Expand Up @@ -2257,6 +2268,60 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
}
}

static void DiagnoseNonConstructibleReason(
Sema &SemaRef, SourceLocation Loc,
const llvm::SmallVector<clang::QualType, 1> &Ts) {
if (Ts.empty()) {
return;
}

bool ContainsVoid = false;
for (const QualType &ArgTy : Ts) {
ContainsVoid |= ArgTy->isVoidType();
}

if (ContainsVoid)
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::CVVoidType;

QualType T = Ts[0];
if (T->isFunctionType())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::FunctionType;

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;

llvm::BumpPtrAllocator OpaqueExprAllocator;
SmallVector<Expr *, 2> ArgExprs;
ArgExprs.reserve(Ts.size() - 1);
for (unsigned I = 1, N = Ts.size(); I != N; ++I) {
QualType ArgTy = Ts[I];
if (ArgTy->isObjectType() || ArgTy->isFunctionType())
ArgTy = SemaRef.Context.getRValueReferenceType(ArgTy);
ArgExprs.push_back(
new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
OpaqueValueExpr(Loc, ArgTy.getNonLValueExprType(SemaRef.Context),
Expr::getValueKindForType(ArgTy)));
}

EnterExpressionEvaluationContext Unevaluated(
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::ContextRAII TUContext(SemaRef,
SemaRef.Context.getTranslationUnitDecl());
InitializedEntity To(InitializedEntity::InitializeTemporary(T));
InitializationKind InitKind(InitializationKind::CreateDirect(Loc, Loc, Loc));
InitializationSequence Init(SemaRef, To, InitKind, ArgExprs);

Init.Diagnose(SemaRef, To, InitKind, ArgExprs);
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}

static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef,
SourceLocation Loc, QualType T) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
Expand Down Expand Up @@ -2296,6 +2361,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
case UTT_IsTriviallyCopyable:
DiagnoseNonTriviallyCopyableReason(*this, E->getBeginLoc(), Args[0]);
break;
case TT_IsConstructible:
DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
break;
default:
break;
}
Expand Down
3 changes: 2 additions & 1 deletion clang/test/CXX/drs/cwg18xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,11 +564,12 @@ struct A {
namespace ex2 {
#if __cplusplus >= 201103L
struct Bar {
struct Baz {
struct Baz { // #cwg1890-Baz
int a = 0;
};
static_assert(__is_constructible(Baz), "");
// since-cxx11-error@-1 {{static assertion failed due to requirement '__is_constructible(cwg1890::ex2::Bar::Baz)'}}
// since-cxx11-note@#cwg1890-Baz {{'Baz' defined here}}
};
#endif
} // namespace ex2
Expand Down
19 changes: 14 additions & 5 deletions clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,30 @@ struct ImplicitlyCopyable {
static_assert(__is_constructible(ImplicitlyCopyable, const ImplicitlyCopyable&));


struct Movable {
struct Movable { // #Movable
template <typename T>
requires __is_constructible(Movable, T) // #err-self-constraint-1
explicit Movable(T op) noexcept; // #1
Movable(Movable&&) noexcept = default; // #2
explicit Movable(T op) noexcept; // #Movable1
Movable(Movable&&) noexcept = default; // #Movable2
};
static_assert(__is_constructible(Movable, Movable&&));
static_assert(__is_constructible(Movable, const Movable&));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(Movable, const Movable &)'}}
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(Movable, const Movable &)'}} \
// expected-error@-1 {{call to implicitly-deleted copy constructor of 'Movable'}} \
// expected-note@#Movable {{'Movable' defined here}} \
// expected-note@#Movable {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const Movable' for 1st argument}} \
// expected-note@#Movable2 {{copy constructor is implicitly deleted because 'Movable' has a user-declared move constructor}} \
// expected-note@#Movable2 {{candidate constructor not viable: no known conversion from 'int' to 'Movable' for 1st argument}} \
// expected-note@#Movable1 {{candidate template ignored: constraints not satisfied [with T = int]}}


static_assert(__is_constructible(Movable, int));
// expected-error@-1{{static assertion failed due to requirement '__is_constructible(Movable, int)'}} \
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(Movable, int)'}} \
// expected-error@-1 {{no matching constructor for initialization of 'Movable'}} \
// expected-note@-1 2{{}}
// expected-error@#err-self-constraint-1{{satisfaction of constraint '__is_constructible(Movable, T)' depends on itself}}
// expected-note@#err-self-constraint-1 4{{}}
// expected-note@#Movable {{'Movable' defined here}}

template <typename T>
struct Members {
Expand Down
66 changes: 66 additions & 0 deletions clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ struct is_trivially_copyable {

template <typename T>
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);

template <typename... Args>
struct is_constructible {
static constexpr bool value = __is_constructible(Args...);
};

template <typename... Args>
constexpr bool is_constructible_v = __is_constructible(Args...);
#endif

#ifdef STD2
Expand All @@ -44,6 +52,17 @@ using is_trivially_copyable = __details_is_trivially_copyable<T>;

template <typename T>
constexpr bool is_trivially_copyable_v = __is_trivially_copyable(T);

template <typename... Args>
struct __details_is_constructible{
static constexpr bool value = __is_constructible(Args...);
};

template <typename... Args>
using is_constructible = __details_is_constructible<Args...>;

template <typename... Args>
constexpr bool is_constructible_v = __is_constructible(Args...);
#endif


Expand Down Expand Up @@ -73,6 +92,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... Args>
struct __details_is_constructible : bool_constant<__is_constructible(Args...)> {};

template <typename... Args>
using is_constructible = __details_is_constructible<Args...>;

template <typename... Args>
constexpr bool is_constructible_v = is_constructible<Args...>::value;
#endif

}
Expand Down Expand Up @@ -100,6 +128,15 @@ static_assert(std::is_trivially_copyable_v<int&>);
// expected-note@-1 {{because it is a reference type}}


static_assert(std::is_constructible<int, int>::value);

static_assert(std::is_constructible<void>::value);
// expected-error-re@-1 {{static assertion failed due to requirement 'std::{{.*}}is_constructible<void>::value'}} \
// expected-note@-1 {{because it is a cv void type}}
static_assert(std::is_constructible_v<void>);
// expected-error@-1 {{static assertion failed due to requirement 'std::is_constructible_v<void>'}} \
// expected-note@-1 {{because it is a cv void type}}

namespace test_namespace {
using namespace std;
static_assert(is_trivially_relocatable<int&>::value);
Expand All @@ -119,6 +156,13 @@ namespace test_namespace {
// expected-error@-1 {{static assertion failed due to requirement 'is_trivially_copyable_v<int &>'}} \
// expected-note@-1 {{'int &' is not trivially copyable}} \
// expected-note@-1 {{because it is a reference type}}

static_assert(is_constructible<void>::value);
// expected-error-re@-1 {{static assertion failed due to requirement '{{.*}}is_constructible<void>::value'}} \
// expected-note@-1 {{because it is a cv void type}}
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}}
}


Expand All @@ -139,6 +183,15 @@ concept C2 = std::is_trivially_copyable_v<T>; // #concept4

template <C2 T> void g2(); // #cand4

template <typename... Args>
requires std::is_constructible<Args...>::value void f3(); // #cand5

template <typename... Args>
concept C3 = std::is_constructible_v<Args...>; // #concept6

template <C3 T> void g3(); // #cand6


void test() {
f<int&>();
// expected-error@-1 {{no matching function for call to 'f'}} \
Expand Down Expand Up @@ -169,6 +222,19 @@ void test() {
// expected-note@#concept4 {{because 'std::is_trivially_copyable_v<int &>' evaluated to false}} \
// expected-note@#concept4 {{'int &' is not trivially copyable}} \
// expected-note@#concept4 {{because it is a reference type}}

f3<void>();
// expected-error@-1 {{no matching function for call to 'f3'}} \
// expected-note@#cand5 {{candidate template ignored: constraints not satisfied [with Args = <void>]}} \
// expected-note-re@#cand5 {{because '{{.*}}is_constructible<void>::value' evaluated to false}} \
// expected-note@#cand5 {{because it is a cv void type}}

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 'std::is_constructible_v<void>' evaluated to false}} \
// expected-note@#concept6 {{because it is a cv void type}}
}
}

Expand Down
62 changes: 62 additions & 0 deletions clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,65 @@ static_assert(__is_trivially_copyable(S12));
// expected-note@-1 {{'S12' is not trivially copyable}} \
// expected-note@#tc-S12 {{'S12' defined here}}
}

namespace constructible {

struct S1 { // #c-S1
S1(int); // #cc-S1
};
static_assert(__is_constructible(S1, char*));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(constructible::S1, char *)'}} \
// expected-error@-1 {{no matching constructor for initialization of 'S1'}} \
// expected-note@#c-S1 {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'char *' to 'const S1' for 1st argument}} \
// expected-note@#c-S1 {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'char *' to 'S1' for 1st argument}} \
// expected-note@#cc-S1 {{candidate constructor not viable: no known conversion from 'char *' to 'int' for 1st argument; dereference the argument with *}} \
// expected-note@#c-S1 {{'S1' defined here}}

struct S2 { // #c-S2
S2(int, float, double); // #cc-S2
};
static_assert(__is_constructible(S2, float));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(constructible::S2, float)'}} \
// expected-note@#c-S2 {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'float' to 'const S2' for 1st argument}} \
// expected-note@#c-S2 {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'float' to 'S2' for 1st argument}} \
// expected-error@-1 {{no matching constructor for initialization of 'S2'}} \
// expected-note@#cc-S2 {{candidate constructor not viable: requires 3 arguments, but 1 was provided}} \
// expected-note@#c-S2 {{'S2' defined here}}

static_assert(__is_constructible(S2, float, void));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(constructible::S2, float, void)'}} \
// expected-note@#c-S2 {{candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 2 were provided}} \
// expected-note@#c-S2 {{candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided}} \
// expected-note@-1{{because it is a cv void type}} \
// expected-error@-1 {{no matching constructor for initialization of 'S2'}} \
// expected-note@#cc-S2 {{candidate constructor not viable: requires 3 arguments, but 2 were provided}} \
// expected-note@#c-S2 {{'S2' defined here}}

static_assert(__is_constructible(int[]));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(int[])'}} \
// expected-note@-1 {{because it is an incomplete array type}}

static_assert(__is_constructible(void));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(void)'}} \
// expected-note@-1 {{because it is a cv void type}}

static_assert(__is_constructible(void, void));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(void, void)'}} \
// expected-note@-1 {{because it is a cv void type}}

static_assert(__is_constructible(const void));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(const void)'}} \
// expected-note@-1 {{because it is a cv void type}}

static_assert(__is_constructible(volatile void));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(volatile void)'}} \
// expected-note@-1 {{because it is a cv void type}}

static_assert(__is_constructible(int ()));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(int ())'}} \
// expected-note@-1 {{because it is a function type}}

static_assert(__is_constructible(void (int, float)));
// expected-error@-1 {{static assertion failed due to requirement '__is_constructible(void (int, float))'}} \
// expected-note@-1 {{because it is a function type}}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ void test() {
// expected-error-re@*:* {{static assertion failed {{.*}}The result of f(value()) must be a specialization of std::expected}}
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
// expected-error@*:* 0-1{{excess elements in struct initializer}}
}

// !std::is_same_v<U:error_type, E>
Expand All @@ -74,6 +75,7 @@ void test() {
// expected-error-re@*:* {{static assertion failed {{.*}}The result of f(value()) must be a specialization of std::expected}}
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
// expected-error@*:* 0-1{{excess elements in struct initializer}}
}

// !std::is_same_v<U:error_type, E>
Expand All @@ -94,6 +96,7 @@ void test() {
// expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(value())) must be a specialization of std::expected}}
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
// expected-error@*:* 0-1{{excess elements in struct initializer}}
}

// !std::is_same_v<U:error_type, E>
Expand All @@ -113,6 +116,7 @@ void test() {
// expected-error-re@*:* {{static assertion failed {{.*}}The result of f(std::move(value())) must be a specialization of std::expected}}
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
// expected-error@*:* 0-1{{excess elements in struct initializer}}
}

// !std::is_same_v<U:error_type, E>
Expand Down
Loading
Loading