Author: Henrik G. Olsson
Date: 2026-05-22T21:55:41-07:00
New Revision: d6be71fd1f10ad5220b69a3a09a942faabbd1a05

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

LOG: [Clang][CodeGen] map `noescape` to capture(address) (was capture(none)) 
(#199281)

`capture(none)` has very restrictive semantics and an easy footgun to
accidentally fire some UB into your code with. Most significantly it
does not allow any visible side-effects of whether a pointer was null or
not to escape the function. This means that the function cannot perform
different side effects depending on whether a pointer marked `noescape`
is null. Relax this to `captures(address)`, which allows information
about the numerical address to escape the function, but no provenance
(i.e. nothing that could be dereferenced) may escape.

As discussed in
https://discourse.llvm.org/t/rfc-updating-the-semantics-of-the-noescape-attribute/90326.

Added: 
    

Modified: 
    clang/lib/CodeGen/CGCall.cpp
    clang/test/CodeGenCXX/noescape.cpp
    clang/test/CodeGenObjC/noescape.m

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 7194b81459922..40cc275d40273 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -3163,7 +3163,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
     }
 
     if (FI.getExtParameterInfo(ArgNo).isNoEscape())
-      Attrs.addCapturesAttr(llvm::CaptureInfo::none());
+      Attrs.addCapturesAttr(
+          llvm::CaptureInfo(llvm::CaptureComponents::Address));
 
     if (Attrs.hasAttributes()) {
       unsigned FirstIRArg, NumIRArgs;

diff  --git a/clang/test/CodeGenCXX/noescape.cpp 
b/clang/test/CodeGenCXX/noescape.cpp
index c3fc90e2ea54d..9c1ea1719bea5 100644
--- a/clang/test/CodeGenCXX/noescape.cpp
+++ b/clang/test/CodeGenCXX/noescape.cpp
@@ -8,26 +8,26 @@ struct S {
   virtual void vm1(int *, int * __attribute__((noescape)));
 };
 
-// CHECK: define{{.*}} void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(none) {{%.*}})
-// CHECK: define{{.*}} void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(none) {{%.*}}) {{.*}} {
-// CHECK: call void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} captures(none) 
{{.*}})
+// CHECK: define{{.*}} void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(address) {{%.*}})
+// CHECK: define{{.*}} void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(address) {{%.*}}) {{.*}} {
+// CHECK: call void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} 
captures(address) {{.*}})
 
 S::S(int *, int * __attribute__((noescape))) {}
 
-// CHECK: define {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef 
captures(none) {{%.*}})
+// CHECK: define {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef 
captures(address) {{%.*}})
 S &S::operator=(int * __attribute__((noescape))) { return *this; }
 
-// CHECK: define{{.*}} void @_ZN1S2m0EPiS0_(ptr {{.*}}, {{.*}} noundef 
captures(none) {{%.*}})
+// CHECK: define{{.*}} void @_ZN1S2m0EPiS0_(ptr {{.*}}, {{.*}} noundef 
captures(address) {{%.*}})
 void S::m0(int *, int * __attribute__((noescape))) {}
 
-// CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} noundef 
captures(none) {{%.*}})
+// CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} noundef 
captures(address) {{%.*}})
 void S::vm1(int *, int * __attribute__((noescape))) {}
 
 // CHECK-LABEL: define{{.*}} void @_Z5test0P1SPiS1_(
-// CHECK: call void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(none) {{.*}})
-// CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef 
captures(none) {{.*}})
-// CHECK: call void @_ZN1S2m0EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(none) {{.*}})
-// CHECK: call void {{.*}}(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(none) 
{{.*}})
+// CHECK: call void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(address) {{.*}})
+// CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef 
captures(address) {{.*}})
+// CHECK: call void @_ZN1S2m0EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(address) {{.*}})
+// CHECK: call void {{.*}}(ptr {{.*}}, {{.*}}, {{.*}} noundef 
captures(address) {{.*}})
 void test0(S *s, int *p0, int *p1) {
   S t(p0, p1);
   t = p1;
@@ -39,27 +39,27 @@ namespace std {
   typedef decltype(sizeof(0)) size_t;
 }
 
-// CHECK: define {{.*}} @_ZnwmPv({{.*}}, {{.*}} captures(none) {{.*}})
+// CHECK: define {{.*}} @_ZnwmPv({{.*}}, {{.*}} captures(address) {{.*}})
 void *operator new(std::size_t, void * __attribute__((noescape)) p) {
   return p;
 }
 
 // CHECK-LABEL: define{{.*}} ptr @_Z5test1Pv(
-// CHECK: %call = call {{.*}} @_ZnwmPv({{.*}}, {{.*}} captures(none) {{.*}})
+// CHECK: %call = call {{.*}} @_ZnwmPv({{.*}}, {{.*}} captures(address) {{.*}})
 void *test1(void *p0) {
   return ::operator new(16, p0);
 }
 
 // CHECK-LABEL: define{{.*}} void @_Z5test2PiS_(
-// CHECK: call void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, {{.*}} 
captures(none) {{.*}})
-// CHECK: define internal void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, 
{{.*}} noundef captures(none) {{%.*}})
+// CHECK: call void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, {{.*}} 
captures(address) {{.*}})
+// CHECK: define internal void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, 
{{.*}} noundef captures(address) {{%.*}})
 void test2(int *p0, int *p1) {
   auto t = [](int *, int * __attribute__((noescape))){};
   t(p0, p1);
 }
 
 // CHECK-LABEL: define{{.*}} void @_Z5test3PFvU8noescapePiES_(
-// CHECK: call void {{.*}}(ptr noundef captures(none) {{.*}})
+// CHECK: call void {{.*}}(ptr noundef captures(address) {{.*}})
 typedef void (*NoEscapeFunc)(__attribute__((noescape)) int *);
 
 void test3(NoEscapeFunc f, int *p) {

diff  --git a/clang/test/CodeGenObjC/noescape.m 
b/clang/test/CodeGenObjC/noescape.m
index e1dbc0eb92e54..80d4bb8e1c876 100644
--- a/clang/test/CodeGenObjC/noescape.m
+++ b/clang/test/CodeGenObjC/noescape.m
@@ -26,37 +26,37 @@
 // CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*ls32l8"]] = linkonce_odr hidden 
unnamed_addr constant { i64, i64, ptr, i64 } { i64 0, i64 40, ptr @{{.*}}, i64 
256 }, align 8
 
 // CHECK-LABEL: define{{.*}} void @test0(
-// CHECK: call void @noescapeFunc0({{.*}}, {{.*}} captures(none) {{.*}})
-// CHECK: declare void @noescapeFunc0(ptr noundef, {{.*}} noundef 
captures(none))
+// CHECK: call void @noescapeFunc0({{.*}}, {{.*}} captures(address) {{.*}})
+// CHECK: declare void @noescapeFunc0(ptr noundef, {{.*}} noundef 
captures(address))
 void test0(BlockTy b) {
   noescapeFunc0(0, b);
 }
 
 // CHECK-LABEL: define{{.*}} void @test1(
-// CHECK: call void @noescapeFunc1({{.*}} captures(none) {{.*}})
-// CHECK: declare void @noescapeFunc1({{.*}} noundef captures(none))
+// CHECK: call void @noescapeFunc1({{.*}} captures(address) {{.*}})
+// CHECK: declare void @noescapeFunc1({{.*}} noundef captures(address))
 void test1(int *i) {
   noescapeFunc1(i);
 }
 
 // CHECK-LABEL: define{{.*}} void @test2(
-// CHECK: call void @noescapeFunc2({{.*}} captures(none) {{.*}})
-// CHECK: declare void @noescapeFunc2({{.*}} noundef captures(none))
+// CHECK: call void @noescapeFunc2({{.*}} captures(address) {{.*}})
+// CHECK: declare void @noescapeFunc2({{.*}} noundef captures(address))
 void test2(id i) {
   noescapeFunc2(i);
 }
 
 // CHECK-LABEL: define{{.*}} void @test3(
-// CHECK: call void @noescapeFunc3({{.*}} captures(none) {{.*}})
-// CHECK: declare void @noescapeFunc3({{.*}} captures(none))
+// CHECK: call void @noescapeFunc3({{.*}} captures(address) {{.*}})
+// CHECK: declare void @noescapeFunc3({{.*}} captures(address))
 void test3(union U u) {
   noescapeFunc3(u);
 }
 
-// CHECK: define internal void @"\01-[C0 m0:]"({{.*}}, {{.*}}, {{.*}} 
captures(none) {{.*}})
+// CHECK: define internal void @"\01-[C0 m0:]"({{.*}}, {{.*}}, {{.*}} 
captures(address) {{.*}})
 
 // CHECK-LABEL: define{{.*}} void @test4(
-// CHECK: call void @objc_msgSend(ptr {{.*}}, ptr {{.*}}, ptr noundef 
captures(none) {{.*}})
+// CHECK: call void @objc_msgSend(ptr {{.*}}, ptr {{.*}}, ptr noundef 
captures(address) {{.*}})
 
 @interface C0
 -(void) m0:(int*)__attribute__((noescape)) p0;
@@ -72,9 +72,9 @@ void test4(C0 *c0, int *p) {
 }
 
 // CHECK-LABEL: define{{.*}} void @test5(
-// CHECK: call void {{.*}}(ptr noundef @{{.*}}, ptr noundef captures(none) 
{{.*}})
-// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef captures(none) {{.*}})
-// CHECK: define internal void @{{.*}}(ptr {{.*}}, ptr noundef captures(none) 
{{.*}})
+// CHECK: call void {{.*}}(ptr noundef @{{.*}}, ptr noundef captures(address) 
{{.*}})
+// CHECK: call void {{.*}}(ptr {{.*}}, ptr noundef captures(address) {{.*}})
+// CHECK: define internal void @{{.*}}(ptr {{.*}}, ptr noundef 
captures(address) {{.*}})
 
 typedef void (^BlockTy2)(__attribute__((noescape)) int *);
 


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to