MandatoryPerformanceOptimizations: support default methods for class existentials

For example:
```
protocol P: AnyObject {
  func foo()
}
extension P {
  func foo() {}
}
class C: P {}

let e: any P = C()
```

Such default methods are SILGen'd with a generic self argument. Therefore we need to specialize such witness methods, even if the conforming type is not generic.

rdar://145855851
This commit is contained in:
Erik Eckstein
2025-04-17 16:06:10 +02:00
parent 7a8a50a2b3
commit d222cf20f1
6 changed files with 242 additions and 66 deletions

View File

@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
import AST
import SIL
/// Performs mandatory optimizations for performance-annotated functions, and global
@@ -39,11 +40,6 @@ let mandatoryPerformanceOptimizations = ModulePass(name: "mandatory-performance-
}
optimizeFunctionsTopDown(using: &worklist, moduleContext)
if moduleContext.options.enableEmbeddedSwift {
// Print errors for generic functions in vtables, which is not allowed in embedded Swift.
checkVTablesForGenericFunctions(moduleContext)
}
}
private func optimizeFunctionsTopDown(using worklist: inout FunctionWorklist,
@@ -131,9 +127,27 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
case let initExRef as InitExistentialRefInst:
if context.options.enableEmbeddedSwift {
specializeWitnessTables(for: initExRef, moduleContext, &worklist)
for c in initExRef.conformances where c.isConcrete {
specializeWitnessTable(for: c, moduleContext) {
worklist.addWitnessMethods(of: $0)
}
}
}
case let bi as BuiltinInst:
switch bi.id {
case .BuildOrdinaryTaskExecutorRef,
.BuildOrdinarySerialExecutorRef,
.BuildComplexEqualitySerialExecutorRef:
specializeWitnessTable(for: bi.substitutionMap.conformances[0], moduleContext) {
worklist.addWitnessMethods(of: $0)
}
default:
break
}
// We need to de-virtualize deinits of non-copyable types to be able to specialize the deinitializers.
case let destroyValue as DestroyValueInst:
if !devirtualizeDeinits(of: destroyValue, simplifyCtxt) {
@@ -282,47 +296,6 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined
return false
}
private func specializeWitnessTables(for initExRef: InitExistentialRefInst, _ context: ModulePassContext,
_ worklist: inout FunctionWorklist)
{
for c in initExRef.conformances where c.isConcrete {
let conformance = c.isInherited ? c.inheritedConformance : c
let origWitnessTable = context.lookupWitnessTable(for: conformance)
if conformance.isSpecialized {
if origWitnessTable == nil {
specializeWitnessTable(forConformance: conformance, errorLocation: initExRef.location, context) {
worklist.addWitnessMethods(of: $0)
}
}
} else if let origWitnessTable {
checkForGenericMethods(in: origWitnessTable, errorLocation: initExRef.location, context)
}
}
}
private func checkForGenericMethods(in witnessTable: WitnessTable,
errorLocation: Location,
_ context: ModulePassContext)
{
for entry in witnessTable.entries {
if case .method(let requirement, let witness) = entry,
let witness,
witness.isGeneric
{
context.diagnosticEngine.diagnose(.cannot_specialize_witness_method, requirement, at: errorLocation)
return
}
}
}
private func checkVTablesForGenericFunctions(_ context: ModulePassContext) {
for vTable in context.vTables where !vTable.class.isGenericAtAnyLevel {
for entry in vTable.entries where entry.implementation.isGeneric {
context.diagnosticEngine.diagnose(.non_final_generic_class_function, at: entry.methodDecl.location)
}
}
}
private extension FullApplySite {
func resultIsUsedInGlobalInitialization() -> SmallProjectionPath? {
guard parentFunction.isGlobalInitOnceFunction,