embedded: fix specialization of associated conformance entries in witness tables

When creating a specialized witness table, we need to get the right specialized conformance.
In IRGen don't emit associated conformance witness table entries if the protocol is not a class protocol.
In this case the associated type can never be used to create an existential. Therefore such a witness table entry is never used at runtime in embedded swift.

Fixes a compiler crash

rdar://146448091
This commit is contained in:
Erik Eckstein
2025-03-11 11:33:10 +01:00
parent ecfa808425
commit 23b4c6fc34
3 changed files with 106 additions and 5 deletions

View File

@@ -144,11 +144,14 @@ func specializeWitnessTable(forConformance conformance: Conformance,
case .associatedType(let requirement, let witness): case .associatedType(let requirement, let witness):
let substType = witness.subst(with: conformance.specializedSubstitutions) let substType = witness.subst(with: conformance.specializedSubstitutions)
return .associatedType(requirement: requirement, witness: substType) return .associatedType(requirement: requirement, witness: substType)
case .associatedConformance(let requirement, let proto, let witness): case .associatedConformance(let requirement, let proto, _):
if witness.isSpecialized { let concreteAssociateConf = conformance.getAssociatedConformance(ofAssociatedType: requirement.type, to: proto)
specializeWitnessTable(forConformance: witness, errorLocation: errorLocation, context, notifyNewWitnessTable) if concreteAssociateConf.isSpecialized {
specializeWitnessTable(forConformance: concreteAssociateConf,
errorLocation: errorLocation,
context, notifyNewWitnessTable)
} }
return .associatedConformance(requirement: requirement, protocol: proto, witness: witness) return .associatedConformance(requirement: requirement, protocol: proto, witness: concreteAssociateConf)
} }
} }
let newWT = context.createWitnessTable(entries: newEntries,conformance: conformance, let newWT = context.createWitnessTable(entries: newEntries,conformance: conformance,

View File

@@ -959,6 +959,13 @@ namespace {
} }
void addAssociatedConformance(const AssociatedConformance &req) { void addAssociatedConformance(const AssociatedConformance &req) {
if (req.getAssociation()->getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
!req.getAssociatedRequirement()->requiresClass()) {
// If it's not a class protocol, the associated type can never be used to create
// an existential. Therefore this witness entry is never used at runtime
// in embedded swift.
return;
}
Entries.push_back(WitnessTableEntry::forAssociatedConformance(req)); Entries.push_back(WitnessTableEntry::forAssociatedConformance(req));
} }
@@ -1748,6 +1755,14 @@ public:
(void)entry; (void)entry;
SILEntries = SILEntries.slice(1); SILEntries = SILEntries.slice(1);
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded) &&
!requirement.getAssociatedRequirement()->requiresClass()) {
// If it's not a class protocol, the associated type can never be used to create
// an existential. Therefore this witness entry is never used at runtime
// in embedded swift.
return;
}
auto associate = auto associate =
ConformanceInContext.getAssociatedType( ConformanceInContext.getAssociatedType(
requirement.getAssociation())->getCanonicalType(); requirement.getAssociation())->getCanonicalType();
@@ -1775,7 +1790,10 @@ public:
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) { if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
// In Embedded Swift associated-conformance entries simply point to the witness table // In Embedded Swift associated-conformance entries simply point to the witness table
// of the associated conformance. // of the associated conformance.
llvm::Constant *witnessEntry = IGM.getAddrOfWitnessTable(associatedConformance.getConcrete()); ProtocolConformanceRef assocConf =
SILWT->getConformance()->getAssociatedConformance(requirement.getAssociation(),
requirement.getAssociatedRequirement());
llvm::Constant *witnessEntry = IGM.getAddrOfWitnessTable(assocConf.getConcrete());
auto &schema = IGM.getOptions().PointerAuth auto &schema = IGM.getOptions().PointerAuth
.ProtocolAssociatedTypeWitnessTableAccessFunctions; .ProtocolAssociatedTypeWitnessTableAccessFunctions;
Table.addSignedPointer(witnessEntry, schema, requirement); Table.addSignedPointer(witnessEntry, schema, requirement);

View File

@@ -156,6 +156,82 @@ struct S: P2 {
} }
} }
protocol Q3 {
func bar()
}
protocol P3<T>: AnyObject {
associatedtype T: Q3
var t: T { get }
func foo()
}
extension P3 {
func foo() {
t.bar()
}
}
final class C3<T: Q3>: P3 {
var t: T
init(t: T) { self.t = t }
}
struct S3<I: BinaryInteger>: Q3 {
var x: I
func bar() {
print(x)
}
}
@inline(never)
func testP3() -> any P3 {
return C3(t: S3(x: 102))
}
protocol P4<T>: AnyObject {
associatedtype T: Q
var t: T { get }
func foo()
}
extension P4 {
func foo() {
t.bar()
}
}
final class C4<T: Q>: P4 {
var t: T
init(t: T) { self.t = t }
}
class K4<I: BinaryInteger>: Q {
var x: I
init(x: I) { self.x = x }
func bar() {
print(x)
}
}
@inline(never)
func testP4() -> any P4 {
return C4(t: K4(x: 437))
}
@main @main
struct Main { struct Main {
static func main() { static func main() {
@@ -181,6 +257,10 @@ struct Main {
// CHECK: Derived2.bar() // CHECK: Derived2.bar()
testConditionalConformance(t: S(i: 27)) testConditionalConformance(t: S(i: 27))
// CHECK: 27 // CHECK: 27
testP3().foo()
// CHECK: 102
testP4().foo()
// CHECK: 437
} }
} }