Skip to content

Commit 4a1d692

Browse files
committed
MandatoryPerformanceOptimizations: specialize witness tables to support class existentials with generic classes
1 parent 401a602 commit 4a1d692

File tree

5 files changed

+113
-3
lines changed

5 files changed

+113
-3
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
117117
_ = context.specializeClassMethodInst(classMethod)
118118
}
119119

120+
case let initExRef as InitExistentialRefInst:
121+
if context.options.enableEmbeddedSwift {
122+
specializeWitnessTables(for: initExRef, moduleContext, &worklist)
123+
}
124+
120125
// We need to de-virtualize deinits of non-copyable types to be able to specialize the deinitializers.
121126
case let destroyValue as DestroyValueInst:
122127
if !devirtualizeDeinits(of: destroyValue, simplifyCtxt) {
@@ -259,6 +264,37 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined
259264
return false
260265
}
261266

267+
private func specializeWitnessTables(for initExRef: InitExistentialRefInst, _ context: ModulePassContext,
268+
_ worklist: inout FunctionWorklist)
269+
{
270+
for conformance in initExRef.conformances where conformance.isConcrete {
271+
let origWitnessTable = context.lookupWitnessTable(for: conformance)
272+
if conformance.isSpecialized {
273+
if origWitnessTable == nil {
274+
let wt = specializeWitnessTable(forConformance: conformance, errorLocation: initExRef.location, context)
275+
worklist.addWitnessMethods(of: wt)
276+
}
277+
} else if let origWitnessTable {
278+
checkForGenericMethods(in: origWitnessTable, errorLocation: initExRef.location, context)
279+
}
280+
}
281+
}
282+
283+
private func checkForGenericMethods(in witnessTable: WitnessTable,
284+
errorLocation: Location,
285+
_ context: ModulePassContext)
286+
{
287+
for entry in witnessTable.entries where entry.kind == .method {
288+
if let method = entry.methodFunction,
289+
method.isGeneric
290+
{
291+
context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_witness_method,
292+
entry.methodRequirement)
293+
return
294+
}
295+
}
296+
}
297+
262298
private func checkVTablesForGenericFunctions(_ context: ModulePassContext) {
263299
for vTable in context.vTables where !vTable.class.isGenericAtAnyLevel {
264300
for entry in vTable.entries where entry.implementation.isGeneric {
@@ -472,6 +508,18 @@ fileprivate struct FunctionWorklist {
472508
}
473509
}
474510

511+
mutating func addWitnessMethods(of witnessTable: WitnessTable) {
512+
for entry in witnessTable.entries where entry.kind == .method {
513+
if let method = entry.methodFunction,
514+
// A new witness table can still contain a generic function if the method couldn't be specialized for
515+
// some reason and an error has been printed. Exclude generic functions to not run into an assert later.
516+
!method.isGeneric
517+
{
518+
pushIfNotVisited(method)
519+
}
520+
}
521+
}
522+
475523
mutating func pushIfNotVisited(_ element: Function) {
476524
if pushedFunctions.insert(element).inserted {
477525
functions.append(element)

SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,38 @@ func specializeVTablesOfSuperclasses(_ moduleContext: ModulePassContext) {
7575
}
7676
}
7777
}
78+
79+
func specializeWitnessTable(forConformance conformance: ProtocolConformance,
80+
errorLocation: Location,
81+
_ context: ModulePassContext) -> WitnessTable
82+
{
83+
let genericConformance = conformance.genericConformance
84+
guard let witnessTable = context.lookupWitnessTable(for: genericConformance) else {
85+
fatalError("no witness table found")
86+
}
87+
assert(witnessTable.isDefinition, "No witness table available")
88+
89+
let newEntries = witnessTable.entries.map { origEntry in
90+
switch origEntry.kind {
91+
case .method:
92+
guard let origMethod = origEntry.methodFunction else {
93+
return origEntry
94+
}
95+
let methodDecl = origEntry.methodRequirement
96+
let methodSubs = conformance.specializedSubstitutions.getMethodSubstitutions(for: origMethod)
97+
98+
guard !methodSubs.conformances.contains(where: {!$0.isValid}),
99+
let specializedMethod = context.specialize(function: origMethod, for: methodSubs) else
100+
{
101+
context.diagnosticEngine.diagnose(errorLocation.sourceLoc, .cannot_specialize_witness_method, methodDecl)
102+
return origEntry
103+
}
104+
return WitnessTable.Entry(methodRequirement: methodDecl, methodFunction: specializedMethod)
105+
default:
106+
// TODO: handle other witness table entry kinds
107+
fatalError("unsupported witness table etnry")
108+
}
109+
return origEntry
110+
}
111+
return context.createWitnessTable(entries: newEntries, conformance: conformance, linkage: .shared, serialized: false)
112+
}

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ ERROR(global_must_be_compile_time_const,none,
377377
"global variable must be a compile-time constant", ())
378378
ERROR(non_final_generic_class_function,none,
379379
"classes cannot have non-final generic fuctions in embedded Swift", ())
380+
ERROR(cannot_specialize_witness_method,none,
381+
"an existential type cannot contain a generic method %0 in embedded Swift", (DeclName))
380382
ERROR(cannot_specialize_class,none,
381383
"cannot specialize %0 because class definition is not available (make sure to build with -wmo)", (Type))
382384
ERROR(embedded_swift_existential_type,none,

test/embedded/existential-class-bound4.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
// REQUIRES: optimized_stdlib
66
// REQUIRES: OS=macosx || OS=linux-gnu
77

8-
// Generic classes don't work yet.
9-
// XFAIL: *
10-
118
protocol ClassBound: AnyObject {
129
func foo()
1310
func bar()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-swift-emit-ir -parse-as-library -module-name main -verify %s -enable-experimental-feature Embedded -wmo
2+
3+
// REQUIRES: swift_in_compiler
4+
// REQUIRES: optimized_stdlib
5+
// REQUIRES: OS=macosx || OS=linux-gnu
6+
7+
public protocol P: AnyObject {
8+
func foo<T>(t: T)
9+
}
10+
11+
final public class Class: P {
12+
public func foo<T>(t: T) {}
13+
}
14+
15+
16+
public func testClass() -> P {
17+
return Class() // expected-error {{an existential type cannot contain a generic method 'foo(t:)' in embedded Swift}}
18+
}
19+
20+
final public class GenClass<X>: P {
21+
public func foo<T>(t: T) {}
22+
}
23+
24+
25+
public func testGenClass() -> P {
26+
return GenClass<Int>() // expected-error {{an existential type cannot contain a generic method 'foo(t:)' in embedded Swift}}
27+
}
28+

0 commit comments

Comments
 (0)