[clang] 0804ca8 - [Clang] Explain why a type trait evaluated to false. (#141238)
via cfe-commits
cfe-commits at lists.llvm.org
Sat May 24 01:36:22 PDT 2025
Author: cor3ntin
Date: 2025-05-24T10:36:18+02:00
New Revision: 0804ca88abbfc5951be1e3f2ccb4fe7d46c3a410
URL: https://github.com/llvm/llvm-project/commit/0804ca88abbfc5951be1e3f2ccb4fe7d46c3a410
DIFF: https://github.com/llvm/llvm-project/commit/0804ca88abbfc5951be1e3f2ccb4fe7d46c3a410.diff
LOG: [Clang] Explain why a type trait evaluated to false. (#141238)
`static_assert(std::is_xx_v<MyType>);` is a common pattern to check that
a type meets a requirement.
This patch produces diagnostics notes when such assertion fails. The
first type trait for which we provide detailed explanation is
std::is_trivially_relocatable.
We employ the same mechanisn when a type trait appears an an unsatisfied
atomic constraint.
I plan to also support `std::is_trivially_replaceable` in a follow up
PR, and hopefully, over time we can support more type traits.
Added:
clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
Modified:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaTypeTraits.cpp
clang/test/SemaObjCXX/objc-weak-type-traits.mm
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 2835e3a9d9960..f2fcfe5d351f4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1762,6 +1762,29 @@ def err_user_defined_msg_constexpr : Error<
"%sub{subst_user_defined_msg}0 must be produced by a "
"constant expression">;
+// Type traits explanations
+def note_unsatisfied_trait : Note<"%0 is not %enum_select<TraitName>{"
+ "%TriviallyRelocatable{trivially relocatable}"
+ "}1">;
+
+def note_unsatisfied_trait_reason
+ : Note<"because it "
+ "%enum_select<TraitNotSatisfiedReason>{"
+ "%Ref{is a reference type}|"
+ "%HasArcLifetime{has an ARC lifetime qualifier}|"
+ "%VLA{is a variably-modified type}|"
+ "%VBase{has a virtual base %1}|"
+ "%NRBase{has a non-trivially-relocatable base %1}|"
+ "%NRField{has a non-trivially-relocatable member %1 of type %2}|"
+ "%DeletedDtr{has a %select{deleted|user-provided}1 destructor}|"
+ "%UserProvidedCtr{has a user provided %select{copy|move}1 "
+ "constructor}|"
+ "%UserProvidedAssign{has a user provided %select{copy|move}1 "
+ "assignment operator}|"
+ "%UnionWithUserDeclaredSMF{is a union with a user-declared "
+ "%sub{select_special_member_kind}1}"
+ "}0">;
+
def warn_consteval_if_always_true : Warning<
"consteval if is always true in an %select{unevaluated|immediate}0 context">,
InGroup<DiagGroup<"redundant-consteval-if">>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1091a7f504b57..bbc5c181c6a10 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5910,6 +5910,11 @@ class Sema final : public SemaBase {
/// with expression \E
void DiagnoseStaticAssertDetails(const Expr *E);
+ /// If E represents a built-in type trait, or a known standard type trait,
+ /// try to print more information about why the type type-trait failed.
+ /// This assumes we already evaluated the expression to a false boolean value.
+ void DiagnoseTypeTraitDetails(const Expr *E);
+
/// Handle a friend type declaration. This works in tandem with
/// ActOnTag.
///
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 7da8e696c90bd..c6a54dc141ded 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1320,6 +1320,7 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
S.Diag(SubstExpr->getSourceRange().getBegin(),
diag::note_atomic_constraint_evaluated_to_false)
<< (int)First << SubstExpr;
+ S.DiagnoseTypeTraitDetails(SubstExpr);
}
template <typename SubstitutionDiagnostic>
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index ead53a995dff1..770ac9839eb98 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17694,6 +17694,8 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
<< DiagSide[0].ValueString << Op->getOpcodeStr()
<< DiagSide[1].ValueString << Op->getSourceRange();
}
+ } else {
+ DiagnoseTypeTraitDetails(E);
}
}
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index bac71c07d9907..71d1d44e93a84 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1917,3 +1917,188 @@ ExprResult Sema::BuildExpressionTrait(ExpressionTrait ET, SourceLocation KWLoc,
return new (Context)
ExpressionTraitExpr(KWLoc, ET, Queried, Value, RParen, Context.BoolTy);
}
+
+static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
+ return llvm::StringSwitch<std::optional<TypeTrait>>(Name)
+ .Case("is_trivially_relocatable",
+ TypeTrait::UTT_IsCppTriviallyRelocatable)
+ .Default(std::nullopt);
+}
+
+using ExtractedTypeTraitInfo =
+ std::optional<std::pair<TypeTrait, llvm::SmallVector<QualType, 1>>>;
+
+// Recognize type traits that are builting type traits, or known standard
+// type traits in <type_traits>. Note that at this point we assume the
+// trait evaluated to false, so we need only to recognize the shape of the
+// outer-most symbol.
+static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) {
+ llvm::SmallVector<QualType, 1> Args;
+ std::optional<TypeTrait> Trait;
+
+ // builtins
+ if (const auto *TraitExpr = dyn_cast<TypeTraitExpr>(E)) {
+ Trait = TraitExpr->getTrait();
+ for (const auto *Arg : TraitExpr->getArgs())
+ Args.push_back(Arg->getType());
+ return {{Trait.value(), std::move(Args)}};
+ }
+ const auto *Ref = dyn_cast<DeclRefExpr>(E);
+ if (!Ref)
+ return std::nullopt;
+
+ // std::is_xxx_v<>
+ if (const auto *VD =
+ dyn_cast<VarTemplateSpecializationDecl>(Ref->getDecl())) {
+ if (!VD->isInStdNamespace())
+ return std::nullopt;
+ StringRef Name = VD->getIdentifier()->getName();
+ if (!Name.consume_back("_v"))
+ return std::nullopt;
+ Trait = StdNameToTypeTrait(Name);
+ if (!Trait)
+ return std::nullopt;
+ for (const auto &Arg : VD->getTemplateArgs().asArray())
+ Args.push_back(Arg.getAsType());
+ return {{Trait.value(), std::move(Args)}};
+ }
+
+ // std::is_xxx<>::value
+ if (const auto *VD = dyn_cast<VarDecl>(Ref->getDecl());
+ Ref->hasQualifier() && VD && VD->getIdentifier()->isStr("value")) {
+ const Type *T = Ref->getQualifier()->getAsType();
+ if (!T)
+ return std::nullopt;
+ const TemplateSpecializationType *Ts =
+ T->getAs<TemplateSpecializationType>();
+ if (!Ts)
+ return std::nullopt;
+ const TemplateDecl *D = Ts->getTemplateName().getAsTemplateDecl();
+ if (!D || !D->isInStdNamespace())
+ return std::nullopt;
+ Trait = StdNameToTypeTrait(D->getIdentifier()->getName());
+ if (!Trait)
+ return std::nullopt;
+ for (const auto &Arg : Ts->template_arguments())
+ Args.push_back(Arg.getAsType());
+ return {{Trait.value(), std::move(Args)}};
+ }
+ return std::nullopt;
+}
+
+static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
+ SourceLocation Loc,
+ const CXXRecordDecl *D) {
+ for (const CXXBaseSpecifier &B : D->bases()) {
+ const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
+ assert(BaseDecl && "invalid base?");
+ if (B.isVirtual())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::VBase << B.getType()
+ << B.getSourceRange();
+ if (!SemaRef.IsCXXTriviallyRelocatableType(B.getType()))
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NRBase << B.getType()
+ << B.getSourceRange();
+ }
+ for (const FieldDecl *Field : D->fields()) {
+ if (!Field->getType()->isReferenceType() &&
+ !SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::NRField << Field << Field->getType()
+ << Field->getSourceRange();
+ }
+ if (D->hasDeletedDestructor())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::DeletedDtr << /*Deleted*/ 0
+ << D->getDestructor()->getSourceRange();
+
+ if (D->hasAttr<TriviallyRelocatableAttr>())
+ return;
+
+ if (D->isUnion()) {
+ auto DiagSPM = [&](CXXSpecialMemberKind K, bool Has) {
+ if (Has)
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::UnionWithUserDeclaredSMF << K;
+ };
+ DiagSPM(CXXSpecialMemberKind::CopyConstructor,
+ D->hasUserDeclaredCopyConstructor());
+ DiagSPM(CXXSpecialMemberKind::CopyAssignment,
+ D->hasUserDeclaredCopyAssignment());
+ DiagSPM(CXXSpecialMemberKind::MoveConstructor,
+ D->hasUserDeclaredMoveConstructor());
+ DiagSPM(CXXSpecialMemberKind::MoveAssignment,
+ D->hasUserDeclaredMoveAssignment());
+ return;
+ }
+
+ if (!D->hasSimpleMoveConstructor() && !D->hasSimpleCopyConstructor()) {
+ const auto *Decl = cast<CXXConstructorDecl>(
+ LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false));
+ if (Decl && Decl->isUserProvided())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::UserProvidedCtr
+ << Decl->isMoveConstructor() << Decl->getSourceRange();
+ }
+ if (!D->hasSimpleMoveAssignment() && !D->hasSimpleCopyAssignment()) {
+ CXXMethodDecl *Decl =
+ LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
+ if (Decl && Decl->isUserProvided())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::UserProvidedAssign
+ << Decl->isMoveAssignmentOperator() << Decl->getSourceRange();
+ }
+ CXXDestructorDecl *Dtr = D->getDestructor();
+ if (Dtr && Dtr->isUserProvided() && !Dtr->isDefaulted())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::DeletedDtr << /*User Provided*/ 1
+ << Dtr->getSourceRange();
+}
+
+static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
+ SourceLocation Loc,
+ QualType T) {
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
+ << T << diag::TraitName::TriviallyRelocatable;
+ if (T->isVariablyModifiedType())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::VLA;
+
+ if (T->isReferenceType())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::Ref;
+ T = T.getNonReferenceType();
+
+ if (T.hasNonTrivialObjCLifetime())
+ SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
+ << diag::TraitNotSatisfiedReason::HasArcLifetime;
+
+ const CXXRecordDecl *D = T->getAsCXXRecordDecl();
+ if (!D || D->isInvalidDecl())
+ return;
+
+ if (D->hasDefinition())
+ DiagnoseNonTriviallyRelocatableReason(SemaRef, Loc, D);
+
+ SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
+}
+
+void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
+ E = E->IgnoreParenImpCasts();
+ if (E->containsErrors())
+ return;
+
+ ExtractedTypeTraitInfo TraitInfo = ExtractTypeTraitFromExpression(E);
+ if (!TraitInfo)
+ return;
+
+ const auto &[Trait, Args] = TraitInfo.value();
+ switch (Trait) {
+ case UTT_IsCppTriviallyRelocatable:
+ DiagnoseNonTriviallyRelocatableReason(*this, E->getBeginLoc(), Args[0]);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
new file mode 100644
index 0000000000000..90cff1e66000c
--- /dev/null
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -0,0 +1,101 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -DSTD1 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -DSTD2 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -DSTD3 %s
+
+namespace std {
+
+#ifdef STD1
+template <typename T>
+struct is_trivially_relocatable {
+ static constexpr bool value = __builtin_is_cpp_trivially_relocatable(T);
+};
+
+template <typename T>
+constexpr bool is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T);
+#endif
+
+#ifdef STD2
+template <typename T>
+struct __details_is_trivially_relocatable {
+ static constexpr bool value = __builtin_is_cpp_trivially_relocatable(T);
+};
+
+template <typename T>
+using is_trivially_relocatable = __details_is_trivially_relocatable<T>;
+
+template <typename T>
+constexpr bool is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T);
+#endif
+
+
+#ifdef STD3
+template< class T, T v >
+struct integral_constant {
+ static constexpr T value = v;
+};
+
+template< bool B >
+using bool_constant = integral_constant<bool, B>;
+
+template <typename T>
+struct __details_is_trivially_relocatable : bool_constant<__builtin_is_cpp_trivially_relocatable(T)> {};
+
+template <typename T>
+using is_trivially_relocatable = __details_is_trivially_relocatable<T>;
+
+template <typename T>
+constexpr bool is_trivially_relocatable_v = is_trivially_relocatable<T>::value;
+#endif
+
+}
+
+static_assert(std::is_trivially_relocatable<int>::value);
+
+static_assert(std::is_trivially_relocatable<int&>::value);
+// expected-error-re at -1 {{static assertion failed due to requirement 'std::{{.*}}is_trivially_relocatable<int &>::value'}} \
+// expected-note at -1 {{'int &' is not trivially relocatable}} \
+// expected-note at -1 {{because it is a reference type}}
+static_assert(std::is_trivially_relocatable_v<int&>);
+// expected-error at -1 {{static assertion failed due to requirement 'std::is_trivially_relocatable_v<int &>'}} \
+// expected-note at -1 {{'int &' is not trivially relocatable}} \
+// expected-note at -1 {{because it is a reference type}}
+
+namespace test_namespace {
+ using namespace std;
+ static_assert(is_trivially_relocatable<int&>::value);
+ // expected-error-re at -1 {{static assertion failed due to requirement '{{.*}}is_trivially_relocatable<int &>::value'}} \
+ // expected-note at -1 {{'int &' is not trivially relocatable}} \
+ // expected-note at -1 {{because it is a reference type}}
+ static_assert(is_trivially_relocatable_v<int&>);
+ // expected-error at -1 {{static assertion failed due to requirement 'is_trivially_relocatable_v<int &>'}} \
+ // expected-note at -1 {{'int &' is not trivially relocatable}} \
+ // expected-note at -1 {{because it is a reference type}}
+}
+
+
+namespace concepts {
+template <typename T>
+requires std::is_trivially_relocatable<T>::value void f(); // #cand1
+
+template <typename T>
+concept C = std::is_trivially_relocatable_v<T>; // #concept2
+
+template <C T> void g(); // #cand2
+
+void test() {
+ f<int&>();
+ // expected-error at -1 {{no matching function for call to 'f'}} \
+ // expected-note@#cand1 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
+ // expected-note-re@#cand1 {{because '{{.*}}is_trivially_relocatable<int &>::value' evaluated to false}} \
+ // expected-note@#cand1 {{'int &' is not trivially relocatable}} \
+ // expected-note@#cand1 {{because it is a reference type}}
+
+ g<int&>();
+ // expected-error at -1 {{no matching function for call to 'g'}} \
+ // expected-note@#cand2 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
+ // expected-note@#cand2 {{because 'int &' does not satisfy 'C'}} \
+ // expected-note@#concept2 {{because 'std::is_trivially_relocatable_v<int &>' evaluated to false}} \
+ // expected-note@#concept2 {{'int &' is not trivially relocatable}} \
+ // expected-note@#concept2 {{because it is a reference type}}
+}
+}
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
new file mode 100644
index 0000000000000..d9cab20f4febd
--- /dev/null
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -0,0 +1,146 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-vla-cxx-extension -Wno-c++26-extensions -std=c++20 %s
+
+struct S : A {}; // expected-error{{expected class name}}
+
+static_assert(__builtin_is_cpp_trivially_relocatable()); // expected-error {{expected a type}}
+static_assert(__builtin_is_cpp_trivially_relocatable(0)); // expected-error {{expected a type}}
+static_assert(__builtin_is_cpp_trivially_relocatable(S));
+static_assert(__builtin_is_cpp_trivially_relocatable(A)); // expected-error{{unknown type name 'A'}}
+
+static_assert(__builtin_is_cpp_trivially_relocatable(int, int)); // expected-error {{type trait requires 1 argument; have 2 arguments}}
+
+static_assert(__builtin_is_cpp_trivially_relocatable(int&));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(int &)'}} \
+// expected-note at -1 {{'int &' is not trivially relocatable}} \
+// expected-note at -1 {{because it is a reference type}}
+
+
+static_assert(!__builtin_is_cpp_trivially_relocatable(int&));
+static_assert(!!__builtin_is_cpp_trivially_relocatable(int&));
+// expected-error at -1{{static assertion failed due to requirement '!!__builtin_is_cpp_trivially_relocatable(int &)'}}
+static_assert(bool(__builtin_is_cpp_trivially_relocatable(int&)));
+// expected-error at -1{{static assertion failed due to requirement 'bool(__builtin_is_cpp_trivially_relocatable(int &))'}}
+
+static_assert(__builtin_is_cpp_trivially_relocatable(int&) && __builtin_is_cpp_trivially_relocatable(int&));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(int &)'}} \
+// expected-note at -1 {{'int &' is not trivially relocatable}} \
+// expected-note at -1 {{because it is a reference type}}
+
+namespace concepts {
+template <typename T>
+requires __builtin_is_cpp_trivially_relocatable(T) void f(); // #cand1
+
+template <typename T>
+concept C = __builtin_is_cpp_trivially_relocatable(T); // #concept2
+
+template <C T> void g(); // #cand2
+
+void test() {
+ f<int&>();
+ // expected-error at -1 {{no matching function for call to 'f'}} \
+ // expected-note@#cand1 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
+ // expected-note@#cand1 {{because '__builtin_is_cpp_trivially_relocatable(int &)' evaluated to false}} \
+ // expected-note@#cand1 {{'int &' is not trivially relocatable}} \
+ // expected-note@#cand1 {{because it is a reference type}}
+
+ g<int&>();
+ // expected-error at -1 {{no matching function for call to 'g'}} \
+ // expected-note@#cand2 {{candidate template ignored: constraints not satisfied [with T = int &]}} \
+ // expected-note@#cand2 {{because 'int &' does not satisfy 'C'}} \
+ // expected-note@#concept2 {{because '__builtin_is_cpp_trivially_relocatable(int &)' evaluated to false}} \
+ // expected-note@#concept2 {{'int &' is not trivially relocatable}} \
+ // expected-note@#concept2 {{because it is a reference type}}
+}
+}
+
+namespace trivially_relocatable {
+
+extern int vla_size;
+static_assert(__builtin_is_cpp_trivially_relocatable(int[vla_size]));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(int[vla_size])'}} \
+// expected-note at -1 {{'int[vla_size]' is not trivially relocatable}} \
+// expected-note at -1 {{because it is a variably-modified type}}
+
+struct S; // expected-note {{forward declaration of 'trivially_relocatable::S'}}
+static_assert(__builtin_is_cpp_trivially_relocatable(S));
+// expected-error at -1 {{incomplete type 'S' used in type trait expression}}
+
+struct B {
+ virtual ~B();
+};
+struct S : virtual B { // #tr-S
+ S();
+ int & a;
+ const int ci;
+ B & b;
+ B c;
+ ~S();
+};
+static_assert(__builtin_is_cpp_trivially_relocatable(S));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(trivially_relocatable::S)'}} \
+// expected-note at -1 {{'S' is not trivially relocatable}} \
+// expected-note at -1 {{because it has a virtual base 'B'}} \
+// expected-note at -1 {{because it has a non-trivially-relocatable base 'B'}} \
+// expected-note at -1 {{because it has a non-trivially-relocatable member 'c' of type 'B'}} \
+// expected-note at -1 {{because it has a user-provided destructor}}
+// expected-note@#tr-S {{'S' defined here}}
+
+struct S2 { // #tr-S2
+ S2(S2&&);
+ S2& operator=(const S2&);
+};
+static_assert(__builtin_is_cpp_trivially_relocatable(S2));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(trivially_relocatable::S2)'}} \
+// expected-note at -1 {{'S2' is not trivially relocatable}} \
+// expected-note at -1 {{because it has a user provided move constructor}} \
+// expected-note at -1 {{because it has a user provided copy assignment operator}} \
+// expected-note@#tr-S2 {{'S2' defined here}}
+
+
+struct S3 { // #tr-S3
+ ~S3() = delete;
+};
+static_assert(__builtin_is_cpp_trivially_relocatable(S3));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(trivially_relocatable::S3)'}} \
+// expected-note at -1 {{'S3' is not trivially relocatable}} \
+// expected-note at -1 {{because it has a deleted destructor}} \
+// expected-note@#tr-S3 {{'S3' defined here}}
+
+
+union U { // #tr-U
+ U(const U&);
+ U(U&&);
+ U& operator=(const U&);
+ U& operator=(U&&);
+};
+static_assert(__builtin_is_cpp_trivially_relocatable(U));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(trivially_relocatable::U)'}} \
+// expected-note at -1 {{'U' is not trivially relocatable}} \
+// expected-note at -1 {{because it is a union with a user-declared copy constructor}} \
+// expected-note at -1 {{because it is a union with a user-declared copy assignment operator}} \
+// expected-note at -1 {{because it is a union with a user-declared move constructor}} \
+// expected-note at -1 {{because it is a union with a user-declared move assignment operator}}
+// expected-note@#tr-U {{'U' defined here}}
+struct S4 trivially_relocatable_if_eligible { // #tr-S4
+ ~S4();
+ B b;
+};
+static_assert(__builtin_is_cpp_trivially_relocatable(S4));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(trivially_relocatable::S4)'}} \
+// expected-note at -1 {{'S4' is not trivially relocatable}} \
+// expected-note at -1 {{because it has a non-trivially-relocatable member 'b' of type 'B'}} \
+// expected-note@#tr-S4 {{'S4' defined here}}
+
+union U2 trivially_relocatable_if_eligible { // #tr-U2
+ U2(const U2&);
+ U2(U2&&);
+ B b;
+};
+static_assert(__builtin_is_cpp_trivially_relocatable(U2));
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(trivially_relocatable::U2)'}} \
+// expected-note at -1 {{'U2' is not trivially relocatable}} \
+// expected-note at -1 {{because it has a deleted destructor}} \
+// expected-note at -1 {{because it has a non-trivially-relocatable member 'b' of type 'B'}} \
+// expected-note@#tr-U2 {{'U2' defined here}}
+
+}
diff --git a/clang/test/SemaObjCXX/objc-weak-type-traits.mm b/clang/test/SemaObjCXX/objc-weak-type-traits.mm
index e8f3e637ac272..91683c904d35d 100644
--- a/clang/test/SemaObjCXX/objc-weak-type-traits.mm
+++ b/clang/test/SemaObjCXX/objc-weak-type-traits.mm
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -fobjc-weak -fobjc-runtime-has-weak -verify -std=c++11 %s -Wno-deprecated-builtins
-// expected-no-diagnostics
// Check the results of the various type-trait query functions on
// lifetime-qualified types in ObjC Weak.
@@ -217,3 +216,9 @@
TRAIT_IS_TRUE(__is_trivially_relocatable, HasStrong);
TRAIT_IS_FALSE(__is_trivially_relocatable, HasWeak);
TRAIT_IS_TRUE(__is_trivially_relocatable, HasUnsafeUnretained);
+
+
+static_assert(__builtin_is_cpp_trivially_relocatable(__weak id), "");
+//expected-error at -1 {{static assertion failed due to requirement '__builtin_is_cpp_trivially_relocatable(__weak id)'}}\
+//expected-note at -1 {{'__weak id' is not trivially relocatable}}\
+//expected-note at -1 {{because it has an ARC lifetime qualifier}}
More information about the cfe-commits
mailing list