Skip to content

Commit aeb0fed

Browse files
committed
Handle missing members in protocols as well.
This means both not crashing when we deserialize the protocol but also emitting correct offsets for dynamic dispatch through the protocol's witness table. Also fix a bug with vtable and witness table slots for materializeForSet accessors for properties that can't be imported. Because materializeForSet doesn't have the type of the property in its signature, it was taking a different failure path from everything else, and that failure path didn't properly set the name or flags for the missing member. Finishes rdar://problem/31878396
1 parent 384b2a6 commit aeb0fed

File tree

6 files changed

+200
-19
lines changed

6 files changed

+200
-19
lines changed

include/swift/SIL/SILWitnessVisitor.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
143143
asDerived().addMethod(func);
144144
}
145145

146+
void visitMissingMemberDecl(MissingMemberDecl *placeholder) {
147+
asDerived().addPlaceholder(placeholder);
148+
}
149+
146150
void visitAssociatedTypeDecl(AssociatedTypeDecl *td) {
147151
// We already visited these in the first pass.
148152
}

lib/IRGen/GenProto.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,13 @@ namespace {
662662
Entries.push_back(WitnessTableEntry::forFunction(ctor));
663663
}
664664

665+
void addPlaceholder(MissingMemberDecl *placeholder) {
666+
for (auto i : range(placeholder->getNumberOfVTableEntries())) {
667+
(void)i;
668+
Entries.push_back(WitnessTableEntry());
669+
}
670+
}
671+
665672
void addAssociatedType(AssociatedTypeDecl *ty) {
666673
Entries.push_back(WitnessTableEntry::forAssociatedType(ty));
667674
}
@@ -1117,6 +1124,10 @@ class AccessorConformanceInfo : public ConformanceInfo {
11171124
return addMethodFromSILWitnessTable(requirement);
11181125
}
11191126

1127+
void addPlaceholder(MissingMemberDecl *placeholder) {
1128+
llvm_unreachable("cannot emit a witness table with placeholders in it");
1129+
}
1130+
11201131
void addAssociatedType(AssociatedTypeDecl *requirement) {
11211132
#ifndef NDEBUG
11221133
auto &entry = SILEntries.front();

lib/SILGen/SILGenType.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ class SILGenConformance : public SILGenWitnessTable<SILGenConformance> {
393393
super::addConstructor(cd, witness);
394394
}
395395

396+
void addPlaceholder(MissingMemberDecl *placeholder) {
397+
llvm_unreachable("generating a witness table with placeholders in it");
398+
}
399+
396400
void addMethod(SILDeclRef requirementRef,
397401
SILDeclRef witnessRef,
398402
IsFreeFunctionWitness_t isFree,
@@ -727,6 +731,10 @@ class SILGenDefaultWitnessTable
727731
super::addConstructor(cd, witness);
728732
}
729733

734+
void addPlaceholder(MissingMemberDecl *placeholder) {
735+
llvm_unreachable("generating a witness table with placeholders in it");
736+
}
737+
730738
void addMethod(SILDeclRef requirementRef,
731739
SILDeclRef witnessRef,
732740
IsFreeFunctionWitness_t isFree,

lib/Serialization/Deserialization.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,8 +2866,11 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
28662866
// If we are an accessor on a var or subscript, make sure it is deserialized
28672867
// first.
28682868
auto accessor = getDeclChecked(accessorStorageDeclID);
2869-
if (!accessor)
2870-
return accessor.takeError();
2869+
if (!accessor) {
2870+
// FIXME: "TypeError" isn't exactly correct for this.
2871+
return llvm::make_error<TypeError>(
2872+
name, takeErrorInfo(accessor.takeError()), errorFlags);
2873+
}
28712874

28722875
// Read generic params before reading the type, because the type may
28732876
// reference generic parameters, and we want them to have a dummy
@@ -4468,6 +4471,26 @@ void ModuleFile::loadAllMembers(Decl *container, uint64_t contextData) {
44684471
// subscripts, and methods that don't need vtable entries.
44694472
};
44704473
llvm::handleAllErrors(next.takeError(), handleMissingClassMember);
4474+
} else if (auto *containingProto = dyn_cast<ProtocolDecl>(container)) {
4475+
auto handleMissingProtocolMember =
4476+
[&](const DeclDeserializationError &error) {
4477+
assert(!error.needsAllocatingVTableEntry());
4478+
if (error.needsVTableEntry())
4479+
containingProto->setHasMissingRequirements(true);
4480+
4481+
if (error.getName().getBaseName() == getContext().Id_init) {
4482+
members.push_back(MissingMemberDecl::forInitializer(
4483+
getContext(), containingProto, error.getName(),
4484+
error.needsVTableEntry(), error.needsAllocatingVTableEntry()));
4485+
} else if (error.needsVTableEntry()) {
4486+
members.push_back(MissingMemberDecl::forMethod(
4487+
getContext(), containingProto, error.getName(),
4488+
error.needsVTableEntry()));
4489+
}
4490+
// FIXME: Handle other kinds of missing members: properties,
4491+
// subscripts, and methods that don't need vtable entries.
4492+
};
4493+
llvm::handleAllErrors(next.takeError(), handleMissingProtocolMember);
44714494
} else {
44724495
llvm::consumeError(next.takeError());
44734496
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module %s | %FileCheck -check-prefix CHECK-WITNESS-TABLE %s
3+
4+
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s
5+
6+
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -enable-experimental-deserialization-recovery | %FileCheck -check-prefix CHECK-RECOVERY %s
7+
8+
// RUN: %target-swift-frontend -typecheck -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -DTEST -enable-experimental-deserialization-recovery -DVERIFY %s -verify
9+
10+
// RUN: %target-swift-frontend -emit-ir -I %t -I %S/Inputs/custom-modules -DTEST %s | %FileCheck -check-prefix CHECK-IR %s
11+
// RUN: %target-swift-frontend -emit-ir -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -DTEST -enable-experimental-deserialization-recovery %s | %FileCheck -check-prefix CHECK-IR %s
12+
13+
#if TEST
14+
15+
import Typedefs
16+
import Lib
17+
18+
// CHECK-IR-LABEL: define{{.*}} void @_T04main19testWitnessDispatch
19+
public func testWitnessDispatch(user: Proto) {
20+
// The important thing in this CHECK line is the "i64 11", which is the offset
21+
// for the witness table slot for 'lastMethod()'. If the layout here
22+
// changes, please check that offset 11 is still correct.
23+
// CHECK-IR-NOT: ret
24+
// CHECK-IR: [[SLOT:%.+]] = getelementptr inbounds i8*, i8** {{%.+}}, i32 11
25+
// CHECK-IR-NOT: ret
26+
// CHECK-IR: [[RAW_METHOD:%.+]] = load i8*, i8** [[SLOT]]
27+
// CHECK-IR-NOT: ret
28+
// CHECK-IR: [[METHOD:%.+]] = bitcast i8* [[RAW_METHOD]] to void (%swift.opaque*, %swift.type*, i8**)*
29+
// CHECK-IR-NOT: ret
30+
// CHECK-IR: call swiftcc void [[METHOD]](
31+
_ = user.lastMethod()
32+
} // CHECK-IR: ret void
33+
34+
// CHECK-IR-LABEL: define{{.*}} void @_T04main19testGenericDispatch
35+
public func testGenericDispatch<T: Proto>(user: T) {
36+
// The important thing in this CHECK line is the "i64 11", which is the offset
37+
// for the witness table slot for 'lastMethod()'. If the layout here
38+
// changes, please check that offset 11 is still correct.
39+
// CHECK-IR-NOT: ret
40+
// CHECK-IR: [[SLOT:%.+]] = getelementptr inbounds i8*, i8** %T.Proto, i32 11
41+
// CHECK-IR-NOT: ret
42+
// CHECK-IR: [[RAW_METHOD:%.+]] = load i8*, i8** [[SLOT]]
43+
// CHECK-IR-NOT: ret
44+
// CHECK-IR: [[METHOD:%.+]] = bitcast i8* [[RAW_METHOD]] to void (%swift.opaque*, %swift.type*, i8**)*
45+
// CHECK-IR-NOT: ret
46+
// CHECK-IR: call swiftcc void [[METHOD]](
47+
_ = user.lastMethod()
48+
} // CHECK-IR: ret void
49+
50+
#if VERIFY
51+
52+
public class TestImpl : Proto {} // expected-error {{type 'TestImpl' cannot conform to protocol 'Proto' because it has requirements that cannot be satisfied}}
53+
54+
#endif // VERIFY
55+
56+
#else // TEST
57+
58+
import Typedefs
59+
60+
// CHECK-LABEL: protocol Proto {
61+
// CHECK-RECOVERY-LABEL: protocol Proto {
62+
public protocol Proto {
63+
// CHECK: var unwrappedProp: UnwrappedInt? { get set }
64+
// CHECK-RECOVERY: var unwrappedProp: Int32?
65+
var unwrappedProp: UnwrappedInt? { get set }
66+
// CHECK: var wrappedProp: WrappedInt? { get set }
67+
// CHECK-RECOVERY: /* placeholder for _ */
68+
// CHECK-RECOVERY: /* placeholder for _ */
69+
// CHECK-RECOVERY: /* placeholder for _ */
70+
var wrappedProp: WrappedInt? { get set }
71+
72+
// CHECK: func returnsUnwrappedMethod() -> UnwrappedInt
73+
// CHECK-RECOVERY: func returnsUnwrappedMethod() -> Int32
74+
func returnsUnwrappedMethod() -> UnwrappedInt
75+
// CHECK: func returnsWrappedMethod() -> WrappedInt
76+
// CHECK-RECOVERY: /* placeholder for returnsWrappedMethod() */
77+
func returnsWrappedMethod() -> WrappedInt
78+
79+
// CHECK: subscript(_: WrappedInt) -> () { get }
80+
// CHECK-RECOVERY: /* placeholder for _ */
81+
subscript(_: WrappedInt) -> () { get }
82+
83+
// CHECK: init()
84+
// CHECK-RECOVERY: init()
85+
init()
86+
87+
// CHECK: init(wrapped: WrappedInt)
88+
// CHECK-RECOVERY: /* placeholder for init(wrapped:) */
89+
init(wrapped: WrappedInt)
90+
91+
func lastMethod()
92+
}
93+
// CHECK: {{^}$}}
94+
// CHECK-RECOVERY: {{^}$}}
95+
96+
// CHECK-LABEL: struct ProtoLibImpl : Proto {
97+
// CHECK-RECOVERY-LABEL: struct ProtoLibImpl : Proto {
98+
public struct ProtoLibImpl : Proto {
99+
public var unwrappedProp: UnwrappedInt?
100+
public var wrappedProp: WrappedInt?
101+
102+
public func returnsUnwrappedMethod() -> UnwrappedInt { fatalError() }
103+
public func returnsWrappedMethod() -> WrappedInt { fatalError() }
104+
105+
public subscript(_: WrappedInt) -> () { return () }
106+
107+
public init() {}
108+
public init(wrapped: WrappedInt) {}
109+
110+
public func lastMethod() {}
111+
}
112+
// CHECK: {{^}$}}
113+
// CHECK-RECOVERY: {{^}$}}
114+
115+
// This is mostly to check when changes are necessary for the CHECK-IR lines
116+
// above.
117+
// CHECK-WITNESS-TABLE-LABEL: sil_witness_table{{.*}} ProtoLibImpl: Proto module Lib {
118+
// 0 CHECK-WITNESS-TABLE-NEXT: #Proto.unwrappedProp!getter.1:
119+
// 1 CHECK-WITNESS-TABLE-NEXT: #Proto.unwrappedProp!setter.1:
120+
// 2 CHECK-WITNESS-TABLE-NEXT: #Proto.unwrappedProp!materializeForSet.1:
121+
// 3 CHECK-WITNESS-TABLE-NEXT: #Proto.wrappedProp!getter.1:
122+
// 4 CHECK-WITNESS-TABLE-NEXT: #Proto.wrappedProp!setter.1:
123+
// 5 CHECK-WITNESS-TABLE-NEXT: #Proto.wrappedProp!materializeForSet.1:
124+
// 6 CHECK-WITNESS-TABLE-NEXT: #Proto.returnsUnwrappedMethod!1:
125+
// 7 CHECK-WITNESS-TABLE-NEXT: #Proto.returnsWrappedMethod!1:
126+
// 8 CHECK-WITNESS-TABLE-NEXT: #Proto.subscript!getter.1:
127+
// 9 CHECK-WITNESS-TABLE-NEXT: #Proto.init!allocator.1:
128+
// 10 CHECK-WITNESS-TABLE-NEXT: #Proto.init!allocator.1:
129+
// 11 CHECK-WITNESS-TABLE-NEXT: #Proto.lastMethod!1:
130+
// CHECK-WITNESS-TABLE: }
131+
132+
#endif // TEST

test/Serialization/Recovery/typedefs.swift

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
// RUN: %target-swift-frontend -typecheck -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -DTEST -enable-experimental-deserialization-recovery -DVERIFY %s -verify
1111
// RUN: %target-swift-frontend -emit-silgen -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -DTEST -enable-experimental-deserialization-recovery %s | %FileCheck -check-prefix CHECK-SIL %s
12+
13+
// RUN: %target-swift-frontend -emit-ir -I %t -I %S/Inputs/custom-modules -DTEST %s | %FileCheck -check-prefix CHECK-IR %s
1214
// RUN: %target-swift-frontend -emit-ir -I %t -I %S/Inputs/custom-modules -Xcc -DBAD -DTEST -enable-experimental-deserialization-recovery %s | %FileCheck -check-prefix CHECK-IR %s
1315

1416
#if TEST
@@ -27,11 +29,11 @@ func testSymbols() {
2729

2830
// CHECK-IR-LABEL: define{{.*}} void @_T08typedefs18testVTableBuildingy3Lib4UserC4user_tF
2931
public func testVTableBuilding(user: User) {
30-
// The important thing in this CHECK line is the "i64 23", which is the offset
32+
// The important thing in this CHECK line is the "i64 24", which is the offset
3133
// for the vtable slot for 'lastMethod()'. If the layout here
32-
// changes, please check that offset 23 is still correct.
34+
// changes, please check that offset 24 is still correct.
3335
// CHECK-IR-NOT: ret
34-
// CHECK-IR: getelementptr inbounds void (%T3Lib4UserC*)*, void (%T3Lib4UserC*)** %{{[0-9]+}}, i64 23
36+
// CHECK-IR: getelementptr inbounds void (%T3Lib4UserC*)*, void (%T3Lib4UserC*)** %{{[0-9]+}}, i64 24
3537
_ = user.lastMethod()
3638
} // CHECK-IR: ret void
3739

@@ -77,6 +79,7 @@ open class User {
7779
// CHECK: var wrappedProp: WrappedInt?
7880
// CHECK-RECOVERY: /* placeholder for _ */
7981
// CHECK-RECOVERY: /* placeholder for _ */
82+
// CHECK-RECOVERY: /* placeholder for _ */
8083
public var wrappedProp: WrappedInt?
8184

8285
// CHECK: func returnsUnwrappedMethod() -> UnwrappedInt
@@ -114,22 +117,22 @@ open class User {
114117
// This is mostly to check when changes are necessary for the CHECK-IR lines
115118
// above.
116119
// CHECK-VTABLE-LABEL: sil_vtable User {
117-
// (8 words of normal class metadata)
118-
// 9 CHECK-VTABLE-NEXT: #User.unwrappedProp!getter.1:
119-
// 10 CHECK-VTABLE-NEXT: #User.unwrappedProp!setter.1:
120-
// 11 CHECK-VTABLE-NEXT: #User.unwrappedProp!materializeForSet.1:
121-
// 12 CHECK-VTABLE-NEXT: #User.wrappedProp!getter.1:
122-
// 13 CHECK-VTABLE-NEXT: #User.wrappedProp!setter.1:
123-
// 14 CHECK-VTABLE-NEXT: #User.wrappedProp!materializeForSet.1:
124-
// 15 CHECK-VTABLE-NEXT: #User.returnsUnwrappedMethod!1:
125-
// 16 CHECK-VTABLE-NEXT: #User.returnsWrappedMethod!1:
126-
// 17 CHECK-VTABLE-NEXT: #User.subscript!getter.1:
127-
// 18 CHECK-VTABLE-NEXT: #User.init!initializer.1:
120+
// (10 words of normal class metadata)
121+
// 10 CHECK-VTABLE-NEXT: #User.unwrappedProp!getter.1:
122+
// 11 CHECK-VTABLE-NEXT: #User.unwrappedProp!setter.1:
123+
// 12 CHECK-VTABLE-NEXT: #User.unwrappedProp!materializeForSet.1:
124+
// 13 CHECK-VTABLE-NEXT: #User.wrappedProp!getter.1:
125+
// 14 CHECK-VTABLE-NEXT: #User.wrappedProp!setter.1:
126+
// 15 CHECK-VTABLE-NEXT: #User.wrappedProp!materializeForSet.1:
127+
// 16 CHECK-VTABLE-NEXT: #User.returnsUnwrappedMethod!1:
128+
// 17 CHECK-VTABLE-NEXT: #User.returnsWrappedMethod!1:
129+
// 18 CHECK-VTABLE-NEXT: #User.subscript!getter.1:
128130
// 19 CHECK-VTABLE-NEXT: #User.init!initializer.1:
129131
// 20 CHECK-VTABLE-NEXT: #User.init!initializer.1:
130-
// 21 CHECK-VTABLE-NEXT: #User.init!allocator.1:
131-
// 22 CHECK-VTABLE-NEXT: #User.init!initializer.1:
132-
// 23 CHECK-VTABLE-NEXT: #User.lastMethod!1:
132+
// 21 CHECK-VTABLE-NEXT: #User.init!initializer.1:
133+
// 22 CHECK-VTABLE-NEXT: #User.init!allocator.1:
134+
// 23 CHECK-VTABLE-NEXT: #User.init!initializer.1:
135+
// 24 CHECK-VTABLE-NEXT: #User.lastMethod!1:
133136
// CHECK-VTABLE: }
134137

135138

0 commit comments

Comments
 (0)