Skip to content

Commit b17f0b5

Browse files
authored
Merge pull request #71855 from xedin/fix-sendable-keypath-conformance-issues
[AST] Special handling for existentials with superclass and marker pr…
2 parents 73c424e + 0ab7e12 commit b17f0b5

File tree

9 files changed

+144
-19
lines changed

9 files changed

+144
-19
lines changed

include/swift/AST/Types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6050,6 +6050,10 @@ class ProtocolCompositionType final : public TypeBase,
60506050
return Bits.ProtocolCompositionType.HasExplicitAnyObject;
60516051
}
60526052

6053+
/// Produce a new type (potentially not be a protoocl composition)
6054+
/// which drops all of the marker protocol types associated with this one.
6055+
Type withoutMarkerProtocols() const;
6056+
60536057
// Implement isa/cast/dyncast/etc.
60546058
static bool classof(const TypeBase *T) {
60556059
return T->getKind() == TypeKind::ProtocolComposition;

lib/AST/ASTMangler.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,13 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
14681468

14691469
case TypeKind::ProtocolComposition: {
14701470
auto *PCT = cast<ProtocolCompositionType>(tybase);
1471+
1472+
if (!AllowMarkerProtocols) {
1473+
auto strippedTy = PCT->withoutMarkerProtocols();
1474+
if (!strippedTy->isEqual(PCT))
1475+
return appendType(strippedTy, sig, forDecl);
1476+
}
1477+
14711478
if (PCT->hasParameterizedExistential()
14721479
|| (PCT->hasInverse() && AllowInverses))
14731480
return appendConstrainedExistential(PCT, sig, forDecl);

lib/AST/ConformanceLookup.cpp

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,46 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
7070

7171
assert(type->isExistentialType());
7272

73+
auto getConstraintType = [&type]() {
74+
if (auto *existentialTy = type->getAs<ExistentialType>())
75+
return existentialTy->getConstraintType();
76+
return type;
77+
};
78+
79+
auto lookupSuperclassConformance = [&](ExistentialLayout &layout) {
80+
if (auto superclass = layout.explicitSuperclass) {
81+
if (auto result =
82+
lookupConformance(superclass, protocol, /*allowMissing=*/false)) {
83+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
84+
result.hasUnavailableConformance())
85+
return ProtocolConformanceRef::forInvalid();
86+
return result;
87+
}
88+
}
89+
return ProtocolConformanceRef::forInvalid();
90+
};
91+
7392
// If the existential type cannot be represented or the protocol does not
7493
// conform to itself, there's no point in looking further.
75-
if (!protocol->existentialConformsToSelf())
94+
if (!protocol->existentialConformsToSelf()) {
95+
// If type is a protocol composition with marker protocols
96+
// check whether superclass conforms, and if it does form
97+
// an inherited conformance. This means that types like:
98+
// `KeyPath<String, Int> & Sendable` don't have to be "opened"
99+
// to satisfy conformance to i.e. `Equatable`.
100+
if (getConstraintType()->is<ProtocolCompositionType>()) {
101+
auto layout = type->getExistentialLayout();
102+
if (llvm::all_of(layout.getProtocols(),
103+
[](const auto *P) { return P->isMarkerProtocol(); })) {
104+
if (auto conformance = lookupSuperclassConformance(layout)) {
105+
return ProtocolConformanceRef(
106+
ctx.getInheritedConformance(type, conformance.getConcrete()));
107+
}
108+
}
109+
}
110+
76111
return ProtocolConformanceRef::forInvalid();
112+
}
77113

78114
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)
79115
&& !ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
@@ -89,10 +125,7 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
89125
// existential to an archetype parameter, so for now we restrict this to
90126
// @objc protocols and marker protocols.
91127
if (!layout.isObjC() && !protocol->isMarkerProtocol()) {
92-
auto constraint = type;
93-
if (auto existential = constraint->getAs<ExistentialType>())
94-
constraint = existential->getConstraintType();
95-
128+
auto constraint = getConstraintType();
96129
// There's a specific exception for protocols with self-conforming
97130
// witness tables, but the existential has to be *exactly* that type.
98131
// TODO: synthesize witness tables on-demand for protocol compositions
@@ -107,16 +140,8 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
107140

108141
// If the existential is class-constrained, the class might conform
109142
// concretely.
110-
if (auto superclass = layout.explicitSuperclass) {
111-
if (auto result = lookupConformance(
112-
superclass, protocol, /*allowMissing=*/false)) {
113-
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
114-
result.hasUnavailableConformance())
115-
result = ProtocolConformanceRef::forInvalid();
116-
117-
return result;
118-
}
119-
}
143+
if (auto conformance = lookupSuperclassConformance(layout))
144+
return conformance;
120145

121146
// Otherwise, the existential might conform abstractly.
122147
for (auto protoDecl : layout.getProtocols()) {

lib/AST/Type.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3704,6 +3704,20 @@ Type ProtocolCompositionType::getInverseOf(const ASTContext &C,
37043704
/*HasExplicitAnyObject=*/false);
37053705
}
37063706

3707+
Type ProtocolCompositionType::withoutMarkerProtocols() const {
3708+
SmallVector<Type, 4> newMembers;
3709+
llvm::copy_if(getMembers(), std::back_inserter(newMembers), [](Type member) {
3710+
auto *P = member->getAs<ProtocolType>();
3711+
return !(P && P->getDecl()->isMarkerProtocol());
3712+
});
3713+
3714+
if (newMembers.size() == getMembers().size())
3715+
return Type(const_cast<ProtocolCompositionType *>(this));
3716+
3717+
return ProtocolCompositionType::get(getASTContext(), newMembers,
3718+
getInverses(), hasExplicitAnyObject());
3719+
}
3720+
37073721
Type ProtocolCompositionType::get(const ASTContext &C,
37083722
ArrayRef<Type> Members,
37093723
InvertibleProtocolSet Inverses,

lib/IRGen/IRGenMangler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,9 @@ class IRGenMangler : public Mangle::ASTMangler {
697697
llvm::function_ref<void ()> body);
698698

699699
std::string mangleTypeSymbol(Type type, const char *Op) {
700+
llvm::SaveAndRestore<bool> savedAllowMarkerProtocols(AllowMarkerProtocols,
701+
false);
702+
700703
beginMangling();
701704
appendType(type, nullptr);
702705
appendOperator(Op);

test/Constraints/protocols.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,3 +546,34 @@ _ = P_61517(false) // expected-error{{type 'any P_61517' cannot be instantiated}
546546

547547
_ = P_61517.init() // expected-error{{type 'any P_61517' cannot be instantiated}}
548548
_ = P_61517.init(false) // expected-error{{type 'any P_61517' cannot be instantiated}}
549+
550+
@_marker
551+
protocol Marker {}
552+
553+
do {
554+
class C : Fooable, Error {
555+
func foo() {}
556+
}
557+
558+
struct Other {}
559+
560+
func overloaded() -> C & Marker {
561+
fatalError()
562+
}
563+
564+
func overloaded() -> Other {
565+
fatalError()
566+
}
567+
568+
func isFooable<T: Fooable>(_: T) {}
569+
570+
isFooable(overloaded()) // Ok
571+
572+
func isError<T: Error>(_: T) {}
573+
574+
isError(overloaded()) // Ok
575+
576+
func isFooableError<T: Fooable & Error>(_: T) {}
577+
578+
isFooableError(overloaded()) // Ok
579+
}

test/IRGen/marker_protocol.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extension Int: P { }
1414
extension Array: P where Element: P { }
1515

1616
// No mention of the marker protocol for runtime type instantiation.
17-
// CHECK-LABEL: @"$sSS_15marker_protocol1P_ptMD" =
17+
// CHECK-LABEL: @"$sSS_yptMD" =
1818
// CHECK-SAME: @"symbolic SS_ypt"
1919

2020
// CHECK-LABEL: @"$s15marker_protocol1QMp" = {{(dllexport |protected )?}}constant
@@ -47,7 +47,7 @@ struct HasMarkers {
4747

4848
// Note: no mention of marker protocols when forming a dictionary.
4949
// CHECK-LABEL: define{{.*}}@"$s15marker_protocol0A12InDictionaryypyF"
50-
// CHECK: call ptr @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSS_15marker_protocol1P_ptMD")
50+
// CHECK: call ptr @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSS_yptMD")
5151
public func markerInDictionary() -> Any {
5252
let dict: [String: P] = ["answer" : 42]
5353
return dict
@@ -85,3 +85,15 @@ protocol P2 {
8585

8686
// CHECK: define{{.*}}$s15marker_protocol3fooyy3FooQz_xtAA1PRzAA2P2RzlF
8787
func foo<T: P & P2>(_: T.Foo, _: T) { }
88+
89+
class C {}
90+
91+
let v1 = (any C & P).self
92+
let v2 = C.self
93+
94+
// CHECK-LABEL: define hidden swiftcc void @"$s15marker_protocol23testProtocolCompositionyyF"()
95+
// CHECK: [[V1:%.*]] = call ptr @__swift_instantiateConcreteTypeFromMangledName(ptr @"$s15marker_protocol1CCMD")
96+
// CHECK: [[V2:%.*]] = load ptr, ptr @"$s15marker_protocol2v2AA1CCmvp"
97+
func testProtocolComposition() {
98+
print(v1 == v2)
99+
}

test/IRGen/marker_protocol_backdeploy.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ protocol R { }
2020
// Suppress marker protocols when forming existentials at runtime
2121
public func takeAnyType<T>(_: T.Type) { }
2222

23-
// CHECK-LABEL: define {{.*}}@"$ss8Sendable_26marker_protocol_backdeploy1QAB1RpMa"
24-
// CHECK: ss8Sendable_26marker_protocol_backdeploy1QAB1RpML
23+
// CHECK-LABEL: define {{.*}}@"$s26marker_protocol_backdeploy1Q_AA1RpMa"
24+
// CHECK: $s26marker_protocol_backdeploy1Q_AA1RpML
2525
// CHECK-NOT: Sendable
2626
// CHECK: s26marker_protocol_backdeploy1QMp
2727
// CHECK-NOT: Sendable
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-run-simple-swift | %FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
protocol P {
5+
init()
6+
func f()
7+
}
8+
class C: P {
9+
required init() {}
10+
func f() { print("C.f") }
11+
}
12+
struct G<T: P> {
13+
func f() { T().f() }
14+
}
15+
16+
G<any (C & Sendable)>().f()
17+
// CHECK: C.f
18+
19+
do {
20+
let v1 = (any C & Sendable).self
21+
let v2 = C.self
22+
23+
print(v1)
24+
// CHECK: C
25+
print(v2)
26+
// CHECK: C
27+
print(v1 == v2)
28+
// CHECK: true
29+
}

0 commit comments

Comments
 (0)