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):
let substType = witness.subst(with: conformance.specializedSubstitutions)
return .associatedType(requirement: requirement, witness: substType)
case .associatedConformance(let requirement, let proto, let witness):
if witness.isSpecialized {
specializeWitnessTable(forConformance: witness, errorLocation: errorLocation, context, notifyNewWitnessTable)
case .associatedConformance(let requirement, let proto, _):
let concreteAssociateConf = conformance.getAssociatedConformance(ofAssociatedType: requirement.type, to: proto)
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,

View File

@@ -959,6 +959,13 @@ namespace {
}
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));
}
@@ -1748,6 +1755,14 @@ public:
(void)entry;
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 =
ConformanceInContext.getAssociatedType(
requirement.getAssociation())->getCanonicalType();
@@ -1775,7 +1790,10 @@ public:
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
// In Embedded Swift associated-conformance entries simply point to the witness table
// 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
.ProtocolAssociatedTypeWitnessTableAccessFunctions;
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
struct Main {
static func main() {
@@ -181,6 +257,10 @@ struct Main {
// CHECK: Derived2.bar()
testConditionalConformance(t: S(i: 27))
// CHECK: 27
testP3().foo()
// CHECK: 102
testP4().foo()
// CHECK: 437
}
}