@@ -1126,6 +1126,40 @@ class PartitionOpError {
11261126 }
11271127 };
11281128
1129+ struct InOutSendingReturnedError {
1130+ const PartitionOp *op;
1131+
1132+ // / The 'inout sending' parameter that caused the error to be emitted.
1133+ Element inoutSendingElement;
1134+
1135+ // / The actual returned value. If we return the 'inout sending' parameter
1136+ // / then this will equal inoutSendingElement.
1137+ Element returnedValue;
1138+
1139+ // / If we are actor isolated due to being in the same region as the out
1140+ // / parameter of a actor isolated method... this is that actor isolation.
1141+ SILDynamicMergedIsolationInfo isolationInfo;
1142+
1143+ InOutSendingReturnedError (const PartitionOp &op,
1144+ Element inoutSendingElement,
1145+ Element returnedValue,
1146+ SILDynamicMergedIsolationInfo isolationInfo = {})
1147+ : op(&op), inoutSendingElement(inoutSendingElement),
1148+ returnedValue (returnedValue), isolationInfo(isolationInfo) {}
1149+
1150+ InOutSendingReturnedError (const PartitionOp &op,
1151+ Element inoutSendingElement,
1152+ SILDynamicMergedIsolationInfo isolationInfo = {})
1153+ : InOutSendingReturnedError(op, inoutSendingElement,
1154+ inoutSendingElement, isolationInfo) {}
1155+
1156+ void print (llvm::raw_ostream &os, RegionAnalysisValueMap &valueMap) const ;
1157+
1158+ SWIFT_DEBUG_DUMPER (dump(RegionAnalysisValueMap &valueMap)) {
1159+ print (llvm::dbgs (), valueMap);
1160+ }
1161+ };
1162+
11291163 struct NonSendableIsolationCrossingResultError {
11301164 const PartitionOp *op;
11311165
@@ -1297,6 +1331,42 @@ struct PartitionOpEvaluator {
12971331 return SILDynamicMergedIsolationInfo ();
12981332 }
12991333
1334+ // / If \p region is not disconnected only because of an out parameter, return
1335+ // / that out parameter. We do not care about other non-disconnected parameters
1336+ // / since we always want to prefer the return error.
1337+ SILValue findNonDisconnectedOutParameterInRegion (Region region) const {
1338+ for (const auto &pair : p.range ()) {
1339+ if (pair.second != region)
1340+ continue ;
1341+ auto info = getIsolationRegionInfo (pair.first );
1342+
1343+ // If we do not have any isolation info, then something bad
1344+ // happened. Return SILValue().
1345+ if (!info)
1346+ return {};
1347+
1348+ // If we have something that is disconnected... continue. We do not care
1349+ // about this.
1350+ if (info.isDisconnected ())
1351+ continue ;
1352+
1353+ // See if we ahve an indirect out parameter...
1354+ if (auto value = asImpl ().getIndirectOutParameter (pair.first )) {
1355+ return value;
1356+ }
1357+
1358+ // If this was not an out parameter, return {}.
1359+ return {};
1360+ }
1361+
1362+ // After going over our loop, if result is not SILValue(), then we found
1363+ // that our region is not disconnected due only to a single 'indirect out'
1364+ // parameter. If SILValue() then we had all disconnected values. If we found
1365+ // multiple non-disconnected things, we would have bailed earlier in the
1366+ // loop itself.
1367+ return SILValue ();
1368+ }
1369+
13001370 bool isTaskIsolatedDerived (Element elt) const {
13011371 return asImpl ().isTaskIsolatedDerived (elt);
13021372 }
@@ -1530,7 +1600,9 @@ struct PartitionOpEvaluator {
15301600 " Require PartitionOp's argument should already be tracked" );
15311601
15321602 // First check if the region of our 'inout sending' element has been
1533- // sent. In that case, we emit a special use after free error.
1603+ // sent. In that case, we emit a special use after free error so that the
1604+ // user knows that they need to reinitialize the 'inout sending'
1605+ // parameter.
15341606 if (auto *sentOperandSet = p.getSentOperandSet (op.getOpArg1 ())) {
15351607 for (auto sentOperand : sentOperandSet->data ()) {
15361608 handleError (InOutSendingNotInitializedAtExitError (op, op.getOpArg1 (),
@@ -1539,7 +1611,7 @@ struct PartitionOpEvaluator {
15391611 return ;
15401612 }
15411613
1542- // If we were not sent, check if our region is actor isolated . If so,
1614+ // If we were not sent, check if our region is not disconnected . If so,
15431615 // error since we need a disconnected value in the inout parameter.
15441616 Region inoutSendingRegion = p.getRegion (op.getOpArg1 ());
15451617 auto dynamicRegionIsolation = getIsolationRegionInfo (inoutSendingRegion);
@@ -1550,12 +1622,82 @@ struct PartitionOpEvaluator {
15501622 return ;
15511623 }
15521624
1553- // Otherwise, emit the error if the dynamic region isolation is not
1554- // disconnected.
1625+ auto handleDirectReturn = [&]() -> bool {
1626+ bool emittedDiagnostic = false ;
1627+
1628+ // For each value we are returning...
1629+ for (auto value : op.getSourceInst ()->getOperandValues ()) {
1630+ // Find its element and check if that element is in the same region as
1631+ // our inout sending param...
1632+ auto elt = asImpl ().getElement (value);
1633+ if (!elt || inoutSendingRegion != p.getRegion (*elt))
1634+ continue ;
1635+
1636+ emittedDiagnostic = true ;
1637+ // Then check if our element is the same as our inoutSendingParam. In
1638+ // that case, we emit a special error that only refers to the
1639+ // inoutSendingParam as one entity.
1640+ //
1641+ // NOTE: This is taking advantage of us looking through loads when
1642+ // determining element identity. When that changes, this code will
1643+ // need to be updated to look through loads.
1644+ if (*elt == op.getOpArg1 ()) {
1645+ handleError (InOutSendingReturnedError (op, op.getOpArg1 (),
1646+ dynamicRegionIsolation));
1647+ continue ;
1648+ }
1649+
1650+ // Otherwise, we need to refer to a different value in the same region
1651+ // as the 'inout sending' parameter. Emit a special error. For that.
1652+ handleError (InOutSendingReturnedError (op, op.getOpArg1 (), *elt,
1653+ dynamicRegionIsolation));
1654+ }
1655+
1656+ return emittedDiagnostic;
1657+ };
1658+
1659+ // Then check if our region isolated value is not disconnected...
15551660 if (!dynamicRegionIsolation.isDisconnected ()) {
1661+ // Before we emit the more general "inout sending" not disconnected at
1662+ // exit error... check if our dynamic region isolation is not
1663+ // disconnected since it is in the same region an out parameter. In that
1664+ // case we want to emit a special 'cannot return value' error.
1665+ if (auto outParam =
1666+ findNonDisconnectedOutParameterInRegion (inoutSendingRegion)) {
1667+ handleError (InOutSendingReturnedError (op, op.getOpArg1 (),
1668+ getElement (outParam).value (),
1669+ dynamicRegionIsolation));
1670+ return ;
1671+ }
1672+
1673+ // If we did not have an out parameter, see if we are returning a
1674+ // value that is in the region as our 'inout sending' parameter.
1675+ if (handleDirectReturn ())
1676+ return ;
1677+
1678+ // Otherwise, we emit the normal not disconnected at exit error.
15561679 handleError (InOutSendingNotDisconnectedAtExitError (
15571680 op, op.getOpArg1 (), dynamicRegionIsolation));
1681+ return ;
15581682 }
1683+
1684+ // Ok, we have a disconnected value. We could still be returning a
1685+ // disconnected value in the same region as an 'inout sending'
1686+ // parameter. We cannot allow that since the caller considers 'inout
1687+ // sending' values to be in their own region on function return. So it
1688+ // would assume that it could potentially send the value in the 'inout
1689+ // sending' parameter and the return value to different isolation
1690+ // domains... allowing for races.
1691+
1692+ // Even though we are disconnected, see if our SILFunction has an actor
1693+ // isolation. In that case, we want to use that since the direct return
1694+ // value will be inferred in the caller to have that isolation.
1695+ if (auto isolation = SILIsolationInfo::getFunctionIsolation (
1696+ op.getSourceInst ()->getFunction ())) {
1697+ dynamicRegionIsolation = {isolation};
1698+ }
1699+
1700+ handleDirectReturn ();
15591701 return ;
15601702 }
15611703 case PartitionOpKind::UnknownPatternError:
@@ -1565,10 +1707,6 @@ struct PartitionOpEvaluator {
15651707 // Then emit an unknown code pattern error.
15661708 return handleError (UnknownCodePatternError (op));
15671709 case PartitionOpKind::NonSendableIsolationCrossingResult: {
1568- // Grab the dynamic dataflow isolation information for our element's
1569- // region.
1570- Region region = p.getRegion (op.getOpArg1 ());
1571-
15721710 // Then emit the error.
15731711 return handleError (
15741712 NonSendableIsolationCrossingResultError (op, op.getOpArg1 ()));
@@ -1753,6 +1891,14 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
17531891 // / isolated value.
17541892 bool isTaskIsolatedDerived (Element elt) const { return false ; }
17551893
1894+ // / If \p elt maps to a representative that is an indirect out parameter,
1895+ // / return that value.
1896+ SILValue getIndirectOutParameter (Element elt) const { return {}; }
1897+
1898+ // / If \p elt maps to a representative that is an 'inout sending' parameter
1899+ // / that returns that value.
1900+ SILValue getInOutSendingParameter (Element elt) const { return {}; }
1901+
17561902 // / By default, do nothing upon error.
17571903 void handleError (PartitionOpError error) {}
17581904
0 commit comments