Skip to content

Commit b06631e

Browse files
committed
[cxx-interop] Remove annotationOnly flag from Escapability request
1 parent c73951b commit b06631e

File tree

5 files changed

+118
-58
lines changed

5 files changed

+118
-58
lines changed

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -531,18 +531,14 @@ enum class CxxEscapability { Escapable, NonEscapable, Unknown };
531531
struct EscapabilityLookupDescriptor final {
532532
const clang::Type *type;
533533
ClangImporter::Implementation *impl;
534-
// Only explicitly ~Escapable annotated types are considered ~Escapable.
535-
// This is for backward compatibility, so we continue to import aggregates
536-
// containing pointers as Escapable types.
537-
bool annotationOnly = true;
538534

539535
friend llvm::hash_code hash_value(const EscapabilityLookupDescriptor &desc) {
540536
return llvm::hash_combine(desc.type);
541537
}
542538

543539
friend bool operator==(const EscapabilityLookupDescriptor &lhs,
544540
const EscapabilityLookupDescriptor &rhs) {
545-
return lhs.type == rhs.type && lhs.annotationOnly == rhs.annotationOnly;
541+
return lhs.type == rhs.type;
546542
}
547543

548544
friend bool operator!=(const EscapabilityLookupDescriptor &lhs,

lib/ClangImporter/ClangImporter.cpp

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5493,12 +5493,18 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
54935493

54945494
// Escapability inference rules:
54955495
// - array and vector types have the same escapability as their element type
5496-
// - pointer and reference types are currently imported as escapable
5496+
// - pointer and reference types are currently imported as unknown
54975497
// (importing them as non-escapable broke backward compatibility)
54985498
// - a record type is escapable or non-escapable if it is explicitly annotated
54995499
// as such
55005500
// - a record type is escapable if it is annotated with SWIFT_ESCAPABLE_IF()
55015501
// and none of the annotation arguments are non-escapable
5502+
// - an aggregate or non-cxx record is escapable if none of their fields or
5503+
// bases are non-escapable (as long as they have a definition)
5504+
// * we only infer escapability for simple types, with no user-declared
5505+
// constructors, virtual bases or virtual member functions
5506+
// * for more complex CxxRecordDecls, we rely solely on escapability
5507+
// annotations
55025508
// - in all other cases, the record has unknown escapability (e.g. no
55035509
// escapability annotations, malformed escapability annotations)
55045510

@@ -5554,14 +5560,8 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
55545560

55555561
continue;
55565562
}
5557-
// The `annotationOnly` flag used to control which types we infered
5558-
// escapability for. Currently, this flag is always set to true, meaning
5559-
// that any type without an annotation (CxxRecordDecls, aggregates, decls
5560-
// lacking definition, etc.) will raise `hasUnknown`.
5561-
if (desc.annotationOnly) {
5562-
hasUnknown = true;
5563-
continue;
5564-
}
5563+
// Only try to infer escapability if the record doesn't have any
5564+
// escapability annotations
55655565
auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl);
55665566
if (recordDecl->getDefinition() &&
55675567
(!cxxRecordDecl || cxxRecordDecl->isAggregate())) {
@@ -5571,7 +5571,11 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
55715571
}
55725572
for (auto field : recordDecl->fields())
55735573
maybePushToStack(field->getType()->getUnqualifiedDesugaredType());
5574-
continue;
5574+
} else {
5575+
// We only infer escapability for simple types, such as aggregates and
5576+
// RecordDecls that are not CxxRecordDecls. For more complex
5577+
// CxxRecordDecls, we rely solely on escapability annotations.
5578+
hasUnknown = true;
55755579
}
55765580
} else if (type->isArrayType()) {
55775581
auto elemTy = cast<clang::ArrayType>(type)
@@ -5582,10 +5586,9 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
55825586
maybePushToStack(vecTy->getElementType()->getUnqualifiedDesugaredType());
55835587
} else if (type->isAnyPointerType() || type->isBlockPointerType() ||
55845588
type->isMemberPointerType() || type->isReferenceType()) {
5585-
if (desc.annotationOnly)
5586-
hasUnknown = true;
5587-
else
5588-
return CxxEscapability::NonEscapable;
5589+
// pointer and reference types are currently imported as unknown
5590+
// (importing them as non-escapable broke backward compatibility)
5591+
hasUnknown = true;
55895592
}
55905593
}
55915594
return hasUnknown ? CxxEscapability::Unknown : CxxEscapability::Escapable;

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

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -118,34 +118,31 @@ struct SWIFT_ESCAPABLE Invalid {
118118

119119
struct SWIFT_NONESCAPABLE NonEscapable {};
120120

121-
template<typename T>
122-
struct HasAnonUnion {
123-
union {
124-
int known;
125-
T unknown;
126-
};
127-
};
121+
using NonEscapableOptional = std::optional<NonEscapable>;
128122

129-
template<typename T>
130-
struct HasAnonStruct {
131-
struct {
132-
int known;
133-
T unknown;
134-
};
135-
};
123+
// Infered as non-escapable
124+
struct Aggregate {
125+
int a;
126+
View b;
127+
bool c;
136128

137-
template<typename T>
138-
struct SWIFT_NONESCAPABLE NonEscapableHasAnonUnion {
139-
union {
140-
int known;
141-
T unknown;
142-
};
143-
};
129+
void someMethod() {}
130+
};
144131

145-
using HasAnonUnionNonEscapable = HasAnonUnion<NonEscapable>;
146-
using HasAnonStructNonEscapable = HasAnonStruct<NonEscapable>;
147-
using NonEscapableHasAnonUnionNonEscapable = NonEscapableHasAnonUnion<NonEscapable>;
148-
using NonEscapableOptional = std::optional<NonEscapable>;
132+
// This is a complex record (has user-declared constructors), so we don't infer escapability.
133+
// By default, it's imported as escapable, which generates an error
134+
// because of the non-escapable field 'View'
135+
struct ComplexRecord {
136+
int a;
137+
View b;
138+
bool c;
139+
140+
ComplexRecord() : a(1), b(), c(false) {}
141+
ComplexRecord(const ComplexRecord &other) = default;
142+
};
143+
144+
Aggregate m1();
145+
ComplexRecord m2();
149146

150147
//--- test.swift
151148
import Test
@@ -233,20 +230,24 @@ public func test3(_ x: inout View) {
233230
// CHECK-NO-LIFETIMES: pointer to non-escapable type 'View' cannot be imported
234231
}
235232

236-
public func anonymousUnions() {
237-
_ = HasAnonUnionNonEscapable()
238-
// CHECK: error: cannot find 'HasAnonUnionNonEscapable' in scope
239-
// CHECK-NO-LIFETIMES: error: cannot find 'HasAnonUnionNonEscapable' in scope
240-
_ = HasAnonStructNonEscapable()
241-
// CHECK: error: cannot find 'HasAnonStructNonEscapable' in scope
242-
// CHECK-NO-LIFETIMES: error: cannot find 'HasAnonStructNonEscapable' in scope
243-
_ = NonEscapableHasAnonUnionNonEscapable()
233+
public func optional() {
244234
_ = NonEscapableOptional()
245235
// CHECK: error: cannot infer the lifetime dependence scope on an initializer with a ~Escapable parameter, specify '@_lifetime(borrow {{.*}})' or '@_lifetime(copy {{.*}})'
246236
// CHECK-NO-LIFETIMES: error: an initializer cannot return a ~Escapable result
247237
// CHECK-NO-LIFETIMES: error: an initializer cannot return a ~Escapable result
248238
}
249239

240+
public func inferedEscapability() {
241+
m1()
242+
// CHECK: nonescapable.h:130:11: error: a function with a ~Escapable result needs a parameter to depend on
243+
// CHECK-NO-LIFETIMES: nonescapable.h:130:11: error: a function cannot return a ~Escapable result
244+
m2()
245+
// CHECK: error: 'm2()' is unavailable: return type is unavailable in Swift
246+
// CHECK: note: 'm2()' has been explicitly marked unavailable here
247+
// CHECK-NO-LIFETIMES: error: 'm2()' is unavailable: return type is unavailable in Swift
248+
// CHECK-NO-LIFETIMES: note: 'm2()' has been explicitly marked unavailable here
249+
}
250+
250251
// CHECK-NOT: error
251252
// CHECK-NOT: warning
252253
// CHECK-NO-LIFETIMES-NOT: error

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,56 @@ using ReadonlyBytes = ReadonlySpan<unsigned char>;
166166
using Bytes = Span<unsigned char>;
167167
} // namespace rdar153081347
168168

169+
struct SWIFT_NONESCAPABLE NonEscapable {
170+
const int *p;
171+
};
172+
173+
template<typename T>
174+
struct HasAnonUnion {
175+
union {
176+
int known;
177+
T unknown;
178+
};
179+
};
180+
181+
template<typename T>
182+
struct HasAnonStruct {
183+
struct {
184+
int known;
185+
T unknown;
186+
};
187+
};
188+
189+
template<typename T>
190+
struct SWIFT_NONESCAPABLE NonEscapableHasAnonUnion {
191+
union {
192+
int known;
193+
T unknown;
194+
};
195+
};
196+
197+
using HasAnonUnionNonEscapable = HasAnonUnion<NonEscapable>;
198+
using HasAnonStructNonEscapable = HasAnonStruct<NonEscapable>;
199+
using NonEscapableHasAnonUnionNonEscapable = NonEscapableHasAnonUnion<NonEscapable>;
200+
201+
HasAnonUnionNonEscapable makeAnonUnionNonEscapable(const Owner &owner [[clang::lifetimebound]]) {
202+
HasAnonUnionNonEscapable result;
203+
result.unknown = {&owner.data};
204+
return result;
205+
}
206+
207+
HasAnonStructNonEscapable makeAnonStructNonEscapable(const Owner &owner [[clang::lifetimebound]]) {
208+
return {1, &owner.data};
209+
}
210+
211+
NonEscapableHasAnonUnionNonEscapable makeNonEscapableHasAnonUnionNonEscapable(
212+
const Owner &owner [[clang::lifetimebound]]) {
213+
NonEscapableHasAnonUnionNonEscapable result;
214+
result.unknown = {&owner.data};
215+
return result;
216+
}
217+
218+
169219
// CHECK: sil {{.*}}[clang makeOwner] {{.*}}: $@convention(c) () -> Owner
170220
// CHECK: sil {{.*}}[clang getView] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> @lifetime(borrow address 0) @owned View
171221
// CHECK: sil {{.*}}[clang getViewFromFirst] {{.*}} : $@convention(c) (@in_guaranteed Owner, @in_guaranteed Owner) -> @lifetime(borrow address 0) @owned View
@@ -182,6 +232,9 @@ using Bytes = Span<unsigned char>;
182232
// CHECK: sil {{.*}}[clang CaptureView.handOut] {{.*}} : $@convention(cxx_method) (@lifetime(copy 1) @inout View, @in_guaranteed CaptureView) -> ()
183233
// CHECK: sil {{.*}}[clang NS.getView] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> @lifetime(borrow address 0) @owned View
184234
// CHECK: sil {{.*}}[clang moveOnlyId] {{.*}} : $@convention(c) (@in_guaranteed MoveOnly) -> @lifetime(borrow {{.*}}0) @out MoveOnly
235+
// CHECK: sil {{.*}}[clang makeAnonUnionNonEscapable] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> @lifetime(borrow address 0) @owned HasAnonUnion<NonEscapable>
236+
// CHECK: sil {{.*}}[clang makeAnonStructNonEscapable] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> @lifetime(borrow address 0) @owned HasAnonStruct<NonEscapable>
237+
// CHECK: sil {{.*}}[clang makeNonEscapableHasAnonUnionNonEscapable] {{.*}} : $@convention(c) (@in_guaranteed Owner) -> @lifetime(borrow address 0) @owned NonEscapableHasAnonUnion<NonEscapable>
185238

186239
//--- test.swift
187240

@@ -215,3 +268,10 @@ func canImportMoveOnlyNonEscapable(_ x: borrowing MoveOnly) {
215268
}
216269

217270
func testInheritedCtors(_ s: rdar153081347.Bytes) {}
271+
272+
func anonymousUnionsAndStructs(_ v: borrowing View) {
273+
let o = makeOwner()
274+
let _ = makeAnonUnionNonEscapable(o)
275+
let _ = makeAnonStructNonEscapable(o)
276+
let _ = makeNonEscapableHasAnonUnionNonEscapable(o)
277+
}

test/Interop/Cxx/class/safe-interop-mode.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,10 @@ struct HoldsShared {
101101
SWIFT_RETURNS_UNRETAINED;
102102
};
103103

104-
template <typename, typename> struct TTake2 {};
105-
template <typename T> struct PassThru {};
104+
template <typename F, typename S> struct SWIFT_ESCAPABLE_IF(F, S) TTake2 {};
105+
template <typename T> struct PassThru {
106+
T field;
107+
};
106108
struct IsUnsafe { int *p; };
107109
struct HasUnsafe : TTake2<PassThru<HasUnsafe>, IsUnsafe> {};
108110
using AlsoUnsafe = PassThru<HasUnsafe>;
@@ -207,17 +209,15 @@ func useTTakeInt(x: TTakeInt) {
207209
}
208210

209211
func useTTakePtr(x: TTakePtr) {
210-
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
211-
_ = x // expected-note{{reference to parameter 'x' involves unsafe type}}
212+
_ = x
212213
}
213214

214215
func useTTakeSafeTuple(x: TTakeSafeTuple) {
215216
_ = x
216217
}
217218

218219
func useTTakeUnsafeTuple(x: TTakeUnsafeTuple) {
219-
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
220-
_ = x // expected-note{{reference to parameter 'x' involves unsafe type}}
220+
_ = x
221221
}
222222

223223
func useTTakeUnsafeTuple(x: HasUnsafe) {

0 commit comments

Comments
 (0)