@@ -232,14 +232,17 @@ class PartitionOpTranslator {
232
232
// raw pointers to ensure it is treated as non-Sendable and strict checking
233
233
// is applied to it
234
234
bool isNonSendableType (SILType type) const {
235
- if (type.getASTType ()->getKind () == TypeKind::BuiltinNativeObject) {
235
+ switch (type.getASTType ()->getKind ()) {
236
+ case TypeKind::BuiltinNativeObject:
237
+ case TypeKind::BuiltinRawPointer:
236
238
// these are very unsafe... definitely not Sendable
237
239
return true ;
240
+ default :
241
+ // consider caching this if it's a performance bottleneck
242
+ return TypeChecker::conformsToProtocol (
243
+ type.getASTType (), sendableProtocol, function->getParentModule ())
244
+ .hasMissingConformance (function->getParentModule ());
238
245
}
239
- // consider caching this if it's a bottleneck
240
- return TypeChecker::conformsToProtocol (
241
- type.getASTType (), sendableProtocol, function->getParentModule ())
242
- .hasMissingConformance (function->getParentModule ());
243
246
}
244
247
245
248
// used to statefully track the instruction currently being translated,
@@ -403,16 +406,29 @@ class PartitionOpTranslator {
403
406
applyInst->getOperandValues ().end ()}
404
407
);
405
408
409
+ return translateIsolationCrossingSILApply (applyInst);
410
+ }
411
+
412
+ // handles the semantics for SIL applies that cross isolation
413
+ // in particular, all arguments are consumed
414
+ std::vector<PartitionOp> translateIsolationCrossingSILApply (
415
+ const SILInstruction *applyInst) {
406
416
ApplyExpr *sourceApply = applyInst->getLoc ().getAsASTNode <ApplyExpr>();
407
417
assert (sourceApply && " only ApplyExpr's should cross isolation domains" );
408
418
409
419
std::vector<PartitionOp> translated;
420
+
421
+ // require all operands
422
+ for (auto op : applyInst->getOperandValues ())
423
+ if (auto trackOp = trackIfNonSendable (op))
424
+ translated.push_back (Require (trackOp.value ()).front ());
425
+
410
426
auto getSourceArg = [&](unsigned i) {
411
- if (i < sourceApply->getArgs ()->size ())
412
- return sourceApply->getArgs ()->getExpr (i);
427
+ if (i < sourceApply->getArgs ()->size ())
428
+ return sourceApply->getArgs ()->getExpr (i);
413
429
assert (false && " SIL instruction has too many arguments for"
414
430
" corresponding AST node" );
415
- return (Expr *)nullptr ;
431
+ return (Expr *)nullptr ;
416
432
};
417
433
418
434
auto getSourceSelf = [&]() {
@@ -441,11 +457,11 @@ class PartitionOpTranslator {
441
457
if (auto applyInstCast = dyn_cast<ApplyInst>(applyInst)) {
442
458
handleSILOperands (applyInstCast->getArgumentsWithoutSelf ());
443
459
if (applyInstCast->hasSelfArgument ())
444
- handleSILSelf (applyInstCast->getSelfArgument ());
460
+ handleSILSelf (applyInstCast->getSelfArgument ());
445
461
} else if (auto applyInstCase = dyn_cast<TryApplyInst>(applyInst)) {
446
462
handleSILOperands (applyInstCast->getArgumentsWithoutSelf ());
447
463
if (applyInstCast->hasSelfArgument ())
448
- handleSILSelf (applyInstCast->getSelfArgument ());
464
+ handleSILSelf (applyInstCast->getSelfArgument ());
449
465
} else {
450
466
llvm_unreachable (" this instruction crossing isolation is not handled yet" );
451
467
}
@@ -501,15 +517,15 @@ class PartitionOpTranslator {
501
517
502
518
// used to index the translations of SILInstructions performed
503
519
// for refrence and debugging
504
- int translationIndex = 0 ;
520
+ static inline int translationIndex = 0 ;
505
521
506
522
// Some SILInstructions contribute to the partition of non-Sendable values
507
523
// being analyzed. translateSILInstruction translate a SILInstruction
508
524
// to its effect on the non-Sendable partition, if it has one.
509
525
//
510
526
// The current pattern of
511
527
std::vector<PartitionOp> translateSILInstruction (SILInstruction *instruction) {
512
- translationIndex++;
528
+ LLVM_DEBUG ( translationIndex++;) ;
513
529
currentInstruction = instruction;
514
530
515
531
// The following instructions are treated as assigning their result to a
@@ -910,11 +926,11 @@ class ConsumeRequireAccumulator {
910
926
// that access ("require") the region consumed. Sorting is by lowest distance
911
927
// first, then arbitrarily. This is used for final diagnostic output.
912
928
void forEachConsumeRequire (
913
- unsigned numRequiresPerConsume,
914
929
llvm::function_ref<void (const PartitionOp& consumeOp, unsigned numProcessed, unsigned numSkipped)>
915
930
processConsumeOp,
916
931
llvm::function_ref<void(const PartitionOp& requireOp)>
917
- processRequireOp) const {
932
+ processRequireOp,
933
+ unsigned numRequiresPerConsume = UINT_MAX) const {
918
934
for (auto [consumeOp, requireOps] : requirementsForConsumptions) {
919
935
unsigned numProcessed = std::min ({(unsigned ) requireOps.size (),
920
936
(unsigned ) numRequiresPerConsume});
@@ -927,6 +943,19 @@ class ConsumeRequireAccumulator {
927
943
}
928
944
}
929
945
}
946
+
947
+ void dump () const {
948
+ forEachConsumeRequire (
949
+ [](const PartitionOp& consumeOp, unsigned numProcessed, unsigned numSkipped) {
950
+ llvm::dbgs () << " ┌──╼ CONSUME: " ;
951
+ consumeOp.dump ();
952
+ },
953
+ [](const PartitionOp& requireOp) {
954
+ llvm::dbgs () << " ├╼ REQUIRE: " ;
955
+ requireOp.dump ();
956
+ }
957
+ );
958
+ }
930
959
};
931
960
932
961
// A RaceTracer is used to accumulate the facts that the main phase of
@@ -1075,8 +1104,9 @@ class RaceTracer {
1075
1104
if (workingPartition.isConsumed (consumedVal))
1076
1105
workingPartition.apply (PartitionOp::AssignFresh (consumedVal));
1077
1106
1107
+ int i = 0 ;
1078
1108
block.forEachPartitionOp ([&](const PartitionOp& partitionOp) {
1079
- if (targetOp && targetOp == partitionOp)
1109
+ if (targetOp == partitionOp)
1080
1110
return false ; // break
1081
1111
workingPartition.apply (partitionOp);
1082
1112
if (workingPartition.isConsumed (consumedVal) && !consumedReason) {
@@ -1093,6 +1123,7 @@ class RaceTracer {
1093
1123
consumedReason = llvm::None;
1094
1124
1095
1125
// continue walking block
1126
+ i++;
1096
1127
return true ;
1097
1128
});
1098
1129
@@ -1102,7 +1133,10 @@ class RaceTracer {
1102
1133
consumedReason = LocalConsumedReason::NonLocal ();
1103
1134
1104
1135
// if consumedReason is none, then consumedVal was not actually consumed
1105
- assert (consumedReason);
1136
+ assert (consumedReason
1137
+ || dumpBlockSearch (SILBlock, consumedVal)
1138
+ && " no consumption was found"
1139
+ );
1106
1140
1107
1141
// if this is a query for consumption reason at block exit, update the cache
1108
1142
if (!targetOp)
@@ -1112,6 +1146,31 @@ class RaceTracer {
1112
1146
return consumedReason.value ();
1113
1147
}
1114
1148
1149
+ bool dumpBlockSearch (SILBasicBlock * SILBlock, TrackableValueID consumedVal) {
1150
+ LLVM_DEBUG (
1151
+ unsigned i = 0 ;
1152
+ const BlockPartitionState &block = blockStates[SILBlock];
1153
+ Partition working = block.getEntryPartition ();
1154
+ llvm::dbgs () << " ┌──────────╼\n │ " ;
1155
+ working.dump ();
1156
+ block.forEachPartitionOp ([&](const PartitionOp &op) {
1157
+ llvm::dbgs () << " ├[" << i++ << " ] " ;
1158
+ op.dump ();
1159
+ working.apply (op);
1160
+ llvm::dbgs () << " │ " ;
1161
+ if (working.isConsumed (consumedVal)) {
1162
+ llvm::errs ().changeColor (llvm::raw_ostream::RED, true );
1163
+ llvm::errs () << " (" << consumedVal << " CONSUMED) " ;
1164
+ llvm::errs ().resetColor ();
1165
+ }
1166
+ working.dump ();
1167
+ return true ;
1168
+ });
1169
+ llvm::dbgs () << " └──────────╼\n " ;
1170
+ );
1171
+ return false ;
1172
+ }
1173
+
1115
1174
public:
1116
1175
RaceTracer (const BasicBlockData<BlockPartitionState>& blockStates)
1117
1176
: blockStates(blockStates) {}
@@ -1126,7 +1185,6 @@ class RaceTracer {
1126
1185
}
1127
1186
};
1128
1187
1129
-
1130
1188
// Instances of PartitionAnalysis perform the region-based Sendable checking.
1131
1189
// Internally, a PartitionOpTranslator is stored to perform the translation from
1132
1190
// SILInstructions to PartitionOps, then a fixed point iteration is run to
@@ -1258,10 +1316,14 @@ class PartitionAnalysis {
1258
1316
void diagnose () {
1259
1317
assert (solved && " diagnose should not be called before solve" );
1260
1318
1261
- // llvm::dbgs() << function->getName() << "\n";
1319
+ LLVM_DEBUG (
1320
+ llvm::dbgs () << " Emitting diagnostics for function "
1321
+ << function->getName () << " \n " );
1262
1322
RaceTracer tracer = blockStates;
1263
1323
1264
1324
for (auto [_, blockState] : blockStates) {
1325
+ // populate the raceTracer with all requires of consumed valued found
1326
+ // throughout the CFG
1265
1327
blockState.diagnoseFailures (
1266
1328
/* handleFailure=*/
1267
1329
[&](const PartitionOp& partitionOp, TrackableValueID consumedVal) {
@@ -1282,16 +1344,23 @@ class PartitionAnalysis {
1282
1344
});
1283
1345
}
1284
1346
1347
+ LLVM_DEBUG (
1348
+ llvm::dbgs () << " Accumulator Complete:\n " ;
1349
+ raceTracer.getAccumulator ().dump ();
1350
+ );
1351
+
1352
+ // ask the raceTracer to report diagnostics at the consumption sites
1353
+ // for all the racy requirement sites entered into it above
1285
1354
raceTracer.getAccumulator ().forEachConsumeRequire (
1286
- NUM_REQUIREMENTS_TO_DIAGNOSE,
1287
1355
/* diagnoseConsume=*/
1288
1356
[&](const PartitionOp& consumeOp,
1289
1357
unsigned numDisplayed, unsigned numHidden) {
1290
1358
1291
1359
if (tryDiagnoseAsCallSite (consumeOp, numDisplayed, numHidden))
1292
1360
return ;
1293
1361
1294
- assert (false );
1362
+ assert (false && " no consumptions besides callsites implemented yet" );
1363
+
1295
1364
// default to more generic diagnostic
1296
1365
auto expr = getExprForPartitionOp (consumeOp);
1297
1366
auto diag = function->getASTContext ().Diags .diagnose (
@@ -1307,7 +1376,8 @@ class PartitionAnalysis {
1307
1376
function->getASTContext ().Diags .diagnose (
1308
1377
expr->getLoc (), diag::possible_racy_access_site)
1309
1378
.highlight (expr->getSourceRange ());
1310
- });
1379
+ },
1380
+ NUM_REQUIREMENTS_TO_DIAGNOSE);
1311
1381
}
1312
1382
1313
1383
// try to interpret this consumeOp as a source-level callsite (ApplyExpr),
@@ -1331,7 +1401,7 @@ class PartitionAnalysis {
1331
1401
assert (false && " sourceExpr should be populated for ApplyExpr consumptions" );
1332
1402
1333
1403
function->getASTContext ().Diags .diagnose (
1334
- apply ->getLoc (), diag::call_site_consumption_yields_race,
1404
+ argExpr ->getLoc (), diag::call_site_consumption_yields_race,
1335
1405
findOriginalValueType (argExpr),
1336
1406
isolationCrossing.value ().getCallerIsolation (),
1337
1407
isolationCrossing.value ().getCalleeIsolation (),
0 commit comments