|
13 | 13 | import AST
|
14 | 14 | import SIL
|
15 | 15 |
|
16 |
| -@discardableResult |
17 | 16 | func specializeVTable(forClassType classType: Type,
|
18 | 17 | errorLocation: Location,
|
19 |
| - _ context: ModulePassContext) -> VTable? |
| 18 | + _ context: ModulePassContext, |
| 19 | + notifyNewFunction: (Function) -> ()) |
20 | 20 | {
|
21 |
| - guard let nominal = classType.nominal, |
22 |
| - let classDecl = nominal as? ClassDecl, |
23 |
| - classType.isGenericAtAnyLevel else |
24 |
| - { |
25 |
| - return nil |
26 |
| - } |
| 21 | + var specializer = VTableSpecializer(errorLocation: errorLocation, context) |
| 22 | + specializer.specializeVTable(forClassType: classType, notifyNewFunction) |
| 23 | +} |
27 | 24 |
|
28 |
| - if context.lookupSpecializedVTable(for: classType) != nil { |
29 |
| - return nil |
30 |
| - } |
| 25 | +private struct VTableSpecializer { |
| 26 | + let errorLocation: Location |
| 27 | + let context: ModulePassContext |
31 | 28 |
|
32 |
| - guard let origVTable = context.lookupVTable(for: classDecl) else { |
33 |
| - context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_class, classType) |
34 |
| - return nil |
35 |
| - } |
| 29 | + // The type of the first class in the hierarchy which implements a method |
| 30 | + private var baseTypesOfMethods = Dictionary<Function, Type>() |
36 | 31 |
|
37 |
| - let classContextSubs = classType.contextSubstitutionMap |
| 32 | + init(errorLocation: Location, _ context: ModulePassContext) { |
| 33 | + self.errorLocation = errorLocation |
| 34 | + self.context = context |
| 35 | + } |
38 | 36 |
|
39 |
| - let newEntries = origVTable.entries.map { origEntry in |
40 |
| - if !origEntry.implementation.isGeneric { |
41 |
| - return origEntry |
| 37 | + mutating func specializeVTable(forClassType classType: Type, _ notifyNewFunction: (Function) -> ()) { |
| 38 | + // First handle super classes. |
| 39 | + // This is also required for non-generic classes - in case a superclass is generic, e.g. |
| 40 | + // `class Derived : Base<Int> {}` - for two reasons: |
| 41 | + // * A vtable of a derived class references the vtable of the super class. And of course the referenced |
| 42 | + // super-class vtable needs to be a specialized vtable. |
| 43 | + // * Even a non-generic derived class can contain generic methods of the base class in case a base-class |
| 44 | + // method is not overridden. |
| 45 | + // |
| 46 | + if let superClassTy = classType.superClassType { |
| 47 | + specializeVTable(forClassType: superClassTy, notifyNewFunction) |
42 | 48 | }
|
43 |
| - let methodSubs = classContextSubs.getMethodSubstitutions(for: origEntry.implementation) |
44 | 49 |
|
45 |
| - guard !methodSubs.conformances.contains(where: {!$0.isValid}), |
46 |
| - let specializedMethod = context.specialize(function: origEntry.implementation, for: methodSubs) else |
47 |
| - { |
48 |
| - context.diagnosticEngine.diagnose(origEntry.methodDecl.location.sourceLoc, .non_final_generic_class_function) |
49 |
| - return origEntry |
| 50 | + let classDecl = classType.nominal! as! ClassDecl |
| 51 | + guard let origVTable = context.lookupVTable(for: classDecl) else { |
| 52 | + context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_class, classType) |
| 53 | + return |
50 | 54 | }
|
51 | 55 |
|
52 |
| - context.deserializeAllCallees(of: specializedMethod, mode: .allFunctions) |
53 |
| - specializedMethod.set(linkage: .public, context) |
54 |
| - specializedMethod.set(isSerialized: false, context) |
| 56 | + for entry in origVTable.entries { |
| 57 | + if baseTypesOfMethods[entry.implementation] == nil { |
| 58 | + baseTypesOfMethods[entry.implementation] = classType |
| 59 | + } |
| 60 | + } |
55 | 61 |
|
56 |
| - return VTable.Entry(kind: origEntry.kind, isNonOverridden: origEntry.isNonOverridden, |
57 |
| - methodDecl: origEntry.methodDecl, implementation: specializedMethod) |
| 62 | + if classType.isGenericAtAnyLevel { |
| 63 | + if context.lookupSpecializedVTable(for: classType) != nil { |
| 64 | + // We already specialized the vtable |
| 65 | + return |
| 66 | + } |
| 67 | + let newEntries = specializeEntries(of: origVTable, notifyNewFunction) |
| 68 | + context.createSpecializedVTable(entries: newEntries, for: classType, isSerialized: false) |
| 69 | + } else { |
| 70 | + if !origVTable.entries.contains(where: { $0.implementation.isGeneric }) { |
| 71 | + // The vtable (of the non-generic class) doesn't contain any generic functions (from a generic base class). |
| 72 | + return |
| 73 | + } |
| 74 | + let newEntries = specializeEntries(of: origVTable, notifyNewFunction) |
| 75 | + context.replaceVTableEntries(of: origVTable, with: newEntries) |
| 76 | + } |
58 | 77 | }
|
59 | 78 |
|
60 |
| - let specializedVTable = context.createSpecializedVTable(entries: newEntries, for: classType, isSerialized: false) |
61 |
| - if let superClassTy = classType.superClassType { |
62 |
| - specializeVTable(forClassType: superClassTy, errorLocation: classDecl.location, context) |
63 |
| - } |
64 |
| - return specializedVTable |
65 |
| -} |
| 79 | + private func specializeEntries(of vTable: VTable, _ notifyNewFunction: (Function) -> ()) -> [VTable.Entry] { |
| 80 | + return vTable.entries.compactMap { entry in |
| 81 | + if !entry.implementation.isGeneric { |
| 82 | + return entry |
| 83 | + } |
| 84 | + let baseType = baseTypesOfMethods[entry.implementation]! |
| 85 | + let classContextSubs = baseType.contextSubstitutionMap |
| 86 | + let methodSubs = classContextSubs.getMethodSubstitutions(for: entry.implementation) |
| 87 | + |
| 88 | + guard !methodSubs.conformances.contains(where: {!$0.isValid}), |
| 89 | + let specializedMethod = context.specialize(function: entry.implementation, for: methodSubs) else |
| 90 | + { |
| 91 | + context.diagnosticEngine.diagnose(entry.methodDecl.location.sourceLoc, .non_final_generic_class_function) |
| 92 | + return nil |
| 93 | + } |
| 94 | + notifyNewFunction(specializedMethod) |
| 95 | + |
| 96 | + context.deserializeAllCallees(of: specializedMethod, mode: .allFunctions) |
| 97 | + specializedMethod.set(linkage: .public, context) |
| 98 | + specializedMethod.set(isSerialized: false, context) |
66 | 99 |
|
67 |
| -func specializeVTablesOfSuperclasses(_ moduleContext: ModulePassContext) { |
68 |
| - for vtable in moduleContext.vTables { |
69 |
| - if !vtable.isSpecialized, |
70 |
| - !vtable.class.isGenericAtAnyLevel, |
71 |
| - let superClassTy = vtable.class.superClassType, |
72 |
| - superClassTy.isGenericAtAnyLevel |
73 |
| - { |
74 |
| - specializeVTable(forClassType: superClassTy, errorLocation: vtable.class.location, moduleContext) |
| 100 | + return VTable.Entry(kind: entry.kind, isNonOverridden: entry.isNonOverridden, |
| 101 | + methodDecl: entry.methodDecl, implementation: specializedMethod) |
75 | 102 | }
|
76 | 103 | }
|
77 | 104 | }
|
|
0 commit comments