@@ -4198,9 +4198,18 @@ namespace {
4198
4198
4199
4199
// Resolve each of the components.
4200
4200
bool didOptionalChain = false ;
4201
- auto keyPathTy = cs.getType (E)->castTo <BoundGenericType>();
4202
- Type baseTy = keyPathTy->getGenericArgs ()[0 ];
4203
- Type leafTy = keyPathTy->getGenericArgs ()[1 ];
4201
+ bool isFunctionType = false ;
4202
+ Type baseTy, leafTy;
4203
+ Type exprType = cs.getType (E);
4204
+ if (auto fnTy = exprType->getAs <FunctionType>()) {
4205
+ baseTy = fnTy->getParams ()[0 ].getPlainType ();
4206
+ leafTy = fnTy->getResult ();
4207
+ isFunctionType = true ;
4208
+ } else {
4209
+ auto keyPathTy = exprType->castTo <BoundGenericType>();
4210
+ baseTy = keyPathTy->getGenericArgs ()[0 ];
4211
+ leafTy = keyPathTy->getGenericArgs ()[1 ];
4212
+ }
4204
4213
4205
4214
// Updates the constraint system with the type of the last resolved
4206
4215
// component. We do it this way because we sometimes insert new
@@ -4405,7 +4414,101 @@ namespace {
4405
4414
// key path.
4406
4415
assert (!baseTy || baseTy->hasUnresolvedType ()
4407
4416
|| baseTy->getWithoutSpecifierType ()->isEqual (leafTy));
4408
- return E;
4417
+
4418
+ if (!isFunctionType)
4419
+ return E;
4420
+
4421
+ // If we've gotten here, the user has used key path literal syntax to form
4422
+ // a closure. The type checker has given E a function type to indicate
4423
+ // this; we're going to change E's type to KeyPath<baseTy, leafTy> and
4424
+ // then wrap it in a larger closure expression with the appropriate type.
4425
+
4426
+ // baseTy has been overwritten by the loop above; restore it.
4427
+ baseTy = exprType->getAs <FunctionType>()->getParams ()[0 ].getPlainType ();
4428
+
4429
+ // Compute KeyPath<baseTy, leafTy> and set E's type back to it.
4430
+ auto kpDecl = cs.getASTContext ().getKeyPathDecl ();
4431
+ auto keyPathTy =
4432
+ BoundGenericType::get (kpDecl, nullptr , { baseTy, leafTy });
4433
+ E->setType (keyPathTy);
4434
+ cs.cacheType (E);
4435
+
4436
+ // To ensure side effects of the key path expression (mainly indices in
4437
+ // subscripts) are only evaluated once, we construct an outer closure,
4438
+ // which is immediately evaluated, and an inner closure, which it returns.
4439
+ // The result looks like this:
4440
+ //
4441
+ // return "{ $kp$ in { $0[keyPath: $kp$] } }( \(E) )"
4442
+
4443
+ auto &ctx = cs.getASTContext ();
4444
+ auto discriminator = AutoClosureExpr::InvalidDiscriminator;
4445
+
4446
+ // The inner closure.
4447
+ //
4448
+ // let closure = "{ $0[keyPath: $kp$] }"
4449
+ auto closureTy =
4450
+ FunctionType::get ({ FunctionType::Param (baseTy) }, leafTy);
4451
+ auto closure = new (ctx)
4452
+ AutoClosureExpr (E, leafTy, discriminator, cs.DC );
4453
+ auto param = new (ctx) ParamDecl (
4454
+ ParamDecl::Specifier::Default, SourceLoc (),
4455
+ /* argument label*/ SourceLoc (), Identifier (),
4456
+ /* parameter name*/ SourceLoc (), ctx.getIdentifier (" $0" ), closure);
4457
+ param->setType (baseTy);
4458
+ param->setInterfaceType (baseTy->mapTypeOutOfContext ());
4459
+
4460
+ // The outer closure.
4461
+ //
4462
+ // let outerClosure = "{ $kp$ in \(closure) }"
4463
+ auto outerClosureTy =
4464
+ FunctionType::get ({ FunctionType::Param (keyPathTy) }, closureTy);
4465
+ auto outerClosure = new (ctx)
4466
+ AutoClosureExpr (closure, closureTy, discriminator, cs.DC );
4467
+ auto outerParam =
4468
+ new (ctx) ParamDecl (ParamDecl::Specifier::Default, SourceLoc (),
4469
+ /* argument label*/ SourceLoc (), Identifier (),
4470
+ /* parameter name*/ SourceLoc (),
4471
+ ctx.getIdentifier (" $kp$" ), outerClosure);
4472
+ outerParam->setType (keyPathTy);
4473
+ outerParam->setInterfaceType (keyPathTy->mapTypeOutOfContext ());
4474
+
4475
+ // let paramRef = "$0"
4476
+ auto *paramRef = new (ctx)
4477
+ DeclRefExpr (param, DeclNameLoc (E->getLoc ()), /* Implicit=*/ true );
4478
+ paramRef->setType (baseTy);
4479
+ cs.cacheType (paramRef);
4480
+
4481
+ // let outerParamRef = "$kp$"
4482
+ auto outerParamRef = new (ctx)
4483
+ DeclRefExpr (outerParam, DeclNameLoc (E->getLoc ()), /* Implicit=*/ true );
4484
+ outerParamRef->setType (keyPathTy);
4485
+ cs.cacheType (outerParamRef);
4486
+
4487
+ // let application = "\(paramRef)[keyPath: \(outerParamRef)]"
4488
+ auto *application = new (ctx)
4489
+ KeyPathApplicationExpr (paramRef,
4490
+ E->getStartLoc (), outerParamRef, E->getEndLoc (),
4491
+ leafTy, /* implicit=*/ true );
4492
+ cs.cacheType (application);
4493
+
4494
+ // Finish up the inner closure.
4495
+ closure->setParameterList (ParameterList::create (ctx, {param}));
4496
+ closure->setBody (application);
4497
+ closure->setType (closureTy);
4498
+ cs.cacheType (closure);
4499
+
4500
+ // Finish up the outer closure.
4501
+ outerClosure->setParameterList (ParameterList::create (ctx, {outerParam}));
4502
+ outerClosure->setBody (closure);
4503
+ outerClosure->setType (outerClosureTy);
4504
+ cs.cacheType (outerClosure);
4505
+
4506
+ // let outerApply = "\( outerClosure )( \(E) )"
4507
+ auto outerApply = CallExpr::createImplicit (ctx, outerClosure, {E}, {});
4508
+ outerApply->setType (closureTy);
4509
+ cs.cacheExprTypes (outerApply);
4510
+
4511
+ return coerceToType (outerApply, exprType, cs.getConstraintLocator (E));
4409
4512
}
4410
4513
4411
4514
KeyPathExpr::Component
0 commit comments