Skip to content

Commit c15ef20

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 7b57998 commit c15ef20

File tree

5 files changed

+90
-7
lines changed

5 files changed

+90
-7
lines changed

lib/SIL/SILVerifier.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3825,20 +3825,20 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
38253825
break;
38263826
}
38273827
case KeyPathPatternComponent::Kind::OptionalChain: {
3828-
require(OptionalType::get(componentTy)->isEqual(baseTy),
3828+
require(baseTy->getAnyOptionalObjectType()->isEqual(componentTy),
38293829
"chaining component should unwrap optional");
38303830
require(leafTy->getAnyOptionalObjectType(),
38313831
"key path with chaining component should have optional "
38323832
"result");
38333833
break;
38343834
}
38353835
case KeyPathPatternComponent::Kind::OptionalForce: {
3836-
require(OptionalType::get(componentTy)->isEqual(baseTy),
3836+
require(baseTy->getAnyOptionalObjectType()->isEqual(componentTy),
38373837
"forcing component should unwrap optional");
38383838
break;
38393839
}
38403840
case KeyPathPatternComponent::Kind::OptionalWrap: {
3841-
require(OptionalType::get(baseTy)->isEqual(componentTy),
3841+
require(componentTy->getAnyOptionalObjectType()->isEqual(baseTy),
38423842
"wrapping component should wrap optional");
38433843
break;
38443844
}

lib/Sema/CSApply.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4100,6 +4100,19 @@ namespace {
41004100
}
41014101
}
41024102

4103+
// Unwrap if we needed to look through an IUO to find the
4104+
// property.
4105+
if (auto objTy = cs.lookThroughImplicitlyUnwrappedOptionalType(
4106+
baseTy->getRValueType())) {
4107+
if (baseTy->is<LValueType>())
4108+
baseTy = LValueType::get(objTy);
4109+
else
4110+
baseTy = objTy;
4111+
4112+
resolvedComponents.push_back(
4113+
KeyPathExpr::Component::forOptionalForce(baseTy, SourceLoc()));
4114+
}
4115+
41034116
auto dc = property->getInnermostDeclContext();
41044117
SmallVector<Substitution, 4> subs;
41054118
if (auto sig = dc->getGenericSignatureOfContext()) {
@@ -4131,6 +4144,19 @@ namespace {
41314144
diag::expr_keypath_mutating_getter,
41324145
subscript->getFullName());
41334146
}
4147+
4148+
// Unwrap if we needed to look through an IUO to find the
4149+
// subscript.
4150+
if (auto objTy = cs.lookThroughImplicitlyUnwrappedOptionalType(
4151+
baseTy->getRValueType())) {
4152+
if (baseTy->is<LValueType>())
4153+
baseTy = LValueType::get(objTy);
4154+
else
4155+
baseTy = objTy;
4156+
4157+
resolvedComponents.push_back(
4158+
KeyPathExpr::Component::forOptionalForce(baseTy, SourceLoc()));
4159+
}
41344160

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

stdlib/public/core/KeyPath.swift

Lines changed: 12 additions & 4 deletions
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.

test/SILGen/keypaths.swift

Lines changed: 26 additions & 0 deletions
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+
}

test/stdlib/KeyPath.swift

Lines changed: 23 additions & 0 deletions
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)