mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Diagnose captured of non-sendable metatypes crossing isolation boundaries
Keep track of all of the type parameters and archetypes that are captured by a local function or closure. Use that information to diagnose cases where a non-Sendable metatype crosses an isolation boundary.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include "swift/Basic/LLVM.h"
|
||||
#include "swift/Basic/OptionSet.h"
|
||||
#include "swift/Basic/SourceLoc.h"
|
||||
#include "swift/AST/Type.h"
|
||||
#include "swift/AST/TypeAlignments.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
@@ -129,6 +130,18 @@ public:
|
||||
unsigned getFlags() const { return Value.getInt(); }
|
||||
};
|
||||
|
||||
/// Describes a type that has been captured by a closure or local function.
|
||||
class CapturedType {
|
||||
Type type;
|
||||
SourceLoc loc;
|
||||
|
||||
public:
|
||||
CapturedType(Type type, SourceLoc loc) : type(type), loc(loc) { }
|
||||
|
||||
Type getType() const { return type; }
|
||||
SourceLoc getLoc() const { return loc; }
|
||||
};
|
||||
|
||||
} // end swift namespace
|
||||
|
||||
namespace swift {
|
||||
@@ -140,26 +153,32 @@ class CaptureInfo {
|
||||
class CaptureInfoStorage final
|
||||
: public llvm::TrailingObjects<CaptureInfoStorage,
|
||||
CapturedValue,
|
||||
GenericEnvironment *> {
|
||||
GenericEnvironment *,
|
||||
CapturedType> {
|
||||
|
||||
DynamicSelfType *DynamicSelf;
|
||||
OpaqueValueExpr *OpaqueValue;
|
||||
unsigned NumCapturedValues;
|
||||
unsigned NumGenericEnvironments;
|
||||
unsigned NumCapturedTypes;
|
||||
|
||||
public:
|
||||
explicit CaptureInfoStorage(DynamicSelfType *dynamicSelf,
|
||||
OpaqueValueExpr *opaqueValue,
|
||||
unsigned numCapturedValues,
|
||||
unsigned numGenericEnvironments)
|
||||
unsigned numGenericEnvironments,
|
||||
unsigned numCapturedTypes)
|
||||
: DynamicSelf(dynamicSelf), OpaqueValue(opaqueValue),
|
||||
NumCapturedValues(numCapturedValues),
|
||||
NumGenericEnvironments(numGenericEnvironments) { }
|
||||
NumGenericEnvironments(numGenericEnvironments),
|
||||
NumCapturedTypes(numCapturedTypes) { }
|
||||
|
||||
ArrayRef<CapturedValue> getCaptures() const;
|
||||
|
||||
ArrayRef<GenericEnvironment *> getGenericEnvironments() const;
|
||||
|
||||
ArrayRef<CapturedType> getCapturedTypes() const;
|
||||
|
||||
DynamicSelfType *getDynamicSelfType() const {
|
||||
return DynamicSelf;
|
||||
}
|
||||
@@ -171,6 +190,14 @@ class CaptureInfo {
|
||||
unsigned numTrailingObjects(OverloadToken<CapturedValue>) const {
|
||||
return NumCapturedValues;
|
||||
}
|
||||
|
||||
unsigned numTrailingObjects(OverloadToken<GenericEnvironment *>) const {
|
||||
return NumGenericEnvironments;
|
||||
}
|
||||
|
||||
unsigned numTrailingObjects(OverloadToken<CapturedType>) const {
|
||||
return NumCapturedTypes;
|
||||
}
|
||||
};
|
||||
|
||||
enum class Flags : unsigned {
|
||||
@@ -187,7 +214,8 @@ public:
|
||||
ArrayRef<CapturedValue> captures,
|
||||
DynamicSelfType *dynamicSelf, OpaqueValueExpr *opaqueValue,
|
||||
bool genericParamCaptures,
|
||||
ArrayRef<GenericEnvironment *> genericEnv=ArrayRef<GenericEnvironment*>());
|
||||
ArrayRef<GenericEnvironment *> genericEnv=ArrayRef<GenericEnvironment*>(),
|
||||
ArrayRef<CapturedType> capturedTypes = ArrayRef<CapturedType>());
|
||||
|
||||
/// A CaptureInfo representing no captures at all.
|
||||
static CaptureInfo empty();
|
||||
@@ -196,11 +224,7 @@ public:
|
||||
return StorageAndFlags.getPointer();
|
||||
}
|
||||
|
||||
bool isTrivial() const {
|
||||
assert(hasBeenComputed());
|
||||
return getCaptures().empty() && !hasGenericParamCaptures() &&
|
||||
!hasDynamicSelfCapture() && !hasOpaqueValueCapture();
|
||||
}
|
||||
bool isTrivial() const;
|
||||
|
||||
/// Returns all captured values and opaque expressions.
|
||||
ArrayRef<CapturedValue> getCaptures() const {
|
||||
@@ -214,6 +238,12 @@ public:
|
||||
return StorageAndFlags.getPointer()->getGenericEnvironments();
|
||||
}
|
||||
|
||||
/// Returns all captured values and opaque expressions.
|
||||
ArrayRef<CapturedType> getCapturedTypes() const {
|
||||
assert(hasBeenComputed());
|
||||
return StorageAndFlags.getPointer()->getCapturedTypes();
|
||||
}
|
||||
|
||||
/// \returns true if the function captures the primary generic environment
|
||||
/// from its innermost declaration context.
|
||||
bool hasGenericParamCaptures() const {
|
||||
|
||||
@@ -5607,6 +5607,10 @@ ERROR(non_sendable_isolated_capture,none,
|
||||
"capture of %1 with non-sendable type %0 in an isolated "
|
||||
"%select{local function|closure}2",
|
||||
(Type, DeclName, bool))
|
||||
ERROR(non_sendable_metatype_capture,none,
|
||||
"capture of non-sendable type %0 in an isolated "
|
||||
"%select{local function|closure}1",
|
||||
(Type, bool))
|
||||
ERROR(self_capture_deinit_task,none,
|
||||
"capture of 'self' in a closure that outlives deinit",
|
||||
())
|
||||
|
||||
@@ -54,6 +54,11 @@ CaptureInfo::CaptureInfoStorage::getGenericEnvironments() const {
|
||||
return llvm::ArrayRef(this->getTrailingObjects<GenericEnvironment *>(), NumGenericEnvironments);
|
||||
}
|
||||
|
||||
ArrayRef<CapturedType>
|
||||
CaptureInfo::CaptureInfoStorage::getCapturedTypes() const {
|
||||
return llvm::ArrayRef(this->getTrailingObjects<CapturedType>(), NumCapturedTypes);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MARK: CaptureInfo
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -62,7 +67,8 @@ CaptureInfo::CaptureInfo(ASTContext &ctx, ArrayRef<CapturedValue> captures,
|
||||
DynamicSelfType *dynamicSelf,
|
||||
OpaqueValueExpr *opaqueValue,
|
||||
bool genericParamCaptures,
|
||||
ArrayRef<GenericEnvironment *> genericEnv) {
|
||||
ArrayRef<GenericEnvironment *> genericEnv,
|
||||
ArrayRef<CapturedType> capturedTypes) {
|
||||
static_assert(IsTriviallyDestructible<CapturedValue>::value,
|
||||
"Capture info is alloc'd on the ASTContext and not destroyed");
|
||||
static_assert(IsTriviallyDestructible<CaptureInfo::CaptureInfoStorage>::value,
|
||||
@@ -79,7 +85,8 @@ CaptureInfo::CaptureInfo(ASTContext &ctx, ArrayRef<CapturedValue> captures,
|
||||
if (genericParamCaptures)
|
||||
flags |= Flags::HasGenericParamCaptures;
|
||||
|
||||
if (captures.empty() && genericEnv.empty() && !dynamicSelf && !opaqueValue) {
|
||||
if (captures.empty() && genericEnv.empty() && !dynamicSelf && !opaqueValue &&
|
||||
capturedTypes.empty()) {
|
||||
*this = CaptureInfo::empty();
|
||||
StorageAndFlags.setInt(flags);
|
||||
return;
|
||||
@@ -87,29 +94,40 @@ CaptureInfo::CaptureInfo(ASTContext &ctx, ArrayRef<CapturedValue> captures,
|
||||
|
||||
size_t storageToAlloc =
|
||||
CaptureInfoStorage::totalSizeToAlloc<CapturedValue,
|
||||
GenericEnvironment *>(captures.size(),
|
||||
genericEnv.size());
|
||||
GenericEnvironment *,
|
||||
CapturedType>(captures.size(),
|
||||
genericEnv.size(),
|
||||
capturedTypes.size());
|
||||
void *storageBuf = ctx.Allocate(storageToAlloc, alignof(CaptureInfoStorage));
|
||||
auto *storage = new (storageBuf) CaptureInfoStorage(dynamicSelf,
|
||||
opaqueValue,
|
||||
captures.size(),
|
||||
genericEnv.size());
|
||||
genericEnv.size(),
|
||||
capturedTypes.size());
|
||||
StorageAndFlags.setPointerAndInt(storage, flags);
|
||||
std::uninitialized_copy(captures.begin(), captures.end(),
|
||||
storage->getTrailingObjects<CapturedValue>());
|
||||
std::uninitialized_copy(genericEnv.begin(), genericEnv.end(),
|
||||
storage->getTrailingObjects<GenericEnvironment *>());
|
||||
std::uninitialized_copy(capturedTypes.begin(), capturedTypes.end(),
|
||||
storage->getTrailingObjects<CapturedType>());
|
||||
}
|
||||
|
||||
CaptureInfo CaptureInfo::empty() {
|
||||
static const CaptureInfoStorage empty{/*dynamicSelf*/nullptr,
|
||||
/*opaqueValue*/nullptr,
|
||||
0, 0};
|
||||
0, 0, 0};
|
||||
CaptureInfo result;
|
||||
result.StorageAndFlags.setPointer(&empty);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CaptureInfo::isTrivial() const {
|
||||
assert(hasBeenComputed());
|
||||
return getCaptures().empty() && !hasGenericParamCaptures() &&
|
||||
!hasDynamicSelfCapture() && !hasOpaqueValueCapture();
|
||||
}
|
||||
|
||||
VarDecl *CaptureInfo::getIsolatedParamCapture() const {
|
||||
for (const auto &capture : getCaptures()) {
|
||||
// NOTE: isLocalCapture() returns false if we have dynamic self metadata
|
||||
@@ -178,6 +196,12 @@ void CaptureInfo::print(raw_ostream &OS) const {
|
||||
OS << genericEnv->getOpenedElementShapeClass();
|
||||
},
|
||||
[&] { OS << ","; });
|
||||
|
||||
interleave(getCapturedTypes(),
|
||||
[&](CapturedType type) {
|
||||
OS << " type=" << type.getType().getString();
|
||||
},
|
||||
[&] { OS << ","; });
|
||||
OS << ')';
|
||||
}
|
||||
|
||||
|
||||
@@ -4425,6 +4425,10 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
|
||||
// Captured pack element environments.
|
||||
llvm::SetVector<GenericEnvironment *> genericEnv;
|
||||
|
||||
// Captured types.
|
||||
SmallVector<CapturedType, 4> capturedTypes;
|
||||
llvm::SmallDenseSet<CanType, 4> alreadyCapturedTypes;
|
||||
|
||||
bool capturesGenericParams = false;
|
||||
DynamicSelfType *capturesDynamicSelf = nullptr;
|
||||
OpaqueValueExpr *capturesOpaqueValue = nullptr;
|
||||
@@ -4620,6 +4624,13 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
|
||||
// Collect non-function captures.
|
||||
recordCapture(capture);
|
||||
}
|
||||
|
||||
for (const auto &capturedType : captureInfo.getCapturedTypes()) {
|
||||
if (alreadyCapturedTypes.insert(capturedType.getType()->getCanonicalType())
|
||||
.second) {
|
||||
capturedTypes.push_back(capturedType);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
collectFunctionCaptures = [&](AnyFunctionRef curFn) {
|
||||
@@ -4697,7 +4708,8 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
|
||||
// Cache the result.
|
||||
CaptureInfo info(Context, resultingCaptures,
|
||||
capturesDynamicSelf, capturesOpaqueValue,
|
||||
capturesGenericParams, genericEnv.getArrayRef());
|
||||
capturesGenericParams, genericEnv.getArrayRef(),
|
||||
capturedTypes);
|
||||
auto inserted = LoweredCaptures.insert({fn, info});
|
||||
assert(inserted.second && "already in map?!");
|
||||
(void)inserted;
|
||||
|
||||
@@ -54,6 +54,10 @@ class FindCapturedVars : public ASTWalker {
|
||||
/// can go here too.
|
||||
llvm::SetVector<GenericEnvironment *> CapturedEnvironments;
|
||||
|
||||
/// The captured types.
|
||||
SmallVector<CapturedType, 4> CapturedTypes;
|
||||
llvm::SmallDenseMap<CanType, unsigned, 4> CapturedTypeEntryNumber;
|
||||
|
||||
SourceLoc GenericParamCaptureLoc;
|
||||
SourceLoc DynamicSelfCaptureLoc;
|
||||
DynamicSelfType *DynamicSelf = nullptr;
|
||||
@@ -83,7 +87,8 @@ public:
|
||||
|
||||
return CaptureInfo(Context, Captures, dynamicSelfToRecord,
|
||||
OpaqueValue, HasGenericParamCaptures,
|
||||
CapturedEnvironments.getArrayRef());
|
||||
CapturedEnvironments.getArrayRef(),
|
||||
CapturedTypes);
|
||||
}
|
||||
|
||||
bool hasGenericParamCaptures() const {
|
||||
@@ -156,6 +161,22 @@ public:
|
||||
}));
|
||||
}
|
||||
|
||||
// Note that we're using a generic type.
|
||||
auto recordUseOfGenericType = [&](Type type) {
|
||||
if (!HasGenericParamCaptures) {
|
||||
GenericParamCaptureLoc = loc;
|
||||
HasGenericParamCaptures = true;
|
||||
}
|
||||
|
||||
auto [insertionPos, inserted] = CapturedTypeEntryNumber.insert(
|
||||
{type->getCanonicalType(), CapturedTypes.size()});
|
||||
if (inserted) {
|
||||
CapturedTypes.push_back(CapturedType(type, loc));
|
||||
} else if (CapturedTypes[insertionPos->second].getLoc().isInvalid()) {
|
||||
CapturedTypes[insertionPos->second] = CapturedType(type, loc);
|
||||
}
|
||||
};
|
||||
|
||||
// Similar to dynamic 'Self', IRGen doesn't really need type metadata
|
||||
// for class-bound archetypes in nearly as many cases as with opaque
|
||||
// archetypes.
|
||||
@@ -174,23 +195,18 @@ public:
|
||||
CapturedEnvironments.insert(env);
|
||||
}
|
||||
|
||||
if ((t->is<PrimaryArchetypeType>() ||
|
||||
t->is<PackArchetypeType>() ||
|
||||
t->is<GenericTypeParamType>()) &&
|
||||
!HasGenericParamCaptures) {
|
||||
GenericParamCaptureLoc = loc;
|
||||
HasGenericParamCaptures = true;
|
||||
if (t->is<PrimaryArchetypeType>() ||
|
||||
t->is<PackArchetypeType>() ||
|
||||
t->is<GenericTypeParamType>()) {
|
||||
recordUseOfGenericType(t);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (auto *gft = type->getAs<GenericFunctionType>()) {
|
||||
TypeCaptureWalker walker(ObjC, [&](Type t) {
|
||||
if (t->is<GenericTypeParamType>() &&
|
||||
!HasGenericParamCaptures) {
|
||||
GenericParamCaptureLoc = loc;
|
||||
HasGenericParamCaptures = true;
|
||||
}
|
||||
if (t->is<GenericTypeParamType>())
|
||||
recordUseOfGenericType(t);
|
||||
});
|
||||
|
||||
for (const auto ¶m : gft->getParams())
|
||||
|
||||
@@ -2777,6 +2777,17 @@ namespace {
|
||||
|
||||
/// Check closure captures for Sendable violations.
|
||||
void checkLocalCaptures(AnyFunctionRef localFunc) {
|
||||
auto *dc = getDeclContext();
|
||||
ASTContext &ctx = dc->getASTContext();
|
||||
|
||||
auto *closure = localFunc.getAbstractClosureExpr();
|
||||
auto *explicitClosure = dyn_cast_or_null<ClosureExpr>(closure);
|
||||
|
||||
bool preconcurrency = false;
|
||||
if (explicitClosure) {
|
||||
preconcurrency = explicitClosure->isIsolatedByPreconcurrency();
|
||||
}
|
||||
|
||||
for (const auto &capture : localFunc.getCaptureInfo().getCaptures()) {
|
||||
if (!capture.isLocalCapture())
|
||||
continue;
|
||||
@@ -2785,18 +2796,9 @@ namespace {
|
||||
if (capture.isOpaqueValue())
|
||||
continue;
|
||||
|
||||
auto *closure = localFunc.getAbstractClosureExpr();
|
||||
auto *explicitClosure = dyn_cast_or_null<ClosureExpr>(closure);
|
||||
|
||||
bool preconcurrency = false;
|
||||
if (explicitClosure) {
|
||||
preconcurrency = explicitClosure->isIsolatedByPreconcurrency();
|
||||
}
|
||||
|
||||
// Diagnose a `self` capture inside an escaping `sending`
|
||||
// `@Sendable` closure in a deinit, which almost certainly
|
||||
// means `self` would escape deinit at runtime.
|
||||
auto *dc = getDeclContext();
|
||||
if (explicitClosure && isa<DestructorDecl>(dc) &&
|
||||
!explicitClosure->getType()->isNoEscape() &&
|
||||
(explicitClosure->isPassedToSendingParameter() ||
|
||||
@@ -2868,6 +2870,49 @@ namespace {
|
||||
/*closure=*/closure != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: When passing to a sending parameter, should this be handled
|
||||
// by region isolation? Or should it always be handled by region
|
||||
// isolation?
|
||||
if (ctx.LangOpts.hasFeature(Feature::StrictSendableMetatypes) &&
|
||||
(mayExecuteConcurrentlyWith(
|
||||
localFunc.getAsDeclContext(), getDeclContext()) ||
|
||||
(explicitClosure && explicitClosure->isPassedToSendingParameter()))) {
|
||||
GenericSignature genericSig;
|
||||
if (auto afd = localFunc.getAbstractFunctionDecl())
|
||||
genericSig = afd->getGenericSignature();
|
||||
|
||||
for (const auto &capturedType :
|
||||
localFunc.getCaptureInfo().getCapturedTypes()) {
|
||||
unsigned genericDepth;
|
||||
Type type = capturedType.getType();
|
||||
if (auto archetype = type->getAs<ArchetypeType>()) {
|
||||
genericDepth = archetype->getInterfaceType()->getRootGenericParam()
|
||||
->getDepth();
|
||||
} else if (type->isTypeParameter()) {
|
||||
genericDepth = type->getRootGenericParam()->getDepth();
|
||||
|
||||
type = localFunc.getAsDeclContext()->mapTypeIntoContext(type);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the local function is generic and this is one of its generic
|
||||
// parameters, ignore it.
|
||||
if (genericSig.getNextDepth() > 0 &&
|
||||
genericDepth < genericSig.getNextDepth() - 1)
|
||||
continue;
|
||||
|
||||
// Check that the metatype is sendable.
|
||||
SendableCheckContext sendableContext(getDeclContext(), preconcurrency);
|
||||
diagnoseNonSendableTypes(MetatypeType::get(type),
|
||||
sendableContext,
|
||||
/*inDerivedConformance*/Type(),
|
||||
capturedType.getLoc(),
|
||||
diag::non_sendable_metatype_capture,
|
||||
/*closure=*/closure != nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -16,20 +16,6 @@ func acceptMetaOnMainActor<T>(_: T.Type) { }
|
||||
// -------------------------------------------------------------------------
|
||||
// Non-Sendable metatype instances that cross into other isolation domains.
|
||||
// -------------------------------------------------------------------------
|
||||
nonisolated func staticCallThroughMeta<T: Q>(_: T.Type) {
|
||||
let x = T.self
|
||||
Task.detached { // expected-error{{risks causing data races}}
|
||||
x.g() // expected-note{{closure captures 'x' which is accessible to code in the current task}}
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func passMeta<T: Q>(_: T.Type) {
|
||||
let x = T.self
|
||||
Task.detached { // expected-error{{risks causing data races}}
|
||||
acceptMeta(x) // expected-note{{closure captures 'x' which is accessible to code in the current task}}
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func staticCallThroughMetaSmuggled<T: Q>(_: T.Type) {
|
||||
let x: Q.Type = T.self
|
||||
Task.detached { // expected-error{{risks causing data races}}
|
||||
@@ -60,14 +46,6 @@ nonisolated func passToMainActorSmuggledAny<T: Q>(_: T.Type) async {
|
||||
// -------------------------------------------------------------------------
|
||||
// Sendable metatype instances that cross into other isolation domains.
|
||||
// -------------------------------------------------------------------------
|
||||
nonisolated func passMetaWithSendable<T: Sendable & Q>(_: T.Type) {
|
||||
let x = T.self
|
||||
Task.detached {
|
||||
acceptMeta(x) // okay, because T is Sendable implies T.Type: Sendable
|
||||
x.g() // okay, because T is Sendable implies T.Type: Sendable
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func passMetaWithSendableSmuggled<T: Sendable & Q>(_: T.Type) {
|
||||
let x: any (Q & Sendable).Type = T.self
|
||||
Task.detached {
|
||||
@@ -80,21 +58,3 @@ nonisolated func passSendableToMainActorSmuggledAny<T: Sendable>(_: T.Type) asyn
|
||||
let x: Sendable.Type = T.self
|
||||
await acceptMetaOnMainActor(x)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Sendable requirements on metatypes
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
nonisolated func passMetaWithMetaSendable<T: Q>(_: T.Type) where T.Type: Sendable {
|
||||
// FIXME: Bogus errors below because we don't currently handle constraints on
|
||||
// metatypes.
|
||||
let x = T.self
|
||||
Task.detached { // expected-error{{risks causing data races}}
|
||||
acceptMeta(x) // expected-note{{closure captures 'x' which is accessible to code in the current task}}
|
||||
// should be okay, because T is Sendable implies T.Type: Sendable
|
||||
x.g() // okay, because T is Sendable implies T.Type: Sendable
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Cannot currently express an existential or opaque type where the
|
||||
// metatype is Sendable.
|
||||
|
||||
@@ -12,14 +12,49 @@ protocol Q {
|
||||
|
||||
nonisolated func acceptMeta<T>(_: T.Type) { }
|
||||
|
||||
nonisolated func staticCallThroughMetaVal<T: Q>(_: T.Type) {
|
||||
let x = T.self // expected-error{{capture of non-sendable type 'T.Type' in an isolated closure}}
|
||||
Task.detached {
|
||||
x.g() // expected-error{{capture of non-sendable type 'T.Type' in an isolated closure}}
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func passMetaVal<T: Q>(_: T.Type) {
|
||||
let x = T.self // expected-error{{capture of non-sendable type 'T.Type' in an isolated closure}}
|
||||
Task.detached {
|
||||
acceptMeta(x) // expected-error{{capture of non-sendable type}}
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func staticCallThroughMeta<T: Q>(_: T.Type) {
|
||||
Task.detached {
|
||||
T.g()
|
||||
T.g() // expected-error{{capture of non-sendable type}}
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func passMeta<T: Q>(_: T.Type) {
|
||||
Task.detached {
|
||||
acceptMeta(T.self) // expected-error{{capture of non-sendable type 'T.Type' in an isolated closure}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nonisolated func staticCallThroughMetaSendable<T: Sendable & Q>(_: T.Type) {
|
||||
Task.detached {
|
||||
T.g()
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func passMetaSendable<T: Sendable & Q>(_: T.Type) {
|
||||
Task.detached {
|
||||
acceptMeta(T.self)
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated func passMetaWithSendableVal<T: Sendable & Q>(_: T.Type) {
|
||||
let x = T.self
|
||||
Task.detached {
|
||||
acceptMeta(x) // okay, because T is Sendable implies T.Type: Sendable
|
||||
x.g() // okay, because T is Sendable implies T.Type: Sendable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ enum TrailingSemi {
|
||||
};
|
||||
|
||||
// The substitution map for a declref should be relatively unobtrusive.
|
||||
// CHECK-AST-LABEL: (func_decl{{.*}}"generic(_:)" "<T : Hashable>" interface_type="<T where T : Hashable> (T) -> ()" access=internal captures=(<generic> )
|
||||
// CHECK-AST-LABEL: (func_decl{{.*}}"generic(_:)" "<T : Hashable>" interface_type="<T where T : Hashable> (T) -> ()" access=internal captures=(<generic> {{.*}})
|
||||
func generic<T: Hashable>(_: T) {}
|
||||
// CHECK-AST: (pattern_binding_decl
|
||||
// CHECK-AST: (processed_init=declref_expr type="(Int) -> ()" location={{.*}} range={{.*}} decl="main.(file).generic@{{.*}} [with (substitution_map generic_signature=<T where T : Hashable> T -> Int)]" function_ref=unapplied))
|
||||
|
||||
@@ -6,7 +6,7 @@ func doSomething<T>(_ t: T) {}
|
||||
|
||||
func outerGeneric<T>(t: T, x: AnyObject) {
|
||||
// Simple case -- closure captures outer generic parameter
|
||||
// CHECK: closure_expr type="() -> ()" {{.*}} discriminator=0 nonisolated captures=(<generic> t<direct>) escaping single_expression
|
||||
// CHECK: closure_expr type="() -> ()" {{.*}} discriminator=0 nonisolated captures=(<generic> t<direct> type=T, type=τ_0_0) escaping single_expression
|
||||
_ = { doSomething(t) }
|
||||
|
||||
// Special case -- closure does not capture outer generic parameters
|
||||
@@ -15,20 +15,20 @@ func outerGeneric<T>(t: T, x: AnyObject) {
|
||||
|
||||
// Special case -- closure captures outer generic parameter, but it does not
|
||||
// appear as the type of any expression
|
||||
// CHECK: closure_expr type="() -> ()" {{.*}} discriminator=2 nonisolated captures=(<generic> x<direct>)
|
||||
// CHECK: closure_expr type="() -> ()" {{.*}} discriminator=2 nonisolated captures=(<generic> x<direct> type=T)
|
||||
_ = { if x is T {} }
|
||||
|
||||
// Nested generic functions always capture outer generic parameters, even if
|
||||
// they're not mentioned in the function body
|
||||
// CHECK: func_decl{{.*}}"innerGeneric(u:)" "<U>" interface_type="<T, U> (u: U) -> ()" {{.*}} captures=(<generic> )
|
||||
// CHECK: func_decl{{.*}}"innerGeneric(u:)" "<U>" interface_type="<T, U> (u: U) -> ()" {{.*}} captures=(<generic> type=τ_1_0)
|
||||
func innerGeneric<U>(u: U) {}
|
||||
|
||||
// Make sure we look through typealiases
|
||||
typealias TT = (a: T, b: T)
|
||||
|
||||
// CHECK: func_decl{{.*}}"localFunction(tt:)" interface_type="<T> (tt: TT) -> ()" {{.*}} captures=(<generic> )
|
||||
// CHECK: func_decl{{.*}}"localFunction(tt:)" interface_type="<T> (tt: TT) -> ()" {{.*}} captures=(<generic> type=τ_0_0)
|
||||
func localFunction(tt: TT) {}
|
||||
|
||||
// CHECK: closure_expr type="(TT) -> ()" {{.*}} captures=(<generic> )
|
||||
// CHECK: closure_expr type="(TT) -> ()" {{.*}} captures=(<generic> type=T)
|
||||
let _: (TT) -> () = { _ in }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user