@@ -1126,6 +1126,40 @@ class PartitionOpError {
1126
1126
}
1127
1127
};
1128
1128
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
+
1129
1163
struct NonSendableIsolationCrossingResultError {
1130
1164
const PartitionOp *op;
1131
1165
@@ -1297,6 +1331,42 @@ struct PartitionOpEvaluator {
1297
1331
return SILDynamicMergedIsolationInfo ();
1298
1332
}
1299
1333
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
+
1300
1370
bool isTaskIsolatedDerived (Element elt) const {
1301
1371
return asImpl ().isTaskIsolatedDerived (elt);
1302
1372
}
@@ -1530,7 +1600,9 @@ struct PartitionOpEvaluator {
1530
1600
" Require PartitionOp's argument should already be tracked" );
1531
1601
1532
1602
// 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.
1534
1606
if (auto *sentOperandSet = p.getSentOperandSet (op.getOpArg1 ())) {
1535
1607
for (auto sentOperand : sentOperandSet->data ()) {
1536
1608
handleError (InOutSendingNotInitializedAtExitError (op, op.getOpArg1 (),
@@ -1539,7 +1611,7 @@ struct PartitionOpEvaluator {
1539
1611
return ;
1540
1612
}
1541
1613
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,
1543
1615
// error since we need a disconnected value in the inout parameter.
1544
1616
Region inoutSendingRegion = p.getRegion (op.getOpArg1 ());
1545
1617
auto dynamicRegionIsolation = getIsolationRegionInfo (inoutSendingRegion);
@@ -1550,12 +1622,82 @@ struct PartitionOpEvaluator {
1550
1622
return ;
1551
1623
}
1552
1624
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...
1555
1660
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.
1556
1679
handleError (InOutSendingNotDisconnectedAtExitError (
1557
1680
op, op.getOpArg1 (), dynamicRegionIsolation));
1681
+ return ;
1558
1682
}
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 ();
1559
1701
return ;
1560
1702
}
1561
1703
case PartitionOpKind::UnknownPatternError:
@@ -1565,10 +1707,6 @@ struct PartitionOpEvaluator {
1565
1707
// Then emit an unknown code pattern error.
1566
1708
return handleError (UnknownCodePatternError (op));
1567
1709
case PartitionOpKind::NonSendableIsolationCrossingResult: {
1568
- // Grab the dynamic dataflow isolation information for our element's
1569
- // region.
1570
- Region region = p.getRegion (op.getOpArg1 ());
1571
-
1572
1710
// Then emit the error.
1573
1711
return handleError (
1574
1712
NonSendableIsolationCrossingResultError (op, op.getOpArg1 ()));
@@ -1753,6 +1891,14 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
1753
1891
// / isolated value.
1754
1892
bool isTaskIsolatedDerived (Element elt) const { return false ; }
1755
1893
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
+
1756
1902
// / By default, do nothing upon error.
1757
1903
void handleError (PartitionOpError error) {}
1758
1904
0 commit comments