@@ -79,7 +79,7 @@ private struct VTableSpecializer {
79
79
}
80
80
81
81
private func specializeEntries( of vTable: VTable , _ notifyNewFunction: ( Function ) -> ( ) ) -> [ VTable . Entry ] {
82
- return vTable. entries. compactMap { entry in
82
+ return vTable. entries. map { entry in
83
83
if !entry. implementation. isGeneric {
84
84
return entry
85
85
}
@@ -91,8 +91,7 @@ private struct VTableSpecializer {
91
91
context. loadFunction ( function: entry. implementation, loadCalleesRecursively: true ) ,
92
92
let specializedMethod = context. specialize ( function: entry. implementation, for: methodSubs) else
93
93
{
94
- context. diagnosticEngine. diagnose ( . non_final_generic_class_function, at: entry. methodDecl. location)
95
- return nil
94
+ return entry
96
95
}
97
96
notifyNewFunction ( specializedMethod)
98
97
@@ -106,16 +105,29 @@ private struct VTableSpecializer {
106
105
}
107
106
}
108
107
109
- func specializeWitnessTable ( forConformance conformance: Conformance ,
110
- errorLocation : Location ,
108
+ /// Specializes a witness table of ` conformance` for the concrete type of the conformance.
109
+ func specializeWitnessTable ( for conformance : Conformance ,
111
110
_ context: ModulePassContext ,
112
111
_ notifyNewWitnessTable: ( WitnessTable ) -> ( ) )
113
112
{
114
- let genericConformance = conformance. genericConformance
115
- guard let witnessTable = context. lookupWitnessTable ( for: genericConformance) else {
113
+ if let existingSpecialization = context. lookupWitnessTable ( for: conformance) ,
114
+ existingSpecialization. isSpecialized
115
+ {
116
+ return
117
+ }
118
+
119
+ let baseConf = conformance. isInherited ? conformance. inheritedConformance: conformance
120
+ if !baseConf. isSpecialized {
121
+ var visited = Set < Conformance > ( )
122
+ specializeDefaultMethods ( for: conformance, visited: & visited, context, notifyNewWitnessTable)
123
+ return
124
+ }
125
+
126
+ guard let witnessTable = context. lookupWitnessTable ( for: baseConf. genericConformance) else {
116
127
fatalError ( " no witness table found " )
117
128
}
118
129
assert ( witnessTable. isDefinition, " No witness table available " )
130
+ let substitutions = baseConf. specializedSubstitutions
119
131
120
132
let newEntries = witnessTable. entries. map { origEntry in
121
133
switch origEntry {
@@ -125,21 +137,22 @@ func specializeWitnessTable(forConformance conformance: Conformance,
125
137
guard let origMethod = witness else {
126
138
return origEntry
127
139
}
128
- let methodSubs = conformance. specializedSubstitutions. getMethodSubstitutions ( for: origMethod)
140
+ let methodSubs = substitutions. getMethodSubstitutions ( for: origMethod,
141
+ // Generic self types need to be handled specially (see `getMethodSubstitutions`)
142
+ selfType: origMethod. hasGenericSelf ( context) ? conformance. type. canonical : nil )
129
143
130
144
guard !methodSubs. conformances. contains ( where: { !$0. isValid} ) ,
131
145
context. loadFunction ( function: origMethod, loadCalleesRecursively: true ) ,
132
146
let specializedMethod = context. specialize ( function: origMethod, for: methodSubs) else
133
147
{
134
- context. diagnosticEngine. diagnose ( . cannot_specialize_witness_method, requirement, at: errorLocation)
135
148
return origEntry
136
149
}
137
150
return . method( requirement: requirement, witness: specializedMethod)
138
151
case . baseProtocol( let requirement, let witness) :
139
152
let baseConf = context. getSpecializedConformance ( of: witness,
140
153
for: conformance. type,
141
154
substitutions: conformance. specializedSubstitutions)
142
- specializeWitnessTable ( forConformance : baseConf, errorLocation : errorLocation , context, notifyNewWitnessTable)
155
+ specializeWitnessTable ( for : baseConf, context, notifyNewWitnessTable)
143
156
return . baseProtocol( requirement: requirement, witness: baseConf)
144
157
case . associatedType( let requirement, let witness) :
145
158
let substType = witness. subst ( with: conformance. specializedSubstitutions)
@@ -150,15 +163,104 @@ func specializeWitnessTable(forConformance conformance: Conformance,
150
163
let concreteAssociateConf = conformance. getAssociatedConformance ( ofAssociatedType: requirement. rawType,
151
164
to: assocConf. protocol)
152
165
if concreteAssociateConf. isSpecialized {
153
- specializeWitnessTable ( forConformance: concreteAssociateConf,
154
- errorLocation: errorLocation,
155
- context, notifyNewWitnessTable)
166
+ specializeWitnessTable ( for: concreteAssociateConf, context, notifyNewWitnessTable)
156
167
}
157
168
return . associatedConformance( requirement: requirement,
158
169
witness: concreteAssociateConf)
159
170
}
160
171
}
161
- let newWT = context. createWitnessTable ( entries: newEntries, conformance: conformance,
162
- linkage: . shared, serialized: false )
172
+ let newWT = context. createSpecializedWitnessTable ( entries: newEntries, conformance: conformance,
173
+ linkage: . shared, serialized: false )
163
174
notifyNewWitnessTable ( newWT)
164
175
}
176
+
177
+ /// Specializes the default methods of a non-generic witness table.
178
+ /// Default implementations (in protocol extentions) of non-generic protocol methods have a generic
179
+ /// self argument. Specialize such methods with the concrete type. Note that it is important to also
180
+ /// specialize inherited conformances so that the concrete self type is correct, even for derived classes.
181
+ private func specializeDefaultMethods( for conformance: Conformance ,
182
+ visited: inout Set < Conformance > ,
183
+ _ context: ModulePassContext ,
184
+ _ notifyNewWitnessTable: ( WitnessTable ) -> ( ) )
185
+ {
186
+ // Avoid infinite recursion, which may happen if an associated conformance is the conformance itself.
187
+ guard visited. insert ( conformance) . inserted,
188
+ let witnessTable = context. lookupWitnessTable ( for: conformance. rootConformance)
189
+ else {
190
+ return
191
+ }
192
+
193
+ assert ( witnessTable. isDefinition, " No witness table available " )
194
+
195
+ var specialized = false
196
+
197
+ let newEntries = witnessTable. entries. map { origEntry in
198
+ switch origEntry {
199
+ case . invalid:
200
+ return WitnessTable . Entry. invalid
201
+ case . method( let requirement, let witness) :
202
+ guard let origMethod = witness,
203
+ // Is it a generic method where only self is generic (= a default witness method)?
204
+ origMethod. isGeneric, origMethod. isNonGenericWitnessMethod ( context)
205
+ else {
206
+ return origEntry
207
+ }
208
+ // Replace the generic self type with the concrete type.
209
+ let methodSubs = SubstitutionMap ( genericSignature: origMethod. genericSignature,
210
+ replacementTypes: [ conformance. type] )
211
+
212
+ guard !methodSubs. conformances. contains ( where: { !$0. isValid} ) ,
213
+ context. loadFunction ( function: origMethod, loadCalleesRecursively: true ) ,
214
+ let specializedMethod = context. specialize ( function: origMethod, for: methodSubs) else
215
+ {
216
+ return origEntry
217
+ }
218
+ specialized = true
219
+ return . method( requirement: requirement, witness: specializedMethod)
220
+ case . baseProtocol( _, let witness) :
221
+ specializeDefaultMethods ( for: witness, visited: & visited, context, notifyNewWitnessTable)
222
+ return origEntry
223
+ case . associatedType:
224
+ return origEntry
225
+ case . associatedConformance( _, let assocConf) :
226
+ specializeDefaultMethods ( for: assocConf, visited: & visited, context, notifyNewWitnessTable)
227
+ return origEntry
228
+ }
229
+ }
230
+ // If the witness table does not contain any default methods, there is no need to create a
231
+ // specialized witness table.
232
+ if specialized {
233
+ let newWT = context. createSpecializedWitnessTable ( entries: newEntries, conformance: conformance,
234
+ linkage: . shared, serialized: false )
235
+ notifyNewWitnessTable ( newWT)
236
+ }
237
+ }
238
+
239
+ private extension Function {
240
+ // True, if this is a non-generic method which might have a generic self argument.
241
+ // Default implementations (in protocol extentions) of non-generic protocol methods have a generic
242
+ // self argument.
243
+ func isNonGenericWitnessMethod( _ context: some Context ) -> Bool {
244
+ switch loweredFunctionType. invocationGenericSignatureOfFunction. genericParameters. count {
245
+ case 0 :
246
+ return true
247
+ case 1 :
248
+ return hasGenericSelf ( context)
249
+ default :
250
+ return false
251
+ }
252
+ }
253
+
254
+ // True, if the self argument is a generic parameter.
255
+ func hasGenericSelf( _ context: some Context ) -> Bool {
256
+ let convention = FunctionConvention ( for: loweredFunctionType,
257
+ hasLoweredAddresses: context. moduleHasLoweredAddresses)
258
+ if convention. hasSelfParameter,
259
+ let selfParam = convention. parameters. last,
260
+ selfParam. type. isGenericTypeParameter
261
+ {
262
+ return true
263
+ }
264
+ return false
265
+ }
266
+ }
0 commit comments