Skip to content

Commit 69a290e

Browse files
committed
Handle IUO unwraps in key paths.
In the type checker, we need to recognize when a member lookup succeeded through an IUO unwrap, and insert the implicit optional-unwrap component into the key path. In the standard library, we need to cope with the fact that IUO is still a first-class type with unique type metadata in a few places. Fix for rdar://problem/33230845.
1 parent 00629d5 commit 69a290e

File tree

5 files changed

+90
-7
lines changed

5 files changed

+90
-7
lines changed

Diff for: lib/SIL/SILVerifier.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -3766,20 +3766,20 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
37663766
break;
37673767
}
37683768
case KeyPathPatternComponent::Kind::OptionalChain: {
3769-
require(OptionalType::get(componentTy)->isEqual(baseTy),
3769+
require(baseTy->getAnyOptionalObjectType()->isEqual(componentTy),
37703770
"chaining component should unwrap optional");
37713771
require(leafTy->getAnyOptionalObjectType(),
37723772
"key path with chaining component should have optional "
37733773
"result");
37743774
break;
37753775
}
37763776
case KeyPathPatternComponent::Kind::OptionalForce: {
3777-
require(OptionalType::get(componentTy)->isEqual(baseTy),
3777+
require(baseTy->getAnyOptionalObjectType()->isEqual(componentTy),
37783778
"forcing component should unwrap optional");
37793779
break;
37803780
}
37813781
case KeyPathPatternComponent::Kind::OptionalWrap: {
3782-
require(OptionalType::get(baseTy)->isEqual(componentTy),
3782+
require(componentTy->getAnyOptionalObjectType()->isEqual(baseTy),
37833783
"wrapping component should wrap optional");
37843784
break;
37853785
}

Diff for: lib/Sema/CSApply.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -3968,6 +3968,19 @@ namespace {
39683968
}
39693969
}
39703970

3971+
// Unwrap if we needed to look through an IUO to find the
3972+
// property.
3973+
if (auto objTy = cs.lookThroughImplicitlyUnwrappedOptionalType(
3974+
baseTy->getRValueType())) {
3975+
if (baseTy->is<LValueType>())
3976+
baseTy = LValueType::get(objTy);
3977+
else
3978+
baseTy = objTy;
3979+
3980+
resolvedComponents.push_back(
3981+
KeyPathExpr::Component::forOptionalForce(baseTy, SourceLoc()));
3982+
}
3983+
39713984
auto dc = property->getInnermostDeclContext();
39723985
SmallVector<Substitution, 4> subs;
39733986
if (auto sig = dc->getGenericSignatureOfContext()) {
@@ -3999,6 +4012,19 @@ namespace {
39994012
diag::expr_keypath_mutating_getter,
40004013
subscript->getFullName());
40014014
}
4015+
4016+
// Unwrap if we needed to look through an IUO to find the
4017+
// subscript.
4018+
if (auto objTy = cs.lookThroughImplicitlyUnwrappedOptionalType(
4019+
baseTy->getRValueType())) {
4020+
if (baseTy->is<LValueType>())
4021+
baseTy = LValueType::get(objTy);
4022+
else
4023+
baseTy = objTy;
4024+
4025+
resolvedComponents.push_back(
4026+
KeyPathExpr::Component::forOptionalForce(baseTy, SourceLoc()));
4027+
}
40024028

40034029
auto dc = subscript->getInnermostDeclContext();
40044030
SmallVector<Substitution, 4> subs;

Diff for: stdlib/public/core/KeyPath.swift

+12-4
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,9 @@ internal struct RawKeyPathComponent {
10201020
return .continue(get(base, argument?.data.baseAddress ?? rawGet))
10211021

10221022
case .optionalChain:
1023-
_sanityCheck(CurValue.self == Optional<NewValue>.self,
1023+
// TODO: IUO shouldn't be a first class type
1024+
_sanityCheck(CurValue.self == Optional<NewValue>.self
1025+
|| CurValue.self == ImplicitlyUnwrappedOptional<NewValue>.self,
10241026
"should be unwrapping optional value")
10251027
_sanityCheck(_isOptional(LeafValue.self),
10261028
"leaf result should be optional")
@@ -1033,12 +1035,16 @@ internal struct RawKeyPathComponent {
10331035
}
10341036

10351037
case .optionalForce:
1036-
_sanityCheck(CurValue.self == Optional<NewValue>.self,
1038+
// TODO: IUO shouldn't be a first class type
1039+
_sanityCheck(CurValue.self == Optional<NewValue>.self
1040+
|| CurValue.self == ImplicitlyUnwrappedOptional<NewValue>.self,
10371041
"should be unwrapping optional value")
10381042
return .continue(unsafeBitCast(base, to: Optional<NewValue>.self)!)
10391043

10401044
case .optionalWrap:
1041-
_sanityCheck(NewValue.self == Optional<CurValue>.self,
1045+
// TODO: IUO shouldn't be a first class type
1046+
_sanityCheck(NewValue.self == Optional<CurValue>.self
1047+
|| CurValue.self == ImplicitlyUnwrappedOptional<CurValue>.self,
10421048
"should be wrapping optional value")
10431049
return .continue(
10441050
unsafeBitCast(base as Optional<CurValue>, to: NewValue.self))
@@ -1121,7 +1127,9 @@ internal struct RawKeyPathComponent {
11211127
return UnsafeRawPointer(Builtin.addressof(&writeback.value))
11221128

11231129
case .optionalForce:
1124-
_sanityCheck(CurValue.self == Optional<NewValue>.self,
1130+
// TODO: ImplicitlyUnwrappedOptional should not be a first-class type
1131+
_sanityCheck(CurValue.self == Optional<NewValue>.self
1132+
|| CurValue.self == ImplicitlyUnwrappedOptional<NewValue>.self,
11251133
"should be unwrapping an optional value")
11261134
// Optional's layout happens to always put the payload at the start
11271135
// address of the Optional value itself, if a value is present at all.

Diff for: test/SILGen/keypaths.swift

+26
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,29 @@ func keyPathForStorageQualified() {
240240
// CHECK-SAME: settable_property $StorageQualified, id ##FinalStorageQualified.disowned
241241
_ = \FinalStorageQualified.disowned
242242
}
243+
244+
struct IUOProperty {
245+
var iuo: IUOBlob!
246+
}
247+
248+
struct IUOBlob {
249+
var x: Int
250+
subscript(y: String) -> String {
251+
get { return y }
252+
set {}
253+
}
254+
}
255+
256+
// CHECK-LABEL: sil hidden @{{.*}}iuoKeyPaths
257+
func iuoKeyPaths() {
258+
// CHECK: = keypath $WritableKeyPath<IUOProperty, Int>,
259+
// CHECK-SAME: stored_property #IUOProperty.iuo
260+
// CHECK-SAME: optional_force
261+
// CHECK-SAME: stored_property #IUOBlob.x
262+
_ = \IUOProperty.iuo.x
263+
// CHECK: = keypath $WritableKeyPath<IUOProperty, Int>,
264+
// CHECK-SAME: stored_property #IUOProperty.iuo
265+
// CHECK-SAME: optional_force
266+
// CHECK-SAME: stored_property #IUOBlob.x
267+
_ = \IUOProperty.iuo!.x
268+
}

Diff for: test/stdlib/KeyPath.swift

+23
Original file line numberDiff line numberDiff line change
@@ -523,4 +523,27 @@ keyPath.test("writebacks nest properly") {
523523
expectEqual(nestedWritebackLog, 0x383736)
524524
}
525525

526+
struct IUOWrapper {
527+
var wrapped: IUOWrapped!
528+
}
529+
530+
struct IUOWrapped {
531+
var value: Int
532+
}
533+
534+
keyPath.test("IUO and key paths") {
535+
var subject = IUOWrapper(wrapped: IUOWrapped(value: 1989))
536+
let kp1 = \IUOWrapper.wrapped.value
537+
538+
expectEqual(subject[keyPath: kp1], 1989)
539+
subject[keyPath: kp1] = 1738
540+
expectEqual(subject[keyPath: kp1], 1738)
541+
expectEqual(subject.wrapped.value, 1738)
542+
543+
let kp2 = \IUOWrapper.wrapped!.value
544+
545+
expectEqual(kp1, kp2)
546+
expectEqual(kp1.hashValue, kp2.hashValue)
547+
}
548+
526549
runAllTests()

0 commit comments

Comments
 (0)