Skip to content

Commit 10c8ce8

Browse files
committed
SILGen: Correctly emit accessors synthesized to witness protocol requirements
We weren't adding them as external decls unless they were for storage on an imported type, which meant SILGen wasn't emitting them if the conforming type was from a different Swift source file, or in whole-module mode, a different module. This led to linker errors. Instead, always add accessors to the external decl list, but skip them in SILGen if they are contained in the DeclContext we are currently emitting (which is a source file or module). Note that they are still emitted with the wrong linkage, from a resilience perspective. Clients must only ever see public exports for getters, setters and materializeForSet emitted because they are required by resilience or the access pattern; 'accidental' accessors synthesized for protocol conformance should not be public.
1 parent 162becc commit 10c8ce8

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

lib/SILGen/SILGenDecl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,16 @@ void SILGenModule::emitExternalWitnessTable(ProtocolConformance *c) {
11351135
void SILGenModule::emitExternalDefinition(Decl *d) {
11361136
switch (d->getKind()) {
11371137
case DeclKind::Func: {
1138+
auto FD = cast<FuncDecl>(d);
1139+
1140+
// If this is a synthesized accessor for storage defined within the
1141+
// context we are currently emitting, we will emit the accessor when
1142+
// we visit the storage; skip it.
1143+
if (FD->getDeclContext()->isChildContextOf(M.getAssociatedContext())) {
1144+
assert(FD->isAccessor());
1145+
break;
1146+
}
1147+
11381148
emitFunction(cast<FuncDecl>(d));
11391149
break;
11401150
}

lib/Sema/CodeSynthesis.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,17 @@ void TypeChecker::synthesizeWitnessAccessorsForStorage(
864864
// by synthesizing the full set of accessors.
865865
if (!storage->hasAccessorFunctions()) {
866866
addTrivialAccessorsToStorage(storage, *this);
867+
868+
// If the storage was not imported from Objective-C, conservatively add
869+
// the accessors to the ExternalDefinitions list anyway, in case the witness
870+
// is in an external module.
871+
if (!needsToBeRegisteredAsExternalDecl(storage)) {
872+
Context.addExternalDecl(storage->getGetter());
873+
if (storage->getSetter())
874+
Context.addExternalDecl(storage->getSetter());
875+
if (storage->getMaterializeForSetFunc())
876+
Context.addExternalDecl(storage->getMaterializeForSetFunc());
877+
}
867878
return;
868879
}
869880

test/SILGen/struct_resilience.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,52 @@ public func functionWithMyResilientTypes(s: MySize, f: MySize -> MySize) -> MySi
108108
// CHECK: return
109109
return f(s)
110110
}
111+
112+
// Make sure we generate getters and setters for stored properties of
113+
// fixed-layout structs that were defined in a different module
114+
115+
protocol PointProtocol {
116+
var x: Int { get set }
117+
var y: Int { get }
118+
}
119+
120+
extension Point : PointProtocol {}
121+
122+
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWV16resilient_struct5Point17struct_resilience13PointProtocolS1_FS2_g1xSi
123+
// CHECK: function_ref @_TFV16resilient_struct5Pointg1xSi
124+
// CHECK: return
125+
126+
// CHECK-LABEL: sil [transparent] [fragile] @_TFV16resilient_struct5Pointg1xSi
127+
// CHECK: struct_extract %0 : $Point, #Point.x
128+
// CHECK: return
129+
130+
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWV16resilient_struct5Point17struct_resilience13PointProtocolS1_FS2_s1xSi
131+
// CHECK: function_ref @_TFV16resilient_struct5Points1xSi
132+
// CHECK: return
133+
134+
// CHECK-LABEL: sil [transparent] [fragile] @_TFV16resilient_struct5Points1xSi
135+
// CHECK: [[ADDR:%.*]] = struct_element_addr {{.*}} : $*Point, #Point.x
136+
// CHECK: assign %0 to [[ADDR]]
137+
// CHECK: return
138+
139+
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWV16resilient_struct5Point17struct_resilience13PointProtocolS1_FS2_m1xSi
140+
// CHECK: function_ref @_TFV16resilient_struct5Pointm1xSi
141+
// CHECK: return
142+
143+
// CHECK-LABEL: sil [transparent] [fragile] @_TFV16resilient_struct5Pointm1xSi
144+
// CHECK: return
145+
146+
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWV16resilient_struct5Point17struct_resilience13PointProtocolS1_FS2_g1ySi
147+
// CHECK: function_ref @_TFV16resilient_struct5Pointg1ySi
148+
// CHECK: return
149+
150+
// CHECK-LABEL: sil [transparent] [fragile] @_TFV16resilient_struct5Pointg1ySi
151+
// CHECK: struct_extract %0 : $Point, #Point.y
152+
// CHECK: return
153+
154+
// CHECK-LABEL: sil_witness_table hidden Point: PointProtocol module struct_resilience {
155+
// CHECK-NEXT: method #PointProtocol.x!getter.1: @_TTWV16resilient_struct5Point17struct_resilience13PointProtocolS1_FS2_g1xSi
156+
// CHECK-NEXT: method #PointProtocol.x!setter.1: @_TTWV16resilient_struct5Point17struct_resilience13PointProtocolS1_FS2_s1xSi
157+
// CHECK-NEXT: method #PointProtocol.x!materializeForSet.1: @_TTWV16resilient_struct5Point17struct_resilience13PointProtocolS1_FS2_m1xSi
158+
// CHECK-NEXT: method #PointProtocol.y!getter.1: @_TTWV16resilient_struct5Point17struct_resilience13PointProtocolS1_FS2_g1ySi
159+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)