ModuleInterface: Avoid printing generic arguments of a ParameterizedProtocolType in inheritance clause

When emitting module interfaces, parameterized protocols in inheritance clauses were being printed with generic arguments (public struct T: Fancy<Float64>), causing errors with protocol conformance verification.
The fix strip ParameterizedProtocolType to its base protocol type when printing inherited types, producing the correct inheritance clause.

Resolves: rdar://161925627
This commit is contained in:
Sam Pyankov
2025-11-18 14:00:45 -08:00
parent b23044c19c
commit 480b64ef50
2 changed files with 103 additions and 1 deletions

View File

@@ -8247,6 +8247,39 @@ static void getSyntacticInheritanceClause(const ProtocolDecl *proto,
}
}
static Type stripParameterizedProtocolArgs(Type type) {
if (!type)
return type;
// For each ParameterizedProtocolType process each member recursively
if (auto *compositionType = type->getAs<ProtocolCompositionType>()) {
SmallVector<Type, 4> processedMembers;
bool hasChanged = false;
for (auto member : compositionType->getMembers()) {
Type processedMember = stripParameterizedProtocolArgs(member);
if (!processedMember)
continue;
processedMembers.push_back(processedMember);
if (processedMember.getPointer() != member.getPointer())
hasChanged = true;
}
// Rebuild ProtocolCompositionType if at least one member had generic args
if (hasChanged) {
return ProtocolCompositionType::get(
type->getASTContext(), processedMembers,
compositionType->getInverses(),
compositionType->hasExplicitAnyObject());
}
return type;
}
// Strip generic arguments of a single ParameterizedProtocolType
if (auto *paramProto = type->getAs<ParameterizedProtocolType>()) {
return paramProto->getBaseType();
}
return type;
}
void
swift::getInheritedForPrinting(
const Decl *decl, const PrintOptions &options,
@@ -8316,7 +8349,16 @@ swift::getInheritedForPrinting(
}
}
Results.push_back(inherited.getEntry(i));
auto entry = inherited.getEntry(i);
if (auto type = entry.getType()) {
Type strippedType = stripParameterizedProtocolArgs(type);
if (strippedType.getPointer() != type.getPointer()) {
entry = InheritedEntry(TypeLoc::withoutLoc(strippedType),
entry.getOptions());
}
}
Results.push_back(entry);
}
// Collect synthesized conformances.

View File

@@ -0,0 +1,60 @@
// RUN: %target-swift-emit-module-interface(%t/Fancy.swiftinterface) %s -module-name Library
// RUN: %target-swift-typecheck-module-from-interface(%t/Fancy.swiftinterface) -module-name Library
// RUN: %FileCheck %s < %t/Fancy.swiftinterface
public protocol Fancy<Stuff> {
associatedtype Stuff
}
// CHECK: public struct T : Library.Fancy {
// CHECK: public typealias Stuff = Swift.Float64
public struct T: Fancy<Float64> {
}
public protocol Q {
}
// CHECK: public struct S : Library.Fancy & Library.Q {
// CHECK: public typealias Stuff = Swift.Int
public struct S: Fancy<Int> & Q {
}
public protocol P {
}
// CHECK: public struct V : Library.Fancy & Library.P & Library.Q {
// CHECK: public typealias Stuff = Swift.CChar32
public struct V: ((Fancy<CChar32> & P) & Q) {
}
public protocol Bar<T> {
associatedtype T
}
// CHECK: public struct X : Library.Bar & Library.Fancy
// CHECK: public typealias Stuff = Swift.CChar32
// CHECK: public typealias T = Swift.Int
public struct X: Fancy<CChar32> & Bar<Int> {
}
public class Base {
}
public protocol B<A>: ~Copyable {
associatedtype A
}
// CHECK: public class Derived : Library.Base & Library.B {
// CHECK: public typealias A = Swift.Int
public class Derived: Base & B<Int> {
}
public protocol R<E>: ~Copyable & ~Escapable {
associatedtype E
}
// CHECK: public struct N : Library.B & Library.R & ~Copyable {
// CHECK: public typealias A = Swift.Float64
// CHECK: public typealias E = Swift.Int
public struct N: R<Int> & B<Float64> & ~Copyable {
}