Skip to content

Commit c266a09

Browse files
committed
[Isolated conformances] Check witness tables for conditional requirements
When establishing whether a given conformance is isolated, look through the witness tables used to satisfy conditional requirements as well. This is because an otherwise-nonisolated conditional conformance can become isolated if one of its associated conformance requirements is satisfied by an isolated conformance. While here, make sure this code works with variadic generics, too.
1 parent 5b6aff7 commit c266a09

File tree

2 files changed

+205
-18
lines changed

2 files changed

+205
-18
lines changed

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 164 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,8 +1154,9 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
11541154
return {foundWitness, hasUninstantiatedSuperclass};
11551155
}
11561156

1157-
/// Determine if
1158-
static bool isExecutingInIsolationOfConformance(
1157+
/// Determine if we are executing within the isolation domain of the global
1158+
/// actor to which the given conformance is isolated.
1159+
static bool _isExecutingInIsolationOfConformance(
11591160
const Metadata *const type,
11601161
const ProtocolConformanceDescriptor *description,
11611162
const WitnessTable *table
@@ -1198,6 +1199,164 @@ static bool isExecutingInIsolationOfConformance(
11981199
return isCurrentGlobalActor(globalActorType, globalActorWitnessTable);
11991200
}
12001201

1202+
static bool _checkConformanceIsolation(
1203+
const Metadata *const type, const WitnessTable *table);
1204+
1205+
/// Check for conformance isolation for a protocol requirement within the
1206+
/// conditional requirements of a witness table.
1207+
static bool _checkConformanceIsolationOfProtocolRequirement(
1208+
const Metadata *const type, const WitnessTable *table,
1209+
const GenericRequirementDescriptor &requirement,
1210+
const WitnessTable *conditionalWitnessTable
1211+
) {
1212+
assert(requirement.Flags.getKind() == GenericRequirementKind::Protocol);
1213+
assert(!requirement.Flags.isPackRequirement());
1214+
1215+
// If the conditional witness table has neither a global actor nor conditional
1216+
// requirements, there's nothing to check.
1217+
auto conditionalDescription = conditionalWitnessTable->getDescription();
1218+
if (!conditionalDescription ||
1219+
!(conditionalDescription->hasConditionalRequirements() ||
1220+
conditionalDescription->hasGlobalActorIsolation()))
1221+
return true;
1222+
1223+
// Recurse into this conformance.
1224+
1225+
// Resolve the conforming type.
1226+
SubstGenericParametersFromMetadata substitutions(type);
1227+
auto result = swift_getTypeByMangledName(
1228+
MetadataState::Abstract, requirement.getParam(),
1229+
/*FIXME:conditionalArgs.data()*/{ },
1230+
[&substitutions](unsigned depth, unsigned index) {
1231+
return substitutions.getMetadata(depth, index).Ptr;
1232+
},
1233+
[&substitutions](const Metadata *type, unsigned index) {
1234+
return substitutions.getWitnessTable(type, index);
1235+
});
1236+
if (result.isError())
1237+
return false;
1238+
1239+
return _checkConformanceIsolation(
1240+
result.getType().getMetadata(), conditionalWitnessTable);
1241+
}
1242+
1243+
/// Check for conformance isolation for a protocol requirement pack within the
1244+
/// conditional requirements of a witness table.
1245+
static bool _checkConformanceIsolationOfProtocolRequirementPack(
1246+
const Metadata *const type, const WitnessTable *table,
1247+
const GenericRequirementDescriptor &requirement,
1248+
WitnessTablePackPointer conditionalWitnessTables
1249+
) {
1250+
assert(requirement.Flags.getKind() == GenericRequirementKind::Protocol);
1251+
assert(requirement.Flags.isPackRequirement());
1252+
1253+
// Check each of the conditional witness tables. If any has neither a global
1254+
// actor nor conditional requirements, there's nothing to check for that one.
1255+
MetadataPackPointer conformingTypes;
1256+
unsigned count = conditionalWitnessTables.getNumElements();
1257+
for (unsigned index = 0; index != count; ++index) {
1258+
auto conditionalWitnessTable =
1259+
conditionalWitnessTables.getElements()[index];
1260+
1261+
// If the conditional witness table has neither a global actor nor conditional
1262+
// requirements, there's nothing to check.
1263+
auto conditionalDescription = conditionalWitnessTable->getDescription();
1264+
if (!conditionalDescription ||
1265+
!(conditionalDescription->hasConditionalRequirements() ||
1266+
conditionalDescription->hasGlobalActorIsolation()))
1267+
continue;
1268+
1269+
// If we don't have it already, get the parameter pack for the
1270+
// conforming types.
1271+
if (!conformingTypes) {
1272+
// Resolve the conforming type.
1273+
SubstGenericParametersFromMetadata substitutions(type);
1274+
auto result = swift::getTypePackByMangledName(
1275+
requirement.getParam(),
1276+
/*FIXME:conditionalArgs.data()*/{ },
1277+
[&substitutions](unsigned depth, unsigned index) {
1278+
return substitutions.getMetadata(depth, index).Ptr;
1279+
},
1280+
[&substitutions](const Metadata *type, unsigned index) {
1281+
return substitutions.getWitnessTable(type, index);
1282+
});
1283+
if (result.isError())
1284+
return false;
1285+
1286+
conformingTypes = result.getType();
1287+
assert(conformingTypes.getNumElements() == count);
1288+
}
1289+
1290+
if (!_checkConformanceIsolation(
1291+
conformingTypes.getElements()[index], conditionalWitnessTable))
1292+
return false;
1293+
}
1294+
1295+
return true;
1296+
}
1297+
1298+
/// Check whether all isolated conformances in the given witness table (and
1299+
/// any witness tables it depends on) are satisfied by the current execution
1300+
/// context.
1301+
///
1302+
/// Returns false if there is an isolated conformance but we are not executing
1303+
/// in that isolation domain.
1304+
static bool _checkConformanceIsolation(const Metadata *const type, const WitnessTable *table) {
1305+
if (!table)
1306+
return true;
1307+
1308+
auto description = table->getDescription();
1309+
if (!description)
1310+
return true;
1311+
1312+
// If this conformance has global actor isolation, check that we are
1313+
// running in that isolation domain.
1314+
if (description->hasGlobalActorIsolation() &&
1315+
!_isExecutingInIsolationOfConformance(type, description, table)) {
1316+
return false;
1317+
}
1318+
1319+
// Check any witness tables that are part of a conditional conformance.
1320+
unsigned instantiationArgIndex = 0;
1321+
for (const auto &requirement: description->getConditionalRequirements()) {
1322+
if (!requirement.Flags.hasKeyArgument())
1323+
continue;
1324+
1325+
switch (requirement.Flags.getKind()) {
1326+
case GenericRequirementKind::Protocol: {
1327+
auto instantiationArg =
1328+
((void* const *)table)[-1 - (int)instantiationArgIndex];
1329+
if (requirement.Flags.isPackRequirement()) {
1330+
if (!_checkConformanceIsolationOfProtocolRequirementPack(
1331+
type, table, requirement,
1332+
WitnessTablePackPointer(instantiationArg)))
1333+
return false;
1334+
} else {
1335+
if (!_checkConformanceIsolationOfProtocolRequirement(
1336+
type, table, requirement,
1337+
(const WitnessTable *)instantiationArg)) {
1338+
return false;
1339+
}
1340+
}
1341+
1342+
break;
1343+
}
1344+
1345+
case GenericRequirementKind::SameType:
1346+
case GenericRequirementKind::BaseClass:
1347+
case GenericRequirementKind::SameConformance:
1348+
case GenericRequirementKind::SameShape:
1349+
case GenericRequirementKind::InvertedProtocols:
1350+
case GenericRequirementKind::Layout:
1351+
break;
1352+
}
1353+
1354+
++instantiationArgIndex;
1355+
}
1356+
1357+
return true;
1358+
}
1359+
12011360
static const WitnessTable *
12021361
swift_conformsToProtocolCommonImpl(const Metadata *const type,
12031362
const ProtocolDescriptor *protocol) {
@@ -1222,17 +1381,9 @@ swift_conformsToProtocolCommonImpl(const Metadata *const type,
12221381
swift_conformsToProtocolMaybeInstantiateSuperclasses(
12231382
type, protocol, true /*instantiateSuperclassMetadata*/);
12241383

1225-
// If the conformance is isolated to a global actor, check whether we are
1226-
// currently executing on that global actor. Otherwise, the type does not
1227-
// conform.
1228-
if (table) {
1229-
if (auto description = table->getDescription()) {
1230-
if (description->hasGlobalActorIsolation() &&
1231-
!isExecutingInIsolationOfConformance(type, description, table)) {
1232-
return nullptr;
1233-
}
1234-
}
1235-
}
1384+
// Check for isolated conformances.
1385+
if (!_checkConformanceIsolation(type, table))
1386+
return nullptr;
12361387

12371388
return table;
12381389
}

test/Concurrency/Runtime/isolated_conformance.swift

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ extension Wrapper: P where T: P {
3131
}
3232
}
3333

34+
@available(SwiftStdlib 5.9, *)
35+
struct WrapMany<each T> {
36+
var wrapped: (repeat each T)
37+
}
38+
39+
@available(SwiftStdlib 5.9, *)
40+
extension WrapMany: P where repeat each T: P {
41+
func f() {
42+
print("Wrapper for many")
43+
}
44+
}
45+
46+
extension Int: P {
47+
func f() { }
48+
}
49+
50+
extension String: P {
51+
func f() { }
52+
}
53+
3454
func tryCastToP(_ value: any Sendable) -> Bool {
3555
if let p = value as? any P {
3656
p.f()
@@ -50,28 +70,44 @@ let wrappedMC = Wrapper(wrapped: mc)
5070
precondition(tryCastToP(mc))
5171
precondition(tryCastToP(wrappedMC))
5272

73+
if #available(SwiftStdlib 5.9, *) {
74+
let wrappedMany = WrapMany(wrapped: (17, mc, "Pack"))
75+
precondition(tryCastToP(wrappedMany))
76+
}
77+
5378
// CHECK: Testing a separate task on the main actor
5479
// CHECK-NEXT: MyClass.f()
5580
// CHECK-NEXT: Wrapper for MyClass.f()
5681
print("Testing a separate task on the main actor")
5782
await Task.detached { @MainActor in
5883
precondition(tryCastToP(mc))
5984
precondition(tryCastToP(wrappedMC))
60-
}.value
6185

62-
// FIXME: Currently not handling the wrapper case appropriately, because
63-
// we don't track whether we used an isolated conformance to satisfy another
64-
// conformance at runtime.
86+
if #available(SwiftStdlib 5.9, *) {
87+
let wrappedMany = WrapMany(wrapped: (17, mc, "Pack"))
88+
precondition(tryCastToP(wrappedMany))
89+
}
90+
91+
}.value
6592

6693
// CHECK: Testing a separate task off the main actor
6794
print("Testing a separate task off the main actor")
6895
await Task.detached {
6996
if #available(SwiftStdlib 6.2, *) {
7097
precondition(!tryCastToP(mc))
71-
// precondition(!tryCastToP(wrappedMC))
98+
precondition(!tryCastToP(wrappedMC))
99+
100+
let wrappedMany = WrapMany(wrapped: (17, mc, "Pack"))
101+
precondition(!tryCastToP(wrappedMany))
72102
} else {
73103
print("Cast succeeds, but shouldn't")
74104
precondition(tryCastToP(mc))
105+
precondition(tryCastToP(wrappedMC))
106+
107+
if #available(SwiftStdlib 5.9, *) {
108+
let wrappedMany = WrapMany(wrapped: (17, mc, "Pack"))
109+
precondition(tryCastToP(wrappedMany))
110+
}
75111
}
76112
}.value
77113

0 commit comments

Comments
 (0)