|
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