Skip to content

Commit ae01d20

Browse files
committed
Runtime checking for associated types conforming to invertible protocols
Emit metadata for runtime checks of conformances of associated types to invertible protocols, e.g., `T.Assoc: Copyable`. This allows us to correctly handle, e.g., dynamic casting involving conditional conformances that have such constraints. The model we use here is to emit an invertible-protocol constraint that leaves only the specific bit clear in the invertible protocol set.
1 parent 72492aa commit ae01d20

File tree

6 files changed

+83
-12
lines changed

6 files changed

+83
-12
lines changed

Diff for: lib/AST/ASTMangler.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -2530,8 +2530,6 @@ void ASTMangler::appendModule(const ModuleDecl *module,
25302530
/// Mangle the name of a protocol as a substitution candidate.
25312531
void ASTMangler::appendProtocolName(const ProtocolDecl *protocol,
25322532
bool allowStandardSubstitution) {
2533-
assert(!protocol->getInvertibleProtocolKind() &&
2534-
"only inverse requirements are mangled");
25352533
assert(AllowMarkerProtocols || !protocol->isMarkerProtocol());
25362534

25372535
if (allowStandardSubstitution && tryAppendStandardSubstitution(protocol))

Diff for: lib/IRGen/GenMeta.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -6972,6 +6972,27 @@ GenericArgumentMetadata irgen::addGenericRequirements(
69726972
case RequirementKind::Conformance: {
69736973
auto protocol = requirement.getProtocolDecl();
69746974

6975+
// If this is an invertible protocol, encode it as an inverted protocol
6976+
// check with all but this protocol masked off.
6977+
if (auto invertible = protocol->getInvertibleProtocolKind()) {
6978+
++metadata.NumRequirements;
6979+
6980+
InvertibleProtocolSet mask(0xFFFF);
6981+
mask.remove(*invertible);
6982+
6983+
auto flags = GenericRequirementFlags(
6984+
GenericRequirementKind::InvertedProtocols,
6985+
/*key argument*/ false,
6986+
/* is parameter pack */false);
6987+
addGenericRequirement(IGM, B, metadata, sig, flags,
6988+
requirement.getFirstType(),
6989+
[&]{
6990+
B.addInt16(0xFFFF);
6991+
B.addInt16(mask.rawBits());
6992+
});
6993+
break;
6994+
}
6995+
69756996
// Marker protocols do not record generic requirements at all.
69766997
if (protocol->isMarkerProtocol()) {
69776998
break;

Diff for: lib/SILOptimizer/Utils/CastOptimizer.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
//===----------------------------------------------------------------------===//
1818

1919
#include "swift/SILOptimizer/Utils/CastOptimizer.h"
20+
#include "swift/AST/ExistentialLayout.h"
2021
#include "swift/AST/GenericSignature.h"
2122
#include "swift/AST/Module.h"
2223
#include "swift/AST/SubstitutionMap.h"
@@ -1472,6 +1473,10 @@ static bool optimizeStaticallyKnownProtocolConformance(
14721473
if (Conformance.isInvalid())
14731474
return false;
14741475

1476+
auto layout = TargetType->getExistentialLayout();
1477+
if (layout.getProtocols().size() != 1)
1478+
return false;
1479+
14751480
SILBuilderWithScope B(Inst);
14761481
SmallVector<ProtocolConformanceRef, 1> NewConformances;
14771482
NewConformances.push_back(Conformance);

Diff for: stdlib/public/runtime/ProtocolConformance.cpp

+20-8
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,10 @@ bool swift::_swift_class_isSubclass(const Metadata *subclass,
13491349
return isSubclass(subclass, superclass);
13501350
}
13511351

1352+
static std::optional<TypeLookupError>
1353+
checkInvertibleRequirements(const Metadata *type,
1354+
InvertibleProtocolSet ignored);
1355+
13521356
static std::optional<TypeLookupError>
13531357
checkGenericRequirement(
13541358
const GenericRequirementDescriptor &req,
@@ -1445,8 +1449,10 @@ checkGenericRequirement(
14451449
}
14461450
case GenericRequirementKind::InvertedProtocols: {
14471451
uint16_t index = req.getInvertedProtocolsGenericParamIndex();
1448-
if (index == 0xFFFF)
1449-
return TYPE_LOOKUP_ERROR_FMT("unable to suppress protocols");
1452+
if (index == 0xFFFF) {
1453+
return checkInvertibleRequirements(subjectType,
1454+
req.getInvertedProtocols());
1455+
}
14501456

14511457
// Expand the suppression set so we can record these protocols.
14521458
if (index >= suppressed.size()) {
@@ -1613,8 +1619,18 @@ checkGenericPackRequirement(
16131619

16141620
case GenericRequirementKind::InvertedProtocols: {
16151621
uint16_t index = req.getInvertedProtocolsGenericParamIndex();
1616-
if (index == 0xFFFF)
1617-
return TYPE_LOOKUP_ERROR_FMT("unable to suppress protocols");
1622+
if (index == 0xFFFF) {
1623+
// Check that each pack element meets the invertible requirements.
1624+
for (size_t i = 0, e = subjectType.getNumElements(); i < e; ++i) {
1625+
const Metadata *elt = subjectType.getElements()[i];
1626+
1627+
if (auto error = checkInvertibleRequirements(
1628+
elt, req.getInvertedProtocols()))
1629+
return error;
1630+
}
1631+
1632+
return std::nullopt;
1633+
}
16181634

16191635
// Expand the suppression set so we can record these protocols.
16201636
if (index >= suppressed.size()) {
@@ -1632,10 +1648,6 @@ checkGenericPackRequirement(
16321648
(unsigned)req.getKind());
16331649
}
16341650

1635-
static std::optional<TypeLookupError>
1636-
checkInvertibleRequirements(const Metadata *type,
1637-
InvertibleProtocolSet ignored);
1638-
16391651
static std::optional<TypeLookupError>
16401652
checkInvertibleRequirementsStructural(const Metadata *type,
16411653
InvertibleProtocolSet ignored) {

Diff for: test/Interpreter/moveonly_generics_casting.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,7 @@ func main() {
125125
attemptCall(Dog<Any>())
126126

127127
// CHECK-FIXME: failed to cast (attemptCall)
128-
typealias NoncopyableAny = ~Copyable
129-
// FIXME crashes: attemptCall(Dog<any NoncopyableAny>())
128+
// FIXME crashes: attemptCall(Dog<any ~Copyable>())
130129

131130
// CHECK: cast succeeded
132131
test_radar124171788(.nothing)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes)
2+
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature SuppressedAssociatedTypes)
3+
4+
// REQUIRES: executable_test
5+
6+
// UNSUPPORTED: use_os_stdlib
7+
// UNSUPPORTED: back_deployment_runtime
8+
9+
protocol P: ~Copyable {
10+
associatedtype A: ~Copyable
11+
}
12+
13+
protocol Q: ~Copyable { }
14+
15+
struct X<T: P & ~Copyable> { }
16+
17+
extension X: Q where T: ~Copyable, T.A: Copyable { }
18+
19+
struct NC: ~Copyable { }
20+
21+
struct WithCopyable: P {
22+
typealias A = Int
23+
}
24+
25+
struct WithNonCopyable: P {
26+
typealias A = NC
27+
}
28+
29+
func tryCastToQ<T: P>(_: T.Type) -> Q? {
30+
let x = X<T>()
31+
return x as? Q
32+
}
33+
34+
precondition(tryCastToQ(_: WithCopyable.self) != nil)
35+
precondition(tryCastToQ(_: WithNonCopyable.self) == nil)
36+

0 commit comments

Comments
 (0)