From fe14d5d6367650529509b28b86c5b50e2103e346 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Fri, 5 Sep 2025 22:11:02 -0700 Subject: [PATCH 1/3] [ASTPrinter] Move LifetimeDescriptor::getString and add test (NFC) This refactor and test case are preparations for the implementation in the next commit. --- include/swift/AST/LifetimeDependence.h | 12 +------- lib/AST/LifetimeDependence.cpp | 12 ++++++++ test/IDE/print_lifetime_attr.swift | 39 ++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 test/IDE/print_lifetime_attr.swift diff --git a/include/swift/AST/LifetimeDependence.h b/include/swift/AST/LifetimeDependence.h index ada4f9d54b4c6..9373440e41fd0 100644 --- a/include/swift/AST/LifetimeDependence.h +++ b/include/swift/AST/LifetimeDependence.h @@ -141,17 +141,7 @@ struct LifetimeDescriptor { return getName().str() == "immortal"; } - std::string getString() const { - switch (kind) { - case DescriptorKind::Named: - return getName().str().str(); - case DescriptorKind::Ordered: - return std::to_string(getIndex()); - case DescriptorKind::Self: - return "self"; - } - llvm_unreachable("Invalid DescriptorKind"); - } + std::string getString() const; }; class LifetimeEntry final diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index c3313aba6562e..2b6ddf15c66c9 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -28,6 +28,18 @@ namespace swift { +std::string LifetimeDescriptor::getString() const { + switch (kind) { + case DescriptorKind::Named: + return getName().str().str(); + case DescriptorKind::Ordered: + return std::to_string(getIndex()); + case DescriptorKind::Self: + return "self"; + } + llvm_unreachable("Invalid DescriptorKind"); +} + LifetimeEntry * LifetimeEntry::create(const ASTContext &ctx, SourceLoc startLoc, SourceLoc endLoc, ArrayRef sources, diff --git a/test/IDE/print_lifetime_attr.swift b/test/IDE/print_lifetime_attr.swift new file mode 100644 index 0000000000000..312c38ec0f064 --- /dev/null +++ b/test/IDE/print_lifetime_attr.swift @@ -0,0 +1,39 @@ +// REQUIRES: swift_feature_Lifetimes +// REQUIRES: swift_feature_LifetimeDependence + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// RUN: %target-swift-ide-test -enable-experimental-feature Lifetimes -enable-experimental-feature LifetimeDependence -print-swift-file-interface -source-filename %t/test.swift > %t/interface.txt +// RUN: diff %t/interface.txt %t/interface.txt.expected + +//--- test.swift +public struct S : ~Escapable { + @_lifetime(immortal) + init() {} +} + +@_lifetime(foo: copy foo) +public func fooFunc(_ foo: inout S) {} + +@_lifetime(&bar) +public func barFunc(_ bar: inout S) -> S { + return bar +} + +@_lifetime(`func`: copy `func`) +public func funcFunc(func: inout S) {} + +//--- interface.txt.expected + +public struct S : ~Escapable { + + @_lifetime(immortal) + internal init() +} +@_lifetime(foo: copy foo) +public func fooFunc(_ foo: inout S) +@_lifetime(&bar) +public func barFunc(_ bar: inout S) -> S +@_lifetime(func: copy func) +public func funcFunc(func: inout S) From d7d839e9f3271dd2c377501839178258dc34eac3 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Fri, 5 Sep 2025 22:18:02 -0700 Subject: [PATCH 2/3] [ASTPrinter] Escape @_lifetime arguments when needed rdar://159992995 --- lib/AST/LifetimeDependence.cpp | 9 ++++++++- test/IDE/print_lifetime_attr.swift | 21 ++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/AST/LifetimeDependence.cpp b/lib/AST/LifetimeDependence.cpp index 2b6ddf15c66c9..173014d77ca47 100644 --- a/lib/AST/LifetimeDependence.cpp +++ b/lib/AST/LifetimeDependence.cpp @@ -12,6 +12,7 @@ #include "swift/AST/LifetimeDependence.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/Builtins.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" @@ -30,8 +31,14 @@ namespace swift { std::string LifetimeDescriptor::getString() const { switch (kind) { - case DescriptorKind::Named: + case DescriptorKind::Named: { + bool shouldEscape = + escapeIdentifierInContext(getName(), PrintNameContext::Normal); + if (shouldEscape) { + return ("`" + getName().str() + "`").str(); + } return getName().str().str(); + } case DescriptorKind::Ordered: return std::to_string(getIndex()); case DescriptorKind::Self: diff --git a/test/IDE/print_lifetime_attr.swift b/test/IDE/print_lifetime_attr.swift index 312c38ec0f064..cf1e91205c358 100644 --- a/test/IDE/print_lifetime_attr.swift +++ b/test/IDE/print_lifetime_attr.swift @@ -24,6 +24,14 @@ public func barFunc(_ bar: inout S) -> S { @_lifetime(`func`: copy `func`) public func funcFunc(func: inout S) {} +public struct T : ~Escapable { + let s: S + @_lifetime(borrow self) + func selfFunc() -> S { return s } + @_lifetime(borrow `self`) + func selfFunc2(`self`: S) -> S { return `self` } +} + //--- interface.txt.expected public struct S : ~Escapable { @@ -35,5 +43,16 @@ public struct S : ~Escapable { public func fooFunc(_ foo: inout S) @_lifetime(&bar) public func barFunc(_ bar: inout S) -> S -@_lifetime(func: copy func) +@_lifetime(`func`: copy `func`) public func funcFunc(func: inout S) + +public struct T : ~Escapable { + + internal let s: S + + @_lifetime(borrow self) + internal func selfFunc() -> S + + @_lifetime(borrow `self`) + internal func selfFunc2(self: S) -> S +} From 5ea84522fa59817155e23e435a67f19115d5c353 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Sun, 7 Sep 2025 12:10:23 -0700 Subject: [PATCH 3/3] update Interop/C/swiftify-import/counted-by-noescape.swift --- test/Interop/C/swiftify-import/counted-by-noescape.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Interop/C/swiftify-import/counted-by-noescape.swift b/test/Interop/C/swiftify-import/counted-by-noescape.swift index 702d0271b9706..fb778a3cd92a7 100644 --- a/test/Interop/C/swiftify-import/counted-by-noescape.swift +++ b/test/Interop/C/swiftify-import/counted-by-noescape.swift @@ -18,7 +18,7 @@ import CountedByNoEscapeClang // CHECK-NEXT: /// This is an auto-generated wrapper for safer interop // CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) -// CHECK-NEXT: @_lifetime(func: copy func) +// CHECK-NEXT: @_lifetime(`func`: copy `func`) // CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func clash(func: inout MutableSpan?, clash where: Int32) // CHECK-NEXT: /// This is an auto-generated wrapper for safer interop @@ -38,7 +38,7 @@ import CountedByNoEscapeClang // CHECK-NEXT: /// This is an auto-generated wrapper for safer interop // CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) -// CHECK-NEXT: @_lifetime(func: copy func) +// CHECK-NEXT: @_lifetime(`func`: copy `func`) // CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func funcRenamed(func: inout MutableSpan?, extension: Int32, init: Int32, open: Int32, var: Int32, is: Int32, as: Int32, in: Int32, guard: Int32, where: Int32) -> UnsafeMutableRawPointer! // CHECK-NEXT: /// This is an auto-generated wrapper for safer interop @@ -53,7 +53,7 @@ import CountedByNoEscapeClang // CHECK-NEXT: /// This is an auto-generated wrapper for safer interop // CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) -// CHECK-NEXT: @_lifetime(func: copy func) +// CHECK-NEXT: @_lifetime(`func`: copy `func`) // CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func keyword(_ func: inout MutableSpan?, _ extension: Int32, _ init: Int32, _ open: Int32, _ var: Int32, _ is: Int32, _ as: Int32, _ in: Int32, _ guard: Int32, _ where: Int32) // CHECK-NEXT: /// This is an auto-generated wrapper for safer interop @@ -83,7 +83,7 @@ import CountedByNoEscapeClang // CHECK-NEXT: /// This is an auto-generated wrapper for safer interop // CHECK-NEXT: @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) -// CHECK-NEXT: @_lifetime(func: copy func) +// CHECK-NEXT: @_lifetime(`func`: copy `func`) // CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func open(func: inout MutableSpan?, open where: Int32) // CHECK-NEXT: /// This is an auto-generated wrapper for safer interop