mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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) {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user