Skip to content

Commit 4db2cf7

Browse files
authored
Merge pull request #70076 from xedin/sendable-keypath-literals
[ConstraintSystem] Implement sendability inference for key path expressions
2 parents cc858ab + 511dad5 commit 4db2cf7

20 files changed

+321
-55
lines changed

include/swift/AST/Expr.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6101,11 +6101,11 @@ class KeyPathExpr : public Expr {
61016101
ParsedPath = path;
61026102
}
61036103

6104-
TypeRepr *getRootType() const {
6104+
TypeRepr *getExplicitRootType() const {
61056105
assert(!isObjC() && "cannot get root type of ObjC keypath");
61066106
return RootType;
61076107
}
6108-
void setRootType(TypeRepr *rootType) {
6108+
void setExplicitRootType(TypeRepr *rootType) {
61096109
assert(!isObjC() && "cannot set root type of ObjC keypath");
61106110
RootType = rootType;
61116111
}
@@ -6116,6 +6116,11 @@ class KeyPathExpr : public Expr {
61166116
/// True if this key path expression has a leading dot.
61176117
bool expectsContextualRoot() const { return HasLeadingDot; }
61186118

6119+
BoundGenericType *getKeyPathType() const;
6120+
6121+
Type getRootType() const;
6122+
Type getValueType() const;
6123+
61196124
static bool classof(const Expr *E) {
61206125
return E->getKind() == ExprKind::KeyPath;
61216126
}

include/swift/SIL/SILInstruction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3908,6 +3908,8 @@ class KeyPathInst final
39083908
}
39093909

39103910
public:
3911+
BoundGenericType *getKeyPathType() const;
3912+
39113913
KeyPathPattern *getPattern() const;
39123914
bool hasPattern() const { return (bool)Pattern; }
39133915

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,14 @@ enum TypeVariableOptions {
339339
TVO_PackExpansion = 0x40,
340340
};
341341

342-
enum class KeyPathCapability : uint8_t {
342+
enum class KeyPathMutability : uint8_t {
343343
ReadOnly,
344344
Writable,
345345
ReferenceWritable
346346
};
347347

348+
using KeyPathCapability = std::pair<KeyPathMutability, /*isSendable=*/bool>;
349+
348350
/// The implementation object for a type variable used within the
349351
/// constraint-solving type checker.
350352
///
@@ -6311,7 +6313,7 @@ class TypeVarRefCollector : public ASTWalker {
63116313
};
63126314

63136315
/// Determine whether given type is a known one
6314-
/// for a key path `{Writable, ReferenceWritable}KeyPath`.
6316+
/// for a key path `{Any, Partial, Writable, ReferenceWritable}KeyPath`.
63156317
bool isKnownKeyPathType(Type type);
63166318

63176319
/// Determine whether given declaration is one for a key path

lib/AST/ASTWalker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
12801280
}
12811281

12821282
if (!E->isObjC()) {
1283-
auto rootType = E->getRootType();
1283+
auto rootType = E->getExplicitRootType();
12841284
if (rootType && doIt(rootType))
12851285
return nullptr;
12861286
}

lib/AST/Expr.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/ASTContext.h"
2121
#include "swift/AST/ASTVisitor.h"
2222
#include "swift/AST/Decl.h" // FIXME: Bad dependency
23+
#include "swift/AST/ExistentialLayout.h"
2324
#include "swift/AST/MacroDiscriminatorContext.h"
2425
#include "swift/AST/ParameterList.h"
2526
#include "swift/AST/Stmt.h"
@@ -2443,6 +2444,35 @@ llvm::Optional<unsigned> KeyPathExpr::findComponentWithSubscriptArg(Expr *arg) {
24432444
return llvm::None;
24442445
}
24452446

2447+
BoundGenericType *KeyPathExpr::getKeyPathType() const {
2448+
auto type = getType();
2449+
if (!type)
2450+
return nullptr;
2451+
2452+
if (auto *existentialTy = type->getAs<ExistentialType>()) {
2453+
auto *sendableTy =
2454+
existentialTy->getConstraintType()->castTo<ProtocolCompositionType>();
2455+
assert(sendableTy->getMembers().size() == 2);
2456+
type = sendableTy->getExistentialLayout().explicitSuperclass;
2457+
assert(type->isKeyPath() || type->isWritableKeyPath() ||
2458+
type->isReferenceWritableKeyPath());
2459+
}
2460+
2461+
return type->castTo<BoundGenericType>();
2462+
}
2463+
2464+
Type KeyPathExpr::getRootType() const {
2465+
auto keyPathTy = getKeyPathType();
2466+
assert(keyPathTy && "key path type has not been set yet");
2467+
return keyPathTy->getGenericArgs()[0];
2468+
}
2469+
2470+
Type KeyPathExpr::getValueType() const {
2471+
auto keyPathTy = getKeyPathType();
2472+
assert(keyPathTy && "key path type has not been set yet");
2473+
return keyPathTy->getGenericArgs()[1];
2474+
}
2475+
24462476
KeyPathExpr::Component KeyPathExpr::Component::forSubscript(
24472477
ASTContext &ctx, ConcreteDeclRef subscript, ArgumentList *argList,
24482478
Type elementType, ArrayRef<ProtocolConformanceRef> indexHashables) {

lib/IDE/KeyPathCompletion.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ void KeyPathTypeCheckCompletionCallback::sawSolutionImpl(
3737
if (ComponentIndex == 0) {
3838
// We are completing on the root and need to extract the key path's root
3939
// type.
40-
if (KeyPath->getRootType()) {
41-
BaseType = S.getResolvedType(KeyPath->getRootType());
40+
if (auto *rootTy = KeyPath->getExplicitRootType()) {
41+
BaseType = S.getResolvedType(rootTy);
4242
} else {
4343
// The key path doesn't have a root TypeRepr set, so we can't look the key
4444
// path's root up through it. Build a constraint locator and look the

lib/IRGen/IRGenSIL.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7123,7 +7123,8 @@ void IRGenSILFunction::visitKeyPathInst(swift::KeyPathInst *I) {
71237123
emitDeallocateDynamicAlloca(*dynamicArgsBuf);
71247124
}
71257125

7126-
auto resultStorageTy = IGM.getTypeInfo(I->getType()).getStorageType();
7126+
auto loweredKeyPathTy = IGM.getLoweredType(I->getKeyPathType());
7127+
auto resultStorageTy = IGM.getTypeInfo(loweredKeyPathTy).getStorageType();
71277128

71287129
Explosion e;
71297130
e.add(Builder.CreateBitCast(call, resultStorageTy));

lib/SIL/IR/SILInstructions.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17+
#include "swift/AST/ExistentialLayout.h"
1718
#include "swift/AST/Expr.h"
1819
#include "swift/AST/ProtocolConformance.h"
1920
#include "swift/Basic/AssertImplements.h"
@@ -2966,6 +2967,17 @@ KeyPathInst::~KeyPathInst() {
29662967
operand.~Operand();
29672968
}
29682969

2970+
BoundGenericType *KeyPathInst::getKeyPathType() const {
2971+
auto kpTy = getType();
2972+
2973+
if (auto existential = kpTy.getAs<ExistentialType>()) {
2974+
return existential->getExistentialLayout()
2975+
.explicitSuperclass->castTo<BoundGenericType>();
2976+
}
2977+
2978+
return kpTy.getAs<BoundGenericType>();
2979+
}
2980+
29692981
KeyPathPattern *KeyPathInst::getPattern() const {
29702982
assert(Pattern && "pattern was reset!");
29712983
return Pattern;

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5423,8 +5423,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
54235423
auto kpTy = KPI->getType();
54245424

54255425
require(kpTy.isObject(), "keypath result must be an object type");
5426-
5427-
auto kpBGT = kpTy.getAs<BoundGenericType>();
5426+
5427+
auto *kpBGT = KPI->getKeyPathType();
5428+
54285429
require(kpBGT, "keypath result must be a generic type");
54295430
require(kpBGT->isKeyPath() ||
54305431
kpBGT->isWritableKeyPath() ||

lib/SILGen/SILGenExpr.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4140,9 +4140,8 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) {
41404140
SmallVector<KeyPathPatternComponent, 4> loweredComponents;
41414141
auto loweredTy = SGF.getLoweredType(E->getType());
41424142

4143-
CanType rootTy = E->getType()->castTo<BoundGenericType>()->getGenericArgs()[0]
4144-
->getCanonicalType();
4145-
4143+
CanType rootTy = E->getRootType()->getCanonicalType();
4144+
41464145
bool needsGenericContext = false;
41474146
if (rootTy->hasArchetype()) {
41484147
needsGenericContext = true;

lib/Sema/CSApply.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5009,6 +5009,11 @@ namespace {
50095009
baseTy = fnTy->getParams()[0].getParameterType();
50105010
leafTy = fnTy->getResult();
50115011
isFunctionType = true;
5012+
} else if (auto *existential = exprType->getAs<ExistentialType>()) {
5013+
auto layout = existential->getExistentialLayout();
5014+
auto keyPathTy = layout.explicitSuperclass->castTo<BoundGenericType>();
5015+
baseTy = keyPathTy->getGenericArgs()[0];
5016+
leafTy = keyPathTy->getGenericArgs()[1];
50125017
} else {
50135018
auto keyPathTy = exprType->castTo<BoundGenericType>();
50145019
baseTy = keyPathTy->getGenericArgs()[0];

lib/Sema/CSBindings.cpp

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -531,22 +531,41 @@ void BindingSet::inferTransitiveBindings(
531531
}
532532
}
533533

534-
static BoundGenericType *getKeyPathType(ASTContext &ctx,
535-
KeyPathCapability capability,
536-
Type rootType, Type valueType) {
537-
switch (capability) {
538-
case KeyPathCapability::ReadOnly:
539-
return BoundGenericType::get(ctx.getKeyPathDecl(), /*parent=*/Type(),
540-
{rootType, valueType});
541-
542-
case KeyPathCapability::Writable:
543-
return BoundGenericType::get(ctx.getWritableKeyPathDecl(),
544-
/*parent=*/Type(), {rootType, valueType});
545-
546-
case KeyPathCapability::ReferenceWritable:
547-
return BoundGenericType::get(ctx.getReferenceWritableKeyPathDecl(),
548-
/*parent=*/Type(), {rootType, valueType});
534+
static Type getKeyPathType(ASTContext &ctx, KeyPathCapability capability,
535+
Type rootType, Type valueType) {
536+
KeyPathMutability mutability;
537+
bool isSendable;
538+
539+
std::tie(mutability, isSendable) = capability;
540+
541+
Type keyPathTy;
542+
switch (mutability) {
543+
case KeyPathMutability::ReadOnly:
544+
keyPathTy = BoundGenericType::get(ctx.getKeyPathDecl(), /*parent=*/Type(),
545+
{rootType, valueType});
546+
break;
547+
548+
case KeyPathMutability::Writable:
549+
keyPathTy = BoundGenericType::get(ctx.getWritableKeyPathDecl(),
550+
/*parent=*/Type(), {rootType, valueType});
551+
break;
552+
553+
case KeyPathMutability::ReferenceWritable:
554+
keyPathTy = BoundGenericType::get(ctx.getReferenceWritableKeyPathDecl(),
555+
/*parent=*/Type(), {rootType, valueType});
556+
break;
557+
}
558+
559+
if (isSendable &&
560+
ctx.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) {
561+
auto *sendable = ctx.getProtocol(KnownProtocolKind::Sendable);
562+
keyPathTy = ProtocolCompositionType::get(
563+
ctx, {keyPathTy, sendable->getDeclaredInterfaceType()},
564+
/*hasExplicitAnyObject=*/false);
565+
return ExistentialType::get(keyPathTy);
549566
}
567+
568+
return keyPathTy;
550569
}
551570

552571
void BindingSet::finalize(

lib/Sema/CSDiagnostics.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,7 +1426,7 @@ SourceRange MemberAccessOnOptionalBaseFailure::getSourceRange() const {
14261426
auto anchor = getAnchor();
14271427
auto keyPathExpr = castToExpr<KeyPathExpr>(anchor);
14281428
if (componentPathElt->getIndex() == 0) {
1429-
if (auto rootType = keyPathExpr->getRootType()) {
1429+
if (auto rootType = keyPathExpr->getExplicitRootType()) {
14301430
return rootType->getSourceRange();
14311431
} else {
14321432
return keyPathExpr->getComponents().front().getLoc();
@@ -1483,7 +1483,7 @@ bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() {
14831483
// For members where the base type is an optional key path root
14841484
// let's emit a tailored note suggesting to use its unwrapped type.
14851485
auto *keyPathExpr = castToExpr<KeyPathExpr>(getAnchor());
1486-
if (auto rootType = keyPathExpr->getRootType()) {
1486+
if (auto rootType = keyPathExpr->getExplicitRootType()) {
14871487
emitDiagnostic(diag::optional_base_not_unwrapped, baseType, Member,
14881488
unwrappedBaseType);
14891489

@@ -6134,7 +6134,7 @@ SourceLoc AnyObjectKeyPathRootFailure::getLoc() const {
61346134
auto anchor = getAnchor();
61356135

61366136
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
6137-
if (auto rootTyRepr = KPE->getRootType())
6137+
if (auto rootTyRepr = KPE->getExplicitRootType())
61386138
return rootTyRepr->getLoc();
61396139
}
61406140

@@ -6145,7 +6145,7 @@ SourceRange AnyObjectKeyPathRootFailure::getSourceRange() const {
61456145
auto anchor = getAnchor();
61466146

61476147
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
6148-
if (auto rootTyRepr = KPE->getRootType())
6148+
if (auto rootTyRepr = KPE->getExplicitRootType())
61496149
return rootTyRepr->getSourceRange();
61506150
}
61516151

lib/Sema/CSGen.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3732,7 +3732,7 @@ namespace {
37323732
TVO_CanBindToHole);
37333733

37343734
// If a root type was explicitly given, then resolve it now.
3735-
if (auto rootRepr = E->getRootType()) {
3735+
if (auto rootRepr = E->getExplicitRootType()) {
37363736
const auto rootObjectTy = resolveTypeReferenceInExpression(
37373737
rootRepr, TypeResolverContext::InExpression, locator);
37383738
if (!rootObjectTy || rootObjectTy->hasError())
@@ -3800,6 +3800,23 @@ namespace {
38003800
auto *args = component.getSubscriptArgs();
38013801
base = addSubscriptConstraints(E, base, /*decl*/ nullptr, args,
38023802
memberLocator, &componentTypeVars);
3803+
3804+
auto &ctx = CS.getASTContext();
3805+
// All of the type variables that appear in subscript arguments
3806+
// need to be connected to a key path, otherwise it won't be
3807+
// possible to determine sendability of the key path type if
3808+
// the arguments are disconnected from it before being fully
3809+
// resolved.
3810+
if (args &&
3811+
ctx.LangOpts.hasFeature(Feature::InferSendableFromCaptures)) {
3812+
SmallPtrSet<TypeVariableType *, 2> referencedVars;
3813+
for (const auto &arg : *args) {
3814+
CS.getType(arg.getExpr())->getTypeVariables(referencedVars);
3815+
}
3816+
3817+
componentTypeVars.append(referencedVars.begin(),
3818+
referencedVars.end());
3819+
}
38033820
break;
38043821
}
38053822

lib/Sema/CSSimplify.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12195,6 +12195,12 @@ ConstraintSystem::simplifyKeyPathConstraint(
1219512195
if (contextualTy->isPlaceholder())
1219612196
return true;
1219712197

12198+
// Situations like `any KeyPath<...> & Sendable`.
12199+
if (contextualTy->isExistentialType()) {
12200+
contextualTy = contextualTy->getExistentialLayout().explicitSuperclass;
12201+
assert(contextualTy);
12202+
}
12203+
1219812204
// If there are no other options the solver might end up picking
1219912205
// `AnyKeyPath` or `PartialKeyPath` based on a contextual conversion.
1220012206
// This is an error during normal type-checking but okay in

0 commit comments

Comments
 (0)