Skip to content

Commit e79e04c

Browse files
author
Gabor Horvath
committed
[cxx-interop] Mark C++ reference parameters @Addressable
C++ code can return values that depend on the storage that backs the references that were passed in as argument. Thus, swift should not introdue temporary copies of that storage before invoking those functions as they could result in lifetime issues.
1 parent a3fac71 commit e79e04c

File tree

5 files changed

+40
-2
lines changed

5 files changed

+40
-2
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3946,6 +3946,9 @@ namespace {
39463946
}
39473947
if (selfIdx) {
39483948
func->setSelfIndex(selfIdx.value());
3949+
if (Impl.SwiftContext.LangOpts.hasFeature(
3950+
Feature::AddressableParameters))
3951+
func->getImplicitSelfDecl()->setAddressable();
39493952
} else {
39503953
func->setStatic();
39513954
func->setImportAsStaticMember();

lib/ClangImporter/ImportType.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2676,17 +2676,25 @@ static ParamDecl *getParameterInfo(ClangImporter::Implementation *impl,
26762676
impl->importSourceLoc(param->getLocation()), bodyName,
26772677
impl->ImportedHeaderUnit);
26782678

2679+
auto &ASTContext = paramInfo->getASTContext();
26792680
// If SendingArgsAndResults are enabled and we have a sending argument,
26802681
// set that the param was sending.
2681-
if (paramInfo->getASTContext().LangOpts.hasFeature(
2682-
Feature::SendingArgsAndResults)) {
2682+
if (ASTContext.LangOpts.hasFeature(Feature::SendingArgsAndResults)) {
26832683
if (auto *attr = param->getAttr<clang::SwiftAttrAttr>()) {
26842684
if (attr->getAttribute() == "sending") {
26852685
paramInfo->setSending();
26862686
}
26872687
}
26882688
}
26892689

2690+
// C++ types taking a reference might return a reference/pointer to a
2691+
// subobject of the referenced storage. In those cases we need to prevent the
2692+
// Swift compiler to pass in a temporary copy to prevent dangling.
2693+
if (ASTContext.LangOpts.hasFeature(Feature::AddressableParameters) &&
2694+
!param->getType().isNull() && param->getType()->isReferenceType()) {
2695+
paramInfo->setAddressable();
2696+
}
2697+
26902698
// Parameters of type const T& imported as T, make sure we borrow from them
26912699
// when they have lifetime annotations.
26922700
bool isBorrowing = (param->getAttr<clang::LifetimeBoundAttr>() ||

test/Interop/Cxx/class/method/Inputs/methods.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ struct HasMethods {
3333

3434
NonTrivialInWrapper nonConstPassThroughAsWrapper(int a) { return {a}; }
3535
NonTrivialInWrapper constPassThroughAsWrapper(int a) const { return {a}; }
36+
37+
void nonTrivialTakesConstRef(const NonTrivialInWrapper& w) const {}
38+
void nonTrivialTakesRef(NonTrivialInWrapper& w) const {}
3639
};
3740

3841
struct ReferenceParams {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
// RUN: %target-swift-emit-silgen -I %S/Inputs -enable-experimental-feature AddressableParameters -enable-experimental-cxx-interop %s | %FileCheck %s
3+
4+
import Methods
5+
6+
// REQUIRES: swift_feature_AddressableParameters
7+
8+
public func addressableTest(x: borrowing @_addressable NonTrivialInWrapper, y: inout NonTrivialInWrapper) {
9+
let m = HasMethods()
10+
m.nonTrivialTakesConstRef(x)
11+
// No copy from the argument to the apply.
12+
// CHECK: addressableTest(x:y:)
13+
// CHECK: bb0([[INPUT:%[0-9]+]] : @noImplicitCopy $*NonTrivialInWrapper, [[INPUT2:%[0-9]+]] : $*NonTrivialInWrapper)
14+
// CHECK: [[WRAPPER:%[0-9]+]] = copyable_to_moveonlywrapper_addr [[INPUT]]
15+
// CHECK: [[MARKED:%[0-9]+]] = mark_unresolved_non_copyable_value [no_consume_or_assign] [[WRAPPER]]
16+
// CHECK: [[UNWRAPPED:%[0-9]+]] = moveonlywrapper_to_copyable_addr [[MARKED]]
17+
// CHECK: %{{[0-9]+}} = apply %{{[0-9]+}}([[UNWRAPPED]], %{{[0-9]+}}) : $@convention(cxx_method) (@in_guaranteed NonTrivialInWrapper, @in_guaranteed HasMethods) -> ()
18+
var m2 = HasMethods()
19+
// CHECK: [[ACCESS:%[0-9]+]] = begin_access [modify] [unknown] [[INPUT2]]
20+
// CHECK: %{{[0-9]+}} = apply %32([[ACCESS]], %{{[0-9]+}}) : $@convention(cxx_method) (@inout NonTrivialInWrapper, @in_guaranteed HasMethods) -> ()
21+
// CHECK-NEXT: end_access [[ACCESS]]
22+
m2.nonTrivialTakesRef(&y)
23+
}

test/Interop/Cxx/class/nonescapable-lifetimebound.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// RUN: %target-swift-frontend -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs -emit-sil %t/test.swift -enable-experimental-feature LifetimeDependence -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s
44
// RUN: not %target-swift-frontend -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs -emit-sil %t/test.swift -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s -check-prefix=CHECK-NO-LIFETIMES
55

6+
// REQUIRES: swift_feature_LifetimeDependence
67

78
//--- Inputs/module.modulemap
89
module Test {

0 commit comments

Comments
 (0)