@@ -79,7 +79,7 @@ private struct VTableSpecializer {
7979 }
8080
8181 private func specializeEntries( of vTable: VTable , _ notifyNewFunction: ( Function ) -> ( ) ) -> [ VTable . Entry ] {
82- return vTable. entries. compactMap { entry in
82+ return vTable. entries. map { entry in
8383 if !entry. implementation. isGeneric {
8484 return entry
8585 }
@@ -91,8 +91,7 @@ private struct VTableSpecializer {
9191 context. loadFunction ( function: entry. implementation, loadCalleesRecursively: true ) ,
9292 let specializedMethod = context. specialize ( function: entry. implementation, for: methodSubs) else
9393 {
94- context. diagnosticEngine. diagnose ( . non_final_generic_class_function, at: entry. methodDecl. location)
95- return nil
94+ return entry
9695 }
9796 notifyNewFunction ( specializedMethod)
9897
@@ -106,16 +105,29 @@ private struct VTableSpecializer {
106105 }
107106}
108107
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 ,
111110 _ context: ModulePassContext ,
112111 _ notifyNewWitnessTable: ( WitnessTable ) -> ( ) )
113112{
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 {
116127 fatalError ( " no witness table found " )
117128 }
118129 assert ( witnessTable. isDefinition, " No witness table available " )
130+ let substitutions = baseConf. specializedSubstitutions
119131
120132 let newEntries = witnessTable. entries. map { origEntry in
121133 switch origEntry {
@@ -125,21 +137,22 @@ func specializeWitnessTable(forConformance conformance: Conformance,
125137 guard let origMethod = witness else {
126138 return origEntry
127139 }
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 )
129143
130144 guard !methodSubs. conformances. contains ( where: { !$0. isValid} ) ,
131145 context. loadFunction ( function: origMethod, loadCalleesRecursively: true ) ,
132146 let specializedMethod = context. specialize ( function: origMethod, for: methodSubs) else
133147 {
134- context. diagnosticEngine. diagnose ( . cannot_specialize_witness_method, requirement, at: errorLocation)
135148 return origEntry
136149 }
137150 return . method( requirement: requirement, witness: specializedMethod)
138151 case . baseProtocol( let requirement, let witness) :
139152 let baseConf = context. getSpecializedConformance ( of: witness,
140153 for: conformance. type,
141154 substitutions: conformance. specializedSubstitutions)
142- specializeWitnessTable ( forConformance : baseConf, errorLocation : errorLocation , context, notifyNewWitnessTable)
155+ specializeWitnessTable ( for : baseConf, context, notifyNewWitnessTable)
143156 return . baseProtocol( requirement: requirement, witness: baseConf)
144157 case . associatedType( let requirement, let witness) :
145158 let substType = witness. subst ( with: conformance. specializedSubstitutions)
@@ -150,15 +163,104 @@ func specializeWitnessTable(forConformance conformance: Conformance,
150163 let concreteAssociateConf = conformance. getAssociatedConformance ( ofAssociatedType: requirement. rawType,
151164 to: assocConf. protocol)
152165 if concreteAssociateConf. isSpecialized {
153- specializeWitnessTable ( forConformance: concreteAssociateConf,
154- errorLocation: errorLocation,
155- context, notifyNewWitnessTable)
166+ specializeWitnessTable ( for: concreteAssociateConf, context, notifyNewWitnessTable)
156167 }
157168 return . associatedConformance( requirement: requirement,
158169 witness: concreteAssociateConf)
159170 }
160171 }
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 )
163174 notifyNewWitnessTable ( newWT)
164175}
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