Skip to content

Commit 8c1231e

Browse files
committed
SILGen support for borrow accessors on Copyable types and address-only ~Copyable types
1 parent 6b8c91e commit 8c1231e

File tree

12 files changed

+798
-50
lines changed

12 files changed

+798
-50
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,18 @@ NOTE(lifetime_outside_scope_escape, none,
12251225

12261226
ERROR(noncopyable_shared_case_block_unimplemented, none,
12271227
"matching a non-'Copyable' value using a case label that has multiple patterns is not implemented", ())
1228-
1228+
1229+
1230+
/// Borrow and mutate accessors
1231+
ERROR(invalid_borrow_accessor_return, none,
1232+
"invalid return value from borrow accessor", ())
1233+
NOTE(borrow_accessor_not_a_projection_note, none,
1234+
"borrow accessors can return either literals, stored properties or computed "
1235+
"properties that have borrow accessors",
1236+
())
1237+
ERROR(invalid_multiple_return_borrow_accessor, none,
1238+
"multiple return statements in borrow accessors are not yet supported",
1239+
())
1240+
12291241
#define UNDEFINE_DIAGNOSTIC_MACROS
12301242
#include "DefineDiagnosticMacros.h"

include/swift/AST/Types.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5396,6 +5396,14 @@ class SILFunctionType final
53965396
return hasErrorResult() && getErrorResult().isFormalIndirect();
53975397
}
53985398

5399+
bool hasGuaranteedAddressResults() const {
5400+
for (auto &result : getResults()) {
5401+
if (result.isGuaranteedAddressResult()) {
5402+
return true;
5403+
}
5404+
}
5405+
return false;
5406+
}
53995407
struct IndirectFormalResultFilter {
54005408
bool operator()(SILResultInfo result) const {
54015409
return result.isFormalIndirect();

include/swift/SIL/SILFunctionConventions.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -677,10 +677,11 @@ inline SILType SILModuleConventions::getSILYieldInterfaceType(
677677
return getSILParamInterfaceType(yield, loweredAddresses);
678678
}
679679

680-
inline SILType SILModuleConventions::getSILResultInterfaceType(
681-
SILResultInfo result,
682-
bool loweredAddresses) {
683-
return SILModuleConventions::isIndirectSILResult(result, loweredAddresses)
680+
inline SILType
681+
SILModuleConventions::getSILResultInterfaceType(SILResultInfo result,
682+
bool loweredAddresses) {
683+
return SILModuleConventions::isIndirectSILResult(result, loweredAddresses) ||
684+
result.isGuaranteedAddressResult()
684685
? SILType::getPrimitiveAddressType(result.getInterfaceType())
685686
: SILType::getPrimitiveObjectType(result.getInterfaceType());
686687
}

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ SILType
179179
SILFunctionType::getDirectFormalResultsType(SILModule &M,
180180
TypeExpansionContext context) {
181181
CanType type;
182+
183+
if (hasGuaranteedAddressResults()) {
184+
assert(getNumDirectFormalResults() == 1);
185+
return SILType::getPrimitiveAddressType(
186+
getSingleDirectFormalResult().getReturnValueType(M, this, context));
187+
}
182188
if (getNumDirectFormalResults() == 0) {
183189
type = getASTContext().TheEmptyTupleType;
184190
} else if (getNumDirectFormalResults() == 1) {
@@ -1374,21 +1380,24 @@ class DestructureResults {
13741380
TypeExpansionContext context;
13751381
bool hasSendingResult;
13761382
bool isBorrowOrMutateAccessor;
1383+
bool hasSelfWithAddressType;
13771384

13781385
public:
13791386
DestructureResults(TypeExpansionContext context, TypeConverter &TC,
13801387
const Conventions &conventions,
13811388
SmallVectorImpl<SILResultInfo> &results,
1382-
bool hasSendingResult, bool isBorrowOrMutateAccessor)
1389+
bool hasSendingResult, bool isBorrowOrMutateAccessor,
1390+
bool hasSelfWithAddressType)
13831391
: TC(TC), Convs(conventions), Results(results), context(context),
13841392
hasSendingResult(hasSendingResult),
1385-
isBorrowOrMutateAccessor(isBorrowOrMutateAccessor) {}
1393+
isBorrowOrMutateAccessor(isBorrowOrMutateAccessor),
1394+
hasSelfWithAddressType(hasSelfWithAddressType) {}
13861395

13871396
void destructure(AbstractionPattern origType, CanType substType) {
1388-
bool hasAddressOnlyReturn = false;
1389-
13901397
// Recur into tuples.
1391-
if (origType.isTuple()) {
1398+
// Do not explode tuples for borrow and mutate accessors since we cannot
1399+
// explode and reconstruct addresses.
1400+
if (origType.isTuple() && !isBorrowOrMutateAccessor) {
13921401
origType.forEachTupleElement(substType,
13931402
[&](TupleElementGenerator &elt) {
13941403
// If the original element type is not a pack expansion, just
@@ -1434,20 +1443,21 @@ class DestructureResults {
14341443

14351444
// Determine the result convention.
14361445
ResultConvention convention;
1437-
if (isFormallyReturnedIndirectly(origType, substType,
1438-
substResultTLForConvention)) {
1439-
hasAddressOnlyReturn = true;
1440-
if (Convs.getResult(substResultTLForConvention) ==
1441-
ResultConvention::Guaranteed) {
1446+
1447+
if (isBorrowOrMutateAccessor) {
1448+
if ((hasSelfWithAddressType && !substResultTL.isTrivial()) ||
1449+
isFormallyReturnedIndirectly(origType, substType,
1450+
substResultTLForConvention)) {
1451+
assert(Convs.getResult(substResultTLForConvention) ==
1452+
ResultConvention::Guaranteed);
14421453
convention = ResultConvention::GuaranteedAddress;
14431454
} else {
1444-
convention = ResultConvention::Indirect;
1455+
convention = ResultConvention::Guaranteed;
14451456
}
1457+
} else if (isFormallyReturnedIndirectly(origType, substType,
1458+
substResultTLForConvention)) {
1459+
convention = ResultConvention::Indirect;
14461460
} else {
1447-
if (isBorrowOrMutateAccessor && hasAddressOnlyReturn) {
1448-
llvm_unreachable("Returning a tuple with address-only and loadable "
1449-
"types is not supported in borrow/mutate accessor");
1450-
}
14511461
convention = Convs.getResult(substResultTLForConvention);
14521462

14531463
// Reduce conventions for trivial types to an unowned convention.
@@ -1456,7 +1466,6 @@ class DestructureResults {
14561466
case ResultConvention::Indirect:
14571467
case ResultConvention::Unowned:
14581468
case ResultConvention::UnownedInnerPointer:
1459-
case ResultConvention::Guaranteed:
14601469
// Leave these as-is.
14611470
break;
14621471

@@ -1468,13 +1477,14 @@ class DestructureResults {
14681477

14691478
case ResultConvention::Autoreleased:
14701479
case ResultConvention::Owned:
1480+
case ResultConvention::Guaranteed:
14711481
// These aren't distinguishable from unowned for trivial types.
14721482
convention = ResultConvention::Unowned;
14731483
break;
14741484
}
14751485
}
14761486
}
1477-
1487+
14781488
SILResultInfo result(substResultTL.getLoweredType().getASTType(),
14791489
convention);
14801490
if (hasSendingResult)
@@ -2738,12 +2748,16 @@ static CanSILFunctionType getSILFunctionType(
27382748
coroutineOrigYieldType, coroutineSubstYieldType,
27392749
yields, coroutineKind);
27402750

2751+
bool hasSelfWithAddressType =
2752+
extInfoBuilder.hasSelfParam() &&
2753+
inputs.back().getSILStorageInterfaceType().isAddress();
2754+
27412755
// Destructure the result tuple type.
27422756
SmallVector<SILResultInfo, 8> results;
27432757
{
2744-
DestructureResults destructurer(expansionContext, TC, conventions, results,
2745-
hasSendingResult,
2746-
isBorrowOrMutateAccessor(constant));
2758+
DestructureResults destructurer(
2759+
expansionContext, TC, conventions, results, hasSendingResult,
2760+
isBorrowOrMutateAccessor(constant), hasSelfWithAddressType);
27472761
destructurer.destructure(origResultType, substFormalResultType);
27482762
}
27492763

lib/SILGen/LValue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class PathComponent {
107107
ValueKind, // random base pointer as an lvalue
108108
PhysicalKeyPathApplicationKind, // applying a key path
109109
BorrowValueKind, // load_borrow the base rvalue for a projection
110+
BorrowMutateKind, // borrow and mutate accessor
110111

111112
// Logical LValue kinds
112113
GetterSetterKind, // property or subscript getter/setter

lib/SILGen/SILGenApply.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5225,6 +5225,8 @@ class CallEmission {
52255225

52265226
CleanupHandle applyCoroutine(SmallVectorImpl<ManagedValue> &yields);
52275227

5228+
ManagedValue applyBorrowAccessor();
5229+
52285230
RValue apply(SGFContext C = SGFContext()) {
52295231
initialWritebackScope.verify();
52305232

@@ -5424,6 +5426,48 @@ CleanupHandle SILGenFunction::emitBeginApply(
54245426
return endApplyHandle;
54255427
}
54265428

5429+
ManagedValue CallEmission::applyBorrowAccessor() {
5430+
auto origFormalType = callee.getOrigFormalType();
5431+
// Get the callee type information.
5432+
auto calleeTypeInfo = callee.getTypeInfo(SGF);
5433+
5434+
std::optional<ManagedValue> self;
5435+
auto fnValue = callee.getFnValue(SGF, self);
5436+
5437+
// Evaluate the arguments.
5438+
SmallVector<ManagedValue, 4> uncurriedArgs;
5439+
std::optional<SILLocation> uncurriedLoc;
5440+
ApplyOptions options = emitArgumentsForNormalApply(
5441+
origFormalType, calleeTypeInfo.substFnType,
5442+
origFormalType.getLifetimeDependencies(), calleeTypeInfo.foreign,
5443+
uncurriedArgs, uncurriedLoc);
5444+
5445+
auto value = SGF.applyBorrowAccessor(uncurriedLoc.value(), fnValue, canUnwind,
5446+
callee.getSubstitutions(), uncurriedArgs,
5447+
calleeTypeInfo.substFnType, options);
5448+
5449+
return value;
5450+
}
5451+
5452+
ManagedValue SILGenFunction::applyBorrowAccessor(
5453+
SILLocation loc, ManagedValue fn, bool canUnwind, SubstitutionMap subs,
5454+
ArrayRef<ManagedValue> args, CanSILFunctionType substFnType,
5455+
ApplyOptions options) {
5456+
// Emit the call.
5457+
SmallVector<SILValue, 4> rawResults;
5458+
emitRawApply(*this, loc, fn, subs, args, substFnType, options,
5459+
/*indirect results*/ {}, /*indirect errors*/ {}, rawResults,
5460+
ExecutorBreadcrumb());
5461+
assert(rawResults.size() == 1);
5462+
auto result = rawResults[0];
5463+
if (result->getType().isMoveOnly()) {
5464+
result = B.createMarkUnresolvedNonCopyableValueInst(
5465+
loc, result,
5466+
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
5467+
}
5468+
return ManagedValue::forForwardedRValue(*this, result);
5469+
}
5470+
54275471
RValue CallEmission::applyFirstLevelCallee(SGFContext C) {
54285472
// Check for a specialized emitter.
54295473
if (auto emitter = callee.getSpecializedEmitter(SGF.SGM)) {
@@ -7813,6 +7857,34 @@ SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor,
78137857
return endApplyHandle;
78147858
}
78157859

7860+
ManagedValue SILGenFunction::emitBorrowAccessor(
7861+
SILLocation loc, SILDeclRef accessor, SubstitutionMap substitutions,
7862+
ArgumentSource &&selfValue, bool isSuper, bool isDirectUse,
7863+
PreparedArguments &&subscriptIndices, bool isOnSelfParameter) {
7864+
Callee callee = emitSpecializedAccessorFunctionRef(
7865+
*this, loc, accessor, substitutions, selfValue, isSuper, isDirectUse,
7866+
isOnSelfParameter);
7867+
7868+
CanAnyFunctionType accessType = callee.getSubstFormalType();
7869+
7870+
FormalEvaluationScope writebackScope(*this);
7871+
CallEmission emission(*this, std::move(callee), std::move(writebackScope));
7872+
7873+
// Self ->
7874+
emission.addSelfParam(loc, std::move(selfValue), accessType.getParams()[0]);
7875+
accessType = cast<AnyFunctionType>(accessType.getResult());
7876+
7877+
// Index or () if none.
7878+
if (subscriptIndices.isNull())
7879+
subscriptIndices.emplace({});
7880+
7881+
emission.addCallSite(loc, std::move(subscriptIndices));
7882+
7883+
emission.setCanUnwind(false);
7884+
7885+
return emission.applyBorrowAccessor();
7886+
}
7887+
78167888
ManagedValue SILGenFunction::emitAsyncLetStart(
78177889
SILLocation loc,
78187890
SILValue taskOptions,

lib/SILGen/SILGenEpilog.cpp

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,45 @@ void SILGenFunction::prepareEpilog(
3333
// emits unreachable if there is no source level return.
3434
NeedsReturn = !(*directResultType)->isEqual(TupleType::getEmpty(getASTContext()));
3535
if (NeedsReturn) {
36-
for (auto directResult : fnConv.getDirectSILResults()) {
37-
SILType resultType = F.getLoweredType(F.mapTypeIntoContext(
38-
fnConv.getSILType(directResult, getTypeExpansionContext())));
39-
// @out tuples do not get flattened in the function's return type, but
40-
// the epilog block expects (recursively) flattened arguments. Flatten
41-
// the type now.
42-
SmallVector<SILType, 4> worklist;
43-
worklist.push_back(resultType);
44-
while (!worklist.empty()) {
45-
auto ty = worklist.pop_back_val();
46-
if (auto tupleType = ty.getASTType()->getAs<TupleType>()) {
47-
assert(!fnConv.useLoweredAddresses() &&
48-
"expanding tuple in non-opaque-values exit block?!");
49-
// Push tuple elements in reverse order (resulting in later tuple
50-
// elements appearing earlier in worklist) so that as the worklist
51-
// is drained by popping the back, arguments are created for the
52-
// earlier types first.
53-
for (auto index :
54-
llvm::reverse(indices(tupleType->getElementTypes()))) {
55-
worklist.push_back(ty.getTupleElementType(index));
36+
if (fnConv.hasGuaranteedAddressResults() ||
37+
fnConv.hasGuaranteedResults()) {
38+
// Do not explode tuples for borrow/mutate accessors
39+
SILType resultType =
40+
F.getLoweredType(F.mapTypeIntoContext(*directResultType));
41+
epilogBB->createPhiArgument(resultType, fnConv.hasGuaranteedResults()
42+
? OwnershipKind::Guaranteed
43+
: OwnershipKind::None);
44+
} else {
45+
for (auto directResult : fnConv.getDirectSILResults()) {
46+
SILType resultType = F.getLoweredType(F.mapTypeIntoContext(
47+
fnConv.getSILType(directResult, getTypeExpansionContext())));
48+
// @out tuples do not get flattened in the function's return type, but
49+
// the epilog block expects (recursively) flattened arguments. Flatten
50+
// the type now.
51+
SmallVector<SILType, 4> worklist;
52+
worklist.push_back(resultType);
53+
while (!worklist.empty()) {
54+
auto ty = worklist.pop_back_val();
55+
if (auto tupleType = ty.getASTType()->getAs<TupleType>()) {
56+
assert(!fnConv.useLoweredAddresses() &&
57+
"expanding tuple in non-opaque-values exit block?!");
58+
// Push tuple elements in reverse order (resulting in later tuple
59+
// elements appearing earlier in worklist) so that as the worklist
60+
// is drained by popping the back, arguments are created for the
61+
// earlier types first.
62+
for (auto index :
63+
llvm::reverse(indices(tupleType->getElementTypes()))) {
64+
worklist.push_back(ty.getTupleElementType(index));
65+
}
66+
} else {
67+
if (fnConv.hasGuaranteedResults()) {
68+
epilogBB->createPhiArgument(ty, OwnershipKind::Guaranteed);
69+
} else if (fnConv.hasGuaranteedAddressResults()) {
70+
epilogBB->createPhiArgument(ty, OwnershipKind::None);
71+
} else {
72+
epilogBB->createPhiArgument(ty, OwnershipKind::Owned);
73+
}
5674
}
57-
} else {
58-
epilogBB->createPhiArgument(ty, OwnershipKind::Owned);
5975
}
6076
}
6177
}

lib/SILGen/SILGenFunction.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class ExecutorBreadcrumb;
5858
struct LValueOptions {
5959
bool IsNonAccessing = false;
6060
bool TryAddressable = false;
61+
bool NeedsBorrow = false;
6162

6263
/// Derive options for accessing the base of an l-value, given that
6364
/// applying the derived component might touch the memory.
@@ -81,6 +82,12 @@ struct LValueOptions {
8182
copy.TryAddressable = addressable;
8283
return copy;
8384
}
85+
86+
LValueOptions withBorrow(bool borrow) const {
87+
auto copy = *this;
88+
copy.NeedsBorrow = borrow;
89+
return copy;
90+
}
8491
};
8592

8693
class PatternMatchContext;
@@ -2065,6 +2072,19 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
20652072
SmallVectorImpl<ManagedValue> &yields,
20662073
bool isOnSelfParameter);
20672074

2075+
ManagedValue emitBorrowAccessor(SILLocation loc, SILDeclRef accessor,
2076+
SubstitutionMap substitutions,
2077+
ArgumentSource &&selfValue, bool isSuper,
2078+
bool isDirectUse,
2079+
PreparedArguments &&subscriptIndices,
2080+
bool isOnSelfParameter);
2081+
2082+
ManagedValue applyBorrowAccessor(SILLocation loc, ManagedValue fn,
2083+
bool canUnwind, SubstitutionMap subs,
2084+
ArrayRef<ManagedValue> args,
2085+
CanSILFunctionType substFnType,
2086+
ApplyOptions options);
2087+
20682088
RValue emitApplyConversionFunction(SILLocation loc,
20692089
Expr *funcExpr,
20702090
Type resultType,
@@ -2211,6 +2231,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
22112231
TSanKind tsanKind = TSanKind::None);
22122232
ManagedValue emitBorrowedLValue(SILLocation loc, LValue &&src,
22132233
TSanKind tsanKind = TSanKind::None);
2234+
std::optional<ManagedValue>
2235+
tryEmitProjectedLValue(SILLocation loc, LValue &&src, TSanKind tsanKind);
22142236
ManagedValue emitConsumedLValue(SILLocation loc, LValue &&src,
22152237
TSanKind tsanKind = TSanKind::None);
22162238
/// Simply projects the LValue as a ManagedValue, allowing the caller

0 commit comments

Comments
 (0)