Skip to content

Commit cbc35f0

Browse files
authored
Merge pull request #9451 from jrose-apple/protocols-have-tables-too
Handle missing members in protocols as well.
2 parents df26b25 + aeb0fed commit cbc35f0

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)