@@ -24,18 +24,18 @@ namespace {
24
24
static const char *SEP_STR = " ╾──────────────────────────────╼\n " ;
25
25
26
26
// SILApplyCrossesIsolation determines if a SIL instruction is an isolation
27
- // crossing apply expressiong . This is done by checking its correspondence
27
+ // crossing apply expression . This is done by checking its correspondence
28
28
// to an ApplyExpr AST node, and then checking the internal flags of that
29
29
// AST node to see if the ActorIsolationChecker determined it crossed isolation.
30
30
// It's possible this is brittle and a more nuanced check is needed, but this
31
31
// suffices for all cases tested so far.
32
32
static bool SILApplyCrossesIsolation (const SILInstruction *inst) {
33
- ApplyExpr *apply = inst->getLoc ().getAsASTNode <ApplyExpr>();
33
+ if (ApplyExpr *apply = inst->getLoc ().getAsASTNode <ApplyExpr>())
34
+ return apply->getIsolationCrossing ().has_value ();
35
+
34
36
// if the instruction doesn't correspond to an ApplyExpr, then it can't
35
37
// cross an isolation domain
36
- if (!apply)
37
- return false ;
38
- return apply->getIsolationCrossing ().has_value ();
38
+ return false ;
39
39
}
40
40
41
41
inline bool isAddress (SILValue val) {
@@ -269,13 +269,16 @@ class PartitionOpTranslator {
269
269
currentInstruction)};
270
270
}
271
271
272
- std::vector<PartitionOp> Consume (TrackableSILValue value) {
272
+ std::vector<PartitionOp> Consume (
273
+ TrackableSILValue value,
274
+ Expr *sourceExpr = nullptr ) {
273
275
assert (valueHasID (value) &&
274
276
" consumed value should already have been encountered" );
275
277
276
278
return {PartitionOp::Consume (
277
279
lookupValueID (value),
278
- currentInstruction)};
280
+ currentInstruction,
281
+ sourceExpr)};
279
282
}
280
283
281
284
std::vector<PartitionOp> Merge (TrackableSILValue fst, TrackableSILValue snd) {
@@ -339,12 +342,10 @@ class PartitionOpTranslator {
339
342
// the translations from source-level SILInstructions.
340
343
341
344
// require all non-sendable sources, merge their regions, and assign the
342
- // resulting region to all non-sendable targets. If consumeSrcs is true then
343
- // consume the source region instead and put all the targets in a single,
344
- // fresh region.
345
+ // resulting region to all non-sendable targets, or assign non-sendable
346
+ // targets to a fresh region if there are no non-sendable sources
345
347
std::vector<PartitionOp> translateSILMultiAssign (
346
- std::vector<SILValue> tgts, std::vector<SILValue> srcs,
347
- bool consumeSrcs = false ) {
348
+ std::vector<SILValue> tgts, std::vector<SILValue> srcs) {
348
349
349
350
std::vector<TrackableSILValue> nonSendableSrcs;
350
351
std::vector<TrackableSILValue> nonSendableTgts;
@@ -362,23 +363,6 @@ class PartitionOpTranslator {
362
363
for (auto op : ops) translated.push_back (op);
363
364
};
364
365
365
- if (consumeSrcs) {
366
- for (auto src : nonSendableSrcs)
367
- add_to_translation (Consume (src));
368
-
369
- // put all the tgts in a fresh region
370
- llvm::Optional<TrackableSILValue> fstTgt;
371
- for (auto tgt : nonSendableTgts)
372
- if (!fstTgt) {
373
- fstTgt = tgt;
374
- add_to_translation (AssignFresh (tgt));
375
- } else {
376
- add_to_translation (Assign (tgt, fstTgt.value ()));
377
- }
378
-
379
- return translated;
380
- }
381
-
382
366
// require all srcs
383
367
for (auto src : nonSendableSrcs)
384
368
add_to_translation (Require (src));
@@ -410,12 +394,69 @@ class PartitionOpTranslator {
410
394
}
411
395
412
396
std::vector<PartitionOp> translateSILApply (const SILInstruction *applyInst) {
413
- return translateSILMultiAssign (
414
- {applyInst->getResult (0 )},
415
- {applyInst->getOperandValues ().begin (),
416
- applyInst->getOperandValues ().end ()},
417
- // consume operands iff the apply crosses isolation
418
- /* consumeSrcs=*/ SILApplyCrossesIsolation (applyInst));
397
+ // if this apply does not cross isolation domains, it has normal,
398
+ // non-consuming multi-assignment semantics
399
+ if (!SILApplyCrossesIsolation (applyInst))
400
+ return translateSILMultiAssign (
401
+ {applyInst->getResult (0 )},
402
+ {applyInst->getOperandValues ().begin (),
403
+ applyInst->getOperandValues ().end ()}
404
+ );
405
+
406
+ ApplyExpr *sourceApply = applyInst->getLoc ().getAsASTNode <ApplyExpr>();
407
+ assert (sourceApply && " only ApplyExpr's should cross isolation domains" );
408
+
409
+ std::vector<PartitionOp> translated;
410
+ auto getSourceArg = [&](unsigned i) {
411
+ if (i < sourceApply->getArgs ()->size ())
412
+ return sourceApply->getArgs ()->getExpr (i);
413
+ assert (false && " SIL instruction has too many arguments for"
414
+ " corresponding AST node" );
415
+ return (Expr *)nullptr ;
416
+ };
417
+
418
+ auto getSourceSelf = [&]() {
419
+ if (auto callExpr = dyn_cast<CallExpr>(sourceApply))
420
+ if (auto calledExpr = dyn_cast<DotSyntaxCallExpr>(callExpr->getDirectCallee ()))
421
+ return calledExpr->getBase ();
422
+ return (Expr *)nullptr ;
423
+ };
424
+
425
+ auto handleSILOperands = [&](OperandValueArrayRef ops) {
426
+ int argNum = 0 ;
427
+ for (auto arg : ops) {
428
+ if (auto trackArg = trackIfNonSendable (arg))
429
+ translated.push_back (
430
+ Consume (trackArg.value (), getSourceArg (argNum)).front ());
431
+ argNum++;
432
+ }
433
+ };
434
+
435
+ auto handleSILSelf = [&](SILValue self) {
436
+ if (auto trackSelf = trackIfNonSendable (self))
437
+ translated.push_back (
438
+ Consume (trackSelf.value (), getSourceSelf ()).front ());
439
+ };
440
+
441
+ if (auto applyInstCast = dyn_cast<ApplyInst>(applyInst)) {
442
+ handleSILOperands (applyInstCast->getArgumentsWithoutSelf ());
443
+ if (applyInstCast->hasSelfArgument ())
444
+ handleSILSelf (applyInstCast->getSelfArgument ());
445
+ } else if (auto applyInstCase = dyn_cast<TryApplyInst>(applyInst)) {
446
+ handleSILOperands (applyInstCast->getArgumentsWithoutSelf ());
447
+ if (applyInstCast->hasSelfArgument ())
448
+ handleSILSelf (applyInstCast->getSelfArgument ());
449
+ } else {
450
+ llvm_unreachable (" this instruction crossing isolation is not handled yet" );
451
+ }
452
+
453
+ // non-sendable results can't be returned from cross-isolation calls without
454
+ // a diagnostic emitted elsewhere. Here, give them a fresh value for better
455
+ // diagnostics hereafter
456
+ if (auto trackResult = trackIfNonSendable (applyInst->getResult (0 )))
457
+ translated.push_back (AssignFresh (trackResult.value ()).front ());
458
+
459
+ return translated;
419
460
}
420
461
421
462
std::vector<PartitionOp> translateSILAssign (SILValue tgt, SILValue src) {
@@ -1204,7 +1245,7 @@ class PartitionAnalysis {
1204
1245
1205
1246
// used for generating informative diagnostics
1206
1247
Expr *getExprForPartitionOp (const PartitionOp& op) {
1207
- SILInstruction *sourceInstr = op.getSourceInst (true );
1248
+ SILInstruction *sourceInstr = op.getSourceInst (/* assertNonNull= */ true );
1208
1249
Expr *expr = sourceInstr->getLoc ().getAsASTNode <Expr>();
1209
1250
assert (expr && " PartitionOp's source location should correspond to"
1210
1251
" an AST node" );
@@ -1231,15 +1272,6 @@ class PartitionAnalysis {
1231
1272
if (hasBeenEmitted (expr)) return ;
1232
1273
1233
1274
raceTracer.traceUseOfConsumedValue (partitionOp, consumedVal);
1234
-
1235
- /*
1236
- * This handles diagnosing accesses to consumed values at the site
1237
- * of access instead of the site of consumption, as this is less
1238
- * useful it will likely be eliminated, but leaving it for now
1239
-
1240
- function->getASTContext().Diags.diagnose(
1241
- expr->getLoc(), diag::consumed_value_used);
1242
- */
1243
1275
},
1244
1276
1245
1277
/* handleConsumeNonConsumable=*/
@@ -1255,10 +1287,18 @@ class PartitionAnalysis {
1255
1287
/* diagnoseConsume=*/
1256
1288
[&](const PartitionOp& consumeOp,
1257
1289
unsigned numDisplayed, unsigned numHidden) {
1290
+
1291
+ if (tryDiagnoseAsCallSite (consumeOp, numDisplayed, numHidden))
1292
+ return ;
1293
+
1294
+ assert (false );
1295
+ // default to more generic diagnostic
1258
1296
auto expr = getExprForPartitionOp (consumeOp);
1259
- function->getASTContext ().Diags .diagnose (
1297
+ auto diag = function->getASTContext ().Diags .diagnose (
1260
1298
expr->getLoc (), diag::consumption_yields_race,
1261
1299
numDisplayed, numDisplayed != 1 , numHidden > 0 , numHidden);
1300
+ if (auto sourceExpr = consumeOp.getSourceExpr ())
1301
+ diag.highlight (sourceExpr->getSourceRange ());
1262
1302
},
1263
1303
1264
1304
/* diagnoseRequire=*/
@@ -1270,6 +1310,36 @@ class PartitionAnalysis {
1270
1310
});
1271
1311
}
1272
1312
1313
+ // try to interpret this consumeOp as a source-level callsite (ApplyExpr),
1314
+ // and report a diagnostic including actor isolation crossing information
1315
+ // returns true iff one was succesfully formed and emitted
1316
+ bool tryDiagnoseAsCallSite (
1317
+ const PartitionOp& consumeOp, unsigned numDisplayed, unsigned numHidden) {
1318
+ SILInstruction *sourceInst = consumeOp.getSourceInst (/* assertNonNull=*/ true );
1319
+ ApplyExpr *apply = sourceInst->getLoc ().getAsASTNode <ApplyExpr>();
1320
+ if (!apply)
1321
+ // consumption does not correspond to an apply expression
1322
+ return false ;
1323
+ auto isolationCrossing = apply->getIsolationCrossing ();
1324
+ if (!isolationCrossing) {
1325
+ assert (false && " ApplyExprs should be consuming only if"
1326
+ " they are isolation crossing" );
1327
+ return false ;
1328
+ }
1329
+ auto argExpr = consumeOp.getSourceExpr ();
1330
+ if (!argExpr)
1331
+ assert (false && " sourceExpr should be populated for ApplyExpr consumptions" );
1332
+
1333
+ function->getASTContext ().Diags .diagnose (
1334
+ apply->getLoc (), diag::call_site_consumption_yields_race,
1335
+ findOriginalValueType (argExpr),
1336
+ isolationCrossing.value ().getCallerIsolation (),
1337
+ isolationCrossing.value ().getCalleeIsolation (),
1338
+ numDisplayed, numDisplayed != 1 , numHidden > 0 , numHidden)
1339
+ .highlight (argExpr->getSourceRange ());
1340
+ return true ;
1341
+ }
1342
+
1273
1343
public:
1274
1344
1275
1345
void dump () LLVM_ATTRIBUTE_USED {
0 commit comments