Skip to content

Commit e2dc62b

Browse files
authored
Merge pull request #11004 from jckarter/iuo-key-path
Handle IUO unwraps in key paths.
2 parents 8a99d67 + 69a290e commit e2dc62b

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
@@ -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
}

lib/Sema/CSApply.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3989,6 +3989,19 @@ namespace {
39893989
}
39903990
}
39913991

3992+
// Unwrap if we needed to look through an IUO to find the
3993+
// property.
3994+
if (auto objTy = cs.lookThroughImplicitlyUnwrappedOptionalType(
3995+
baseTy->getRValueType())) {
3996+
if (baseTy->is<LValueType>())
3997+
baseTy = LValueType::get(objTy);
3998+
else
3999+
baseTy = objTy;
4000+
4001+
resolvedComponents.push_back(
4002+
KeyPathExpr::Component::forOptionalForce(baseTy, SourceLoc()));
4003+
}
4004+
39924005
auto dc = property->getInnermostDeclContext();
39934006
SmallVector<Substitution, 4> subs;
39944007
if (auto sig = dc->getGenericSignatureOfContext()) {
@@ -4020,6 +4033,19 @@ namespace {
40204033
diag::expr_keypath_mutating_getter,
40214034
subscript->getFullName());
40224035
}
4036+
4037+
// Unwrap if we needed to look through an IUO to find the
4038+
// subscript.
4039+
if (auto objTy = cs.lookThroughImplicitlyUnwrappedOptionalType(
4040+
baseTy->getRValueType())) {
4041+
if (baseTy->is<LValueType>())
4042+
baseTy = LValueType::get(objTy);
4043+
else
4044+
baseTy = objTy;
4045+
4046+
resolvedComponents.push_back(
4047+
KeyPathExpr::Component::forOptionalForce(baseTy, SourceLoc()));
4048+
}
40234049

40244050
auto dc = subscript->getInnermostDeclContext();
40254051
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)