Skip to content

Commit 93e659b

Browse files
committed
Don't depend on implicit lifetime of Escapable inout
Escapable inout parameters don't have an implicit lifetime that can be borrowed or copied, because the caller doesn't know what the lifetime of the new value is, if the callee writes to the parameter. Depending on the lifetime of an Escapable inout parameter without explicit lifetime annotation results in an error, so _SwiftifyImport should not do that.
1 parent 6a28b6a commit 93e659b

File tree

5 files changed

+77
-37
lines changed

5 files changed

+77
-37
lines changed

lib/AST/ConformanceLookup.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -534,19 +534,7 @@ static ProtocolConformanceRef getPackTypeConformance(
534534
}
535535

536536
static bool shouldExpandExtensionMacro(Evaluator &evaluator, NominalTypeDecl *nominal) {
537-
if (evaluator.hasActiveRequest(ExpandExtensionMacros{nominal})) {
538-
return false;
539-
}
540-
541-
// Expanding an extension macro for this type will require pretty printing the node,
542-
// leading to evaluation of it's members. Once the macro expansion requrest has started
543-
// it's too late to check for cycles.
544-
for (auto member : nominal->getMembers())
545-
if (auto VD = dyn_cast<ValueDecl>(member))
546-
if (evaluator.hasActiveRequest(InterfaceTypeRequest{VD}))
547-
return false;
548-
549-
return true;
537+
return !evaluator.hasActiveRequest(ExpandExtensionMacros{nominal});
550538
}
551539

552540
ProtocolConformanceRef

lib/ClangImporter/ImportDecl.cpp

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9225,6 +9225,30 @@ class SwiftifyProtocolInfoPrinter : public SwiftifyInfoPrinter {
92259225
}
92269226
};
92279227

9228+
static bool isLifetimeTarget(const FuncDecl *Func, const ParamDecl *Param,
9229+
size_t paramIndex) {
9230+
for (auto attr : Func->getAttrs().getAttributes<LifetimeAttr>()) {
9231+
auto entry = attr->getLifetimeEntry();
9232+
auto descriptorOpt = entry->getTargetDescriptor();
9233+
if (!descriptorOpt.has_value())
9234+
continue;
9235+
auto descriptor = descriptorOpt.value();
9236+
switch (descriptor.getDescriptorKind()) {
9237+
case LifetimeDescriptor::DescriptorKind::Named:
9238+
if (Param->getParameterName() == descriptor.getName())
9239+
return true;
9240+
continue;
9241+
case LifetimeDescriptor::DescriptorKind::Ordered:
9242+
if (paramIndex == descriptor.getIndex())
9243+
return true;
9244+
continue;
9245+
case LifetimeDescriptor::DescriptorKind::Self:
9246+
continue;
9247+
}
9248+
}
9249+
return false;
9250+
}
9251+
92289252
static bool swiftifyImpl(SwiftifyInfoPrinter &printer, const FuncDecl *MappedDecl) {
92299253
const clang::Decl *ClangDecl = MappedDecl->getClangDecl();
92309254
auto FuncD = dyn_cast<clang::FunctionDecl>(ClangDecl);
@@ -9270,11 +9294,23 @@ static bool swiftifyImpl(SwiftifyInfoPrinter &printer, const FuncDecl *MappedDec
92709294
paramHasLifetimeInfo = true;
92719295
}
92729296
if (clangParam->hasAttr<clang::LifetimeBoundAttr>()) {
9273-
printer.printLifetimeboundReturn(
9274-
index, !paramHasBoundsInfo &&
9275-
swiftParam->getInterfaceType()->isEscapable());
9276-
paramHasLifetimeInfo = true;
9277-
returnHasLifetimeInfo = true;
9297+
// Escapable types should have their ownership borrowed, while
9298+
// nonescapable should have their lifetime copied.
9299+
// If this parameter has bounds info, the wrapper version of it will be
9300+
// nonescapable (Span) however, so then it should be copied despite being
9301+
// escapable in the original.
9302+
bool shouldBorrowLifetime =
9303+
!paramHasBoundsInfo && swiftParam->getInterfaceType()->isEscapable();
9304+
// For inout parameters the callee could set the parameter to a new value
9305+
// with a lifetime that the caller is not aware of. So unless there is
9306+
// explicit lifetime info for the parameter, we can't borrow the lifetime
9307+
// of an inout.
9308+
if (!swiftParam->isInOut() ||
9309+
isLifetimeTarget(MappedDecl, swiftParam, index)) {
9310+
printer.printLifetimeboundReturn(index, shouldBorrowLifetime);
9311+
paramHasLifetimeInfo = true;
9312+
returnHasLifetimeInfo = true;
9313+
}
92789314
}
92799315
if (paramIsStdSpan && paramHasLifetimeInfo)
92809316
attachMacro = true;

test/Interop/C/swiftify-import/counted-by-noescape.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,25 @@
1111

1212
import CountedByNoEscapeClang
1313

14-
// CHECK: @available(macOS 9999, *)
14+
// CHECK: @available(macOS {{.*}}, *)
1515
// CHECK-NEXT: @lifetime(p: copy p)
1616
// CHECK-NEXT: @_alwaysEmitIntoClient public func complexExpr(_ len: Int32, _ offset: Int32, _ p: inout MutableSpan<Int32>)
17-
// CHECK-NEXT: @available(macOS 9999, *)
17+
// CHECK-NEXT: @available(macOS {{.*}}, *)
1818
// CHECK-NEXT: @lifetime(p: copy p)
1919
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ p: inout MutableSpan<Int32>)
20-
// CHECK-NEXT: @available(macOS 9999, *)
20+
// CHECK-NEXT: @available(macOS {{.*}}, *)
2121
// CHECK-NEXT: @lifetime(p: copy p)
2222
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ p: inout MutableSpan<Int32>)
23-
// CHECK-NEXT: @available(macOS 9999, *)
23+
// CHECK-NEXT: @available(macOS {{.*}}, *)
2424
// CHECK-NEXT: @lifetime(copy p)
2525
// CHECK-NEXT: @lifetime(p: copy p)
2626
// CHECK-NEXT: @_alwaysEmitIntoClient public func returnLifetimeBound(_ len1: Int32, _ p: inout MutableSpan<Int32>) -> MutableSpan<Int32>
2727
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func returnPointer(_ len: Int32) -> UnsafeMutableBufferPointer<Int32>
28-
// CHECK-NEXT: @available(macOS 9999, *)
28+
// CHECK-NEXT: @available(macOS {{.*}}, *)
2929
// CHECK-NEXT: @lifetime(p1: copy p1)
3030
// CHECK-NEXT: @lifetime(p2: copy p2)
3131
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int32, _ p1: inout MutableSpan<Int32>, _ p2: inout MutableSpan<Int32>)
32-
// CHECK-NEXT: @available(macOS 9999, *)
32+
// CHECK-NEXT: @available(macOS {{.*}}, *)
3333
// CHECK-NEXT: @lifetime(p: copy p)
3434
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ p: inout MutableSpan<Int32>)
3535
// CHECK-NEXT: @available(macOS 9999, *)

test/Interop/C/swiftify-import/sized-by-noescape.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99
// Check that ClangImporter correctly infers and expands @_SwiftifyImport macros for functions with __sized_by __noescape parameters.
1010
import SizedByNoEscapeClang
1111

12-
// CHECK: @available(macOS 9999, *)
12+
// CHECK: @available(macOS {{.*}}, *)
1313
// CHECK-NEXT: @_alwaysEmitIntoClient public func complexExpr(_ len: Int{{.*}}, _ offset: Int{{.*}}, _ p: RawSpan)
14-
// CHECK-NEXT: @available(macOS 9999, *)
14+
// CHECK-NEXT: @available(macOS {{.*}}, *)
1515
// CHECK-NEXT: @_alwaysEmitIntoClient public func nonnull(_ p: RawSpan)
16-
// CHECK-NEXT: @available(macOS 9999, *)
16+
// CHECK-NEXT: @available(macOS {{.*}}, *)
1717
// CHECK-NEXT: @_alwaysEmitIntoClient public func nullUnspecified(_ p: RawSpan)
18-
// CHECK-NEXT: @available(macOS 9999, *)
18+
// CHECK-NEXT: @available(macOS {{.*}}, *)
1919
// CHECK-NEXT: @_alwaysEmitIntoClient public func opaque(_ p: RawSpan)
2020
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func returnPointer(_ len: Int{{.*}}) -> UnsafeRawBufferPointer
21-
// CHECK-NEXT: @available(macOS 9999, *)
21+
// CHECK-NEXT: @available(macOS {{.*}}, *)
2222
// CHECK-NEXT: @_alwaysEmitIntoClient public func shared(_ len: Int{{.*}}, _ p1: RawSpan, _ p2: RawSpan)
23-
// CHECK-NEXT: @available(macOS 9999, *)
23+
// CHECK-NEXT: @available(macOS {{.*}}, *)
2424
// CHECK-NEXT: @_alwaysEmitIntoClient public func simple(_ p: RawSpan)
2525
// CHECK-NEXT: @available(macOS 9999, *)
2626
// CHECK-NEXT: @_alwaysEmitIntoClient public func swiftAttr(_ p: RawSpan)

test/Interop/Cxx/stdlib/std-span-interface.swift

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import StdSpan
1313
import CxxStdlib
1414

1515
// CHECK: struct DependsOnSelf {
16-
// CHECK: @lifetime(borrow self)
16+
// CHECK: @available(macOS {{.*}}, *)
17+
// CHECK-NEXT: @lifetime(borrow self)
1718
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public borrowing func get() -> Span<CInt>
1819
// CHECK-NEXT: borrowing func get() -> ConstSpanOfInt
1920

@@ -23,51 +24,66 @@ import CxxStdlib
2324
// CHECK-NEXT: func funcWithSafeWrapper3(_ v: borrowing VecOfInt) -> ConstSpanOfInt
2425
// CHECK: struct X {
2526
// CHECK-NEXT: init()
27+
// CHECK-NEXT: @available(macOS {{.*}}, *)
2628
// CHECK-NEXT: @_alwaysEmitIntoClient public mutating func methodWithSafeWrapper(_ s: Span<CInt>)
2729
// CHECK-NEXT: mutating func methodWithSafeWrapper(_ s: ConstSpanOfInt)
2830
// CHECK-NEXT: }
2931
// CHECK: struct SpanWithoutTypeAlias {
3032
// CHECK-NEXT: init()
33+
// CHECK-NEXT: @available(macOS {{.*}}, *)
3134
// CHECK-NEXT: @lifetime(borrow self)
3235
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public mutating func bar() -> Span<CInt>
3336
// CHECK-NEXT: mutating func bar() -> std.{{.*}}span<__cxxConst<CInt>, _C{{.*}}_{{.*}}>
37+
// CHECK-NEXT: @available(macOS {{.*}}, *)
3438
// CHECK-NEXT: @_alwaysEmitIntoClient public mutating func foo(_ s: Span<CInt>)
3539
// CHECK-NEXT: mutating func foo(_ s: std.{{.*}}span<__cxxConst<CInt>, _C{{.*}}_{{.*}}>)
3640
// CHECK-NEXT: }
3741

38-
// CHECK: @lifetime(s: copy s)
42+
// CHECK: @available(macOS {{.*}}, *)
43+
// CHECK-NEXT: @lifetime(s: copy s)
3944
// CHECK-NEXT: @_alwaysEmitIntoClient public func FuncWithMutableSafeWrapper(_ s: inout MutableSpan<CInt>)
45+
// CHECK-NEXT: @available(macOS {{.*}}, *)
4046
// CHECK-NEXT: @lifetime(copy s)
4147
// CHECK-NEXT: @lifetime(s: copy s)
4248
// CHECK-NEXT: @_alwaysEmitIntoClient public func FuncWithMutableSafeWrapper2(_ s: inout MutableSpan<CInt>) -> MutableSpan<CInt>
43-
// CHECK-NEXT: @lifetime(borrow v)
44-
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func FuncWithMutableSafeWrapper3(_ v: inout VecOfInt) -> MutableSpan<CInt>
49+
// CHECK-NOT: FuncWithMutableSafeWrapper3
50+
// CHECK-NEXT: @available(macOS {{.*}}, *)
4551
// CHECK-NEXT: @lifetime(copy p)
4652
// CHECK-NEXT: @lifetime(p: copy p)
4753
// CHECK-NEXT: @_alwaysEmitIntoClient public func MixedFuncWithMutableSafeWrapper1(_ p: inout MutableSpan<Int32>) -> MutableSpan<CInt>
48-
// CHECK-NEXT: @lifetime(borrow v)
49-
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func MixedFuncWithMutableSafeWrapper2(_ v: inout VecOfInt, _ len: Int32) -> MutableSpan<Int32>
54+
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func MixedFuncWithMutableSafeWrapper2(_ v: inout VecOfInt, _ len: Int32) -> UnsafeMutableBufferPointer<Int32>
55+
// CHECK-NEXT: @available(macOS {{.*}}, *)
5056
// CHECK-NEXT: @lifetime(s: copy s)
5157
// CHECK-NEXT: @_alwaysEmitIntoClient public func MixedFuncWithMutableSafeWrapper3(_ s: inout MutableSpan<CInt>, _ p: UnsafeMutableBufferPointer<Int32>)
58+
// CHECK-NEXT: @available(macOS {{.*}}, *)
5259
// CHECK-NEXT: @lifetime(s: copy s)
5360
// CHECK-NEXT: @lifetime(p: copy p)
5461
// CHECK-NEXT: @_alwaysEmitIntoClient public func MixedFuncWithMutableSafeWrapper4(_ s: inout MutableSpan<CInt>, _ p: inout MutableSpan<Int32>)
62+
// CHECK-NEXT: @available(macOS {{.*}}, *)
5563
// CHECK-NEXT: @lifetime(p: copy p)
5664
// CHECK-NEXT: @_alwaysEmitIntoClient public func MixedFuncWithMutableSafeWrapper5(_ s: SpanOfInt, _ p: inout MutableSpan<Int32>)
5765
// CHECK-NEXT: @_alwaysEmitIntoClient public func MixedFuncWithMutableSafeWrapper6(_ s: SpanOfInt, _ p: UnsafeMutableBufferPointer<Int32>)
5866
// CHECK-NEXT: @_alwaysEmitIntoClient public func MixedFuncWithMutableSafeWrapper7(_ p: UnsafeMutableBufferPointer<Int32>) -> SpanOfInt
5967

60-
// CHECK: @_alwaysEmitIntoClient public func funcWithSafeWrapper(_ s: Span<CInt>)
68+
// CHECK: @available(macOS {{.*}}, *)
69+
// CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper(_ s: Span<CInt>)
70+
// CHECK-NEXT: @available(macOS {{.*}}, *)
6171
// CHECK-NEXT: @lifetime(copy s)
6272
// CHECK-NEXT: @_alwaysEmitIntoClient public func funcWithSafeWrapper2(_ s: Span<CInt>) -> Span<CInt>
73+
// CHECK-NEXT: @available(macOS {{.*}}, *)
6374
// CHECK-NEXT: @lifetime(borrow v)
6475
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func funcWithSafeWrapper3(_ v: borrowing VecOfInt) -> Span<CInt>
76+
// CHECK-NEXT: @available(macOS {{.*}}, *)
6577
// CHECK-NEXT: @lifetime(copy p)
6678
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper1(_ p: Span<Int32>) -> Span<CInt>
79+
// CHECK-NEXT: @available(macOS {{.*}}, *)
6780
// CHECK-NEXT: @lifetime(borrow v)
6881
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public func mixedFuncWithSafeWrapper2(_ v: borrowing VecOfInt, _ len: Int32) -> Span<Int32>
82+
// CHECK-NEXT: @available(macOS {{.*}}, *)
6983
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper3(_ s: Span<CInt>, _ p: UnsafeMutableBufferPointer<Int32>)
84+
// CHECK-NEXT: @available(macOS {{.*}}, *)
7085
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper4(_ s: Span<CInt>, _ p: Span<Int32>)
86+
// CHECK-NEXT: @available(macOS {{.*}}, *)
7187
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper5(_ s: ConstSpanOfInt, _ p: Span<Int32>)
7288
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper6(_ s: ConstSpanOfInt, _ p: UnsafeMutableBufferPointer<Int32>)
7389
// CHECK-NEXT: @_alwaysEmitIntoClient public func mixedFuncWithSafeWrapper7(_ p: UnsafeBufferPointer<Int32>) -> ConstSpanOfInt

0 commit comments

Comments
 (0)