[clang] 742af32 - Reapply "[clang] Fix sema on ObjCLifetime conversion (#178524)" (#180817)

via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 13 12:27:26 PST 2026


Author: Peter Rong
Date: 2026-02-13T20:27:20Z
New Revision: 742af32b67c0d70ced4837fbde778ee5ea6529b4

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

LOG: Reapply "[clang] Fix sema on ObjCLifetime conversion (#178524)" (#180817)

Clang can't handle objc lifetime correctly when casting We reuse the
approach similar to lifetime: First remove it before the conversion,
then add it back.

Add a test

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

Added: 
    clang/test/CodeGenObjCXX/arc-lifetime-rvalue-ref-binding.mm
    clang/test/SemaObjCXX/arc-lifetime-rvalue-ref-binding.mm

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaInit.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index dd11ee3646922..6063c3fbf48c8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -296,6 +296,7 @@ Miscellaneous Clang Crashes Fixed
   generic lambda. (#GH172289)
 - Fixed a crash in C++ overload resolution with ``_Atomic``-qualified argument types. (#GH170433)
 - Fixed an assertion when diagnosing address-space qualified ``new``/``delete`` in language-defined address spaces such as OpenCL ``__local``. (#GH178319)
+- Fixed an assertion failure in ObjC++ ARC when binding a rvalue reference to reference with 
diff erent lifetimes (#GH178524)
 
 OpenACC Specific Changes
 ------------------------

diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index ff278bc7471bd..989f7cd80cbef 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5651,7 +5651,16 @@ static void TryReferenceInitializationCore(Sema &S,
       T1QualsIgnoreAS.removeAddressSpace();
       T2QualsIgnoreAS.removeAddressSpace();
     }
-    QualType cv1T4 = S.Context.getQualifiedType(cv2T2, T1QualsIgnoreAS);
+    // Strip the existing ObjC lifetime qualifier from cv2T2 before combining
+    // with T1's qualifiers.
+    QualType T2ForQualConv = cv2T2;
+    if (T1Quals.getObjCLifetime() != T2Quals.getObjCLifetime()) {
+      Qualifiers T2BaseQuals =
+          T2ForQualConv.getQualifiers().withoutObjCLifetime();
+      T2ForQualConv = S.Context.getQualifiedType(
+          T2ForQualConv.getUnqualifiedType(), T2BaseQuals);
+    }
+    QualType cv1T4 = S.Context.getQualifiedType(T2ForQualConv, T1QualsIgnoreAS);
     if (T1QualsIgnoreAS != T2QualsIgnoreAS)
       Sequence.AddQualificationConversionStep(cv1T4, ValueKind);
     Sequence.AddReferenceBindingStep(cv1T4, ValueKind == VK_PRValue);

diff  --git a/clang/test/CodeGenObjCXX/arc-lifetime-rvalue-ref-binding.mm b/clang/test/CodeGenObjCXX/arc-lifetime-rvalue-ref-binding.mm
new file mode 100644
index 0000000000000..71412a115b8ae
--- /dev/null
+++ b/clang/test/CodeGenObjCXX/arc-lifetime-rvalue-ref-binding.mm
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -O0 -disable-llvm-passes -o - %s | FileCheck %s
+
+// Test for correct IR generation when binding ObjC ARC __strong rvalues
+// to const __autoreleasing references. Previously, this caused an assertion
+// failure in Qualifiers::addConsistentQualifiers.
+
+// The const id& parameter has implicit __autoreleasing lifetime.
+void take(const id&);
+
+// CHECK-LABEL: define{{.*}} void @_Z19test_rvalue_bindingv()
+// CHECK: [[OBJ:%.*]] = alloca ptr, align 8
+// CHECK: store ptr null, ptr [[OBJ]], align 8
+// CHECK: call void @_Z4takeRU15__autoreleasingKP11objc_object(ptr noundef nonnull align 8 dereferenceable(8) [[OBJ]])
+// CHECK: call void @llvm.objc.storeStrong(ptr [[OBJ]], ptr null)
+// CHECK: ret void
+void test_rvalue_binding() {
+  id obj = nullptr;
+  take(static_cast<id&&>(obj));
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z19test_lvalue_bindingv()
+// CHECK: [[OBJ:%.*]] = alloca ptr, align 8
+// CHECK: store ptr null, ptr [[OBJ]], align 8
+// CHECK: call void @_Z4takeRU15__autoreleasingKP11objc_object(ptr noundef nonnull align 8 dereferenceable(8) [[OBJ]])
+// CHECK: call void @llvm.objc.storeStrong(ptr [[OBJ]], ptr null)
+// CHECK: ret void
+void test_lvalue_binding() {
+  id obj = nullptr;
+  take(obj);
+}
+
+// Test with fold expressions and perfect forwarding (original crash case).
+template <typename... Args>
+void call(Args... args) {
+  (take(static_cast<Args&&>(args)), ...);
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z20test_fold_expressionv()
+// CHECK: call void @_Z4callIJU8__strongP11objc_objectEEvDpT_(ptr noundef null)
+void test_fold_expression() {
+  call<id>(nullptr);
+}
+
+// CHECK-LABEL: define{{.*}} void @_Z4callIJU8__strongP11objc_objectEEvDpT_(ptr noundef %args)
+// CHECK: [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK: store ptr null, ptr [[ARGS_ADDR]], align 8
+// CHECK: call void @llvm.objc.storeStrong(ptr [[ARGS_ADDR]], ptr %args)
+// CHECK: call void @_Z4takeRU15__autoreleasingKP11objc_object(ptr noundef nonnull align 8 dereferenceable(8) [[ARGS_ADDR]])
+// CHECK: call void @llvm.objc.storeStrong(ptr [[ARGS_ADDR]], ptr null)
+// CHECK: ret void
+
+// Test that binding a prvalue to an __autoreleasing rvalue reference emits
+// retain+autorelease (not retain+release), per ARC semantics for __autoreleasing.
+// CHECK-LABEL: define{{.*}} void @_Z32test_autoreleasing_rvalue_ref_prP11objc_object(ptr noundef %a)
+// CHECK: [[A_ADDR:%.*]] = alloca ptr, align 8
+// CHECK: [[R:%.*]] = alloca ptr, align 8
+// CHECK: [[REF_TMP:%.*]] = alloca ptr, align 8
+// CHECK: store ptr null, ptr [[A_ADDR]], align 8
+// CHECK: call void @llvm.objc.storeStrong(ptr [[A_ADDR]], ptr %a)
+// CHECK: [[LOAD:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// CHECK: [[RETAINED:%.*]] = call ptr @llvm.objc.retain(ptr [[LOAD]])
+// CHECK: [[AUTORELEASED:%.*]] = call ptr @llvm.objc.autorelease(ptr [[RETAINED]])
+// CHECK: store ptr [[AUTORELEASED]], ptr [[REF_TMP]], align 8
+// CHECK: store ptr [[REF_TMP]], ptr [[R]], align 8
+// CHECK: call void @llvm.objc.storeStrong(ptr [[A_ADDR]], ptr null)
+// CHECK: ret void
+void test_autoreleasing_rvalue_ref_pr(id a) {
+  id __autoreleasing && r = id{a};
+}

diff  --git a/clang/test/SemaObjCXX/arc-lifetime-rvalue-ref-binding.mm b/clang/test/SemaObjCXX/arc-lifetime-rvalue-ref-binding.mm
new file mode 100644
index 0000000000000..d6d9c4a6289ee
--- /dev/null
+++ b/clang/test/SemaObjCXX/arc-lifetime-rvalue-ref-binding.mm
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fobjc-arc -verify %s
+// RUN: %clang_cc1 -std=c++17 -fobjc-arc -ast-dump %s 2>&1 | FileCheck %s
+// expected-no-diagnostics
+
+// Test for binding ObjC ARC __strong rvalues to const __autoreleasing references.
+// This previously caused an assertion failure in Qualifiers::addConsistentQualifiers
+// when the compiler attempted to add conflicting ObjC lifetime qualifiers.
+
+// The const id& parameter has implicit __autoreleasing lifetime.
+void take(const id&);
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_rvalue_binding
+// CHECK: CallExpr
+// CHECK: ImplicitCastExpr {{.*}} 'const __autoreleasing id' xvalue <NoOp>
+// CHECK-NEXT: CXXStaticCastExpr {{.*}} '__strong id' xvalue static_cast<__strong id &&> <NoOp>
+void test_rvalue_binding() {
+  id obj = nullptr;
+  take(static_cast<id&&>(obj));
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_lvalue_binding
+// CHECK: CallExpr
+// CHECK: ImplicitCastExpr {{.*}} 'const __autoreleasing id' lvalue <NoOp>
+// CHECK-NEXT: DeclRefExpr {{.*}} '__strong id' lvalue
+void test_lvalue_binding() {
+  id obj = nullptr;
+  take(obj);
+}
+
+// Test with fold expressions and perfect forwarding (original crash case).
+template <typename... Args>
+void call(Args... args) {
+  (take(static_cast<Args&&>(args)), ...);
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} test_fold_expression
+void test_fold_expression() {
+  call<id>(nullptr);
+}


        


More information about the cfe-commits mailing list