forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGenericSpecialization.swift
157 lines (141 loc) · 6.97 KB
/
GenericSpecialization.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//===--- GenericSpecialization.swift ---------------------------------------==//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import AST
import SIL
func specializeVTable(forClassType classType: Type,
errorLocation: Location,
_ context: ModulePassContext,
notifyNewFunction: (Function) -> ())
{
var specializer = VTableSpecializer(errorLocation: errorLocation, context)
specializer.specializeVTable(forClassType: classType, notifyNewFunction)
}
private struct VTableSpecializer {
let errorLocation: Location
let context: ModulePassContext
// The type of the first class in the hierarchy which implements a method
private var baseTypesOfMethods = Dictionary<Function, Type>()
init(errorLocation: Location, _ context: ModulePassContext) {
self.errorLocation = errorLocation
self.context = context
}
mutating func specializeVTable(forClassType classType: Type, _ notifyNewFunction: (Function) -> ()) {
// First handle super classes.
// This is also required for non-generic classes - in case a superclass is generic, e.g.
// `class Derived : Base<Int> {}` - for two reasons:
// * A vtable of a derived class references the vtable of the super class. And of course the referenced
// super-class vtable needs to be a specialized vtable.
// * Even a non-generic derived class can contain generic methods of the base class in case a base-class
// method is not overridden.
//
if let superClassTy = classType.superClassType {
specializeVTable(forClassType: superClassTy, notifyNewFunction)
}
let classDecl = classType.nominal! as! ClassDecl
guard let origVTable = context.lookupVTable(for: classDecl) else {
if context.enableWMORequiredDiagnostics {
context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_class, classType)
}
return
}
for entry in origVTable.entries {
if baseTypesOfMethods[entry.implementation] == nil {
baseTypesOfMethods[entry.implementation] = classType
}
}
if classType.isGenericAtAnyLevel {
if context.lookupSpecializedVTable(for: classType) != nil {
// We already specialized the vtable
return
}
let newEntries = specializeEntries(of: origVTable, notifyNewFunction)
context.createSpecializedVTable(entries: newEntries, for: classType, isSerialized: false)
} else {
if !origVTable.entries.contains(where: { $0.implementation.isGeneric }) {
// The vtable (of the non-generic class) doesn't contain any generic functions (from a generic base class).
return
}
let newEntries = specializeEntries(of: origVTable, notifyNewFunction)
context.replaceVTableEntries(of: origVTable, with: newEntries)
}
}
private func specializeEntries(of vTable: VTable, _ notifyNewFunction: (Function) -> ()) -> [VTable.Entry] {
return vTable.entries.compactMap { entry in
if !entry.implementation.isGeneric {
return entry
}
let baseType = baseTypesOfMethods[entry.implementation]!
let classContextSubs = baseType.contextSubstitutionMap
let methodSubs = classContextSubs.getMethodSubstitutions(for: entry.implementation)
guard !methodSubs.conformances.contains(where: {!$0.isValid}),
context.loadFunction(function: entry.implementation, loadCalleesRecursively: true),
let specializedMethod = context.specialize(function: entry.implementation, for: methodSubs) else
{
context.diagnosticEngine.diagnose(entry.methodDecl.location.sourceLoc, .non_final_generic_class_function)
return nil
}
notifyNewFunction(specializedMethod)
context.deserializeAllCallees(of: specializedMethod, mode: .allFunctions)
specializedMethod.set(linkage: .public, context)
specializedMethod.set(isSerialized: false, context)
return VTable.Entry(kind: entry.kind, isNonOverridden: entry.isNonOverridden,
methodDecl: entry.methodDecl, implementation: specializedMethod)
}
}
}
func specializeWitnessTable(forConformance conformance: Conformance,
errorLocation: Location,
_ context: ModulePassContext,
_ notifyNewWitnessTable: (WitnessTable) -> ())
{
let genericConformance = conformance.genericConformance
guard let witnessTable = context.lookupWitnessTable(for: genericConformance) else {
fatalError("no witness table found")
}
assert(witnessTable.isDefinition, "No witness table available")
let newEntries = witnessTable.entries.map { origEntry in
switch origEntry {
case .invalid:
return WitnessTable.Entry.invalid
case .method(let requirement, let witness):
guard let origMethod = witness else {
return origEntry
}
let methodSubs = conformance.specializedSubstitutions.getMethodSubstitutions(for: origMethod)
guard !methodSubs.conformances.contains(where: {!$0.isValid}),
context.loadFunction(function: origMethod, loadCalleesRecursively: true),
let specializedMethod = context.specialize(function: origMethod, for: methodSubs) else
{
context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_witness_method, requirement)
return origEntry
}
return .method(requirement: requirement, witness: specializedMethod)
case .baseProtocol(let requirement, let witness):
let baseConf = context.getSpecializedConformance(of: witness,
for: conformance.type,
substitutions: conformance.specializedSubstitutions)
specializeWitnessTable(forConformance: baseConf, errorLocation: errorLocation, context, notifyNewWitnessTable)
return .baseProtocol(requirement: requirement, witness: baseConf)
case .associatedType(let requirement, let witness):
let substType = witness.subst(with: conformance.specializedSubstitutions)
return .associatedType(requirement: requirement, witness: substType)
case .associatedConformance(let requirement, let proto, let witness):
if witness.isSpecialized {
specializeWitnessTable(forConformance: witness, errorLocation: errorLocation, context, notifyNewWitnessTable)
}
return .associatedConformance(requirement: requirement, protocol: proto, witness: witness)
}
}
let newWT = context.createWitnessTable(entries: newEntries,conformance: conformance,
linkage: .shared, serialized: false)
notifyNewWitnessTable(newWT)
}