mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #77900 from gottesmm/rdar127477211
[region-isolation] Perform checking of non-Sendable results using rbi rather than Sema.
This commit is contained in:
@@ -1098,6 +1098,28 @@ NOTE(regionbasedisolation_out_sending_cannot_be_actor_isolated_note_named, none,
|
||||
"returning %1 %0 risks causing data races since the caller assumes that %0 can be safely sent to other isolation domains",
|
||||
(Identifier, StringRef))
|
||||
|
||||
//===
|
||||
// non-Sendable Results
|
||||
|
||||
// Example: returning main-actor isolated result to a custom-actor isolated context risks causing data races
|
||||
ERROR(rbi_isolation_crossing_result, none,
|
||||
"non-Sendable %0-typed result can not be returned from %1 %kind2 to %3 context",
|
||||
(Type, ActorIsolation, const ValueDecl *, ActorIsolation))
|
||||
ERROR(rbi_isolation_crossing_result_no_decl, none,
|
||||
"non-Sendable %0-typed result can not be returned from %1 function to %2 context",
|
||||
(Type, ActorIsolation, ActorIsolation))
|
||||
NOTE(rbi_non_sendable_nominal,none,
|
||||
"%kind0 does not conform to the 'Sendable' protocol",
|
||||
(const ValueDecl *))
|
||||
NOTE(rbi_nonsendable_function_type,none,
|
||||
"a function type must be marked '@Sendable' to conform to 'Sendable'", ())
|
||||
NOTE(rbi_add_nominal_sendable_conformance,none,
|
||||
"consider making %kind0 conform to the 'Sendable' protocol",
|
||||
(const ValueDecl *))
|
||||
NOTE(rbi_add_generic_parameter_sendable_conformance,none,
|
||||
"consider making generic parameter %0 conform to the 'Sendable' protocol",
|
||||
(Type))
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MARK: Misc Diagnostics
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -59,4 +59,8 @@ PARTITION_OP_ERROR(InOutSendingNotDisconnectedAtExit)
|
||||
/// to our user so that we can emit that error as we process.
|
||||
PARTITION_OP_ERROR(UnknownCodePattern)
|
||||
|
||||
/// Used to signify that an isolation crossing function is returning a
|
||||
/// non-Sendable value.
|
||||
PARTITION_OP_ERROR(NonSendableIsolationCrossingResult)
|
||||
|
||||
#undef PARTITION_OP_ERROR
|
||||
|
||||
@@ -462,6 +462,14 @@ enum class PartitionOpKind : uint8_t {
|
||||
///
|
||||
/// Takes one parameter, the inout parameter that we need to check.
|
||||
InOutSendingAtFunctionExit,
|
||||
|
||||
/// This is the result of an isolation crossing apply site. We need to emit a
|
||||
/// special error since we never allow this.
|
||||
///
|
||||
/// DISCUSSION: This is actually just a form of "send". Sadly, we can not use
|
||||
/// "send" directly since "send" expects a SILOperand and these are values. So
|
||||
/// to work around the API issue, we have to use a different, specific entry.
|
||||
NonSendableIsolationCrossingResult,
|
||||
};
|
||||
|
||||
/// PartitionOp represents a primitive operation that can be performed on
|
||||
@@ -574,6 +582,12 @@ public:
|
||||
sourceInst);
|
||||
}
|
||||
|
||||
static PartitionOp
|
||||
NonSendableIsolationCrossingResult(Element elt, SILInstruction *sourceInst) {
|
||||
return PartitionOp(PartitionOpKind::NonSendableIsolationCrossingResult, elt,
|
||||
sourceInst);
|
||||
}
|
||||
|
||||
bool operator==(const PartitionOp &other) const {
|
||||
return opKind == other.opKind && opArgs == other.opArgs &&
|
||||
source == other.source;
|
||||
@@ -1053,6 +1067,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct NonSendableIsolationCrossingResultError {
|
||||
const PartitionOp *op;
|
||||
|
||||
Element returnValueElement;
|
||||
|
||||
NonSendableIsolationCrossingResultError(const PartitionOp &op,
|
||||
Element returnValue)
|
||||
: op(&op), returnValueElement(returnValue) {}
|
||||
|
||||
void print(llvm::raw_ostream &os, RegionAnalysisValueMap &valueMap) const;
|
||||
|
||||
SWIFT_DEBUG_DUMPER(dump(RegionAnalysisValueMap &valueMap)) {
|
||||
print(llvm::dbgs(), valueMap);
|
||||
}
|
||||
};
|
||||
|
||||
#define PARTITION_OP_ERROR(NAME) \
|
||||
static_assert(std::is_copy_constructible_v<NAME##Error>, \
|
||||
#NAME " must be copy constructable");
|
||||
@@ -1482,8 +1512,15 @@ public:
|
||||
|
||||
// Then emit an unknown code pattern error.
|
||||
return handleError(UnknownCodePatternError(op));
|
||||
}
|
||||
case PartitionOpKind::NonSendableIsolationCrossingResult:
|
||||
// Grab the dynamic dataflow isolation information for our element's
|
||||
// region.
|
||||
Region region = p.getRegion(op.getOpArgs()[0]);
|
||||
|
||||
// Then emit the error.
|
||||
return handleError(
|
||||
NonSendableIsolationCrossingResultError(op, op.getOpArgs()[0]));
|
||||
}
|
||||
llvm_unreachable("Covered switch isn't covered?!");
|
||||
}
|
||||
|
||||
|
||||
@@ -1526,6 +1526,12 @@ struct PartitionOpBuilder {
|
||||
PartitionOp::UnknownPatternError(lookupValueID(value), currentInst));
|
||||
}
|
||||
|
||||
void addNonSendableIsolationCrossingResultError(SILValue value) {
|
||||
currentInstPartitionOps.emplace_back(
|
||||
PartitionOp::NonSendableIsolationCrossingResult(lookupValueID(value),
|
||||
currentInst));
|
||||
}
|
||||
|
||||
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
|
||||
|
||||
void print(llvm::raw_ostream &os) const;
|
||||
@@ -2394,14 +2400,58 @@ public:
|
||||
handleSILOperands(applySite.getOperandsWithoutIndirectResults());
|
||||
}
|
||||
|
||||
// non-sendable results can't be returned from cross-isolation calls without
|
||||
// a diagnostic emitted elsewhere. Here, give them a fresh value for better
|
||||
// diagnostics hereafter
|
||||
// Create a new assign fresh for each one of our values and unless our
|
||||
// return value is sending, emit an extra error bit on the results that are
|
||||
// non-Sendable.
|
||||
SmallVector<SILValue, 8> applyResults;
|
||||
getApplyResults(*applySite, applyResults);
|
||||
for (auto result : applyResults)
|
||||
if (auto value = tryToTrackValue(result))
|
||||
|
||||
auto substCalleeType = applySite.getSubstCalleeType();
|
||||
|
||||
// Today, all values in result info are sending or none are. So this is a
|
||||
// safe to check that we have a sending result. In the future if we allow
|
||||
// for sending to vary, then this code will need to be updated to vary with
|
||||
// each result.
|
||||
auto results = substCalleeType->getResults();
|
||||
auto *applyExpr = applySite->getLoc().getAsASTNode<ApplyExpr>();
|
||||
|
||||
// We only emit the error if we do not have a sending result and if our
|
||||
// callee isn't nonisolated.
|
||||
//
|
||||
// DISCUSSION: If our callee is non-isolated, we know that the value must
|
||||
// have been returned as a non-sent disconnected value. The reason why this
|
||||
// is different from a sending result is that the result may be part of the
|
||||
// region of the operands while the sending result will not be. In either
|
||||
// case though, we do not want to emit the error.
|
||||
bool emitIsolationCrossingResultError =
|
||||
(results.empty() || !results[0].hasOption(SILResultInfo::IsSending)) &&
|
||||
// We have to check if we actually have an apply expr since we may not
|
||||
// have one if we are processing a SIL test case since SIL does not have
|
||||
// locations (which is how we grab our AST information).
|
||||
!(applyExpr && applyExpr->getIsolationCrossing()
|
||||
->getCalleeIsolation()
|
||||
.isNonisolated());
|
||||
|
||||
for (auto result : applyResults) {
|
||||
if (auto value = tryToTrackValue(result)) {
|
||||
builder.addAssignFresh(value->getRepresentative().getValue());
|
||||
if (emitIsolationCrossingResultError)
|
||||
builder.addNonSendableIsolationCrossingResultError(
|
||||
value->getRepresentative().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// If we are supposed to emit isolation crossing errors, go through our
|
||||
// parameters and add the error on any indirect results that are
|
||||
// non-Sendable.
|
||||
if (emitIsolationCrossingResultError) {
|
||||
for (auto result : applySite.getIndirectSILResults()) {
|
||||
if (auto value = tryToTrackValue(result)) {
|
||||
builder.addNonSendableIsolationCrossingResultError(
|
||||
value->getRepresentative().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DestValues>
|
||||
|
||||
@@ -849,26 +849,6 @@ private:
|
||||
|
||||
void initForApply(Operand *op, ApplyExpr *expr);
|
||||
void initForAutoclosure(Operand *op, AutoClosureExpr *expr);
|
||||
|
||||
Expr *getFoundExprForSelf(ApplyExpr *sourceApply) {
|
||||
if (auto callExpr = dyn_cast<CallExpr>(sourceApply))
|
||||
if (auto calledExpr =
|
||||
dyn_cast<DotSyntaxCallExpr>(callExpr->getDirectCallee()))
|
||||
return calledExpr->getBase();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Expr *getFoundExprForParam(ApplyExpr *sourceApply, unsigned argNum) {
|
||||
auto *expr = sourceApply->getArgs()->getExpr(argNum);
|
||||
|
||||
// If we have an erasure expression, lets use the original type. We do
|
||||
// this since we are not saying the specific parameter that is the
|
||||
// issue and we are using the type to explain it to the user.
|
||||
if (auto *erasureExpr = dyn_cast<ErasureExpr>(expr))
|
||||
expr = erasureExpr->getSubExpr();
|
||||
|
||||
return expr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -2284,6 +2264,223 @@ void AssignIsolatedIntoSendingResultDiagnosticEmitter::emit() {
|
||||
type);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MARK: NonSendableIsolationCrossingResult Emitter
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Add Fix-It text for the given nominal type to adopt Sendable.
|
||||
static void addSendableFixIt(const NominalTypeDecl *nominal,
|
||||
InFlightDiagnostic &diag, bool unchecked) {
|
||||
if (nominal->getInherited().empty()) {
|
||||
SourceLoc fixItLoc = nominal->getBraces().Start;
|
||||
diag.fixItInsert(fixItLoc,
|
||||
unchecked ? ": @unchecked Sendable" : ": Sendable");
|
||||
} else {
|
||||
auto fixItLoc = nominal->getInherited().getEndLoc();
|
||||
diag.fixItInsertAfter(fixItLoc,
|
||||
unchecked ? ", @unchecked Sendable" : ", Sendable");
|
||||
}
|
||||
}
|
||||
|
||||
/// Add Fix-It text for the given generic param declaration type to adopt
|
||||
/// Sendable.
|
||||
static void addSendableFixIt(const GenericTypeParamDecl *genericArgument,
|
||||
InFlightDiagnostic &diag, bool unchecked) {
|
||||
if (genericArgument->getInherited().empty()) {
|
||||
auto fixItLoc = genericArgument->getLoc();
|
||||
diag.fixItInsertAfter(fixItLoc,
|
||||
unchecked ? ": @unchecked Sendable" : ": Sendable");
|
||||
} else {
|
||||
auto fixItLoc = genericArgument->getInherited().getEndLoc();
|
||||
diag.fixItInsertAfter(fixItLoc,
|
||||
unchecked ? ", @unchecked Sendable" : ", Sendable");
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct NonSendableIsolationCrossingResultDiagnosticEmitter {
|
||||
RegionAnalysisValueMap &valueMap;
|
||||
|
||||
using Error = PartitionOpError::NonSendableIsolationCrossingResultError;
|
||||
Error error;
|
||||
|
||||
bool emittedErrorDiagnostic = false;
|
||||
|
||||
/// The value assigned as the equivalence class representative. It is
|
||||
/// guaranteed to be from the isolation crossing function since we never treat
|
||||
/// isolation crossing functions as being look through.
|
||||
SILValue representative;
|
||||
|
||||
NonSendableIsolationCrossingResultDiagnosticEmitter(
|
||||
RegionAnalysisValueMap &valueMap, Error error)
|
||||
: valueMap(valueMap), error(error),
|
||||
representative(valueMap.getRepresentative(error.returnValueElement)) {}
|
||||
|
||||
void emit();
|
||||
|
||||
ASTContext &getASTContext() const {
|
||||
return error.op->getSourceInst()->getFunction()->getASTContext();
|
||||
}
|
||||
|
||||
template <typename... T, typename... U>
|
||||
InFlightDiagnostic diagnoseError(SourceLoc loc, Diag<T...> diag,
|
||||
U &&...args) {
|
||||
emittedErrorDiagnostic = true;
|
||||
return std::move(getASTContext()
|
||||
.Diags.diagnose(loc, diag, std::forward<U>(args)...)
|
||||
.warnUntilSwiftVersion(6));
|
||||
}
|
||||
|
||||
template <typename... T, typename... U>
|
||||
InFlightDiagnostic diagnoseError(SILLocation loc, Diag<T...> diag,
|
||||
U &&...args) {
|
||||
return diagnoseError(loc.getSourceLoc(), diag, std::forward<U>(args)...);
|
||||
}
|
||||
|
||||
template <typename... T, typename... U>
|
||||
InFlightDiagnostic diagnoseError(SILInstruction *inst, Diag<T...> diag,
|
||||
U &&...args) {
|
||||
return diagnoseError(inst->getLoc(), diag, std::forward<U>(args)...);
|
||||
}
|
||||
|
||||
template <typename... T, typename... U>
|
||||
InFlightDiagnostic diagnoseNote(SourceLoc loc, Diag<T...> diag, U &&...args) {
|
||||
return getASTContext().Diags.diagnose(loc, diag, std::forward<U>(args)...);
|
||||
}
|
||||
|
||||
template <typename... T, typename... U>
|
||||
InFlightDiagnostic diagnoseNote(SILLocation loc, Diag<T...> diag,
|
||||
U &&...args) {
|
||||
return diagnoseNote(loc.getSourceLoc(), diag, std::forward<U>(args)...);
|
||||
}
|
||||
|
||||
template <typename... T, typename... U>
|
||||
InFlightDiagnostic diagnoseNote(SILInstruction *inst, Diag<T...> diag,
|
||||
U &&...args) {
|
||||
return diagnoseNote(inst->getLoc(), diag, std::forward<U>(args)...);
|
||||
}
|
||||
|
||||
std::optional<DiagnosticBehavior> getBehaviorLimit() const {
|
||||
return representative->getType().getConcurrencyDiagnosticBehavior(
|
||||
representative->getFunction());
|
||||
}
|
||||
|
||||
void emitUnknownPatternError() {
|
||||
if (shouldAbortOnUnknownPatternMatchError()) {
|
||||
llvm::report_fatal_error(
|
||||
"RegionIsolation: Aborting on unknown pattern match error");
|
||||
}
|
||||
|
||||
diagnoseError(error.op->getSourceInst(),
|
||||
diag::regionbasedisolation_unknown_pattern)
|
||||
.limitBehaviorIf(getBehaviorLimit());
|
||||
}
|
||||
|
||||
Type getType() const {
|
||||
if (auto *applyExpr =
|
||||
error.op->getSourceInst()->getLoc().getAsASTNode<ApplyExpr>()) {
|
||||
return applyExpr->getType();
|
||||
}
|
||||
|
||||
// If we do not have an ApplyExpr, see if we can just infer the type from
|
||||
// the SILFunction type. This is only used in SIL test cases.
|
||||
if (auto fas = FullApplySite::isa(error.op->getSourceInst())) {
|
||||
return fas.getSubstCalleeType()
|
||||
->getAllResultsSubstType(fas.getModule(),
|
||||
fas.getFunction()->getTypeExpansionContext())
|
||||
.getASTType();
|
||||
}
|
||||
|
||||
return Type();
|
||||
}
|
||||
|
||||
const ValueDecl *getCalledDecl() const {
|
||||
if (auto *applyExpr =
|
||||
error.op->getSourceInst()->getLoc().getAsASTNode<ApplyExpr>()) {
|
||||
if (auto calledValue =
|
||||
applyExpr->getCalledValue(true /*look through conversions*/)) {
|
||||
return calledValue;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<ApplyIsolationCrossing> getIsolationCrossing() const {
|
||||
if (auto *applyExpr =
|
||||
error.op->getSourceInst()->getLoc().getAsASTNode<ApplyExpr>()) {
|
||||
if (auto isolationCrossing = applyExpr->getIsolationCrossing()) {
|
||||
return *isolationCrossing;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a SIL based test case, just return the actual isolation
|
||||
// crossing.
|
||||
if (auto fas = FullApplySite::isa(error.op->getSourceInst())) {
|
||||
if (auto isolationCrossing = fas.getIsolationCrossing())
|
||||
return *isolationCrossing;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void NonSendableIsolationCrossingResultDiagnosticEmitter::emit() {
|
||||
auto isolationCrossing = getIsolationCrossing();
|
||||
if (!isolationCrossing)
|
||||
return emitUnknownPatternError();
|
||||
|
||||
auto type = getType();
|
||||
if (auto *decl = getCalledDecl()) {
|
||||
diagnoseError(error.op->getSourceInst(), diag::rbi_isolation_crossing_result,
|
||||
type, isolationCrossing->getCalleeIsolation(), getCalledDecl(),
|
||||
isolationCrossing->getCallerIsolation())
|
||||
.limitBehaviorIf(getBehaviorLimit());
|
||||
} else {
|
||||
diagnoseError(error.op->getSourceInst(), diag::rbi_isolation_crossing_result_no_decl,
|
||||
type, isolationCrossing->getCalleeIsolation(),
|
||||
isolationCrossing->getCallerIsolation())
|
||||
.limitBehaviorIf(getBehaviorLimit());
|
||||
}
|
||||
if (type->is<FunctionType>()) {
|
||||
diagnoseNote(error.op->getSourceInst(),
|
||||
diag::rbi_nonsendable_function_type);
|
||||
return;
|
||||
}
|
||||
|
||||
auto *moduleDecl = error.op->getSourceInst()->getModule().getSwiftModule();
|
||||
if (auto *nominal = type->getNominalOrBoundGenericNominal()) {
|
||||
// If the nominal type is in the current module, suggest adding `Sendable`
|
||||
// if it makes sense.
|
||||
if (nominal->getParentModule() == moduleDecl &&
|
||||
(isa<StructDecl>(nominal) || isa<EnumDecl>(nominal))) {
|
||||
auto note = nominal->diagnose(diag::rbi_add_nominal_sendable_conformance,
|
||||
nominal);
|
||||
addSendableFixIt(nominal, note, /*unchecked*/ false);
|
||||
} else {
|
||||
nominal->diagnose(diag::rbi_non_sendable_nominal, nominal);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto genericArchetype = type->getAs<ArchetypeType>()) {
|
||||
auto interfaceType = genericArchetype->getInterfaceType();
|
||||
if (auto genericParamType = interfaceType->getAs<GenericTypeParamType>()) {
|
||||
auto *genericParamTypeDecl = genericParamType->getDecl();
|
||||
if (genericParamTypeDecl &&
|
||||
genericParamTypeDecl->getModuleContext() == moduleDecl) {
|
||||
auto diag = genericParamTypeDecl->diagnose(
|
||||
diag::rbi_add_generic_parameter_sendable_conformance, type);
|
||||
addSendableFixIt(genericParamTypeDecl, diag, /*unchecked=*/false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MARK: Diagnostic Evaluator
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -2370,6 +2567,7 @@ struct DiagnosticEvaluator final
|
||||
case PartitionOpError::InOutSendingNotDisconnectedAtExit:
|
||||
case PartitionOpError::SentNeverSendable:
|
||||
case PartitionOpError::AssignNeverSendableIntoSendingResult:
|
||||
case PartitionOpError::NonSendableIsolationCrossingResult:
|
||||
// We are going to process these later... but dump so we can see that we
|
||||
// handled an error here. The rest of the explicit handlers will dump as
|
||||
// appropriate if they want to emit an error here (some will squelch the
|
||||
@@ -2510,6 +2708,14 @@ void SendNonSendableImpl::emitVerbatimErrors() {
|
||||
diagnosticInferrer.run();
|
||||
continue;
|
||||
}
|
||||
case PartitionOpError::NonSendableIsolationCrossingResult: {
|
||||
auto e = erasedError.getNonSendableIsolationCrossingResultError();
|
||||
REGIONBASEDISOLATION_LOG(e.print(llvm::dbgs(), info->getValueMap()));
|
||||
NonSendableIsolationCrossingResultDiagnosticEmitter diagnosticInferrer(
|
||||
info->getValueMap(), e);
|
||||
diagnosticInferrer.emit();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
llvm_unreachable("Covered switch isn't covered?!");
|
||||
}
|
||||
|
||||
@@ -94,14 +94,22 @@ void PartitionOpError::InOutSendingNotDisconnectedAtExitError::print(
|
||||
os << '\n';
|
||||
}
|
||||
|
||||
void PartitionOpError::NonSendableIsolationCrossingResultError::print(
|
||||
llvm::raw_ostream &os, RegionAnalysisValueMap &valueMap) const {
|
||||
os << " Emitting Error. Kind: NonSendableIsolationCrossingResultError\n"
|
||||
" Inst: "
|
||||
<< *op->getSourceInst() << " Result ID: %%" << returnValueElement
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MARK: PartitionOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const {
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
switch (opKind) {
|
||||
case PartitionOpKind::Assign: {
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
os << "assign ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
@@ -112,7 +120,6 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const {
|
||||
os << "assign_fresh %%" << opArgs[0];
|
||||
break;
|
||||
case PartitionOpKind::Send: {
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
os << "send ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
@@ -120,7 +127,6 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const {
|
||||
break;
|
||||
}
|
||||
case PartitionOpKind::UndoSend: {
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
os << "undo_send ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
@@ -128,7 +134,6 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const {
|
||||
break;
|
||||
}
|
||||
case PartitionOpKind::Merge: {
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
os << "merge ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
@@ -136,7 +141,6 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const {
|
||||
break;
|
||||
}
|
||||
case PartitionOpKind::Require: {
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
os << "require ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
@@ -148,12 +152,17 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const {
|
||||
os << "%%" << opArgs[0];
|
||||
break;
|
||||
case PartitionOpKind::InOutSendingAtFunctionExit:
|
||||
constexpr static char extraSpaceLiteral[10] = " ";
|
||||
os << "inout_sending_at_function_exit ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
os << "%%" << opArgs[0];
|
||||
break;
|
||||
case PartitionOpKind::NonSendableIsolationCrossingResult:
|
||||
os << "nonsendable_isolationcrossing_result ";
|
||||
if (extraSpace)
|
||||
os << extraSpaceLiteral;
|
||||
os << "%%" << opArgs[0];
|
||||
break;
|
||||
}
|
||||
os << ": " << *getSourceInst();
|
||||
}
|
||||
|
||||
@@ -3865,54 +3865,8 @@ namespace {
|
||||
unsatisfiedIsolation, setThrows, usesDistributedThunk);
|
||||
}
|
||||
|
||||
// Sendable checking for arguments is deferred to region isolation.
|
||||
|
||||
// FIXME: Defer sendable checking for result types to region isolation
|
||||
// always.
|
||||
//
|
||||
// Check for sendability of the result type if we do not have a
|
||||
// sending result.
|
||||
if ((!ctx.LangOpts.hasFeature(Feature::RegionBasedIsolation) ||
|
||||
!fnType->hasSendingResult())) {
|
||||
assert(ctx.LangOpts.hasFeature(Feature::SendingArgsAndResults) &&
|
||||
"SendingArgsAndResults should be enabled if RegionIsolation is "
|
||||
"enabled");
|
||||
// See if we are a autoclosure that has a direct callee that has the
|
||||
// same non-transferred type value returned. If so, do not emit an
|
||||
// error... we are going to emit an error on the call expr and do not
|
||||
// want to emit the error twice.
|
||||
auto willDoubleError = [&]() -> bool {
|
||||
auto *autoclosure = dyn_cast<AutoClosureExpr>(apply->getFn());
|
||||
if (!autoclosure)
|
||||
return false;
|
||||
auto *await =
|
||||
dyn_cast<AwaitExpr>(autoclosure->getSingleExpressionBody());
|
||||
if (!await)
|
||||
return false;
|
||||
auto *subCallExpr = dyn_cast<CallExpr>(await->getSubExpr());
|
||||
if (!subCallExpr)
|
||||
return false;
|
||||
return subCallExpr->getType().getPointer() ==
|
||||
fnType->getResult().getPointer();
|
||||
};
|
||||
|
||||
if (!willDoubleError()) {
|
||||
if (calleeDecl) {
|
||||
return diagnoseNonSendableTypes(fnType->getResult(), getDeclContext(),
|
||||
/*inDerivedConformance*/ Type(),
|
||||
apply->getLoc(),
|
||||
diag::non_sendable_result_into_actor,
|
||||
calleeDecl,
|
||||
*unsatisfiedIsolation);
|
||||
}
|
||||
|
||||
return diagnoseNonSendableTypes(fnType->getResult(), getDeclContext(),
|
||||
/*inDerivedConformance*/ Type(),
|
||||
apply->getLoc(),
|
||||
diag::non_sendable_call_result_type,
|
||||
*unsatisfiedIsolation);
|
||||
}
|
||||
}
|
||||
// Sendable checking for arguments and results are deferred to region
|
||||
// isolation.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -298,7 +298,6 @@ class BarFrame: PictureFrame {
|
||||
@available(SwiftStdlib 5.5, *)
|
||||
@SomeGlobalActor
|
||||
class BazFrame: NotIsolatedPictureFrame {
|
||||
// expected-note@-1 2 {{class 'BazFrame' does not conform to the 'Sendable' protocol}}
|
||||
init() {
|
||||
super.init(size: 0)
|
||||
}
|
||||
@@ -322,12 +321,10 @@ func check() async {
|
||||
_ = await BarFrame()
|
||||
_ = await FooFrame()
|
||||
_ = await BazFrame()
|
||||
// expected-warning@-1 {{non-sendable result type 'BazFrame' cannot be sent from global actor 'SomeGlobalActor'-isolated context in call to initializer 'init()'; this is an error in the Swift 6 language mode}}
|
||||
|
||||
_ = await BarFrame(size: 0)
|
||||
_ = await FooFrame(size: 0)
|
||||
_ = await BazFrame(size: 0)
|
||||
// expected-warning@-1 {{non-sendable result type 'BazFrame' cannot be sent from global actor 'SomeGlobalActor'-isolated context in call to initializer 'init(size:)'; this is an error in the Swift 6 language mode}}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 5.5, *)
|
||||
|
||||
@@ -356,25 +356,17 @@ actor Calculator {
|
||||
}
|
||||
|
||||
@OrangeActor func doSomething() async {
|
||||
// We will error on the next line when we get past type checking. But since we
|
||||
// error in the type checker, we do not make further progress.
|
||||
let _ = (await bananaAdd(1))(2)
|
||||
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from global actor 'BananaActor'-isolated context in call to global function 'bananaAdd'}}
|
||||
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
let _ = await (await bananaAdd(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}}
|
||||
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from global actor 'BananaActor'-isolated context in call to global function 'bananaAdd'}}
|
||||
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
|
||||
let calc = Calculator()
|
||||
|
||||
let _ = (await calc.addCurried(1))(2)
|
||||
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from actor-isolated context in call to instance method 'addCurried'}}
|
||||
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
let _ = await (await calc.addCurried(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}}
|
||||
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from actor-isolated context in call to instance method 'addCurried'}}
|
||||
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
|
||||
let plusOne = await calc.addCurried(await calc.add(0, 1))
|
||||
// expected-warning@-1{{non-sendable result type '(Int) -> Int' cannot be sent from actor-isolated context in call to instance method 'addCurried'}}
|
||||
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
let _ = plusOne(2)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// REQUIRES: concurrency
|
||||
// REQUIRES: asserts
|
||||
|
||||
class NotConcurrent { } // expected-note 13{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
|
||||
class NotConcurrent { } // expected-note 12{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
|
||||
// expected-tns-allow-typechecker-note @-1 {{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -102,7 +102,7 @@ extension A1 {
|
||||
_ = other.localLet // expected-warning{{non-sendable type 'NotConcurrent' of property 'localLet' cannot exit actor-isolated context}}
|
||||
// expected-warning@-1 {{expression is 'async' but is not marked with 'await'}}
|
||||
// expected-note@-2 {{property access is 'async'}}
|
||||
_ = await other.synchronous() // expected-warning{{non-sendable result type 'NotConcurrent?' cannot be sent from actor-isolated context in call to instance method 'synchronous()'}}
|
||||
_ = await other.synchronous() // expected-tns-warning {{non-Sendable 'NotConcurrent?'-typed result can not be returned from actor-isolated instance method 'synchronous()' to actor-isolated context}}
|
||||
_ = await other.asynchronous(nil)
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ struct HasSubscript {
|
||||
subscript (i: Int) -> NotConcurrent? { nil }
|
||||
}
|
||||
|
||||
class ClassWithGlobalActorInits { // expected-note 2{{class 'ClassWithGlobalActorInits' does not conform to the 'Sendable' protocol}}
|
||||
class ClassWithGlobalActorInits { // expected-tns-note 2{{class 'ClassWithGlobalActorInits' does not conform to the 'Sendable' protocol}}
|
||||
@SomeGlobalActor
|
||||
init(_: NotConcurrent) { }
|
||||
|
||||
@@ -182,10 +182,10 @@ func globalTestMain(nc: NotConcurrent) async {
|
||||
// expected-tns-note @-1 {{sending global actor 'SomeGlobalActor'-isolated 'a' to global actor 'SomeGlobalActor'-isolated global function 'globalAsync' risks causing data races between global actor 'SomeGlobalActor'-isolated and local main actor-isolated uses}}
|
||||
await globalSync(a) // expected-tns-note {{access can happen concurrently}}
|
||||
_ = await ClassWithGlobalActorInits(nc)
|
||||
// expected-warning @-1 {{non-sendable result type 'ClassWithGlobalActorInits' cannot be sent from global actor 'SomeGlobalActor'-isolated context in call to initializer 'init(_:)'}}
|
||||
// expected-tns-warning @-1 {{non-Sendable 'ClassWithGlobalActorInits'-typed result can not be returned from global actor 'SomeGlobalActor'-isolated initializer 'init(_:)' to main actor-isolated context}}
|
||||
// expected-tns-warning @-2 {{sending 'nc' risks causing data races}}
|
||||
// expected-tns-note @-3 {{sending main actor-isolated 'nc' to global actor 'SomeGlobalActor'-isolated initializer 'init(_:)' risks causing data races between global actor 'SomeGlobalActor'-isolated and main actor-isolated uses}}
|
||||
_ = await ClassWithGlobalActorInits() // expected-warning{{non-sendable result type 'ClassWithGlobalActorInits' cannot be sent from global actor 'SomeGlobalActor'-isolated context in call to initializer 'init()'}}
|
||||
_ = await ClassWithGlobalActorInits() // expected-tns-warning {{non-Sendable 'ClassWithGlobalActorInits'-typed result can not be returned from global actor 'SomeGlobalActor'-isolated initializer 'init()' to main actor-isolated context}}
|
||||
}
|
||||
|
||||
@SomeGlobalActor
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
@available(SwiftStdlib 5.1, *)
|
||||
struct NS1 { }
|
||||
// expected-note @-1 {{consider making struct 'NS1' conform to the 'Sendable' protocol}}
|
||||
|
||||
@available(SwiftStdlib 5.1, *)
|
||||
@available(*, unavailable)
|
||||
@@ -96,7 +95,7 @@ public actor MyActor: MyProto {
|
||||
await nonisolatedAsyncFunc1(ns1)
|
||||
// expected-tns-warning @-1 {{sending 'ns1' risks causing data races}}
|
||||
// expected-tns-note @-2 {{sending 'self'-isolated 'ns1' to nonisolated global function 'nonisolatedAsyncFunc1' risks causing data races between nonisolated and 'self'-isolated uses}}
|
||||
_ = await nonisolatedAsyncFunc2() // expected-warning{{non-sendable result type 'NS1' cannot be sent from nonisolated context in call to global function 'nonisolatedAsyncFunc2()'}}
|
||||
_ = await nonisolatedAsyncFunc2()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -strict-concurrency=complete %S/Inputs/StrictModule.swift
|
||||
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
|
||||
|
||||
// We leave this as just type check since we are checking something that is cross module.
|
||||
// RUN: %target-swift-frontend -typecheck -strict-concurrency=targeted -disable-availability-checking -I %t 2>&1 %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -c -strict-concurrency=complete -disable-availability-checking -I %t 2>&1 %s | %FileCheck %s
|
||||
|
||||
// REQUIRES: concurrency
|
||||
|
||||
@@ -15,9 +14,9 @@ actor A {
|
||||
}
|
||||
|
||||
func testA(a: A) async {
|
||||
_ = await a.f() // CHECK: warning: cannot call function returning non-sendable type '[StrictStruct : NonStrictClass]' across actors}}
|
||||
// CHECK: note: struct 'StrictStruct' does not conform to the 'Sendable' protocol
|
||||
// CHECK: note: class 'NonStrictClass' does not conform to the 'Sendable' protocol
|
||||
_ = await a.f()
|
||||
// CHECK: warning: non-Sendable '[StrictStruct : NonStrictClass]'-typed result can not be returned from actor-isolated instance method 'f()' to nonisolated context; this is an error in the Swift 6 language mode
|
||||
// CHECK: note: note: generic struct 'Dictionary' does not conform to the 'Sendable' protocol
|
||||
}
|
||||
|
||||
extension NonStrictStruct: @unchecked Sendable { }
|
||||
|
||||
@@ -21,7 +21,7 @@ import _Concurrency
|
||||
|
||||
class Klass {}
|
||||
|
||||
class NonSendableKlass {
|
||||
class NonSendableKlass { // expected-note 2{{}}
|
||||
var klass: Klass
|
||||
|
||||
func asyncCall() async
|
||||
@@ -121,10 +121,6 @@ bb0(%0 : $*{ var NonSendableKlass }):
|
||||
return %9999 : $()
|
||||
}
|
||||
|
||||
// This doesn't error since the @out parameter is not transferred when it is initialized.
|
||||
//
|
||||
// DISCUSSION: The frontend prevents us from using such a value. But we
|
||||
// shouldn't crash on such values.
|
||||
sil [ossa] @transfer_does_not_transfer_out_parameters_1 : $@convention(thin) @async () -> () {
|
||||
bb0:
|
||||
%0 = alloc_stack $NonSendableKlass
|
||||
@@ -133,7 +129,7 @@ bb0:
|
||||
|
||||
%1 = alloc_stack $NonSendableKlass
|
||||
%f = function_ref @transferIndirectWithOutResult : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
|
||||
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f<NonSendableKlass>(%1, %0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
|
||||
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f<NonSendableKlass>(%1, %0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 // expected-warning {{}}
|
||||
|
||||
%useIndirect = function_ref @useIndirect : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
apply %useIndirect<NonSendableKlass>(%1) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
@@ -155,7 +151,7 @@ bb0:
|
||||
|
||||
%1 = alloc_stack $NonSendableKlass
|
||||
%f = function_ref @transferIndirectWithOutResult : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
|
||||
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f<NonSendableKlass>(%1, %0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0
|
||||
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f<NonSendableKlass>(%1, %0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 // expected-warning {{}}
|
||||
|
||||
%f2 = function_ref @transferIndirect : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f2<NonSendableKlass>(%1) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
|
||||
|
||||
@@ -18,7 +18,7 @@ class NonSendableKlass {
|
||||
|
||||
class SendableKlass : @unchecked Sendable {}
|
||||
|
||||
struct NonSendableStruct { // expected-note {{}}
|
||||
struct NonSendableStruct {
|
||||
var ns = NonSendableKlass()
|
||||
}
|
||||
|
||||
@@ -722,7 +722,7 @@ func asyncLetWithoutCapture() async {
|
||||
//
|
||||
// NOTE: Error below will go away in next commit.
|
||||
async let x: NonSendableKlass = await returnValueFromMain()
|
||||
// expected-warning @-1 {{non-sendable result type 'NonSendableKlass' cannot be sent from main actor-isolated context in call to global function 'returnValueFromMain()'}}
|
||||
// expected-warning @-1 {{non-Sendable 'NonSendableKlass'-typed result can not be returned from main actor-isolated global function 'returnValueFromMain()' to nonisolated context}}
|
||||
let y = await x
|
||||
await transferToMain(y) // expected-warning {{sending 'y' risks causing data races}}
|
||||
// expected-note @-1 {{sending 'y' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
|
||||
@@ -774,7 +774,6 @@ extension NonSendableStruct {
|
||||
async let subTask6: NonSendableStruct = self
|
||||
// expected-warning @-1 {{sending 'self' risks causing data races}}
|
||||
// expected-note @-2 {{sending 'actor'-isolated 'self' into async let risks causing data races between nonisolated and 'actor'-isolated uses}}
|
||||
// expected-warning @-3 {{non-sendable result type 'NonSendableStruct' cannot be sent from nonisolated context in call to async function}}
|
||||
_ = await subTask6
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
|
||||
class NonSendable {
|
||||
// expected-note@-1 3{{class 'NonSendable' does not conform to the 'Sendable' protocol}}
|
||||
var x = 0
|
||||
}
|
||||
|
||||
@@ -31,10 +30,8 @@ func callActorFuncsFromNonisolated(a : A, ns : NonSendable) async {
|
||||
// Non-sendable value passed from actor isolated to nonisolated
|
||||
|
||||
await a.actorTakesNS(ns)
|
||||
//deferred-warning@-1{{passing argument of non-sendable type 'NonSendable' into actor-isolated context may introduce data races}}
|
||||
|
||||
_ = await a.actorRetsNS()
|
||||
//expected-warning@-1{{non-sendable result type 'NonSendable' cannot be sent from actor-isolated context in call to instance method 'actorRetsNS()'}}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 5.1, *)
|
||||
@@ -52,7 +49,6 @@ actor A {
|
||||
//deferred-warning@-1{{passing argument of non-sendable type 'NonSendable' outside of actor-isolated context may introduce data races}}
|
||||
|
||||
_ = await retsNS()
|
||||
//expected-warning@-1{{non-sendable result type 'NonSendable' cannot be sent from nonisolated context in call to global function 'retsNS()'}}
|
||||
}
|
||||
|
||||
func callActorFuncsFromDiffActor(ns : NonSendable, a : A) async {
|
||||
@@ -62,6 +58,5 @@ actor A {
|
||||
//deferred-warning@-1{{passing argument of non-sendable type 'NonSendable' into actor-isolated context may introduce data races}}
|
||||
|
||||
_ = await a.actorRetsNS()
|
||||
//expected-warning@-1{{non-sendable result type 'NonSendable' cannot be sent from actor-isolated context in call to instance method 'actorRetsNS()'}}
|
||||
}
|
||||
}
|
||||
|
||||
137
test/Concurrency/transfernonsendable_rbi_result.swift
Normal file
137
test/Concurrency/transfernonsendable_rbi_result.swift
Normal file
@@ -0,0 +1,137 @@
|
||||
// RUN: %target-swift-frontend -target %target-swift-5.1-abi-triple -swift-version 6 -parse-as-library %s -emit-sil -o /dev/null -verify
|
||||
|
||||
// REQUIRES: asserts
|
||||
// REQUIRES: concurrency
|
||||
|
||||
///////////////////////
|
||||
// MARK: Declaration //
|
||||
///////////////////////
|
||||
|
||||
actor Custom {
|
||||
}
|
||||
|
||||
@globalActor
|
||||
struct CustomActor {
|
||||
static var shared: Custom {
|
||||
return Custom()
|
||||
}
|
||||
}
|
||||
|
||||
class NonSendable {} // expected-note 3{{}}
|
||||
|
||||
func passNonSendable(_: NonSendable) async { }
|
||||
|
||||
func returnsNonSendable() async -> NonSendable { NonSendable() }
|
||||
|
||||
@MainActor
|
||||
func mainActorPassNonSendable(_: NonSendable) async { }
|
||||
|
||||
@MainActor
|
||||
func mainActorReturnNonSendable() async -> NonSendable { NonSendable() }
|
||||
|
||||
@MainActor
|
||||
func mainActorGenericPassNonSendable<T>(_: T) async { }
|
||||
|
||||
@MainActor
|
||||
func mainActorGenericReturnNonSendable<T>() async -> T { fatalError() }
|
||||
|
||||
@MainActor
|
||||
func mainActorAsyncFunc3() async -> ((Int) -> Int) {
|
||||
return { (_ y: Int) in y }
|
||||
}
|
||||
|
||||
/////////////////
|
||||
// MARK: Tests //
|
||||
/////////////////
|
||||
|
||||
@MainActor func mainActorResult(_ x : Int) -> ((Int) -> Int) {
|
||||
return { (_ y : Int) in x + y }
|
||||
}
|
||||
|
||||
actor Calculator {
|
||||
func addCurried(_ x : Int) -> ((Int) -> Int) {
|
||||
return { (_ y : Int) in x + y }
|
||||
}
|
||||
|
||||
func add(_ x : Int, _ y : Int) -> Int {
|
||||
return x + y
|
||||
}
|
||||
}
|
||||
|
||||
@CustomActor
|
||||
func testActorCrossingBoundary() async {
|
||||
let _ = (await mainActorResult(1))(5)
|
||||
// expected-error @-1 {{non-Sendable '(Int) -> Int'-typed result can not be returned from main actor-isolated global function 'mainActorResult' to global actor 'CustomActor'-isolated context}}
|
||||
// expected-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
let _ = await (await mainActorResult(1))(2)
|
||||
// expected-error @-1 {{non-Sendable '(Int) -> Int'-typed result can not be returned from main actor-isolated global function 'mainActorResult' to global actor 'CustomActor'-isolated context}}
|
||||
// expected-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
// expected-warning @-3 {{no 'async' operations occur within 'await' expression}}
|
||||
|
||||
let calc = Calculator()
|
||||
|
||||
let _ = (await calc.addCurried(1))(2)
|
||||
// expected-error @-1 {{non-Sendable '(Int) -> Int'-typed result can not be returned from actor-isolated instance method 'addCurried' to global actor 'CustomActor'-isolated context}}
|
||||
// expected-note@-2{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
let _ = await (await calc.addCurried(1))(2) // expected-warning{{no 'async' operations occur within 'await' expression}}
|
||||
// expected-error @-1 {{non-Sendable '(Int) -> Int'-typed result can not be returned from actor-isolated instance method 'addCurried' to global actor 'CustomActor'-isolated context}}
|
||||
// expected-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
|
||||
let plusOne = await calc.addCurried(await calc.add(0, 1))
|
||||
// expected-error @-1 {{non-Sendable '(Int) -> Int'-typed result can not be returned from actor-isolated instance method 'addCurried' to global actor 'CustomActor'-isolated context}}
|
||||
// expected-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
|
||||
let _ = plusOne(2)
|
||||
}
|
||||
|
||||
actor A {
|
||||
let actorNS = NonSendable()
|
||||
|
||||
func actorTakesNS(_ : NonSendable) async {}
|
||||
|
||||
func actorRetsNS() async -> NonSendable { NonSendable() }
|
||||
|
||||
func callNonisolatedFuncsFromActor(ns: NonSendable) async {
|
||||
// Non-sendable value passed from nonisolated to actor isolated
|
||||
|
||||
await passNonSendable(ns)
|
||||
// expected-error @-1 {{sending 'ns' risks causing data races}}
|
||||
// expected-note @-2 {{sending 'self'-isolated 'ns' to nonisolated global function 'passNonSendable' risks causing data races between nonisolated and 'self'-isolated uses}}
|
||||
|
||||
_ = await returnsNonSendable()
|
||||
}
|
||||
|
||||
func callActorFuncsFromDiffActor(ns : NonSendable, a : A) async {
|
||||
// Non-sendable value passed between the isolation of two different actors
|
||||
|
||||
await a.actorTakesNS(ns)
|
||||
// expected-error @-1 {{sending 'ns' risks causing data races}}
|
||||
// expected-note @-2 {{sending 'self'-isolated 'ns' to actor-isolated instance method 'actorTakesNS' risks causing data races between actor-isolated and 'self'-isolated uses}}
|
||||
|
||||
_ = await a.actorRetsNS()
|
||||
// expected-error @-1 {{non-Sendable 'NonSendable'-typed result can not be returned from actor-isolated instance method 'actorRetsNS()' to actor-isolated context}}
|
||||
}
|
||||
|
||||
func validateErrorForPassingIsolatedNonSendable(_ ns: NonSendable) async {
|
||||
await mainActorGenericPassNonSendable(ns)
|
||||
// expected-error @-1 {{sending 'ns' risks causing data races}}
|
||||
// expected-note @-2 {{sending 'self'-isolated 'ns' to main actor-isolated global function 'mainActorGenericPassNonSendable' risks causing data races between main actor-isolated and 'self'-isolated uses}}
|
||||
}
|
||||
|
||||
func validateErrorReturningFromNonIsolated() async {
|
||||
let _ = await returnsNonSendable()
|
||||
}
|
||||
}
|
||||
|
||||
func callActorFuncsFromNonisolated(a : A, ns : NonSendable) async {
|
||||
await a.actorTakesNS(ns)
|
||||
// expected-error @-1 {{sending 'ns' risks causing data races}}
|
||||
// expected-note @-2 {{sending task-isolated 'ns' to actor-isolated instance method 'actorTakesNS' risks causing data races between actor-isolated and task-isolated uses}}
|
||||
|
||||
_ = await a.actorRetsNS()
|
||||
// expected-error @-1 {{non-Sendable 'NonSendable'-typed result can not be returned from actor-isolated instance method 'actorRetsNS()' to nonisolated context}}
|
||||
}
|
||||
|
||||
func testGenericResults() async {
|
||||
let _: NonSendable = await mainActorGenericReturnNonSendable()
|
||||
// expected-error @-1 {{non-Sendable 'NonSendable'-typed result can not be returned from main actor-isolated global function 'mainActorGenericReturnNonSendable()' to nonisolated context}}
|
||||
}
|
||||
@@ -238,7 +238,7 @@ func asyncLetReabstractionThunkTest() async {
|
||||
func asyncLetReabstractionThunkTest2() async {
|
||||
// We emit the error here since we are returning a main actor isolated value.
|
||||
async let newValue: NonSendableKlass = await getMainActorValueAsync()
|
||||
// expected-warning @-1 {{non-sendable result type 'NonSendableKlass' cannot be sent from main actor-isolated context in call to global function 'getMainActorValueAsync()'}}
|
||||
// expected-warning @-1 {{non-Sendable 'NonSendableKlass'-typed result can not be returned from main actor-isolated global function 'getMainActorValueAsync()' to nonisolated context}}
|
||||
|
||||
let _ = await newValue
|
||||
|
||||
@@ -261,7 +261,7 @@ func asyncLetReabstractionThunkTest2() async {
|
||||
@MainActor func asyncLetReabstractionThunkTestGlobalActor2() async {
|
||||
// We emit the error here since we are returning a main actor isolated value.
|
||||
async let newValue: NonSendableKlass = await getMainActorValueAsync()
|
||||
// expected-warning @-1 {{non-sendable result type 'NonSendableKlass' cannot be sent from main actor-isolated context in call to global function 'getMainActorValueAsync()'}}
|
||||
// expected-warning @-1 {{non-Sendable 'NonSendableKlass'-typed result can not be returned from main actor-isolated global function 'getMainActorValueAsync()' to nonisolated context}}
|
||||
|
||||
let _ = await newValue
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ enum MaybeFile: ~Copyable { // should implicitly conform
|
||||
case closed
|
||||
}
|
||||
|
||||
struct NotSendableMO: ~Copyable { // expected-note {{consider making struct 'NotSendableMO' conform to the 'Sendable' protocol}}
|
||||
// expected-complete-note @-1 {{consider making struct 'NotSendableMO' conform to the 'Sendable' protocol}}
|
||||
struct NotSendableMO: ~Copyable {
|
||||
var ref: Ref
|
||||
}
|
||||
|
||||
@@ -48,8 +47,8 @@ func processFiles(_ a: A, _ anotherFile: borrowing FileDescriptor) async {
|
||||
await a.takeMaybeFile(.available(anotherFile))
|
||||
_ = A(.available(anotherFile))
|
||||
|
||||
let ns = await a.getRef() // expected-warning {{non-sendable result type 'NotSendableMO' cannot be sent from actor-isolated context in call to instance method 'getRef()'}}
|
||||
await takeNotSendable(ns) // expected-complete-warning {{passing argument of non-sendable type 'NotSendableMO' outside of main actor-isolated context may introduce data races}}
|
||||
let ns = await a.getRef()
|
||||
await takeNotSendable(ns)
|
||||
|
||||
switch (await a.giveFileDescriptor()) {
|
||||
case let .available(fd):
|
||||
|
||||
@@ -97,6 +97,7 @@ struct MockedPartitionOpEvaluatorWithFailureCallback final
|
||||
case PartitionOpError::AssignNeverSendableIntoSendingResult:
|
||||
case PartitionOpError::InOutSendingNotInitializedAtExit:
|
||||
case PartitionOpError::InOutSendingNotDisconnectedAtExit:
|
||||
case PartitionOpError::NonSendableIsolationCrossingResult:
|
||||
llvm_unreachable("Unsupported");
|
||||
case PartitionOpError::LocalUseAfterSend: {
|
||||
auto state = error.getLocalUseAfterSendError();
|
||||
|
||||
Reference in New Issue
Block a user