From cff835e061f6809c1491d5c1c905ac2fcd3483a4 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 24 Nov 2024 12:40:29 -0800 Subject: [PATCH 1/6] [region-isolation] Perform checking of non-Sendable results using rbi rather than Sema. In terms of the test suite the only difference is that we allow for non-Sendable types to be returned from nonisolated functions. This is safe due to the rules of rbi. We do still error when we return non-Sendable functions across isolation boundaries though. The reason that I am doing this now is that I am implementing a prototype that allows for nonisolated functions to inherit isolation from their caller. This would have required me to implement support both in Sema for results and arguments in SIL. Rather than implement results in Sema, I just finished the work of transitioning the result checking out of Sema and into SIL. The actual prototype will land in a subsequent change. rdar://127477211 --- include/swift/AST/DiagnosticsSIL.def | 22 ++ .../SILOptimizer/Utils/PartitionOpError.def | 4 + .../swift/SILOptimizer/Utils/PartitionUtils.h | 39 ++- lib/SILOptimizer/Analysis/RegionAnalysis.cpp | 60 ++++- .../Mandatory/SendNonSendable.cpp | 246 ++++++++++++++++-- lib/SILOptimizer/Utils/PartitionUtils.cpp | 12 + lib/Sema/TypeCheckConcurrency.cpp | 50 +--- 7 files changed, 359 insertions(+), 74 deletions(-) diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 4060a53762f8b..7cc5be8fa11b5 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -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 //===----------------------------------------------------------------------===// diff --git a/include/swift/SILOptimizer/Utils/PartitionOpError.def b/include/swift/SILOptimizer/Utils/PartitionOpError.def index 93cb15dc6bf44..98253626e95ac 100644 --- a/include/swift/SILOptimizer/Utils/PartitionOpError.def +++ b/include/swift/SILOptimizer/Utils/PartitionOpError.def @@ -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 diff --git a/include/swift/SILOptimizer/Utils/PartitionUtils.h b/include/swift/SILOptimizer/Utils/PartitionUtils.h index 8e9e37be789d5..faf8334b5daa8 100644 --- a/include/swift/SILOptimizer/Utils/PartitionUtils.h +++ b/include/swift/SILOptimizer/Utils/PartitionUtils.h @@ -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 @@ class PartitionOp { 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 @@ class PartitionOpError { } }; + 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 " must be copy constructable"); @@ -1482,8 +1512,15 @@ struct PartitionOpEvaluator { // 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?!"); } diff --git a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp index d2cd28a7ced68..df35e0761caa5 100644 --- a/lib/SILOptimizer/Analysis/RegionAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/RegionAnalysis.cpp @@ -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 @@ class PartitionOpTranslator { 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 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(); + + // 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 diff --git a/lib/SILOptimizer/Mandatory/SendNonSendable.cpp b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp index 96a869d786eea..9f8c0b769ad4c 100644 --- a/lib/SILOptimizer/Mandatory/SendNonSendable.cpp +++ b/lib/SILOptimizer/Mandatory/SendNonSendable.cpp @@ -849,26 +849,6 @@ class UseAfterSendDiagnosticInferrer { void initForApply(Operand *op, ApplyExpr *expr); void initForAutoclosure(Operand *op, AutoClosureExpr *expr); - - Expr *getFoundExprForSelf(ApplyExpr *sourceApply) { - if (auto callExpr = dyn_cast(sourceApply)) - if (auto calledExpr = - dyn_cast(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(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 + InFlightDiagnostic diagnoseError(SourceLoc loc, Diag diag, + U &&...args) { + emittedErrorDiagnostic = true; + return std::move(getASTContext() + .Diags.diagnose(loc, diag, std::forward(args)...) + .warnUntilSwiftVersion(6)); + } + + template + InFlightDiagnostic diagnoseError(SILLocation loc, Diag diag, + U &&...args) { + return diagnoseError(loc.getSourceLoc(), diag, std::forward(args)...); + } + + template + InFlightDiagnostic diagnoseError(SILInstruction *inst, Diag diag, + U &&...args) { + return diagnoseError(inst->getLoc(), diag, std::forward(args)...); + } + + template + InFlightDiagnostic diagnoseNote(SourceLoc loc, Diag diag, U &&...args) { + return getASTContext().Diags.diagnose(loc, diag, std::forward(args)...); + } + + template + InFlightDiagnostic diagnoseNote(SILLocation loc, Diag diag, + U &&...args) { + return diagnoseNote(loc.getSourceLoc(), diag, std::forward(args)...); + } + + template + InFlightDiagnostic diagnoseNote(SILInstruction *inst, Diag diag, + U &&...args) { + return diagnoseNote(inst->getLoc(), diag, std::forward(args)...); + } + + std::optional 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()) { + 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()) { + if (auto calledValue = + applyExpr->getCalledValue(true /*look through conversions*/)) { + return calledValue; + } + } + + return nullptr; + } + + std::optional getIsolationCrossing() const { + if (auto *applyExpr = + error.op->getSourceInst()->getLoc().getAsASTNode()) { + 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()) { + 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(nominal) || isa(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()) { + auto interfaceType = genericArchetype->getInterfaceType(); + if (auto genericParamType = interfaceType->getAs()) { + 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?!"); } diff --git a/lib/SILOptimizer/Utils/PartitionUtils.cpp b/lib/SILOptimizer/Utils/PartitionUtils.cpp index f5501cdfcde5a..6ef81a9b98006 100644 --- a/lib/SILOptimizer/Utils/PartitionUtils.cpp +++ b/lib/SILOptimizer/Utils/PartitionUtils.cpp @@ -94,6 +94,14 @@ 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 //===----------------------------------------------------------------------===// @@ -154,6 +162,10 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const { os << extraSpaceLiteral; os << "%%" << opArgs[0]; break; + case PartitionOpKind::NonSendableIsolationCrossingResult: + os << "nonsendable_isolationcrossing_result "; + os << "%%" << opArgs[0]; + break; } os << ": " << *getSourceInst(); } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 684c4577a9d9f..f0a02939949df 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -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(apply->getFn()); - if (!autoclosure) - return false; - auto *await = - dyn_cast(autoclosure->getSingleExpressionBody()); - if (!await) - return false; - auto *subCallExpr = dyn_cast(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; } From c48a9cc72fb08c0af5ef561b7ed8dd00d3492a62 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 26 Nov 2024 14:58:41 -0800 Subject: [PATCH 2/6] test updates --- .../actor_call_implicitly_async.swift | 12 +- .../concurrent_value_checking.swift | 10 +- test/Concurrency/sendable_checking.swift | 3 +- .../sendable_module_checking.swift | 9 +- test/Concurrency/transfernonsendable.sil | 10 +- .../transfernonsendable_asynclet.swift | 5 +- ...nonsendable_defer_and_typecheck_only.swift | 5 - .../transfernonsendable_rbi_result.swift | 137 ++++++++++++++++++ .../transfernonsendable_sending_results.swift | 4 +- 9 files changed, 156 insertions(+), 39 deletions(-) create mode 100644 test/Concurrency/transfernonsendable_rbi_result.swift diff --git a/test/Concurrency/actor_call_implicitly_async.swift b/test/Concurrency/actor_call_implicitly_async.swift index 553064f61a1eb..6c79a12a0e994 100644 --- a/test/Concurrency/actor_call_implicitly_async.swift +++ b/test/Concurrency/actor_call_implicitly_async.swift @@ -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) } diff --git a/test/Concurrency/concurrent_value_checking.swift b/test/Concurrency/concurrent_value_checking.swift index b5974050eac5a..95614ea3c9d3d 100644 --- a/test/Concurrency/concurrent_value_checking.swift +++ b/test/Concurrency/concurrent_value_checking.swift @@ -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 diff --git a/test/Concurrency/sendable_checking.swift b/test/Concurrency/sendable_checking.swift index bfb8fd4b5ecf5..4b0e03c170409 100644 --- a/test/Concurrency/sendable_checking.swift +++ b/test/Concurrency/sendable_checking.swift @@ -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() } } diff --git a/test/Concurrency/sendable_module_checking.swift b/test/Concurrency/sendable_module_checking.swift index 6da3f9ca29dbd..47cbb27b008ce 100644 --- a/test/Concurrency/sendable_module_checking.swift +++ b/test/Concurrency/sendable_module_checking.swift @@ -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 { } diff --git a/test/Concurrency/transfernonsendable.sil b/test/Concurrency/transfernonsendable.sil index 4ea454526ada7..292c01744c120 100644 --- a/test/Concurrency/transfernonsendable.sil +++ b/test/Concurrency/transfernonsendable.sil @@ -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(%1, %0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 + apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f(%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(%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(%1, %0) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> @out τ_0_0 + apply [caller_isolation=nonisolated] [callee_isolation=global_actor] %f(%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(%1) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> () diff --git a/test/Concurrency/transfernonsendable_asynclet.swift b/test/Concurrency/transfernonsendable_asynclet.swift index 238dc72761e3f..350353c05398b 100644 --- a/test/Concurrency/transfernonsendable_asynclet.swift +++ b/test/Concurrency/transfernonsendable_asynclet.swift @@ -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 } } diff --git a/test/Concurrency/transfernonsendable_defer_and_typecheck_only.swift b/test/Concurrency/transfernonsendable_defer_and_typecheck_only.swift index 4ba64c8ebe487..c6e2e372f1c5b 100644 --- a/test/Concurrency/transfernonsendable_defer_and_typecheck_only.swift +++ b/test/Concurrency/transfernonsendable_defer_and_typecheck_only.swift @@ -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()'}} } } diff --git a/test/Concurrency/transfernonsendable_rbi_result.swift b/test/Concurrency/transfernonsendable_rbi_result.swift new file mode 100644 index 0000000000000..ea730c83b2d92 --- /dev/null +++ b/test/Concurrency/transfernonsendable_rbi_result.swift @@ -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) async { } + +@MainActor +func mainActorGenericReturnNonSendable() 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}} +} diff --git a/test/Concurrency/transfernonsendable_sending_results.swift b/test/Concurrency/transfernonsendable_sending_results.swift index 72fdd4adfa398..a3615b29546fe 100644 --- a/test/Concurrency/transfernonsendable_sending_results.swift +++ b/test/Concurrency/transfernonsendable_sending_results.swift @@ -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 From 72acb3d4643e2cbe9a472fe57756ffb8c8acad73 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 2 Dec 2024 14:30:34 -0500 Subject: [PATCH 3/6] Update unit test --- unittests/SILOptimizer/PartitionUtilsTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/unittests/SILOptimizer/PartitionUtilsTest.cpp b/unittests/SILOptimizer/PartitionUtilsTest.cpp index 2d4ab2bfe69ec..9247017ef66d7 100644 --- a/unittests/SILOptimizer/PartitionUtilsTest.cpp +++ b/unittests/SILOptimizer/PartitionUtilsTest.cpp @@ -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(); From 3b8e384218598b17ad86c7c671793fc58dbc5d22 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 2 Dec 2024 16:52:14 -0500 Subject: [PATCH 4/6] Fix another test. Importantly since this test had a typechecker error within it, we do not get return value diagnostics anymore since that occurs now at SIL time. --- test/Sema/moveonly_sendable.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/Sema/moveonly_sendable.swift b/test/Sema/moveonly_sendable.swift index 8cba965343a81..0ea0334be56c9 100644 --- a/test/Sema/moveonly_sendable.swift +++ b/test/Sema/moveonly_sendable.swift @@ -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): From 784d1932dbb409daadbc4f9c7c1bac16490b6ac8 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 3 Dec 2024 14:25:37 -0800 Subject: [PATCH 5/6] Fix another test. I tried to port this behavior to another test that validated the behavior using RBI. When I tried to do so I ran into the issue that the behavior in this test only reproduces in targeted mode not in complete mode which is required for rbi to run. --- test/ClangImporter/objc_async.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 35368883d294a..a02117c3ae2e4 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -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, *) From d9f5ef8d0355f5ebff29c55c3264a6d98a219343 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 2 Dec 2024 19:04:15 -0500 Subject: [PATCH 6/6] Fix small compilation from MSVC. --- lib/SILOptimizer/Utils/PartitionUtils.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/Utils/PartitionUtils.cpp b/lib/SILOptimizer/Utils/PartitionUtils.cpp index 6ef81a9b98006..d0dd74a1e2759 100644 --- a/lib/SILOptimizer/Utils/PartitionUtils.cpp +++ b/lib/SILOptimizer/Utils/PartitionUtils.cpp @@ -107,9 +107,9 @@ void PartitionOpError::NonSendableIsolationCrossingResultError::print( //===----------------------------------------------------------------------===// 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; @@ -120,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; @@ -128,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; @@ -136,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; @@ -144,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; @@ -156,7 +152,6 @@ 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; @@ -164,6 +159,8 @@ void PartitionOp::print(llvm::raw_ostream &os, bool extraSpace) const { break; case PartitionOpKind::NonSendableIsolationCrossingResult: os << "nonsendable_isolationcrossing_result "; + if (extraSpace) + os << extraSpaceLiteral; os << "%%" << opArgs[0]; break; }