Skip to content

[Clang] Add detailed notes explaining why is_constructible evaluates to false (Revert 16d5db7) #151935

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 4 commits into
base: main
Choose a base branch
from

Conversation

cor3ntin
Copy link
Contributor

@cor3ntin cor3ntin commented Aug 4, 2025

Adds explanation why is_constructible evaluates to false.

This reapplies as-is e476f96.
This was reverted in 16d5db7 because of a test failure in libc++.

The test failure in libc++ is interesting in that, in the absence of nested diagnostics a bunch of diagnostics are emitted as error instead of notes, which we cannot silence with -verify-ignore-unexpected.

The fix here is to prevent the diagnostics to be emitted in the first place.
However this is clearly not ideal and we should make sure to deploy a better solution in the clang 22 time frame, in the lines of https://discourse.llvm.org/t/rfc-add-a-new-text-diagnostics-format-that-supports-nested-diagnostics/87641/12

Fixes #150601

…alse. Updated the diagnostics checks in libc++ tests. (llvm#144220)

Added explanation why a is constructible evaluated to false. Also fixed
problem with ExtractTypeTraitFromExpression. In case std::is_xxx_v<>
with variadic pack it tries to get template argument, but fails in
expression Arg.getAsType() due to Arg.getKind() ==
TemplateArgument::ArgKind::Pack, but not
TemplateArgument::ArgKind::Type.
Reverts llvm#144127
Fixies
llvm#143309 (comment)
@cor3ntin cor3ntin requested review from Endilll and a team as code owners August 4, 2025 10:01
@cor3ntin cor3ntin requested review from snarang181 and erichkeane and removed request for a team, Endilll and snarang181 August 4, 2025 10:01
@llvmbot llvmbot added clang Clang issues not falling into any other category libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Aug 4, 2025
@cor3ntin cor3ntin requested a review from Sirraide August 4, 2025 10:01
@llvmbot
Copy link
Member

llvmbot commented Aug 4, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-libcxx

Author: Corentin Jabot (cor3ntin)

Changes

Adds explanation why is_constructible evaluates to false.

This reapplies as-is e476f96.
This was reverted in 16d5db7 because of a test failure in libc++.

The test failure in libc++ is interesting in that, in the absence of nested diagnostics a bunch of diagnostics are emitted as error instead of notes, which we cannot silence with -verify-ignore-unexpected.

The fix here is to prevent the diagnostics to be emitted in the first place.
However this is clearly not ideal and we should make sure to deploy a better solution in the clang 22 time frame, in the lines of https://discourse.llvm.org/t/rfc-add-a-new-text-diagnostics-format-that-supports-nested-diagnostics/87641/12

Fixes #150601


Patch is 30.50 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151935.diff

15 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4-1)
  • (modified) clang/lib/Sema/SemaTypeTraits.cpp (+70-2)
  • (modified) clang/test/CXX/drs/cwg18xx.cpp (+2-1)
  • (modified) clang/test/SemaCXX/overload-resolution-deferred-templates.cpp (+14-5)
  • (modified) clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp (+66)
  • (modified) clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp (+62)
  • (modified) libcxx/include/__expected/expected.h (+2-2)
  • (modified) libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp (+4)
  • (modified) libcxx/test/libcxx/utilities/expected/expected.expected/or_else.mandates.verify.cpp (+4)
  • (modified) libcxx/test/libcxx/utilities/expected/expected.expected/value.observers.verify.cpp (+2-2)
  • (modified) libcxx/test/libcxx/utilities/expected/expected.void/and_then.mandates.verify.cpp (+4)
  • (modified) libcxx/test/std/containers/sequences/array/array.creation/to_array.verify.cpp (+2-2)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp (+2)
  • (modified) libcxx/test/std/utilities/function.objects/func.bind.partial/bind_back.verify.cpp (+2)
  • (modified) libcxx/test/std/utilities/function.objects/func.bind_front/bind_front.verify.cpp (+1)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0ba06d6f2dee3..24b3cf1213faa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1812,7 +1812,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<
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index c2f0600295e9e..1d8687e4bf1c1 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -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"
@@ -1963,6 +1965,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_constructible", TypeTrait::TT_IsConstructible)
       .Default(std::nullopt);
 }
 
@@ -1999,8 +2002,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)}};
   }
 
@@ -2273,6 +2284,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)
@@ -2559,6 +2624,9 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
   case UTT_IsStandardLayout:
     DiagnoseNonStandardLayoutReason(*this, E->getBeginLoc(), Args[0]);
     break;
+  case TT_IsConstructible:
+    DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args);
+    break;
   default:
     break;
   }
diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp
index 5b4551ba0143b..9948075852135 100644
--- a/clang/test/CXX/drs/cwg18xx.cpp
+++ b/clang/test/CXX/drs/cwg18xx.cpp
@@ -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
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index 8b469dd22943a..135865c8450f5 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -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 {
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
index cf33ac283ab42..f3ddbbfe15bdc 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags-std.cpp
@@ -42,6 +42,14 @@ static constexpr bool value = __is_standard_layout(T);
 };
 template <typename T>
 constexpr bool is_standard_layout_v = __is_standard_layout(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
@@ -97,6 +105,17 @@ template <typename T>
 using is_standard_layout = __details_is_standard_layout<T>;
 template <typename T>
 constexpr bool is_standard_layout_v = __is_standard_layout(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
 
 
@@ -149,6 +168,15 @@ template <typename T>
 using is_standard_layout = __details_is_standard_layout<T>;
 template <typename T>
 constexpr bool is_standard_layout_v = is_standard_layout<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
 
 }
@@ -211,6 +239,15 @@ static_assert(std::is_assignable_v<int&, void>);
 // expected-error@-1 {{static assertion failed due to requirement 'std::is_assignable_v<int &, void>'}} \
 // expected-error@-1 {{assigning to 'int' from incompatible type 'void'}}
 
+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);
@@ -256,6 +293,13 @@ namespace test_namespace {
     // expected-error@-1 {{static assertion failed due to requirement 'is_empty_v<int &>'}} \
     // expected-note@-1 {{'int &' is not empty}} \
     // 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}}
 }
 
 
@@ -284,6 +328,15 @@ concept C4 = std::is_assignable_v<T, U>; // #concept8
 
 template <C4<void> T> void g4();  // #cand8
 
+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'}} \
@@ -327,6 +380,19 @@ void test() {
     // expected-note@#cand8 {{because 'C4<int &, void>' evaluated to false}} \
     // expected-note@#concept8 {{because 'std::is_assignable_v<int &, void>' evaluated to false}} \
     // expected-error@#concept8 {{assigning to 'int' from incompatible type 'void'}}
+
+    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}}
 }
 }
 
diff --git a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
index cc923d206ab35..54806a93ddf80 100644
--- a/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
+++ b/clang/test/SemaCXX/type-traits-unsatisfied-diags.cpp
@@ -489,6 +489,68 @@ static_assert(__is_trivially_copyable(S12));
 // 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}}
+}
+
 namespace assignable {
 struct S1;
 static_assert(__is_assignable(S1&, const S1&));
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 0f446b870723b..5061e1703ed15 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -845,7 +845,7 @@ class expected : private __expected_base<_Tp, _Err> {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& value() const&& {
-    static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
+    static_assert(bool(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error())))>,
                   "error_type has to be both copy constructible and constructible from decltype(std::move(error()))");
     if (!this->__has_val()) {
       std::__throw_bad_expected_access<_Err>(std::move(error()));
@@ -854,7 +854,7 @@ class expected : private __expected_base<_Tp, _Err> {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
-    static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
+    static_assert(bool(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error())))>,
                   "error_type has to be both copy constructible and constructible from decltype(std::move(error()))");
     if (!this->__has_val()) {
       std::__throw_bad_expected_access<_Err>(std::move(error()));
diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp
index c46ab633295c1..fbd2317ebeee2 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp
@@ -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>
@@ -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>
@@ -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 initializ...
[truncated]

@cor3ntin cor3ntin requested a review from vhscampos August 4, 2025 10:01
@cor3ntin
Copy link
Contributor Author

cor3ntin commented Aug 4, 2025

@egorshamshura FYI

@cor3ntin cor3ntin requested a review from EricWF August 4, 2025 10:02
Copy link

github-actions bot commented Aug 4, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@cor3ntin cor3ntin force-pushed the corentin/GH150601 branch from 50c7eff to 5d99930 Compare August 4, 2025 10:11
@egorshamshura
Copy link
Contributor

egorshamshura commented Aug 4, 2025

@egorshamshura FYI

Thanks for your help. I could not figure it out quickly on my own, and then life got in the way and I forgot... So I hope to return to implementing some more type traits diagnostic in the near future. @_@

@@ -43,6 +43,7 @@ void cant_construct_data_handle_type() {
int data;
std::mdspan<int, std::extents<int>, std::layout_right, convertible_accessor_but_not_handle<int>> m_nc(&data);
// expected-error-re@*:* {{{{.*}}no matching constructor for initialization of {{.*}} (aka 'not_const_convertible_handle<const int>')}}
// expected-error@*:* 0-1{{no matching constructor for initialization of 'not_const_convertible_handle<const int>'}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Urg. Thanks for fixing these tests. They appear to be way to brittle to changes in compiler diagnostics.

@@ -55,6 +55,7 @@ void test() {
// expected-error-re@*:* {{static assertion failed {{.*}}The result of f() must be a specialization of std::expected}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so these tests are a mess. libc++ shouldn't be using verify tests like this.

Given how brittle these tests are, and how little they actually meaningfully test today, I think it's fair to simply make them work again. However you can; let's just not modify the headers to work around these tests.

@@ -845,7 +845,7 @@ class expected : private __expected_base<_Tp, _Err> {
}

_LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& value() const&& {
static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
static_assert(bool(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's drop this change and work around the expected tests wholly within the tests.

Copy link
Member

@EricWF EricWF left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

libcxx test failure when libcxx is build with no exceptions and no RTTI
5 participants