Skip to content

Commit 551604a

Browse files
authored
Merge pull request swiftlang#40183 from hamishknight/the-path-less-traveled
2 parents 67cb7b7 + 76c6254 commit 551604a

File tree

9 files changed

+175
-161
lines changed

9 files changed

+175
-161
lines changed

include/swift/AST/Expr.h

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5562,24 +5562,43 @@ class KeyPathExpr : public Expr {
55625562
private:
55635563
llvm::MutableArrayRef<Component> Components;
55645564

5565-
public:
5566-
/// Create a new #keyPath expression.
5567-
KeyPathExpr(ASTContext &C,
5568-
SourceLoc keywordLoc, SourceLoc lParenLoc,
5569-
ArrayRef<Component> components,
5570-
SourceLoc rParenLoc,
5571-
bool isImplicit = false);
5565+
KeyPathExpr(SourceLoc startLoc, Expr *parsedRoot, Expr *parsedPath,
5566+
SourceLoc endLoc, bool hasLeadingDot, bool isObjC,
5567+
bool isImplicit);
55725568

5569+
/// Create a key path with unresolved root and path expressions.
55735570
KeyPathExpr(SourceLoc backslashLoc, Expr *parsedRoot, Expr *parsedPath,
5574-
bool hasLeadingDot, bool isImplicit = false)
5575-
: Expr(ExprKind::KeyPath, isImplicit), StartLoc(backslashLoc),
5576-
EndLoc(parsedPath ? parsedPath->getEndLoc() : parsedRoot->getEndLoc()),
5577-
ParsedRoot(parsedRoot), ParsedPath(parsedPath),
5578-
HasLeadingDot(hasLeadingDot) {
5579-
assert((parsedRoot || parsedPath) &&
5580-
"keypath must have either root or path");
5581-
Bits.KeyPathExpr.IsObjC = false;
5582-
}
5571+
bool hasLeadingDot, bool isImplicit);
5572+
5573+
/// Create a key path with components.
5574+
KeyPathExpr(ASTContext &ctx, SourceLoc startLoc,
5575+
ArrayRef<Component> components, SourceLoc endLoc, bool isObjC,
5576+
bool isImplicit);
5577+
5578+
public:
5579+
/// Create a new parsed Swift key path expression.
5580+
static KeyPathExpr *createParsed(ASTContext &ctx, SourceLoc backslashLoc,
5581+
Expr *parsedRoot, Expr *parsedPath,
5582+
bool hasLeadingDot);
5583+
5584+
/// Create a new parsed #keyPath expression.
5585+
static KeyPathExpr *createParsedPoundKeyPath(ASTContext &ctx,
5586+
SourceLoc keywordLoc,
5587+
SourceLoc lParenLoc,
5588+
ArrayRef<Component> components,
5589+
SourceLoc rParenLoc);
5590+
5591+
/// Create an implicit Swift key path expression with a set of resolved
5592+
/// components.
5593+
static KeyPathExpr *createImplicit(ASTContext &ctx, SourceLoc backslashLoc,
5594+
ArrayRef<Component> components,
5595+
SourceLoc endLoc);
5596+
5597+
/// Create an implicit Swift key path expression with a root and path
5598+
/// expression to be resolved.
5599+
static KeyPathExpr *createImplicit(ASTContext &ctx, SourceLoc backslashLoc,
5600+
Expr *parsedRoot, Expr *parsedPath,
5601+
bool hasLeadingDot);
55835602

55845603
SourceLoc getLoc() const { return StartLoc; }
55855604
SourceRange getSourceRange() const { return SourceRange(StartLoc, EndLoc); }
@@ -5592,10 +5611,9 @@ class KeyPathExpr : public Expr {
55925611
return Components;
55935612
}
55945613

5595-
/// Resolve the components of an un-type-checked expr. This copies over the
5596-
/// components from the argument array.
5597-
void resolveComponents(ASTContext &C,
5598-
ArrayRef<Component> resolvedComponents);
5614+
/// Set the key path components. This copies over the components from the
5615+
/// argument array.
5616+
void setComponents(ASTContext &C, ArrayRef<Component> newComponents);
55995617

56005618
/// Indicates if the key path expression is composed by a single invalid
56015619
/// component. e.g. missing component `\Root`

lib/AST/Expr.cpp

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,34 +1987,81 @@ OpenedArchetypeType *OpenExistentialExpr::getOpenedArchetype() const {
19871987
return type->castTo<OpenedArchetypeType>();
19881988
}
19891989

1990-
KeyPathExpr::KeyPathExpr(ASTContext &C, SourceLoc keywordLoc,
1991-
SourceLoc lParenLoc, ArrayRef<Component> components,
1992-
SourceLoc rParenLoc, bool isImplicit)
1993-
: Expr(ExprKind::KeyPath, isImplicit), StartLoc(keywordLoc),
1994-
LParenLoc(lParenLoc), EndLoc(rParenLoc),
1995-
Components(C.AllocateUninitialized<Component>(components.size())) {
1996-
// Copy components into the AST context.
1997-
std::uninitialized_copy(components.begin(), components.end(),
1998-
Components.begin());
1999-
2000-
Bits.KeyPathExpr.IsObjC = true;
1990+
KeyPathExpr::KeyPathExpr(SourceLoc startLoc, Expr *parsedRoot,
1991+
Expr *parsedPath, SourceLoc endLoc, bool hasLeadingDot,
1992+
bool isObjC, bool isImplicit)
1993+
: Expr(ExprKind::KeyPath, isImplicit), StartLoc(startLoc), EndLoc(endLoc),
1994+
ParsedRoot(parsedRoot), ParsedPath(parsedPath),
1995+
HasLeadingDot(hasLeadingDot) {
1996+
assert(!(isObjC && (parsedRoot || parsedPath)) &&
1997+
"Obj-C key paths should only have components");
1998+
Bits.KeyPathExpr.IsObjC = isObjC;
1999+
}
2000+
2001+
KeyPathExpr::KeyPathExpr(SourceLoc backslashLoc, Expr *parsedRoot,
2002+
Expr *parsedPath, bool hasLeadingDot, bool isImplicit)
2003+
: KeyPathExpr(backslashLoc, parsedRoot, parsedPath,
2004+
parsedPath ? parsedPath->getEndLoc()
2005+
: parsedRoot->getEndLoc(),
2006+
hasLeadingDot, /*isObjC*/ false, isImplicit) {
2007+
assert((parsedRoot || parsedPath) &&
2008+
"Key path must have either root or path");
2009+
}
2010+
2011+
KeyPathExpr::KeyPathExpr(ASTContext &ctx, SourceLoc startLoc,
2012+
ArrayRef<Component> components, SourceLoc endLoc,
2013+
bool isObjC, bool isImplicit)
2014+
: KeyPathExpr(startLoc, /*parsedRoot*/ nullptr, /*parsedPath*/ nullptr,
2015+
endLoc, /*hasLeadingDot*/ false, isObjC, isImplicit) {
2016+
assert(!components.empty());
2017+
Components = ctx.AllocateCopy(components);
2018+
}
2019+
2020+
KeyPathExpr *KeyPathExpr::createParsedPoundKeyPath(
2021+
ASTContext &ctx, SourceLoc keywordLoc, SourceLoc lParenLoc,
2022+
ArrayRef<Component> components, SourceLoc rParenLoc) {
2023+
return new (ctx) KeyPathExpr(ctx, keywordLoc, components, rParenLoc,
2024+
/*isObjC*/ true, /*isImplicit*/ false);
2025+
}
2026+
2027+
KeyPathExpr *KeyPathExpr::createParsed(ASTContext &ctx, SourceLoc backslashLoc,
2028+
Expr *parsedRoot, Expr *parsedPath,
2029+
bool hasLeadingDot) {
2030+
return new (ctx) KeyPathExpr(backslashLoc, parsedRoot, parsedPath,
2031+
hasLeadingDot, /*isImplicit*/ false);
2032+
}
2033+
2034+
KeyPathExpr *KeyPathExpr::createImplicit(ASTContext &ctx,
2035+
SourceLoc backslashLoc,
2036+
ArrayRef<Component> components,
2037+
SourceLoc endLoc) {
2038+
return new (ctx) KeyPathExpr(ctx, backslashLoc, components, endLoc,
2039+
/*isObjC*/ false, /*isImplicit*/ true);
2040+
}
2041+
2042+
KeyPathExpr *KeyPathExpr::createImplicit(ASTContext &ctx,
2043+
SourceLoc backslashLoc,
2044+
Expr *parsedRoot, Expr *parsedPath,
2045+
bool hasLeadingDot) {
2046+
return new (ctx) KeyPathExpr(backslashLoc, parsedRoot, parsedPath,
2047+
hasLeadingDot, /*isImplicit*/ true);
20012048
}
20022049

20032050
void
2004-
KeyPathExpr::resolveComponents(ASTContext &C,
2005-
ArrayRef<KeyPathExpr::Component> resolvedComponents) {
2051+
KeyPathExpr::setComponents(ASTContext &C,
2052+
ArrayRef<KeyPathExpr::Component> newComponents) {
20062053
// Reallocate the components array if it needs to be.
2007-
if (Components.size() < resolvedComponents.size()) {
2008-
Components = C.Allocate<Component>(resolvedComponents.size());
2054+
if (Components.size() < newComponents.size()) {
2055+
Components = C.Allocate<Component>(newComponents.size());
20092056
for (unsigned i : indices(Components)) {
20102057
::new ((void*)&Components[i]) Component{};
20112058
}
20122059
}
20132060

2014-
for (unsigned i : indices(resolvedComponents)) {
2015-
Components[i] = resolvedComponents[i];
2061+
for (unsigned i : indices(newComponents)) {
2062+
Components[i] = newComponents[i];
20162063
}
2017-
Components = Components.slice(0, resolvedComponents.size());
2064+
Components = Components.slice(0, newComponents.size());
20182065
}
20192066

20202067
Optional<unsigned> KeyPathExpr::findComponentWithSubscriptArg(Expr *arg) {

lib/Parse/ParseExpr.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -656,17 +656,17 @@ ParserResult<Expr> Parser::parseExprKeyPath() {
656656
// Add the code completion expression to the path result.
657657
CodeCompletionExpr *CC = new (Context)
658658
CodeCompletionExpr(pathResult.getPtrOrNull(), Tok.getLoc());
659-
auto keypath = new (Context)
660-
KeyPathExpr(backslashLoc, rootResult.getPtrOrNull(), CC, hasLeadingDot);
659+
auto *keypath = KeyPathExpr::createParsed(
660+
Context, backslashLoc, rootResult.getPtrOrNull(), CC, hasLeadingDot);
661661
if (CodeCompletion)
662662
CodeCompletion->completeExprKeyPath(keypath, DotLoc);
663663
consumeToken(tok::code_complete);
664664
return makeParserCodeCompletionResult(keypath);
665665
}
666666

667-
auto keypath =
668-
new (Context) KeyPathExpr(backslashLoc, rootResult.getPtrOrNull(),
669-
pathResult.getPtrOrNull(), hasLeadingDot);
667+
auto *keypath = KeyPathExpr::createParsed(
668+
Context, backslashLoc, rootResult.getPtrOrNull(),
669+
pathResult.getPtrOrNull(), hasLeadingDot);
670670
return makeParserResult(parseStatus, keypath);
671671
}
672672

@@ -690,8 +690,8 @@ ParserResult<Expr> Parser::parseExprKeyPathObjC() {
690690
auto handleCodeCompletion = [&](SourceLoc DotLoc) -> ParserResult<Expr> {
691691
KeyPathExpr *expr = nullptr;
692692
if (!components.empty()) {
693-
expr = new (Context)
694-
KeyPathExpr(Context, keywordLoc, lParenLoc, components, Tok.getLoc());
693+
expr = KeyPathExpr::createParsedPoundKeyPath(
694+
Context, keywordLoc, lParenLoc, components, Tok.getLoc());
695695
}
696696

697697
if (CodeCompletion)
@@ -764,7 +764,7 @@ ParserResult<Expr> Parser::parseExprKeyPathObjC() {
764764
}
765765

766766
// We're done: create the key-path expression.
767-
return makeParserResult<Expr>(new (Context) KeyPathExpr(
767+
return makeParserResult<Expr>(KeyPathExpr::createParsedPoundKeyPath(
768768
Context, keywordLoc, lParenLoc, components, rParenLoc));
769769
}
770770

lib/Sema/CSApply.cpp

Lines changed: 28 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,23 +2222,23 @@ namespace {
22222222
Expr *buildKeyPathDynamicMemberArgExpr(BoundGenericType *keyPathTy,
22232223
SourceLoc dotLoc,
22242224
ConstraintLocator *memberLoc) {
2225+
using Component = KeyPathExpr::Component;
22252226
auto &ctx = cs.getASTContext();
22262227
auto *anchor = getAsExpr(memberLoc->getAnchor());
22272228

2228-
SmallVector<KeyPathExpr::Component, 2> components;
2229+
auto makeKeyPath = [&](ArrayRef<Component> components) -> Expr * {
2230+
auto *kp = KeyPathExpr::createImplicit(ctx, /*backslashLoc*/ dotLoc,
2231+
components, anchor->getEndLoc());
2232+
kp->setType(keyPathTy);
2233+
cs.cacheExprTypes(kp);
22292234

2230-
// Let's create a KeyPath expression and fill in "parsed path"
2231-
// after component is built.
2232-
auto *keyPath = new (ctx) KeyPathExpr(/*backslashLoc=*/dotLoc,
2233-
/*parsedRoot=*/nullptr,
2234-
/*parsedPath=*/anchor,
2235-
/*hasLeadingDot=*/false,
2236-
/*isImplicit=*/true);
2237-
// Type of the keypath expression we are forming is known
2238-
// in advance, so let's set it right away.
2239-
keyPath->setType(keyPathTy);
2240-
cs.cacheType(keyPath);
2235+
// See whether there's an equivalent ObjC key path string we can produce
2236+
// for interop purposes.
2237+
checkAndSetObjCKeyPathString(kp);
2238+
return kp;
2239+
};
22412240

2241+
SmallVector<Component, 2> components;
22422242
auto *componentLoc = cs.getConstraintLocator(
22432243
memberLoc,
22442244
LocatorPathElt::KeyPathDynamicMember(keyPathTy->getAnyNominal()));
@@ -2251,95 +2251,44 @@ namespace {
22512251
case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
22522252
buildKeyPathSubscriptComponent(overload, dotLoc, /*args=*/nullptr,
22532253
componentLoc, components);
2254-
keyPath->resolveComponents(ctx, components);
2255-
cs.cacheExprTypes(keyPath);
2256-
return keyPath;
2254+
return makeKeyPath(components);
22572255
}
22582256

22592257
default:
22602258
break;
22612259
}
22622260

2263-
// We can't reuse existing expression because type-check
2264-
// based diagnostics could hold the reference to original AST.
2265-
Expr *componentExpr = nullptr;
2266-
auto *dotExpr = new (ctx) KeyPathDotExpr(dotLoc);
2267-
2268-
// Determines whether this index is built to be used for
2269-
// one of the existing keypath components e.g. `\Lens<[Int]>.count`
2270-
// instead of a regular expression e.g. `lens[0]`.
2271-
bool forKeyPathComponent = false;
2272-
// Looks like keypath dynamic member lookup was used inside
2273-
// of a keypath expression e.g. `\Lens<[Int]>.count` where
2274-
// `count` is referenced using dynamic lookup.
22752261
if (auto *KPE = dyn_cast<KeyPathExpr>(anchor)) {
2262+
// Looks like keypath dynamic member lookup was used inside
2263+
// of a keypath expression e.g. `\Lens<[Int]>.count` where
2264+
// `count` is referenced using dynamic lookup.
22762265
auto kpElt = memberLoc->findFirst<LocatorPathElt::KeyPathComponent>();
22772266
assert(kpElt && "no keypath component node");
2278-
auto &origComponent = KPE->getComponents()[kpElt->getIndex()];
2279-
2280-
using ComponentKind = KeyPathExpr::Component::Kind;
2281-
if (origComponent.getKind() == ComponentKind::UnresolvedProperty) {
2282-
anchor = new (ctx) UnresolvedDotExpr(
2283-
dotExpr, dotLoc, origComponent.getUnresolvedDeclName(),
2284-
DeclNameLoc(origComponent.getLoc()),
2285-
/*Implicit=*/true);
2286-
} else if (origComponent.getKind() ==
2287-
ComponentKind::UnresolvedSubscript) {
2288-
anchor = SubscriptExpr::create(
2289-
ctx, dotExpr, origComponent.getSubscriptArgs(), ConcreteDeclRef(),
2290-
/*implicit=*/true, AccessSemantics::Ordinary);
2267+
auto &comp = KPE->getComponents()[kpElt->getIndex()];
2268+
2269+
if (comp.getKind() == Component::Kind::UnresolvedProperty) {
2270+
buildKeyPathPropertyComponent(overload, comp.getLoc(), componentLoc,
2271+
components);
2272+
} else if (comp.getKind() == Component::Kind::UnresolvedSubscript) {
2273+
buildKeyPathSubscriptComponent(overload, comp.getLoc(),
2274+
comp.getSubscriptArgs(), componentLoc,
2275+
components);
22912276
} else {
22922277
return nullptr;
22932278
}
2294-
2295-
anchor->setType(simplifyType(overload.openedType));
2296-
cs.cacheType(anchor);
2297-
forKeyPathComponent = true;
2279+
return makeKeyPath(components);
22982280
}
22992281

23002282
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
2301-
componentExpr =
2302-
forKeyPathComponent
2303-
? UDE
2304-
: new (ctx) UnresolvedDotExpr(dotExpr, dotLoc, UDE->getName(),
2305-
UDE->getNameLoc(),
2306-
/*Implicit=*/true);
2307-
23082283
buildKeyPathPropertyComponent(overload, UDE->getLoc(), componentLoc,
23092284
components);
23102285
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
2311-
componentExpr = SE;
2312-
// If this is not for a keypath component, we have to copy
2313-
// original subscript expression because expression based
2314-
// diagnostics might have a reference to it, so it couldn't
2315-
// be modified.
2316-
if (!forKeyPathComponent) {
2317-
componentExpr = SubscriptExpr::create(
2318-
ctx, dotExpr, SE->getArgs(),
2319-
SE->hasDecl() ? SE->getDecl() : ConcreteDeclRef(),
2320-
/*implicit=*/true, SE->getAccessSemantics());
2321-
}
2322-
23232286
buildKeyPathSubscriptComponent(overload, SE->getLoc(), SE->getArgs(),
23242287
componentLoc, components);
23252288
} else {
23262289
return nullptr;
23272290
}
2328-
2329-
assert(componentExpr);
2330-
Type ty = simplifyType(cs.getType(anchor));
2331-
componentExpr->setType(ty);
2332-
cs.cacheType(componentExpr);
2333-
2334-
keyPath->setParsedPath(componentExpr);
2335-
keyPath->resolveComponents(ctx, components);
2336-
cs.cacheExprTypes(keyPath);
2337-
2338-
// See whether there's an equivalent ObjC key path string we can produce
2339-
// for interop purposes.
2340-
checkAndSetObjCKeyPathString(keyPath);
2341-
2342-
return keyPath;
2291+
return makeKeyPath(components);
23432292
}
23442293

23452294
/// Bridge the given value (which is an error type) to NSError.
@@ -4898,7 +4847,7 @@ namespace {
48984847
}
48994848

49004849
// Set the resolved components, and cache their types.
4901-
E->resolveComponents(cs.getASTContext(), resolvedComponents);
4850+
E->setComponents(cs.getASTContext(), resolvedComponents);
49024851
cs.cacheExprTypes(E);
49034852

49044853
// See whether there's an equivalent ObjC key path string we can produce

lib/Sema/PreCheckExpr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2008,7 +2008,7 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) {
20082008
std::reverse(components.begin(), components.end());
20092009

20102010
KPE->setRootType(rootType);
2011-
KPE->resolveComponents(getASTContext(), components);
2011+
KPE->setComponents(getASTContext(), components);
20122012
}
20132013

20142014
Expr *PreCheckExpression::simplifyTypeConstructionWithLiteralArg(Expr *E) {

0 commit comments

Comments
 (0)