Skip to content

Commit b3c66cc

Browse files
authored
Merge pull request #18708 from atrick/without-actually-asserting
Fix static exclusivity verification for withoutActuallyEscaping.
2 parents 77ea0d5 + 1bb7b37 commit b3c66cc

27 files changed

+397
-84
lines changed

docs/SIL.rst

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4714,7 +4714,9 @@ convert_function
47144714
````````````````
47154715
::
47164716

4717-
sil-instruction ::= 'convert_function' sil-operand 'to' sil-type
4717+
sil-instruction ::= 'convert_function' sil-operand 'to'
4718+
('[' 'without_actually_escaping' ']')?
4719+
sil-type
47184720

47194721
%1 = convert_function %0 : $T -> U to $T' -> U'
47204722
// %0 must be of a function type $T -> U ABI-compatible with $T' -> U'
@@ -4744,6 +4746,18 @@ without ``@noescape`` are ABI compatible because they have no context. To
47444746
convert from an escaping to a ``@noescape`` thick function type use
47454747
``convert_escape_to_noescape``.
47464748

4749+
With the ``without_actually_escaping`` attribute, the
4750+
``convert_function`` may be used to convert a non-escaping closure
4751+
into an escaping function type. This attribute must be present
4752+
whenever the closure operand has an unboxed capture (via
4753+
@inout_aliasable) *and* the resulting function type is escaping. (This
4754+
only happens as a result of withoutActuallyEscaping()). If the
4755+
attribute is present then the resulting function type must be
4756+
escaping, but the operand's function type may or may not be
4757+
@noescape. Note that a non-escaping closure may have unboxed captured
4758+
even though its SIL function type is "escaping".
4759+
4760+
47474761
convert_escape_to_noescape
47484762
```````````````````````````
47494763
::

include/swift/SIL/InstructionUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ SILValue stripBorrow(SILValue V);
8686
/// copies the value of its first operand, possibly changing its type or
8787
/// ownership state, but otherwise having no effect.
8888
///
89+
/// The returned instruction may have additional "incidental" operands;
90+
/// mark_dependence for example.
91+
///
8992
/// This is useful for checking all users of a value to verify that the value is
9093
/// only used in recognizable patterns without otherwise "escaping". These are
9194
/// instructions that the use-visitor can recurse into. Note that the value's

include/swift/SIL/SILBuilder.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -886,9 +886,11 @@ class SILBuilder {
886886
}
887887

888888
ConvertFunctionInst *createConvertFunction(SILLocation Loc, SILValue Op,
889-
SILType Ty) {
890-
return insert(ConvertFunctionInst::create(
891-
getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes));
889+
SILType Ty,
890+
bool WithoutActuallyEscaping) {
891+
return insert(ConvertFunctionInst::create(getSILDebugLocation(Loc), Op, Ty,
892+
getFunction(), C.OpenedArchetypes,
893+
WithoutActuallyEscaping));
892894
}
893895

894896
ConvertEscapeToNoEscapeInst *

include/swift/SIL/SILCloner.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,10 +1020,10 @@ template<typename ImplClass>
10201020
void
10211021
SILCloner<ImplClass>::visitConvertFunctionInst(ConvertFunctionInst *Inst) {
10221022
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1023-
doPostProcess(Inst,
1024-
getBuilder().createConvertFunction(getOpLocation(Inst->getLoc()),
1025-
getOpValue(Inst->getOperand()),
1026-
getOpType(Inst->getType())));
1023+
doPostProcess(
1024+
Inst, getBuilder().createConvertFunction(
1025+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()),
1026+
getOpType(Inst->getType()), Inst->withoutActuallyEscaping()));
10271027
}
10281028

10291029
template <typename ImplClass>

include/swift/SIL/SILFunction.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,14 @@ class SILFunction
231231
/// serialization.
232232
bool WasDeserializedCanonical = false;
233233

234+
/// True if this is a reabstraction thunk of escaping function type whose
235+
/// single argument is a potentially non-escaping closure. This is an escape
236+
/// hatch to allow non-escaping functions to be stored or passed as an
237+
/// argument with escaping function type. The thunk argument's function type
238+
/// is not necessarily @noescape. The only relevant aspect of the argument is
239+
/// that it may have unboxed capture (i.e. @inout_aliasable parameters).
240+
bool IsWithoutActuallyEscapingThunk = false;
241+
234242
static void
235243
validateSubclassScope(SubclassScope scope, IsThunk_t isThunk,
236244
const GenericSpecializationInformation *genericInfo) {
@@ -385,6 +393,18 @@ class SILFunction
385393
WasDeserializedCanonical = val;
386394
}
387395

396+
/// Returns true if this is a reabstraction thunk of escaping function type
397+
/// whose single argument is a potentially non-escaping closure. i.e. the
398+
/// thunks' function argument may itself have @inout_aliasable parameters.
399+
bool isWithoutActuallyEscapingThunk() const {
400+
return IsWithoutActuallyEscapingThunk;
401+
}
402+
403+
void setWithoutActuallyEscapingThunk(bool val = true) {
404+
assert(!val || isThunk() == IsReabstractionThunk);
405+
IsWithoutActuallyEscapingThunk = val;
406+
}
407+
388408
/// Returns the calling convention used by this entry point.
389409
SILFunctionTypeRepresentation getRepresentation() const {
390410
return getLoweredFunctionType()->getRepresentation();

include/swift/SIL/SILInstruction.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3957,19 +3957,37 @@ class ConvertFunctionInst final
39573957
friend SILBuilder;
39583958

39593959
ConvertFunctionInst(SILDebugLocation DebugLoc, SILValue Operand,
3960-
ArrayRef<SILValue> TypeDependentOperands, SILType Ty)
3960+
ArrayRef<SILValue> TypeDependentOperands, SILType Ty,
3961+
bool WithoutActuallyEscaping)
39613962
: UnaryInstructionWithTypeDependentOperandsBase(
39623963
DebugLoc, Operand, TypeDependentOperands, Ty) {
3964+
SILInstruction::Bits.ConvertFunctionInst.WithoutActuallyEscaping =
3965+
WithoutActuallyEscaping;
39633966
assert((Operand->getType().castTo<SILFunctionType>()->isNoEscape() ==
39643967
Ty.castTo<SILFunctionType>()->isNoEscape() ||
39653968
Ty.castTo<SILFunctionType>()->getRepresentation() !=
39663969
SILFunctionType::Representation::Thick) &&
39673970
"Change of escapeness is not ABI compatible");
39683971
}
39693972

3970-
static ConvertFunctionInst *
3971-
create(SILDebugLocation DebugLoc, SILValue Operand, SILType Ty,
3972-
SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes);
3973+
static ConvertFunctionInst *create(SILDebugLocation DebugLoc,
3974+
SILValue Operand, SILType Ty,
3975+
SILFunction &F,
3976+
SILOpenedArchetypesState &OpenedArchetypes,
3977+
bool WithoutActuallyEscaping);
3978+
3979+
public:
3980+
/// Returns `true` if this converts a non-escaping closure into an escaping
3981+
/// function type. `True` must be returned whenever the closure operand has an
3982+
/// unboxed capture (via @inout_aliasable) *and* the resulting function type
3983+
/// is escaping. (This only happens as a result of
3984+
/// withoutActuallyEscaping()). If `true` is returned, then the resulting
3985+
/// function type must be escaping, but the operand's function type may or may
3986+
/// not be @noescape. Note that a non-escaping closure may have unboxed
3987+
/// captured even though its SIL function type is "escaping".
3988+
bool withoutActuallyEscaping() const {
3989+
return SILInstruction::Bits.ConvertFunctionInst.WithoutActuallyEscaping;
3990+
}
39733991
};
39743992

39753993
/// ConvertEscapeToNoEscapeInst - Change the type of a escaping function value

include/swift/SIL/SILNode.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,8 @@ class alignas(8) SILNode {
315315
IsInvariant : 1
316316
);
317317

318-
UIWTDOB_BITFIELD_EMPTY(ConvertFunctionInst, ConversionInst);
318+
UIWTDOB_BITFIELD(ConvertFunctionInst, ConversionInst, 1,
319+
WithoutActuallyEscaping : 1);
319320
UIWTDOB_BITFIELD_EMPTY(PointerToThinFunctionInst, ConversionInst);
320321
UIWTDOB_BITFIELD_EMPTY(UnconditionalCheckedCastInst, ConversionInst);
321322
UIWTDOB_BITFIELD_EMPTY(UpcastInst, ConversionInst);

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const uint16_t VERSION_MAJOR = 0;
5555
/// describe what change you made. The content of this comment isn't important;
5656
/// it just ensures a conflict if two people change the module format.
5757
/// Don't worry about adhering to the 80-column limit for this line.
58-
const uint16_t VERSION_MINOR = 435; // Last change: serialize new-style function parameters
58+
const uint16_t VERSION_MINOR = 436; // Last change: without_actually_escaping.
5959

6060
using DeclIDField = BCFixed<31>;
6161

lib/IRGen/LoadableByAddress.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2609,7 +2609,8 @@ void LoadableByAddress::recreateConvInstrs() {
26092609
case SILInstructionKind::ConvertFunctionInst: {
26102610
auto instr = cast<ConvertFunctionInst>(convInstr);
26112611
newInstr = convBuilder.createConvertFunction(
2612-
instr->getLoc(), instr->getOperand(), newType);
2612+
instr->getLoc(), instr->getOperand(), newType,
2613+
instr->withoutActuallyEscaping());
26132614
break;
26142615
}
26152616
case SILInstructionKind::ConvertEscapeToNoEscapeInst: {

lib/ParseSIL/ParseSIL.cpp

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,7 @@ static bool parseDeclSILOptional(bool *isTransparent,
886886
Inline_t *inlineStrategy,
887887
OptimizationMode *optimizationMode,
888888
bool *isLet, bool *isWeakLinked,
889+
bool *isWithoutActuallyEscapingThunk,
889890
SmallVectorImpl<std::string> *Semantics,
890891
SmallVectorImpl<ParsedSpecAttr> *SpecAttrs,
891892
ValueDecl **ClangDecl,
@@ -914,6 +915,9 @@ static bool parseDeclSILOptional(bool *isTransparent,
914915
*isThunk = IsSignatureOptimizedThunk;
915916
else if (isThunk && SP.P.Tok.getText() == "reabstraction_thunk")
916917
*isThunk = IsReabstractionThunk;
918+
else if (isWithoutActuallyEscapingThunk
919+
&& SP.P.Tok.getText() == "without_actually_escaping")
920+
*isWithoutActuallyEscapingThunk = true;
917921
else if (isGlobalInit && SP.P.Tok.getText() == "global_init")
918922
*isGlobalInit = true;
919923
else if (isWeakLinked && SP.P.Tok.getText() == "_weakLinked")
@@ -2976,6 +2980,7 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
29762980
SourceLoc ToLoc;
29772981
bool not_guaranteed = false;
29782982
bool escaped = false;
2983+
bool without_actually_escaping = false;
29792984
if (Opcode == SILInstructionKind::ConvertEscapeToNoEscapeInst) {
29802985
StringRef attrName;
29812986
if (parseSILOptional(attrName, *this)) {
@@ -2989,17 +2994,26 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
29892994
if (parseSILOptional(escaped, *this, "escaped"))
29902995
return true;
29912996
}
2992-
if (parseTypedValueRef(Val, B) ||
2993-
parseSILIdentifier(ToToken, ToLoc,
2994-
diag::expected_tok_in_sil_instr, "to") ||
2995-
parseSILType(Ty) ||
2996-
parseSILDebugLocation(InstLoc, B))
2997+
if (parseTypedValueRef(Val, B)
2998+
|| parseSILIdentifier(ToToken, ToLoc, diag::expected_tok_in_sil_instr,
2999+
"to"))
29973000
return true;
29983001

29993002
if (ToToken.str() != "to") {
30003003
P.diagnose(ToLoc, diag::expected_tok_in_sil_instr, "to");
30013004
return true;
30023005
}
3006+
if (Opcode == SILInstructionKind::ConvertFunctionInst) {
3007+
StringRef attrName;
3008+
if (parseSILOptional(attrName, *this)) {
3009+
if (attrName.equals("without_actually_escaping"))
3010+
without_actually_escaping = true;
3011+
else
3012+
return true;
3013+
}
3014+
}
3015+
if (parseSILType(Ty) || parseSILDebugLocation(InstLoc, B))
3016+
return true;
30033017

30043018
switch (Opcode) {
30053019
default: llvm_unreachable("Out of sync with parent switch");
@@ -3019,7 +3033,8 @@ bool SILParser::parseSILInstruction(SILBuilder &B) {
30193033
ResultVal = B.createUpcast(InstLoc, Val, Ty);
30203034
break;
30213035
case SILInstructionKind::ConvertFunctionInst:
3022-
ResultVal = B.createConvertFunction(InstLoc, Val, Ty);
3036+
ResultVal =
3037+
B.createConvertFunction(InstLoc, Val, Ty, without_actually_escaping);
30233038
break;
30243039
case SILInstructionKind::ConvertEscapeToNoEscapeInst:
30253040
ResultVal = B.createConvertEscapeToNoEscape(InstLoc, Val, Ty, escaped,
@@ -5186,6 +5201,7 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
51865201
bool isCanonical = false;
51875202
IsThunk_t isThunk = IsNotThunk;
51885203
bool isGlobalInit = false, isWeakLinked = false;
5204+
bool isWithoutActuallyEscapingThunk = false;
51895205
Inline_t inlineStrategy = InlineDefault;
51905206
OptimizationMode optimizationMode = OptimizationMode::NotSet;
51915207
SmallVector<std::string, 1> Semantics;
@@ -5196,7 +5212,8 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
51965212
parseDeclSILOptional(&isTransparent, &isSerialized, &isCanonical,
51975213
&isThunk, &isGlobalInit,
51985214
&inlineStrategy, &optimizationMode, nullptr,
5199-
&isWeakLinked, &Semantics, &SpecAttrs,
5215+
&isWeakLinked, &isWithoutActuallyEscapingThunk,
5216+
&Semantics, &SpecAttrs,
52005217
&ClangDecl, &MRK, FunctionState) ||
52015218
P.parseToken(tok::at_sign, diag::expected_sil_function_name) ||
52025219
P.parseIdentifier(FnName, FnNameLoc, diag::expected_sil_function_name) ||
@@ -5224,6 +5241,8 @@ bool SILParserTUState::parseDeclSIL(Parser &P) {
52245241
FunctionState.F->setThunk(IsThunk_t(isThunk));
52255242
FunctionState.F->setGlobalInit(isGlobalInit);
52265243
FunctionState.F->setWeakLinked(isWeakLinked);
5244+
FunctionState.F->setWithoutActuallyEscapingThunk(
5245+
isWithoutActuallyEscapingThunk);
52275246
FunctionState.F->setInlineStrategy(inlineStrategy);
52285247
FunctionState.F->setOptimizationMode(optimizationMode);
52295248
FunctionState.F->setEffectsKind(MRK);
@@ -5403,7 +5422,7 @@ bool SILParserTUState::parseSILGlobal(Parser &P) {
54035422
if (parseSILLinkage(GlobalLinkage, P) ||
54045423
parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
54055424
nullptr, nullptr, &isLet, nullptr, nullptr, nullptr,
5406-
nullptr, nullptr, State) ||
5425+
nullptr, nullptr, nullptr, State) ||
54075426
P.parseToken(tok::at_sign, diag::expected_sil_value_name) ||
54085427
P.parseIdentifier(GlobalName, NameLoc, diag::expected_sil_value_name) ||
54095428
P.parseToken(tok::colon, diag::expected_sil_type))
@@ -5451,7 +5470,7 @@ bool SILParserTUState::parseSILProperty(Parser &P) {
54515470
IsSerialized_t Serialized = IsNotSerialized;
54525471
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
54535472
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
5454-
nullptr, nullptr, SP))
5473+
nullptr, nullptr, nullptr, SP))
54555474
return true;
54565475

54575476
ValueDecl *VD;
@@ -5519,7 +5538,7 @@ bool SILParserTUState::parseSILVTable(Parser &P) {
55195538
IsSerialized_t Serialized = IsNotSerialized;
55205539
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
55215540
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
5522-
nullptr, nullptr, VTableState))
5541+
nullptr, nullptr, nullptr, VTableState))
55235542
return true;
55245543

55255544
// Parse the class name.
@@ -5869,7 +5888,7 @@ bool SILParserTUState::parseSILWitnessTable(Parser &P) {
58695888
IsSerialized_t isSerialized = IsNotSerialized;
58705889
if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
58715890
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
5872-
nullptr, nullptr, WitnessState))
5891+
nullptr, nullptr, nullptr, WitnessState))
58735892
return true;
58745893

58755894
Scope S(&P, ScopeKind::TopLevel);

lib/SIL/SILInstructions.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,19 +1971,18 @@ PointerToThinFunctionInst::create(SILDebugLocation DebugLoc, SILValue Operand,
19711971
TypeDependentOperands, Ty);
19721972
}
19731973

1974-
ConvertFunctionInst *
1975-
ConvertFunctionInst::create(SILDebugLocation DebugLoc, SILValue Operand,
1976-
SILType Ty, SILFunction &F,
1977-
SILOpenedArchetypesState &OpenedArchetypes) {
1974+
ConvertFunctionInst *ConvertFunctionInst::create(
1975+
SILDebugLocation DebugLoc, SILValue Operand, SILType Ty, SILFunction &F,
1976+
SILOpenedArchetypesState &OpenedArchetypes, bool WithoutActuallyEscaping) {
19781977
SILModule &Mod = F.getModule();
19791978
SmallVector<SILValue, 8> TypeDependentOperands;
19801979
collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, F,
19811980
Ty.getASTType());
19821981
unsigned size =
19831982
totalSizeToAlloc<swift::Operand>(1 + TypeDependentOperands.size());
19841983
void *Buffer = Mod.allocateInst(size, alignof(ConvertFunctionInst));
1985-
auto *CFI = ::new (Buffer)
1986-
ConvertFunctionInst(DebugLoc, Operand, TypeDependentOperands, Ty);
1984+
auto *CFI = ::new (Buffer) ConvertFunctionInst(
1985+
DebugLoc, Operand, TypeDependentOperands, Ty, WithoutActuallyEscaping);
19871986
// If we do not have lowered SIL, make sure that are not performing
19881987
// ABI-incompatible conversions.
19891988
//

lib/SIL/SILPrinter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1424,7 +1424,10 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
14241424
}
14251425

14261426
void visitConvertFunctionInst(ConvertFunctionInst *CI) {
1427-
printUncheckedConversionInst(CI, CI->getOperand());
1427+
*this << getIDAndType(CI->getOperand()) << " to ";
1428+
if (CI->withoutActuallyEscaping())
1429+
*this << "[without_actually_escaping] ";
1430+
*this << CI->getType();
14281431
}
14291432
void visitConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *CI) {
14301433
*this << (CI->isLifetimeGuaranteed() ? "" : "[not_guaranteed] ")
@@ -2299,6 +2302,8 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
22992302
break;
23002303
case IsReabstractionThunk: OS << "[reabstraction_thunk] "; break;
23012304
}
2305+
if (isWithoutActuallyEscapingThunk())
2306+
OS << "[without_actually_escaping] ";
23022307

23032308
if (isGlobalInit())
23042309
OS << "[global_init] ";

lib/SILGen/SILGenBuilder.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,13 @@ ManagedValue SILGenBuilder::createPartialApply(SILLocation loc, SILValue fn,
207207
return getSILGenFunction().emitManagedRValueWithCleanup(result);
208208
}
209209

210-
ManagedValue SILGenBuilder::createConvertFunction(SILLocation loc,
211-
ManagedValue fn,
212-
SILType resultTy) {
210+
ManagedValue
211+
SILGenBuilder::createConvertFunction(SILLocation loc, ManagedValue fn,
212+
SILType resultTy,
213+
bool withoutActuallyEscaping) {
213214
CleanupCloner cloner(*this, fn);
214-
SILValue result =
215-
createConvertFunction(loc, fn.forward(getSILGenFunction()), resultTy);
215+
SILValue result = SILBuilder::createConvertFunction(
216+
loc, fn.forward(getSILGenFunction()), resultTy, withoutActuallyEscaping);
216217
return cloner.clone(result);
217218
}
218219

lib/SILGen/SILGenBuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,8 @@ class SILGenBuilder : public SILBuilder {
365365

366366
using SILBuilder::createConvertFunction;
367367
ManagedValue createConvertFunction(SILLocation loc, ManagedValue fn,
368-
SILType resultTy);
368+
SILType resultTy,
369+
bool WithoutActuallyEscaping = false);
369370

370371
using SILBuilder::createConvertEscapeToNoEscape;
371372
ManagedValue

0 commit comments

Comments
 (0)