[clang] aba63c5 - [clang] The `__reference_meows_from_temporary` builtins should SFINAE friendly when the 1st type is not a reference type (#206527)

via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 29 22:05:22 PDT 2026


Author: Yihan Wang
Date: 2026-06-30T13:05:18+08:00
New Revision: aba63c572000e14a6ad24f403c50e90f0001dd05

URL: https://github.com/llvm/llvm-project/commit/aba63c572000e14a6ad24f403c50e90f0001dd05
DIFF: https://github.com/llvm/llvm-project/commit/aba63c572000e14a6ad24f403c50e90f0001dd05.diff

LOG: [clang] The `__reference_meows_from_temporary` builtins should SFINAE friendly when the 1st type is not a reference type (#206527)

Suppose that `__reference_constructs_from_temporary` is defined as:

```cpp
__reference_constructs_from_temporary(_Tp, _Up);
```
A non-reference type can never bind to a temporary, so the result is
always `false` for such a `_Tp`. We should short-circuit before reaching
the instantiations by check the type of `_Tp`. But clang's
`__reference_constructs_from_temporary` eagerly instantiates the
construction of `_Up` (including the element's constructor exception
specification) even when `_Tp` is not a reference, which can hard-error
on misbehaved types.

The following code should be accepted, but clang raise a hard error:

```cpp
struct NoConv {};
struct Bad { template<class T> Bad(T v) noexcept(noexcept(member_ = v)) {} int member_; };
static_assert(!__reference_constructs_from_temporary(Bad, NoConv&&));
static_assert(!__reference_converts_from_temporary(Bad, NoConv&&));
static_assert(!__reference_binds_to_temporary(Bad, NoConv&&));
```

The PR refine the implementation of these builtins by short-circuits on
a non-reference first operand.

Fixes https://github.com/llvm/llvm-project/issues/206524.

---------

Signed-off-by: yronglin <yronglin777 at gmail.com>

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/Sema/SemaTypeTraits.cpp
    clang/test/SemaCXX/type-traits.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 09ec3594ab31f..b372e5b58068b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -729,6 +729,9 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
   crash when using it with `-fms-extensions` on other platforms. (#GH184318)
 - Fixed a compiler crash due to an unresolved overloaded function type when
   calling `__builtin_bit_cast`. (#GH200112)
+- Clang now SFINAE friendly when the ``__reference_meows_from_temporary`` builtins
+  should SFINAE friendly when the 1st type is not a reference type. (#GH206524)
+
 
 #### Bug Fixes to Attribute Support
 

diff  --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index c79b3f7045ca6..4de73601e273d 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -1285,6 +1285,16 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
     //     T t(create<Args>()...);
     assert(!Args.empty());
 
+    // LWG3819: For reference_meows_from_temporary traits, && is not added to
+    // the source object type.
+    // Otherwise, compute the result of add_rvalue_reference_t.
+    bool UseRawObjectType =
+        Kind == clang::BTT_ReferenceBindsToTemporary ||
+        Kind == clang::BTT_ReferenceConstructsFromTemporary ||
+        Kind == clang::BTT_ReferenceConvertsFromTemporary;
+    if (UseRawObjectType && !Args[0]->getType()->isReferenceType())
+      return false;
+
     // Precondition: T and all types in the parameter pack Args shall be
     // complete types, (possibly cv-qualified) void, or arrays of
     // unknown bound.
@@ -1308,14 +1318,6 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
     if (RD && RD->isAbstract())
       return false;
 
-    // LWG3819: For reference_meows_from_temporary traits, && is not added to
-    // the source object type.
-    // Otherwise, compute the result of add_rvalue_reference_t.
-    bool UseRawObjectType =
-        Kind == clang::BTT_ReferenceBindsToTemporary ||
-        Kind == clang::BTT_ReferenceConstructsFromTemporary ||
-        Kind == clang::BTT_ReferenceConvertsFromTemporary;
-
     llvm::BumpPtrAllocator OpaqueExprAllocator;
     SmallVector<Expr *, 2> ArgExprs;
     ArgExprs.reserve(Args.size() - 1);

diff  --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 8decb1f61395e..ff74461308fb1 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -3203,6 +3203,8 @@ class ConvertsToRefPrivate {
   mutable T obj = 42;
 };
 
+struct NoConv {};
+struct Bad { template<class T> Bad(T v) noexcept(noexcept(member_ = v)) {} int member_; };
 
 void reference_binds_to_temporary_checks() {
   static_assert(!(__reference_binds_to_temporary(int &, int &)));
@@ -3243,6 +3245,10 @@ void reference_binds_to_temporary_checks() {
   // Test that function references are never considered bound to temporaries.
   static_assert(!__reference_binds_to_temporary(void(&)(), void()));
   static_assert(!__reference_binds_to_temporary(void(&&)(), void()));
+
+  // Make sure we don't emit "assigning to 'int' from incompatible type 'NoConv'" in SFINAE context.
+  static_assert(!__reference_binds_to_temporary(Bad, NoConv&&));
+
 }
 
 
@@ -3325,7 +3331,8 @@ void reference_constructs_from_temporary_checks() {
   static_assert(!__reference_constructs_from_temporary(const int&, ExplicitConversionRef));
   static_assert(!__reference_constructs_from_temporary(int&&, ExplicitConversionRvalueRef));
 
-
+  // Make sure we don't emit "assigning to 'int' from incompatible type 'NoConv'" in SFINAE context.
+  static_assert(!__reference_constructs_from_temporary(Bad, NoConv&&));
 }
 
 template<typename A, typename B, bool result = __reference_converts_from_temporary(A, B)>
@@ -3394,6 +3401,9 @@ void reference_converts_from_temporary_checks() {
   static_assert(!__reference_converts_from_temporary(AllPrivate, AllPrivate));
   // Make sure we don't emit "calling a private constructor" in SFINAE context.
   static_assert(!reference_converts_from_temporary_sfinae<AllPrivate, AllPrivate>());
+
+  // Make sure we don't emit "assigning to 'int' from incompatible type 'NoConv'" in SFINAE context.
+  static_assert(!__reference_converts_from_temporary(Bad, NoConv&&));
 }
 
 void array_rank() {


        


More information about the cfe-commits mailing list