Skip to content

[Concurrency] Proper serialization/interface printing for actor-isolation attributes #34305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
8 changes: 6 additions & 2 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -2693,9 +2693,13 @@ enum class CustomAttrTypeKind {
/// any contextual type parameters.
NonGeneric,

/// Property delegates have some funky rules, like allowing
/// Property wrappers have some funky rules, like allowing
/// unbound generic types.
PropertyDelegate,
PropertyWrapper,

/// Global actors are represented as custom type attributes. They don't
/// have any particularly interesting semantics.
GlobalActor,
};

void simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value);
Expand Down
8 changes: 6 additions & 2 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1427,8 +1427,12 @@ void swift::simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value) {
out << "non-generic";
return;

case CustomAttrTypeKind::PropertyDelegate:
out << "property-delegate";
case CustomAttrTypeKind::PropertyWrapper:
out << "property-wrapper";
return;

case CustomAttrTypeKind::GlobalActor:
out << "global-actor";
return;
}
llvm_unreachable("bad kind");
Expand Down
70 changes: 50 additions & 20 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@

using namespace swift;

/// Determine whether it makes sense to infer an attribute in the given
/// context.
static bool shouldInferAttributeInContext(const DeclContext *dc) {
auto sourceFile = dc->getParentSourceFile();
if (!sourceFile)
return false;

switch (sourceFile->Kind) {
case SourceFileKind::Interface:
case SourceFileKind::SIL:
return false;

case SourceFileKind::Library:
case SourceFileKind::Main:
return true;
}
}

/// Check whether the @asyncHandler attribute can be applied to the given
/// function declaration.
///
Expand Down Expand Up @@ -108,7 +126,7 @@ bool IsAsyncHandlerRequest::evaluate(
return true;
}

if (!func->getASTContext().LangOpts.EnableExperimentalConcurrency)
if (!shouldInferAttributeInContext(func->getDeclContext()))
return false;

// Are we in a context where inference is possible?
Expand Down Expand Up @@ -1013,15 +1031,18 @@ static Optional<ActorIsolation> getIsolationFromAttributes(Decl *decl) {
// If the declaration is marked with a global actor, report it as being
// part of that global actor.
if (globalActorAttr) {
TypeResolutionOptions options(TypeResolverContext::None);
TypeResolution resolver = TypeResolution::forInterface(
decl->getInnermostDeclContext(), options, nullptr);
Type globalActorType = resolver.resolveType(
globalActorAttr->first->getTypeRepr(), nullptr);
ASTContext &ctx = decl->getASTContext();
auto dc = decl->getInnermostDeclContext();
Type globalActorType = evaluateOrDefault(
ctx.evaluator,
CustomAttrTypeRequest{
globalActorAttr->first, dc, CustomAttrTypeKind::GlobalActor},
Type());
if (!globalActorType || globalActorType->hasError())
return ActorIsolation::forUnspecified();

return ActorIsolation::forGlobalActor(globalActorType);
return ActorIsolation::forGlobalActor(
globalActorType->mapTypeOutOfContext());
}

llvm_unreachable("Forgot about an attribute?");
Expand Down Expand Up @@ -1135,23 +1156,32 @@ ActorIsolation ActorIsolationRequest::evaluate(
}

// Disable inference of actor attributes outside of normal Swift source files.
if (auto sourceFile = value->getDeclContext()->getParentSourceFile()) {
switch (sourceFile->Kind) {
case SourceFileKind::Interface:
case SourceFileKind::SIL:
return defaultIsolation;

case SourceFileKind::Library:
case SourceFileKind::Main:
// Attempt inference below.
break;
}
} else {
if (!shouldInferAttributeInContext(value->getDeclContext()))
return defaultIsolation;
}

// Function used when returning an inferred isolation.
auto inferredIsolation = [&](ActorIsolation inferred) {
// Add an implicit attribute to capture the actor isolation that was
// inferred, so that (e.g.) it will be printed and serialized.
ASTContext &ctx = value->getASTContext();
switch (inferred) {
case ActorIsolation::Independent:
value->getAttrs().add(new (ctx) ActorIndependentAttr(true));
break;

case ActorIsolation::GlobalActor: {
auto typeExpr = TypeExpr::createImplicit(inferred.getGlobalActor(), ctx);
auto attr = CustomAttr::create(
ctx, SourceLoc(), typeExpr, /*implicit=*/true);
value->getAttrs().add(attr);
break;
}

case ActorIsolation::ActorInstance:
case ActorIsolation::Unspecified:
// Nothing to do.
break;
}
return inferred;
};

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckPropertyWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ Type AttachedPropertyWrapperTypeRequest::evaluate(Evaluator &evaluator,
auto ty = evaluateOrDefault(
evaluator,
CustomAttrTypeRequest{customAttr, var->getDeclContext(),
CustomAttrTypeKind::PropertyDelegate},
CustomAttrTypeKind::PropertyWrapper},
Type());
if (!ty || ty->hasError()) {
return ErrorType::get(var->getASTContext());
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3974,7 +3974,7 @@ Type CustomAttrTypeRequest::evaluate(Evaluator &eval, CustomAttr *attr,

OpenUnboundGenericTypeFn unboundTyOpener = nullptr;
// Property delegates allow their type to be an unbound generic.
if (typeKind == CustomAttrTypeKind::PropertyDelegate) {
if (typeKind == CustomAttrTypeKind::PropertyWrapper) {
unboundTyOpener = [](auto unboundTy) {
// FIXME: Don't let unbound generic types
// escape type resolution. For now, just
Expand Down
44 changes: 44 additions & 0 deletions test/ModuleInterface/actor_isolation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -o %t/Test.swiftmodule -emit-module-interface-path %t/Test.swiftinterface -module-name Test -enable-experimental-concurrency %s
// RUN: %FileCheck %s --check-prefix FROMSOURCE --check-prefix CHECK < %t/Test.swiftinterface
// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules %t/Test.swiftmodule -disable-objc-attr-requires-foundation-module -emit-module-interface-path %t/TestFromModule.swiftinterface -module-name Test -enable-experimental-concurrency
// RUN: %FileCheck %s --check-prefix FROMMODULE --check-prefix CHECK < %t/TestFromModule.swiftinterface

// REQUIRES: concurrency
import _Concurrency

// CHECK: actor public class SomeActor
public actor class SomeActor {
@actorIndependent func maine() { }
}

// CHECK: @globalActor public struct SomeGlobalActor
@globalActor
public struct SomeGlobalActor {
public static let shared = SomeActor()
}

// CHECK: @{{(Test.)?}}SomeGlobalActor public protocol P1
// CHECK-NEXT: @{{(Test.)?}}SomeGlobalActor func method()
@SomeGlobalActor
public protocol P1 {
func method()
}

// CHECK: class C1
// CHECK-NEXT: @{{(Test.)?}}SomeGlobalActor public func method()
public class C1: P1 {
public func method() { }
}

@SomeGlobalActor
public class C2 { }

// CHECK: @{{(Test.)?}}SomeGlobalActor public class C2
public class C3: C2 { }

// CHECK: actor public class SomeSubActor
// CHECK-NEXT: @actorIndependent public func maine()
public actor class SomeSubActor: SomeActor {
override public func maine() { }
}