Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions include/swift/AST/AnyFunctionRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ class AnyFunctionRef {
TheFunction.get<AbstractClosureExpr *>()->setCaptureInfo(captures);
}

void getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
getCaptureInfo().getLocalCaptures(Result);
}

bool hasType() const {
if (auto *AFD = TheFunction.dyn_cast<AbstractFunctionDecl *>())
return AFD->hasInterfaceType();
Expand Down
9 changes: 0 additions & 9 deletions include/swift/AST/CaptureInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,6 @@ class CaptureInfo {
return StorageAndFlags.getPointer()->getCaptures();
}

/// Return a filtered list of the captures for this function,
/// filtering out global variables. This function returns the list that
/// actually needs to be closed over.
///
void getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const;

/// \returns true if getLocalCaptures() will return a non-empty list.
bool hasLocalCaptures() const;

/// \returns true if the function captures any generic type parameters.
bool hasGenericParamCaptures() const {
// FIXME: Ideally, everywhere that synthesizes a function should include
Expand Down
4 changes: 0 additions & 4 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8019,10 +8019,6 @@ class FuncDecl : public AbstractFunctionDecl {
/// prior to type checking.
bool isBinaryOperator() const;

void getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
return getCaptureInfo().getLocalCaptures(Result);
}

ParamDecl **getImplicitSelfDeclStorage();

/// Get the supertype method this method overrides, if any.
Expand Down
27 changes: 0 additions & 27 deletions lib/AST/CaptureInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,34 +59,7 @@ CaptureInfo CaptureInfo::empty() {
return result;
}

bool CaptureInfo::hasLocalCaptures() const {
for (auto capture : getCaptures()) {
if (capture.isLocalCapture())
return true;
}
return false;
}


void CaptureInfo::
getLocalCaptures(SmallVectorImpl<CapturedValue> &Result) const {
if (!hasLocalCaptures()) return;

Result.reserve(getCaptures().size());

// Filter out global variables.
for (auto capture : getCaptures()) {
if (!capture.isLocalCapture())
continue;

Result.push_back(capture);
}
}

VarDecl *CaptureInfo::getIsolatedParamCapture() const {
if (!hasLocalCaptures())
return nullptr;

for (const auto &capture : getCaptures()) {
// NOTE: isLocalCapture() returns false if we have dynamic self metadata
// since dynamic self metadata is never an isolated capture. So we can just
Expand Down
54 changes: 35 additions & 19 deletions lib/SIL/IR/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4139,11 +4139,21 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
DynamicSelfType *capturesDynamicSelf = nullptr;
OpaqueValueExpr *capturesOpaqueValue = nullptr;

std::function<void (CaptureInfo captureInfo, DeclContext *dc)> collectCaptures;
std::function<void (CaptureInfo captureInfo)> collectCaptures;
std::function<void (AnyFunctionRef)> collectFunctionCaptures;
std::function<void (SILDeclRef)> collectConstantCaptures;

collectCaptures = [&](CaptureInfo captureInfo, DeclContext *dc) {
auto recordCapture = [&](CapturedValue capture) {
ValueDecl *value = capture.getDecl();
auto existing = captures.find(value);
if (existing != captures.end()) {
existing->second = existing->second.mergeFlags(capture);
} else {
captures.insert(std::pair<ValueDecl *, CapturedValue>(value, capture));
}
};

collectCaptures = [&](CaptureInfo captureInfo) {
assert(captureInfo.hasBeenComputed());

if (captureInfo.hasGenericParamCaptures())
Expand All @@ -4153,9 +4163,10 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
if (captureInfo.hasOpaqueValueCapture())
capturesOpaqueValue = captureInfo.getOpaqueValue();

SmallVector<CapturedValue, 4> localCaptures;
captureInfo.getLocalCaptures(localCaptures);
for (auto capture : localCaptures) {
for (auto capture : captureInfo.getCaptures()) {
if (!capture.isLocalCapture())
continue;

// If the capture is of another local function, grab its transitive
// captures instead.
if (auto capturedFn = getAnyFunctionRefFromCapture(capture)) {
Expand Down Expand Up @@ -4287,13 +4298,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
}

// Collect non-function captures.
ValueDecl *value = capture.getDecl();
auto existing = captures.find(value);
if (existing != captures.end()) {
existing->second = existing->second.mergeFlags(capture);
} else {
captures.insert(std::pair<ValueDecl *, CapturedValue>(value, capture));
}
recordCapture(capture);
}
};

Expand All @@ -4305,8 +4310,21 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
return;

PrettyStackTraceAnyFunctionRef("lowering local captures", curFn);
auto dc = curFn.getAsDeclContext();
collectCaptures(curFn.getCaptureInfo(), dc);
collectCaptures(curFn.getCaptureInfo());

if (auto *afd = curFn.getAbstractFunctionDecl()) {
// If a local function inherits isolation from the enclosing context,
// make sure we capture the isolated parameter, if we haven't already.
if (afd->isLocalCapture()) {
auto actorIsolation = getActorIsolation(afd);
if (actorIsolation.getKind() == ActorIsolation::ActorInstance) {
if (auto *var = actorIsolation.getActorInstance()) {
assert(isa<ParamDecl>(var));
recordCapture(CapturedValue(var, 0, afd->getLoc()));
}
}
}
}

// A function's captures also include its default arguments, because
// when we reference a function we don't track which default arguments
Expand All @@ -4317,7 +4335,7 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
if (auto *AFD = curFn.getAbstractFunctionDecl()) {
for (auto *P : *AFD->getParameters()) {
if (P->hasDefaultExpr())
collectCaptures(P->getDefaultArgumentCaptureInfo(), dc);
collectCaptures(P->getDefaultArgumentCaptureInfo());
}
}
};
Expand All @@ -4330,10 +4348,8 @@ TypeConverter::getLoweredLocalCaptures(SILDeclRef fn) {
if (auto *afd = dyn_cast<AbstractFunctionDecl>(curFn.getDecl())) {
auto *param = getParameterAt(static_cast<ValueDecl *>(afd),
curFn.defaultArgIndex);
if (param->hasDefaultExpr()) {
auto dc = afd->getInnermostDeclContext();
collectCaptures(param->getDefaultArgumentCaptureInfo(), dc);
}
if (param->hasDefaultExpr())
collectCaptures(param->getDefaultArgumentCaptureInfo());
return;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/SILGen/SILGenConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ void SILGenFunction::emitExpectedExecutor() {
// completely.
if (F.isAsync() ||
(wantDataRaceChecks && funcDecl->isLocalCapture())) {
if (auto isolatedParam = funcDecl->getCaptureInfo()
.getIsolatedParamCapture()) {
auto loweredCaptures = SGM.Types.getLoweredLocalCaptures(SILDeclRef(funcDecl));
if (auto isolatedParam = loweredCaptures.getIsolatedParamCapture()) {
loadExpectedExecutorForLocalVar(isolatedParam);
} else {
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
Expand Down
14 changes: 8 additions & 6 deletions lib/SILGen/SILGenType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,6 @@ class SILGenVTable : public SILVTableVisitor<SILGenVTable> {
void emitVTable() {
PrettyStackTraceDecl("silgen emitVTable", theClass);

// Imported types don't have vtables right now.
if (theClass->hasClangNode())
return;

// Populate our list of base methods and overrides.
visitAncestor(theClass);

Expand Down Expand Up @@ -317,6 +313,10 @@ class SILGenVTable : public SILVTableVisitor<SILGenVTable> {
}

void visitAncestor(ClassDecl *ancestor) {
// Imported types don't have vtables right now.
if (ancestor->hasClangNode())
return;

auto *superDecl = ancestor->getSuperclassDecl();
if (superDecl)
visitAncestor(superDecl);
Expand Down Expand Up @@ -1153,8 +1153,10 @@ class SILGenType : public TypeMemberVisitor<SILGenType> {

// Build a vtable if this is a class.
if (auto theClass = dyn_cast<ClassDecl>(theType)) {
SILGenVTable genVTable(SGM, theClass);
genVTable.emitVTable();
if (!theClass->hasClangNode()) {
SILGenVTable genVTable(SGM, theClass);
genVTable.emitVTable();
}
}

// If this is a nominal type that is move only, emit a deinit table for it.
Expand Down
1 change: 0 additions & 1 deletion lib/SILOptimizer/Differentiation/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,6 @@ findMinimalDerivativeConfiguration(AbstractFunctionDecl *original,
original->getInterfaceType()->castTo<AnyFunctionType>());

if (silParameterIndices->getCapacity() < parameterIndices->getCapacity()) {
assert(original->getCaptureInfo().hasLocalCaptures());
silParameterIndices =
silParameterIndices->extendingCapacity(original->getASTContext(),
parameterIndices->getCapacity());
Expand Down
10 changes: 4 additions & 6 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2478,9 +2478,9 @@ namespace {

/// Check closure captures for Sendable violations.
void checkLocalCaptures(AnyFunctionRef localFunc) {
SmallVector<CapturedValue, 2> captures;
localFunc.getCaptureInfo().getLocalCaptures(captures);
for (const auto &capture : captures) {
for (const auto &capture : localFunc.getCaptureInfo().getCaptures()) {
if (!capture.isLocalCapture())
continue;
if (capture.isDynamicSelfMetadata())
continue;
if (capture.isOpaqueValue())
Expand Down Expand Up @@ -5174,9 +5174,7 @@ ActorIsolation ActorIsolationRequest::evaluate(
llvm_unreachable("context cannot have erased isolation");

case ActorIsolation::ActorInstance:
if (auto param = func->getCaptureInfo().getIsolatedParamCapture())
return inferredIsolation(enclosingIsolation);
break;
return inferredIsolation(enclosingIsolation);

case ActorIsolation::GlobalActor:
return inferredIsolation(enclosingIsolation);
Expand Down
26 changes: 26 additions & 0 deletions test/Concurrency/actor_isolation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,8 @@ func checkLocalFunctions() async {
print(k)
}

func callee(_: () -> ()) {}

@available(SwiftStdlib 5.1, *)
actor LocalFunctionIsolatedActor {
func a() -> Bool { // expected-note{{calls to instance method 'a()' from outside of its actor context are implicitly asynchronous}}
Expand All @@ -774,6 +776,30 @@ actor LocalFunctionIsolatedActor {
}
return c()
}

func hasRecursiveLocalFunction() {
func recursiveLocalFunction(n: Int) {
_ = a()
callee { _ = a() }
if n > 0 { recursiveLocalFunction(n: n - 1) }
}

recursiveLocalFunction(n: 10)
}

func hasRecursiveLocalFunctions() {
recursiveLocalFunction()

func recursiveLocalFunction() {
anotherRecursiveLocalFunction()
}

func anotherRecursiveLocalFunction() {
callee { _ = a() }
_ = a()
}
}

}

// ----------------------------------------------------------------------
Expand Down
55 changes: 55 additions & 0 deletions test/SILGen/local_function_isolation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// RUN: %target-swift-frontend -emit-silgen %s -disable-availability-checking | %FileCheck %s

// REQUIRES: concurrency

class NotSendable {}

func callee(_ ns: NotSendable) {}

actor MyActor {
func isolatedToSelf(ns: NotSendable) {
// CHECK-LABEL: sil private [ossa] @$s24local_function_isolation7MyActorC14isolatedToSelf2nsyAA11NotSendableC_tF08implicitH7CaptureL_yyYaF : $@convention(thin) @async (@guaranteed NotSendable, @sil_isolated @guaranteed MyActor) -> () {
func implicitSelfCapture() async {

// CHECK: [[COPY:%.*]] = copy_value %1 : $MyActor
// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow [[COPY]] : $MyActor
// CHECK-NEXT: hop_to_executor [[BORROW]] : $MyActor

// CHECK: [[FN:%.*]] = function_ref @$s24local_function_isolation4testyyYaF : $@convention(thin) @async () -> ()
// CHECK-NEXT: apply [[FN]]() : $@convention(thin) @async () -> ()
await test()

// CHECK: hop_to_executor [[BORROW]] : $MyActor
// CHECK: [[FN:%.*]] = function_ref @$s24local_function_isolation6calleeyyAA11NotSendableCF : $@convention(thin) (@guaranteed NotSendable) -> ()
// CHECK-NEXT: apply [[FN]](%0) : $@convention(thin) (@guaranteed NotSendable) -> ()

// we need to hop back to 'self' here
callee(ns)

// CHECK: end_borrow [[BORROW]] : $MyActor
// CHECK-NEXT: destroy_value [[COPY]] : $MyActor
}
}
}

func f(isolation: isolated MyActor, ns: NotSendable) {
// CHECK-LABEL: sil private [ossa] @$s24local_function_isolation1f0C02nsyAA7MyActorCYi_AA11NotSendableCtF23implicitIsolatedCaptureL_yyYaF : $@convention(thin) @async (@guaranteed NotSendable, @sil_isolated @guaranteed MyActor) -> () {
func implicitIsolatedCapture() async {

// CHECK: [[COPY:%.*]] = copy_value %1 : $MyActor
// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow [[COPY]] : $MyActor
// CHECK-NEXT: hop_to_executor [[BORROW]] : $MyActor

// CHECK: [[FN:%.*]] = function_ref @$s24local_function_isolation4testyyYaF : $@convention(thin) @async () -> ()
// CHECK-NEXT: apply [[FN]]() : $@convention(thin) @async () -> ()
await test()

// we need to hop back to 'isolation' here
callee(ns)

// CHECK: end_borrow [[BORROW]] : $MyActor
// CHECK-NEXT: destroy_value [[COPY]] : $MyActor
}
}

func test() async {}