Skip to content

Commit b68f855

Browse files
committed
[Concurrency] Introduce "unsafe" @sendable and @mainactor parameters.
Introduce the notion of "unsafe" @sendable parameters, indicated by the hidden @_unsafeSendable parameter attribute. Closure arguments to such parameters are treated as @sendable within code that has already adopted concurrency, but are otherwise enert, allowing them to be applied to existing concurrency-related APIs to smooth the transition path to concurrency. Additionally, introduce the notion of an "unsafe" @mainactor closure, for cases where we have determined that the closure will execute on the main actor but it (also) isn't part of the type system. Pattern-match uses of the Dispatch library's DispatchQueue to infer both kinds of "unsafe" as appropriate, especially (e.g.) matching the pattern DispatchQueue.main.async { ... } to treat the closure as unsafe @sendable and @mainactor, allowing such existing code to better integrate with concurrency. Implements rdar://75988966.
1 parent e0bd269 commit b68f855

16 files changed

+224
-41
lines changed

include/swift/AST/Attr.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,10 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(nonisolated, Nonisolated,
637637
APIBreakingToAdd | APIStableToRemove,
638638
112)
639639

640+
CONTEXTUAL_SIMPLE_DECL_ATTR(_unsafeSendable, UnsafeSendable,
641+
OnParam | UserInaccessible |
642+
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
643+
113)
640644

641645
#undef TYPE_ATTR
642646
#undef DECL_ATTR_ALIAS

include/swift/AST/Decl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6097,8 +6097,17 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
60976097
/// constructor.
60986098
bool hasDynamicSelfResult() const;
60996099

6100+
61006101
AbstractFunctionDecl *getAsyncAlternative() const;
61016102

6103+
/// Determine whether this function is implicitly known to have its
6104+
/// parameters of function type be @_unsafeSendable.
6105+
///
6106+
/// This hard-codes knowledge of a number of functions that will
6107+
/// eventually have @_unsafeSendable and, eventually, @Sendable,
6108+
/// on their parameters of function type.
6109+
bool hasKnownUnsafeSendableFunctionParams() const;
6110+
61026111
using DeclContext::operator new;
61036112
using Decl::getASTContext;
61046113
};

include/swift/AST/Expr.h

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3811,6 +3811,12 @@ class ClosureExpr : public AbstractClosureExpr {
38113811
SeparatelyTypeChecked,
38123812
};
38133813

3814+
/// Bits used to indicate contextual information that is concurrency-specific.
3815+
enum UnsafeConcurrencyBits {
3816+
Sendable = 1 << 0,
3817+
MainActor = 1 << 1
3818+
};
3819+
38143820
private:
38153821
/// The attributes attached to the closure.
38163822
DeclAttributes Attributes;
@@ -3825,7 +3831,10 @@ class ClosureExpr : public AbstractClosureExpr {
38253831
/// the CaptureListExpr which would normally maintain this sort of
38263832
/// information about captured variables), we need to have some way to access
38273833
/// this information directly on the ClosureExpr.
3828-
VarDecl * CapturedSelfDecl;
3834+
///
3835+
/// The integer indicates how the closure is contextually concurrent.
3836+
llvm::PointerIntPair<VarDecl *, 2, uint8_t>
3837+
CapturedSelfDeclAndUnsafeConcurrent;
38293838

38303839
/// The location of the "async", if present.
38313840
SourceLoc AsyncLoc;
@@ -3855,7 +3864,7 @@ class ClosureExpr : public AbstractClosureExpr {
38553864
: AbstractClosureExpr(ExprKind::Closure, Type(), /*Implicit=*/false,
38563865
discriminator, parent),
38573866
Attributes(attributes), BracketRange(bracketRange),
3858-
CapturedSelfDecl(capturedSelfDecl),
3867+
CapturedSelfDeclAndUnsafeConcurrent(capturedSelfDecl, 0),
38593868
AsyncLoc(asyncLoc), ThrowsLoc(throwsLoc), ArrowLoc(arrowLoc),
38603869
InLoc(inLoc),
38613870
ExplicitResultTypeAndBodyState(explicitResultType, BodyState::Parsed),
@@ -3961,7 +3970,27 @@ class ClosureExpr : public AbstractClosureExpr {
39613970

39623971
/// VarDecl captured by this closure under the literal name \c self , if any.
39633972
VarDecl *getCapturedSelfDecl() const {
3964-
return CapturedSelfDecl;
3973+
return CapturedSelfDeclAndUnsafeConcurrent.getPointer();
3974+
}
3975+
3976+
bool isUnsafeSendable() const {
3977+
return CapturedSelfDeclAndUnsafeConcurrent.getInt() &
3978+
UnsafeConcurrencyBits::Sendable;
3979+
}
3980+
3981+
bool isUnsafeMainActor() const {
3982+
return CapturedSelfDeclAndUnsafeConcurrent.getInt() &
3983+
UnsafeConcurrencyBits::MainActor;
3984+
}
3985+
3986+
void setContextuallyConcurrent(bool sendable, bool forMainActor) {
3987+
uint8_t bits = 0;
3988+
if (sendable)
3989+
bits |= UnsafeConcurrencyBits::Sendable;
3990+
if (forMainActor)
3991+
bits |= UnsafeConcurrencyBits::MainActor;
3992+
3993+
CapturedSelfDeclAndUnsafeConcurrent.setInt(bits);
39653994
}
39663995

39673996
/// Get the type checking state of this closure's body.

include/swift/AST/KnownSDKTypes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
3838
// TODO(async): These might move to the stdlib module when concurrency is
3939
// standardized
4040
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
41+
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
4142

4243
#undef KNOWN_SDK_TYPE_DECL

include/swift/AST/Types.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3343,6 +3343,7 @@ struct ParameterListInfo {
33433343
SmallBitVector defaultArguments;
33443344
SmallBitVector acceptsUnlabeledTrailingClosures;
33453345
SmallBitVector propertyWrappers;
3346+
SmallBitVector unsafeSendable;
33463347

33473348
public:
33483349
ParameterListInfo() { }
@@ -3361,6 +3362,11 @@ struct ParameterListInfo {
33613362
/// property wrapper.
33623363
bool hasExternalPropertyWrapper(unsigned paramIdx) const;
33633364

3365+
/// Whether the given parameter is unsafe Sendable, meaning that
3366+
/// we will treat it as Sendable in a context that has adopted concurrency
3367+
/// features.
3368+
bool isUnsafeSendable(unsigned paramIdx) const;
3369+
33643370
/// Retrieve the number of non-defaulted parameters.
33653371
unsigned numNonDefaultedParameters() const {
33663372
return defaultArguments.count();

lib/AST/Decl.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7331,6 +7331,27 @@ void AbstractFunctionDecl::addDerivativeFunctionConfiguration(
73317331
DerivativeFunctionConfigs->insert(config);
73327332
}
73337333

7334+
bool AbstractFunctionDecl::hasKnownUnsafeSendableFunctionParams() const {
7335+
auto nominal = getDeclContext()->getSelfNominalTypeDecl();
7336+
if (!nominal)
7337+
return false;
7338+
7339+
// DispatchQueue operations.
7340+
auto nominalName = nominal->getName().str();
7341+
if (nominalName == "DispatchQueue") {
7342+
auto name = getBaseName().userFacingName();
7343+
return llvm::StringSwitch<bool>(name)
7344+
.Case("sync", true)
7345+
.Case("async", true)
7346+
.Case("asyncAndWait", true)
7347+
.Case("asyncAfter", true)
7348+
.Case("concurrentPerform", true)
7349+
.Default(false);
7350+
}
7351+
7352+
return false;
7353+
}
7354+
73347355
void FuncDecl::setResultInterfaceType(Type type) {
73357356
getASTContext().evaluator.cacheOutput(ResultTypeRequest{this},
73367357
std::move(type));

lib/AST/Type.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,12 +913,35 @@ static bool allowsUnlabeledTrailingClosureParameter(const ParamDecl *param) {
913913
return paramType->is<AnyFunctionType>();
914914
}
915915

916+
/// Determine whether the parameter is contextually Sendable.
917+
static bool isParamUnsafeSendable(const ParamDecl *param) {
918+
// Check for @_unsafeSendable.
919+
if (param->getAttrs().hasAttribute<UnsafeSendableAttr>())
920+
return true;
921+
922+
// Check that the parameter is of function type.
923+
Type paramType = param->isVariadic() ? param->getVarargBaseTy()
924+
: param->getInterfaceType();
925+
paramType = paramType->getRValueType()->lookThroughAllOptionalTypes();
926+
if (!paramType->is<FunctionType>())
927+
return false;
928+
929+
// Check whether this function is known to have @_unsafeSendable function
930+
// parameters.
931+
auto func = dyn_cast<AbstractFunctionDecl>(param->getDeclContext());
932+
if (!func)
933+
return false;
934+
935+
return func->hasKnownUnsafeSendableFunctionParams();
936+
}
937+
916938
ParameterListInfo::ParameterListInfo(
917939
ArrayRef<AnyFunctionType::Param> params,
918940
const ValueDecl *paramOwner,
919941
bool skipCurriedSelf) {
920942
defaultArguments.resize(params.size());
921943
propertyWrappers.resize(params.size());
944+
unsafeSendable.resize(params.size());
922945

923946
// No parameter owner means no parameter list means no default arguments
924947
// - hand back the zeroed bitvector.
@@ -970,6 +993,10 @@ ParameterListInfo::ParameterListInfo(
970993
if (param->hasAttachedPropertyWrapper()) {
971994
propertyWrappers.set(i);
972995
}
996+
997+
if (isParamUnsafeSendable(param)) {
998+
unsafeSendable.set(i);
999+
}
9731000
}
9741001
}
9751002

@@ -988,6 +1015,12 @@ bool ParameterListInfo::hasExternalPropertyWrapper(unsigned paramIdx) const {
9881015
return paramIdx < propertyWrappers.size() ? propertyWrappers[paramIdx] : false;
9891016
}
9901017

1018+
bool ParameterListInfo::isUnsafeSendable(unsigned paramIdx) const {
1019+
return paramIdx < unsafeSendable.size()
1020+
? unsafeSendable[paramIdx]
1021+
: false;
1022+
}
1023+
9911024
/// Turn a param list into a symbolic and printable representation that does not
9921025
/// include the types, something like (_:, b:, c:)
9931026
std::string swift::getParamListAsString(ArrayRef<AnyFunctionType::Param> params) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8289,35 +8289,6 @@ bool importer::isImportedAsStatic(const clang::OverloadedOperatorKind op) {
82898289
}
82908290
}
82918291

8292-
Type ClangImporter::Implementation::getMainActorType() {
8293-
if (MainActorType)
8294-
return *MainActorType;
8295-
8296-
auto finish = [&](Type type) -> Type {
8297-
MainActorType = type;
8298-
return type;
8299-
};
8300-
8301-
if (!SwiftContext.LangOpts.EnableExperimentalConcurrency) {
8302-
return finish(Type());
8303-
}
8304-
8305-
auto module = SwiftContext.getLoadedModule(SwiftContext.Id_Concurrency);
8306-
if (!module)
8307-
return finish(Type());
8308-
8309-
SmallVector<ValueDecl *, 1> decls;
8310-
module->lookupValue(
8311-
SwiftContext.getIdentifier("MainActor"),
8312-
NLKind::QualifiedLookup, decls);
8313-
for (auto decl : decls) {
8314-
if (auto typeDecl = dyn_cast<TypeDecl>(decl))
8315-
return finish(typeDecl->getDeclaredInterfaceType());
8316-
}
8317-
8318-
return finish(Type());
8319-
}
8320-
83218292
unsigned ClangImporter::Implementation::getClangSwiftAttrSourceBuffer(
83228293
StringRef attributeText) {
83238294
auto known = ClangSwiftAttrSourceBuffers.find(attributeText);
@@ -8529,7 +8500,7 @@ void ClangImporter::Implementation::importAttributes(
85298500
// point at which to do name lookup for imported entities.
85308501
if (auto isMainActor = isMainActorAttr(SwiftContext, swiftAttr)) {
85318502
bool isUnsafe = *isMainActor;
8532-
if (Type mainActorType = getMainActorType()) {
8503+
if (Type mainActorType = SwiftContext.getMainActorType()) {
85338504
auto typeExpr = TypeExpr::createImplicit(mainActorType, SwiftContext);
85348505
auto attr = CustomAttr::create(SwiftContext, SourceLoc(), typeExpr);
85358506
attr->setArgIsUnsafe(isUnsafe);

lib/ClangImporter/ImportType.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1731,7 +1731,7 @@ Type ClangImporter::Implementation::applyParamAttributes(
17311731

17321732
// Map the main-actor attribute.
17331733
if (isMainActorAttr(SwiftContext, swiftAttr)) {
1734-
if (Type mainActor = getMainActorType()) {
1734+
if (Type mainActor = SwiftContext.getMainActorType()) {
17351735
type = applyToFunctionType(type, [&](ASTExtInfo extInfo) {
17361736
return extInfo.withGlobalActor(mainActor);
17371737
});

lib/ClangImporter/ImporterImpl.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
405405
/// Clang arguments used to create the Clang invocation.
406406
std::vector<std::string> ClangArgs;
407407

408-
/// The main actor type, populated the first time we look for it.
409-
Optional<Type> MainActorType;
410-
411408
/// Mapping from Clang swift_attr attribute text to the Swift source buffer
412409
/// IDs that contain that attribute text. These are re-used when parsing the
413410
/// Swift attributes on import.
@@ -818,9 +815,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
818815
/// Map a Clang identifier name to its imported Swift equivalent.
819816
StringRef getSwiftNameFromClangName(StringRef name);
820817

821-
/// Look for the MainActor type in the _Concurrency library.
822-
Type getMainActorType();
823-
824818
/// Retrieve the Swift source buffer ID that corresponds to the given
825819
/// swift_attr attribute text, creating one if necessary.
826820
unsigned getClangSwiftAttrSourceBuffer(StringRef attributeText);

0 commit comments

Comments
 (0)