Skip to content

Commit 1f65ee2

Browse files
committed
Distinguish between withoutActuallyEscaping and passing @NoEscape
Objective C closures when reporting that a closure has escaped rdar://39682865
1 parent 9048e97 commit 1f65ee2

File tree

20 files changed

+109
-32
lines changed

20 files changed

+109
-32
lines changed

docs/SIL.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2892,7 +2892,7 @@ Performs a copy of an Objective-C block. Unlike retains of other
28922892
reference-counted types, this can produce a different value from the operand if
28932893
the block is copied from the stack to the heap.
28942894

2895-
Additionally consumes the ``withoutEscaping`` operand ``%1`` which is the
2895+
Additionally, consumes the ``withoutEscaping`` operand ``%1`` which is the
28962896
closure sentinel. SILGen emits these instructions when it passes @noescape
28972897
swift closures to Objective C. A mandatory SIL pass will lower this instruction
28982898
into a ``copy_block`` and a ``is_escaping``/``cond_fail``/``destroy_value`` at

include/swift/Runtime/HeapObject.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,19 @@ bool swift_isUniquelyReferencedOrPinned_nonNull_native(
286286
/// one.
287287
/// This runtime call will print an error message with file name and location if
288288
/// the closure is escaping but it will not abort.
289+
///
290+
/// \p type: 0 - withoutActuallyEscaping verification
291+
/// Was the closure passed to a withoutActuallyEscaping block
292+
/// escaped in the block?
293+
/// 1 - @objc closure sentinel verfication
294+
/// Was the closure passed to Objective-C escaped?
289295
SWIFT_RUNTIME_EXPORT
290296
bool swift_isEscapingClosureAtFileLocation(const struct HeapObject *object,
291297
const unsigned char *filename,
292298
int32_t filenameLength,
293-
int32_t line);
299+
int32_t line,
300+
int32_t column,
301+
unsigned type);
294302

295303
/// Deallocate the given memory.
296304
///

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,11 +680,13 @@ FUNCTION(IsUniquelyReferencedOrPinned_nonNull_native,
680680
// bool swift_isEscapingClosureAtFileLocation(const struct HeapObject *object,
681681
// const unsigned char *filename,
682682
// int32_t filenameLength,
683-
// int32_t line);
683+
// int32_t line,
684+
// int32_t col,
685+
// unsigned type);
684686
FUNCTION(IsEscapingClosureAtFileLocation, swift_isEscapingClosureAtFileLocation,
685687
C_CC,
686688
RETURNS(Int1Ty),
687-
ARGS(RefCountedPtrTy, Int8PtrTy, Int32Ty, Int32Ty),
689+
ARGS(RefCountedPtrTy, Int8PtrTy, Int32Ty, Int32Ty, Int32Ty, Int32Ty),
688690
ATTRS(NoUnwind, ZExt))
689691

690692
// void swift_arrayInitWithCopy(opaque*, opaque*, size_t, type*);

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,10 +1569,11 @@ class SILBuilder {
15691569
getSILDebugLocation(Loc), value, Int1Ty));
15701570
}
15711571
IsEscapingClosureInst *createIsEscapingClosure(SILLocation Loc,
1572-
SILValue operand) {
1572+
SILValue operand,
1573+
unsigned VerificationType) {
15731574
auto Int1Ty = SILType::getBuiltinIntegerType(1, getASTContext());
15741575
return insert(new (getModule()) IsEscapingClosureInst(
1575-
getSILDebugLocation(Loc), operand, Int1Ty));
1576+
getSILDebugLocation(Loc), operand, Int1Ty, VerificationType));
15761577
}
15771578

15781579
DeallocStackInst *createDeallocStack(SILLocation Loc, SILValue operand) {

include/swift/SIL/SILCloner.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,8 +2031,9 @@ void SILCloner<ImplClass>::visitIsEscapingClosureInst(
20312031
IsEscapingClosureInst *Inst) {
20322032
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
20332033
doPostProcess(
2034-
Inst, getBuilder().createIsEscapingClosure(
2035-
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand())));
2034+
Inst, getBuilder().createIsEscapingClosure(getOpLocation(Inst->getLoc()),
2035+
getOpValue(Inst->getOperand()),
2036+
Inst->getVerificationType()));
20362037
}
20372038

20382039
template<typename ImplClass>

include/swift/SIL/SILInstruction.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6341,9 +6341,17 @@ class IsEscapingClosureInst
63416341
SingleValueInstruction> {
63426342
friend SILBuilder;
63436343

6344+
unsigned VerificationType;
6345+
63446346
IsEscapingClosureInst(SILDebugLocation DebugLoc, SILValue Operand,
6345-
SILType BoolTy)
6346-
: UnaryInstructionBase(DebugLoc, Operand, BoolTy) {}
6347+
SILType BoolTy, unsigned VerificationType)
6348+
: UnaryInstructionBase(DebugLoc, Operand, BoolTy),
6349+
VerificationType(VerificationType) {}
6350+
6351+
public:
6352+
enum { WithoutActuallyEscaping, ObjCEscaping };
6353+
6354+
unsigned getVerificationType() const { return VerificationType; }
63476355
};
63486356

63496357
//===----------------------------------------------------------------------===//

lib/IRGen/GenHeap.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,16 +1433,18 @@ emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull,
14331433
return call;
14341434
}
14351435

1436-
llvm::Value *IRGenFunction::emitIsEscapingClosureCall(llvm::Value *value,
1437-
SourceLoc sourceLoc) {
1436+
llvm::Value *IRGenFunction::emitIsEscapingClosureCall(
1437+
llvm::Value *value, SourceLoc sourceLoc, unsigned verificationType) {
14381438
auto loc = SILLocation::decode(sourceLoc, IGM.Context.SourceMgr);
14391439
auto line = llvm::ConstantInt::get(IGM.Int32Ty, loc.Line);
1440+
auto col = llvm::ConstantInt::get(IGM.Int32Ty, loc.Column);
14401441
auto filename = IGM.getAddrOfGlobalString(loc.Filename);
14411442
auto filenameLength =
14421443
llvm::ConstantInt::get(IGM.Int32Ty, loc.Filename.size());
1444+
auto type = llvm::ConstantInt::get(IGM.Int32Ty, verificationType);
14431445
llvm::CallInst *call =
14441446
Builder.CreateCall(IGM.getIsEscapingClosureAtFileLocationFn(),
1445-
{value, filename, filenameLength, line});
1447+
{value, filename, filenameLength, line, col, type});
14461448
call->setDoesNotThrow();
14471449
return call;
14481450
}

lib/IRGen/IRGenFunction.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,11 @@ class IRGenFunction {
445445
llvm::Value *emitIsUniqueCall(llvm::Value *value, SourceLoc loc,
446446
bool isNonNull, bool checkPinned);
447447

448-
llvm::Value *emitIsEscapingClosureCall(llvm::Value *value, SourceLoc loc);
448+
llvm::Value *emitIsEscapingClosureCall(llvm::Value *value, SourceLoc loc,
449+
unsigned verificationType);
449450

450-
//--- Expression emission ------------------------------------------------------
451+
//--- Expression emission
452+
//------------------------------------------------------
451453
public:
452454
void emitFakeExplosion(const TypeInfo &type, Explosion &explosion);
453455

lib/IRGen/IRGenSIL.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3919,7 +3919,8 @@ void IRGenSILFunction::visitIsEscapingClosureInst(
39193919
assert(closure.empty());
39203920
if (context->getType()->isIntegerTy())
39213921
context = Builder.CreateIntToPtr(context, IGM.RefCountedPtrTy);
3922-
auto result = emitIsEscapingClosureCall(context, i->getLoc().getSourceLoc());
3922+
auto result = emitIsEscapingClosureCall(context, i->getLoc().getSourceLoc(),
3923+
i->getVerificationType());
39233924
Explosion out;
39243925
out.add(result);
39253926
setLoweredExplosion(i, out);

lib/ParseSIL/ParseSIL.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2687,7 +2687,6 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
26872687
UNARY_INSTRUCTION(CopyBlock)
26882688
UNARY_INSTRUCTION(IsUnique)
26892689
UNARY_INSTRUCTION(IsUniqueOrPinned)
2690-
UNARY_INSTRUCTION(IsEscapingClosure)
26912690
UNARY_INSTRUCTION(DestroyAddr)
26922691
UNARY_INSTRUCTION(CopyValue)
26932692
UNARY_INSTRUCTION(CopyUnownedValue)
@@ -2715,6 +2714,20 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
27152714
#undef UNARY_INSTRUCTION
27162715
#undef REFCOUNTING_INSTRUCTION
27172716

2717+
case SILInstructionKind::IsEscapingClosureInst: {
2718+
bool IsObjcVerifcationType = false;
2719+
if (parseSILOptional(IsObjcVerifcationType, *this, "objc"))
2720+
return true;
2721+
if (parseTypedValueRef(Val, B) ||
2722+
parseSILDebugLocation(InstLoc, B))
2723+
return true;
2724+
ResultVal = B.createIsEscapingClosure(
2725+
InstLoc, Val,
2726+
IsObjcVerifcationType ? IsEscapingClosureInst::ObjCEscaping
2727+
: IsEscapingClosureInst::WithoutActuallyEscaping);
2728+
break;
2729+
}
2730+
27182731
case SILInstructionKind::DebugValueInst:
27192732
case SILInstructionKind::DebugValueAddrInst: {
27202733
SILDebugVariable VarInfo;

lib/SIL/SILPrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,8 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
18021802
*this << getIDAndType(CUI->getOperand());
18031803
}
18041804
void visitIsEscapingClosureInst(IsEscapingClosureInst *CUI) {
1805+
if (CUI->getVerificationType())
1806+
*this << "[objc] ";
18051807
*this << getIDAndType(CUI->getOperand());
18061808
}
18071809
void visitDeallocStackInst(DeallocStackInst *DI) {

lib/SIL/SILVerifier.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4204,6 +4204,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
42044204
SILFunctionTypeRepresentation::Thick,
42054205
"is_escaping_closure must have a thick "
42064206
"function operand");
4207+
require(IEC->getVerificationType() == IsEscapingClosureInst::ObjCEscaping ||
4208+
IEC->getVerificationType() ==
4209+
IsEscapingClosureInst::WithoutActuallyEscaping,
4210+
"unknown verfication type");
42074211
}
42084212

42094213
// This verifies that the entry block of a SIL function doesn't have

lib/SILGen/SILGenExpr.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5499,8 +5499,9 @@ RValue RValueEmitter::visitMakeTemporarilyEscapableExpr(
54995499
// Now create the verification of the withoutActuallyEscaping operand.
55005500
// Either we fail the uniquenes check (which means the closure has escaped)
55015501
// and abort or we continue and destroy the ultimate reference.
5502-
auto isEscaping =
5503-
SGF.B.createIsEscapingClosure(loc, borrowedClosure.getValue());
5502+
auto isEscaping = SGF.B.createIsEscapingClosure(
5503+
loc, borrowedClosure.getValue(),
5504+
IsEscapingClosureInst::WithoutActuallyEscaping);
55045505
SGF.B.createCondFail(loc, isEscaping);
55055506
return rvalue;
55065507
}

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,8 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *CB) {
441441
for (auto LifetimeEndPoint : LifetimeEndPoints) {
442442
SILBuilderWithScope B(LifetimeEndPoint);
443443
auto IsEscaping =
444-
B.createIsEscapingClosure(Loc, B.createLoadBorrow(generatedLoc, Slot));
444+
B.createIsEscapingClosure(Loc, B.createLoadBorrow(generatedLoc, Slot),
445+
IsEscapingClosureInst::ObjCEscaping);
445446
B.createCondFail(Loc, IsEscaping);
446447
B.createDestroyAddr(generatedLoc, Slot);
447448
}

lib/Serialization/DeserializeSIL.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1616,12 +1616,22 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB,
16161616
REFCOUNTING_INSTRUCTION(UnownedRelease)
16171617
UNARY_INSTRUCTION(IsUnique)
16181618
UNARY_INSTRUCTION(IsUniqueOrPinned)
1619-
UNARY_INSTRUCTION(IsEscapingClosure)
16201619
UNARY_INSTRUCTION(AbortApply)
16211620
UNARY_INSTRUCTION(EndApply)
16221621
#undef UNARY_INSTRUCTION
16231622
#undef REFCOUNTING_INSTRUCTION
16241623

1624+
case SILInstructionKind::IsEscapingClosureInst: {
1625+
assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand.");
1626+
unsigned verificationType = Attr;
1627+
ResultVal = Builder.createIsEscapingClosure(
1628+
Loc,
1629+
getLocalValue(
1630+
ValID, getSILType(MF->getType(TyID), (SILValueCategory)TyCategory)),
1631+
verificationType);
1632+
break;
1633+
}
1634+
16251635
case SILInstructionKind::DestructureTupleInst: {
16261636
assert(RecordKind == SIL_ONE_OPERAND && "Layout should be OneOperand.");
16271637
SILValue Operand = getLocalValue(

lib/Serialization/SerializeSIL.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1253,11 +1253,11 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
12531253
case SILInstructionKind::UnownedReleaseInst:
12541254
case SILInstructionKind::IsUniqueInst:
12551255
case SILInstructionKind::IsUniqueOrPinnedInst:
1256-
case SILInstructionKind::IsEscapingClosureInst:
12571256
case SILInstructionKind::AbortApplyInst:
12581257
case SILInstructionKind::EndApplyInst:
12591258
case SILInstructionKind::ReturnInst:
12601259
case SILInstructionKind::UncheckedOwnershipConversionInst:
1260+
case SILInstructionKind::IsEscapingClosureInst:
12611261
case SILInstructionKind::ThrowInst: {
12621262
unsigned Attr = 0;
12631263
if (auto *LI = dyn_cast<LoadInst>(&SI))
@@ -1274,6 +1274,8 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
12741274
Attr = RCI->isNonAtomic();
12751275
else if (auto *UOCI = dyn_cast<UncheckedOwnershipConversionInst>(&SI)) {
12761276
Attr = unsigned(SILValue(UOCI).getOwnershipKind());
1277+
} else if (auto *IEC = dyn_cast<IsEscapingClosureInst>(&SI)) {
1278+
Attr = IEC->getVerificationType();
12771279
}
12781280
writeOneOperandLayout(SI.getKind(), Attr, SI.getOperand(0));
12791281
break;

stdlib/public/runtime/SwiftObject.mm

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,22 +1419,39 @@ static bool usesNativeSwiftReferenceCounting_nonNull(
14191419
bool swift::swift_isEscapingClosureAtFileLocation(const HeapObject *object,
14201420
const unsigned char *filename,
14211421
int32_t filenameLength,
1422-
int32_t line) {
1422+
int32_t line, int32_t column,
1423+
unsigned verifcationType) {
1424+
assert((verifcationType == 0 || verifcationType == 1) &&
1425+
"Unknown verifcation type");
1426+
14231427
bool isEscaping =
14241428
object != nullptr && !object->refCounts.isUniquelyReferenced();
14251429

14261430
// Print a message if the closure escaped.
14271431
if (isEscaping) {
1428-
auto *message = "Fatal error: closure argument was escaped in "
1429-
"withoutActuallyEscaping block";
1432+
auto *message = (verifcationType == 0)
1433+
? "closure argument was escaped in "
1434+
"withoutActuallyEscaping block"
1435+
: "closure argument passed as @noescape "
1436+
"to Objective-C has escaped";
14301437
auto messageLength = strlen(message);
14311438

1432-
if (_swift_shouldReportFatalErrorsToDebugger())
1433-
_swift_reportToDebugger(RuntimeErrorFlagFatal, message);
1434-
14351439
char *log;
1436-
swift_asprintf(&log, "%.*s: file %.*s, line %" PRIu32 "\n", messageLength,
1437-
message, filenameLength, filename, line);
1440+
swift_asprintf(
1441+
&log, "%.*s: file %.*s, line %" PRIu32 ", column %" PRIu32 " \n",
1442+
messageLength, message, filenameLength, filename, line, column);
1443+
1444+
printCurrentBacktrace(2/*framesToSkip*/);
1445+
1446+
if (_swift_shouldReportFatalErrorsToDebugger()) {
1447+
RuntimeErrorDetails details = {
1448+
.version = RuntimeErrorDetails::currentVersion,
1449+
.errorType = "escaping-closure-violation",
1450+
.currentStackDescription = "Closure has escaped",
1451+
.framesToSkip = 1,
1452+
};
1453+
_swift_reportToDebugger(RuntimeErrorFlagFatal, log, &details);
1454+
}
14381455

14391456
swift_reportError(RuntimeErrorFlagFatal, log);
14401457
free(log);

test/SIL/Parser/basic.sil

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,7 +1480,9 @@ bb0(%0 : $*Builtin.NativeObject):
14801480
sil @is_escaping_closure : $@convention(thin)(@guaranteed @callee_guaranteed () -> ()) -> Builtin.Int1 {
14811481
bb0(%0 : $@callee_guaranteed () -> ()):
14821482
// CHECK: %1 = is_escaping_closure %0 : $@callee_guaranteed () -> ()
1483+
// CHECK: %2 = is_escaping_closure [objc] %0 : $@callee_guaranteed () -> ()
14831484
%1 = is_escaping_closure %0: $@callee_guaranteed () -> ()
1485+
%2 = is_escaping_closure [objc] %0: $@callee_guaranteed () -> ()
14841486
return %1 : $Builtin.Int1
14851487
}
14861488

test/SILOptimizer/closure_lifetime_fixup_objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public protocol DangerousEscaper {
5252

5353
// Release sentinel closure copy (5).
5454
// CHECK: strong_release [[BLOCK_COPY]] : $@convention(block) @noescape () -> ()
55-
// CHECK: [[ESCAPED:%.*]] = is_escaping_closure [[OPT_SENTINEL]] : $Optional<@callee_guaranteed () -> ()>
55+
// CHECK: [[ESCAPED:%.*]] = is_escaping_closure [objc] [[OPT_SENTINEL]] : $Optional<@callee_guaranteed () -> ()>
5656
// CHECK: cond_fail [[ESCAPED]] : $Builtin.Int1
5757

5858
// Release of sentinel copy (4).

test/multifile/objc_closure_escape/main.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public func test() {
4141
}
4242

4343
testSuite.test("testEscaping") {
44-
// CHECK: closure argument was escaped in withoutActuallyEscaping block
44+
// CHECK: closure argument passed as @noescape to Objective-C has escaped: file {{.*}}main.swift, line 19, column 26
4545
expectCrashLater()
4646
test()
4747
}

0 commit comments

Comments
 (0)