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); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
