[CS] Fix key path dynamic member IUOs

Previously we were only handling IUO unwrapping in
visitKeyPathExpr, but not for callers that were
building their own property and subscript
components such as dynamic member lookup.

Move the IUO unwrapping logic into
buildKeyPath[Property/Subscript]Component,
and adjust their signatures to allow them to
append multiple components. Also add
buildKeyPathOptionalForceComponent to allow more
convenient adding of an optional force component.

Resolves SR-11893.
This commit is contained in:
Hamish Knight
2019-12-03 16:38:47 -08:00
parent c1849ba621
commit ef6a240372
2 changed files with 93 additions and 74 deletions

View File

@@ -1524,7 +1524,7 @@ namespace {
auto &ctx = cs.getASTContext();
auto *anchor = memberLoc->getAnchor();
KeyPathExpr::Component component;
SmallVector<KeyPathExpr::Component, 2> components;
// Let's create a KeyPath expression and fill in "parsed path"
// after component is built.
@@ -1546,10 +1546,10 @@ namespace {
// calls necessary to resolve a member reference.
if (overload.choice.getKind() ==
OverloadChoiceKind::KeyPathDynamicMemberLookup) {
keyPath->resolveComponents(ctx,
buildKeyPathSubscriptComponent(
overload, dotLoc, /*indexExpr=*/nullptr,
ctx.Id_dynamicMember, componentLoc));
buildKeyPathSubscriptComponent(overload, dotLoc, /*indexExpr=*/nullptr,
ctx.Id_dynamicMember, componentLoc,
components);
keyPath->resolveComponents(ctx, components);
return keyPath;
}
@@ -1599,8 +1599,8 @@ namespace {
UDE->getNameLoc(),
/*Implicit=*/true);
component = buildKeyPathPropertyComponent(overload, UDE->getLoc(),
componentLoc);
buildKeyPathPropertyComponent(overload, UDE->getLoc(), componentLoc,
components);
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
componentExpr = SE;
// If this is not for a keypath component, we have to copy
@@ -1628,9 +1628,9 @@ namespace {
/*implicit=*/true, SE->getAccessSemantics());
}
component = buildKeyPathSubscriptComponent(
overload, SE->getLoc(), SE->getIndex(), SE->getArgumentLabels(),
componentLoc);
buildKeyPathSubscriptComponent(overload, SE->getLoc(), SE->getIndex(),
SE->getArgumentLabels(), componentLoc,
components);
} else {
return nullptr;
}
@@ -1643,7 +1643,7 @@ namespace {
cs.setType(keyPath, 0, ty);
keyPath->setParsedPath(componentExpr);
keyPath->resolveComponents(ctx, {component});
keyPath->resolveComponents(ctx, components);
return keyPath;
}
@@ -4190,24 +4190,6 @@ namespace {
resolvedComponents.push_back(origComponent);
continue;
}
auto getObjectType = [](Type optionalTy) -> Type {
Type objectTy;
if (auto lvalue = optionalTy->getAs<LValueType>()) {
objectTy = lvalue->getObjectType()->getOptionalObjectType();
if (optionalTy->hasUnresolvedType() && !objectTy) {
objectTy = optionalTy;
}
objectTy = LValueType::get(objectTy);
} else {
objectTy = optionalTy->getOptionalObjectType();
if (optionalTy->hasUnresolvedType() && !objectTy) {
objectTy = optionalTy;
}
}
assert(objectTy);
return objectTy;
};
auto kind = origComponent.getKind();
Optional<SelectedOverload> foundDecl;
@@ -4249,17 +4231,8 @@ namespace {
break;
}
auto component = buildKeyPathPropertyComponent(
*foundDecl, origComponent.getLoc(), locator);
resolvedComponents.push_back(component);
if (shouldForceUnwrapResult(foundDecl->choice, locator)) {
auto objectTy = getObjectType(baseTy);
auto loc = origComponent.getLoc();
resolvedComponents.push_back(
KeyPathExpr::Component::forOptionalForce(objectTy, loc));
}
buildKeyPathPropertyComponent(*foundDecl, origComponent.getLoc(),
locator, resolvedComponents);
break;
}
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
@@ -4273,17 +4246,9 @@ namespace {
if (!isDynamicMember)
subscriptLabels = origComponent.getSubscriptLabels();
auto component = buildKeyPathSubscriptComponent(
buildKeyPathSubscriptComponent(
*foundDecl, origComponent.getLoc(), origComponent.getIndexExpr(),
subscriptLabels, locator);
resolvedComponents.push_back(component);
if (shouldForceUnwrapResult(foundDecl->choice, locator)) {
auto objectTy = getObjectType(baseTy);
auto loc = origComponent.getLoc();
resolvedComponents.push_back(
KeyPathExpr::Component::forOptionalForce(objectTy, loc));
}
subscriptLabels, locator, resolvedComponents);
break;
}
case KeyPathExpr::Component::Kind::OptionalChain: {
@@ -4301,13 +4266,9 @@ namespace {
KeyPathExpr::Component::forOptionalChain(objectTy, loc));
break;
}
case KeyPathExpr::Component::Kind::OptionalForce: {
auto objectTy = getObjectType(baseTy);
auto loc = origComponent.getLoc();
resolvedComponents.push_back(
KeyPathExpr::Component::forOptionalForce(objectTy, loc));
case KeyPathExpr::Component::Kind::OptionalForce:
buildKeyPathOptionalForceComponent(resolvedComponents);
break;
}
case KeyPathExpr::Component::Kind::Invalid: {
auto component = origComponent;
component.setComponentType(leafTy);
@@ -4466,10 +4427,37 @@ namespace {
return coerceToType(outerApply, exprType, cs.getConstraintLocator(E));
}
KeyPathExpr::Component
buildKeyPathPropertyComponent(const SelectedOverload &overload,
SourceLoc componentLoc,
ConstraintLocator *locator) {
void buildKeyPathOptionalForceComponent(
SmallVectorImpl<KeyPathExpr::Component> &components) {
assert(!components.empty());
// Unwrap the last component type, preserving @lvalue-ness.
auto optionalTy = components.back().getComponentType();
Type objectTy;
if (auto lvalue = optionalTy->getAs<LValueType>()) {
objectTy = lvalue->getObjectType()->getOptionalObjectType();
if (optionalTy->hasUnresolvedType() && !objectTy) {
objectTy = optionalTy;
}
objectTy = LValueType::get(objectTy);
} else {
objectTy = optionalTy->getOptionalObjectType();
if (optionalTy->hasUnresolvedType() && !objectTy) {
objectTy = optionalTy;
}
}
assert(objectTy);
auto loc = components.back().getLoc();
components.push_back(
KeyPathExpr::Component::forOptionalForce(objectTy, loc));
}
void buildKeyPathPropertyComponent(
const SelectedOverload &overload, SourceLoc componentLoc,
ConstraintLocator *locator,
SmallVectorImpl<KeyPathExpr::Component> &components) {
auto resolvedTy = simplifyType(overload.openedType);
if (auto *property = overload.choice.getDeclOrNull()) {
// Key paths can only refer to properties currently.
auto varDecl = cast<VarDecl>(property);
@@ -4479,26 +4467,24 @@ namespace {
// There is a fix which diagnoses such situation already.
assert(!varDecl->isStatic());
auto resolvedTy = overload.openedType;
resolvedTy = simplifyType(resolvedTy);
// Compute the concrete reference to the member.
auto ref = resolveConcreteDeclRef(property, locator);
return KeyPathExpr::Component::forProperty(ref, resolvedTy,
componentLoc);
components.push_back(
KeyPathExpr::Component::forProperty(ref, resolvedTy, componentLoc));
} else {
auto fieldIndex = overload.choice.getTupleIndex();
components.push_back(KeyPathExpr::Component::forTupleElement(
fieldIndex, resolvedTy, componentLoc));
}
auto fieldIndex = overload.choice.getTupleIndex();
auto resolvedTy = overload.openedType;
resolvedTy = simplifyType(resolvedTy);
return KeyPathExpr::Component::forTupleElement(fieldIndex, resolvedTy,
componentLoc);
if (shouldForceUnwrapResult(overload.choice, locator))
buildKeyPathOptionalForceComponent(components);
}
KeyPathExpr::Component buildKeyPathSubscriptComponent(
void buildKeyPathSubscriptComponent(
SelectedOverload &overload, SourceLoc componentLoc, Expr *indexExpr,
ArrayRef<Identifier> labels, ConstraintLocator *locator) {
ArrayRef<Identifier> labels, ConstraintLocator *locator,
SmallVectorImpl<KeyPathExpr::Component> &components) {
auto subscript = cast<SubscriptDecl>(overload.choice.getDecl());
assert(!subscript->isGetterMutating());
@@ -4568,10 +4554,14 @@ namespace {
conformances.push_back(hashableConformance);
}
return KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr(
auto comp = KeyPathExpr::Component::forSubscriptWithPrebuiltIndexExpr(
ref, newIndexExpr, cs.getASTContext().AllocateCopy(newLabels),
resolvedTy, componentLoc,
cs.getASTContext().AllocateCopy(conformances));
components.push_back(comp);
if (shouldForceUnwrapResult(overload.choice, locator))
buildKeyPathOptionalForceComponent(components);
}
Expr *visitKeyPathDotExpr(KeyPathDotExpr *E) {

View File

@@ -390,3 +390,32 @@ func test_constrained_ext_vs_dynamic_member() {
// CHECK: function_ref @$s29keypath_dynamic_member_lookup8SR_11465VAASHRzlE9hashValueSivg
_ = SR_11465<Int>(rawValue: 1).hashValue // Ok, keep choice from constrained extension
}
// SR-11893: Make sure we properly handle IUO unwraps for key path dynamic members.
struct SR_11893_Base {
var i: Int!
subscript(_ x: Int) -> Int! { x }
}
@dynamicMemberLookup
struct SR_11893 {
subscript(dynamicMember kp: KeyPath<SR_11893_Base, Int>) -> Void { () }
}
// CHECK-LABEL: sil hidden @$s29keypath_dynamic_member_lookup13testIUOUnwrapyyAA8SR_11893VF : $@convention(thin) (SR_11893) -> ()
func testIUOUnwrap(_ x: SR_11893) {
// CHECK: keypath $KeyPath<SR_11893_Base, Int>, (root $SR_11893_Base; stored_property #SR_11893_Base.i : $Optional<Int>; optional_force : $Int)
x.i
// CHECK: keypath $KeyPath<SR_11893_Base, Int>, (root $SR_11893_Base; gettable_property $Optional<Int>, id @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicig : $@convention(method) (Int, SR_11893_Base) -> Optional<Int>, getter @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicipACTK : $@convention(thin) (@in_guaranteed SR_11893_Base, UnsafeRawPointer) -> @out Optional<Int>, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int; optional_force : $Int)
x[5]
// FIXME(SR-11896): We should not vend a WritableKeyPath for the "outer" key path here.
// CHECK: [[INNER_KP:%[0-9]+]] = keypath $KeyPath<SR_11893_Base, Int>, (root $SR_11893_Base; stored_property #SR_11893_Base.i : $Optional<Int>; optional_force : $Int)
// CHECK: keypath $WritableKeyPath<SR_11893, ()>, (root $SR_11893; gettable_property $(), id @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcig : $@convention(method) (@guaranteed KeyPath<SR_11893_Base, Int>, SR_11893) -> (), getter @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11893, UnsafeRawPointer) -> @out (), indices [%$0 : $KeyPath<SR_11893_Base, Int> : $KeyPath<SR_11893_Base, Int>], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_KP]])
_ = \SR_11893.i
// CHECK: [[INNER_SUB_KP:%[0-9]+]] = keypath $KeyPath<SR_11893_Base, Int>, (root $SR_11893_Base; gettable_property $Optional<Int>, id @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicig : $@convention(method) (Int, SR_11893_Base) -> Optional<Int>, getter @$s29keypath_dynamic_member_lookup13SR_11893_BaseVySiSgSicipACTK : $@convention(thin) (@in_guaranteed SR_11893_Base, UnsafeRawPointer) -> @out Optional<Int>, indices [%$0 : $Int : $Int], indices_equals @$sSiTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$sSiTh : $@convention(thin) (UnsafeRawPointer) -> Int; optional_force : $Int)
// CHECK: keypath $KeyPath<SR_11893, ()>, (root $SR_11893; gettable_property $(), id @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcig : $@convention(method) (@guaranteed KeyPath<SR_11893_Base, Int>, SR_11893) -> (), getter @$s29keypath_dynamic_member_lookup8SR_11893V0B6Memberys7KeyPathCyAA0E11_11893_BaseVSiG_tcipACTK : $@convention(thin) (@in_guaranteed SR_11893, UnsafeRawPointer) -> @out (), indices [%$0 : $KeyPath<SR_11893_Base, Int> : $KeyPath<SR_11893_Base, Int>], indices_equals @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTH : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool, indices_hash @$ss7KeyPathCy29keypath_dynamic_member_lookup13SR_11893_BaseVSiGTh : $@convention(thin) (UnsafeRawPointer) -> Int) ([[INNER_SUB_KP]])
_ = \SR_11893.[5]
}