@@ -24,18 +24,18 @@ namespace {
2424static const char *SEP_STR = " ╾──────────────────────────────╼\n " ;
2525
2626// 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
2828// to an ApplyExpr AST node, and then checking the internal flags of that
2929// AST node to see if the ActorIsolationChecker determined it crossed isolation.
3030// It's possible this is brittle and a more nuanced check is needed, but this
3131// suffices for all cases tested so far.
3232static 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+
3436 // if the instruction doesn't correspond to an ApplyExpr, then it can't
3537 // cross an isolation domain
36- if (!apply)
37- return false ;
38- return apply->getIsolationCrossing ().has_value ();
38+ return false ;
3939}
4040
4141inline bool isAddress (SILValue val) {
@@ -269,13 +269,16 @@ class PartitionOpTranslator {
269269 currentInstruction)};
270270 }
271271
272- std::vector<PartitionOp> Consume (TrackableSILValue value) {
272+ std::vector<PartitionOp> Consume (
273+ TrackableSILValue value,
274+ Expr *sourceExpr = nullptr ) {
273275 assert (valueHasID (value) &&
274276 " consumed value should already have been encountered" );
275277
276278 return {PartitionOp::Consume (
277279 lookupValueID (value),
278- currentInstruction)};
280+ currentInstruction,
281+ sourceExpr)};
279282 }
280283
281284 std::vector<PartitionOp> Merge (TrackableSILValue fst, TrackableSILValue snd) {
@@ -339,12 +342,10 @@ class PartitionOpTranslator {
339342 // the translations from source-level SILInstructions.
340343
341344 // 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
345347 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) {
348349
349350 std::vector<TrackableSILValue> nonSendableSrcs;
350351 std::vector<TrackableSILValue> nonSendableTgts;
@@ -362,23 +363,6 @@ class PartitionOpTranslator {
362363 for (auto op : ops) translated.push_back (op);
363364 };
364365
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-
382366 // require all srcs
383367 for (auto src : nonSendableSrcs)
384368 add_to_translation (Require (src));
@@ -410,12 +394,69 @@ class PartitionOpTranslator {
410394 }
411395
412396 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;
419460 }
420461
421462 std::vector<PartitionOp> translateSILAssign (SILValue tgt, SILValue src) {
@@ -1204,7 +1245,7 @@ class PartitionAnalysis {
12041245
12051246 // used for generating informative diagnostics
12061247 Expr *getExprForPartitionOp (const PartitionOp& op) {
1207- SILInstruction *sourceInstr = op.getSourceInst (true );
1248+ SILInstruction *sourceInstr = op.getSourceInst (/* assertNonNull= */ true );
12081249 Expr *expr = sourceInstr->getLoc ().getAsASTNode <Expr>();
12091250 assert (expr && " PartitionOp's source location should correspond to"
12101251 " an AST node" );
@@ -1231,15 +1272,6 @@ class PartitionAnalysis {
12311272 if (hasBeenEmitted (expr)) return ;
12321273
12331274 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- */
12431275 },
12441276
12451277 /* handleConsumeNonConsumable=*/
@@ -1255,10 +1287,18 @@ class PartitionAnalysis {
12551287 /* diagnoseConsume=*/
12561288 [&](const PartitionOp& consumeOp,
12571289 unsigned numDisplayed, unsigned numHidden) {
1290+
1291+ if (tryDiagnoseAsCallSite (consumeOp, numDisplayed, numHidden))
1292+ return ;
1293+
1294+ assert (false );
1295+ // default to more generic diagnostic
12581296 auto expr = getExprForPartitionOp (consumeOp);
1259- function->getASTContext ().Diags .diagnose (
1297+ auto diag = function->getASTContext ().Diags .diagnose (
12601298 expr->getLoc (), diag::consumption_yields_race,
12611299 numDisplayed, numDisplayed != 1 , numHidden > 0 , numHidden);
1300+ if (auto sourceExpr = consumeOp.getSourceExpr ())
1301+ diag.highlight (sourceExpr->getSourceRange ());
12621302 },
12631303
12641304 /* diagnoseRequire=*/
@@ -1270,6 +1310,36 @@ class PartitionAnalysis {
12701310 });
12711311 }
12721312
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+
12731343public:
12741344
12751345 void dump () LLVM_ATTRIBUTE_USED {
0 commit comments