Skip to content

Commit cb64295

Browse files
committed
[Concurrency] Actor-isolated members cannot satisfy protocol requirements.
Enforce the actor-isolation constraint that an actor-isolated member (e.g., a non-async instance method or an instance property) cannot be used to conform to a protocol requirement.
1 parent 075e7d4 commit cb64295

File tree

10 files changed

+169
-43
lines changed

10 files changed

+169
-43
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4167,6 +4167,13 @@ NOTE(actor_mutable_state,none,
41674167
WARNING(shared_mutable_state_access,none,
41684168
"reference to %0 %1 is not concurrency-safe because it involves "
41694169
"shared mutable state", (DescriptiveDeclKind, DeclName))
4170+
NOTE(actor_isolated_witness,none,
4171+
"actor-isolated %0 %1 cannot be used to satisfy a protocol requirement",
4172+
(DescriptiveDeclKind, DeclName))
4173+
NOTE(actor_isolated_witness_could_be_async_handler,none,
4174+
"actor-isolated %0 %1 cannot be used to satisfy a protocol requirement; "
4175+
"did you mean to make it an asychronous handler?",
4176+
(DescriptiveDeclKind, DeclName))
41704177

41714178
//------------------------------------------------------------------------------
41724179
// MARK: Type Check Types

lib/Sema/MiscDiagnostics.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "MiscDiagnostics.h"
1818
#include "ConstraintSystem.h"
1919
#include "TypeCheckAvailability.h"
20+
#include "TypeCheckConcurrency.h"
2021
#include "TypeChecker.h"
2122
#include "swift/AST/ASTWalker.h"
2223
#include "swift/AST/NameLookup.h"

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// This file implements type checking support for Swift's concurrency model.
1414
//
1515
//===----------------------------------------------------------------------===//
16+
#include "TypeCheckConcurrency.h"
1617
#include "TypeChecker.h"
1718
#include "swift/AST/ASTWalker.h"
1819
#include "swift/AST/ParameterList.h"
@@ -21,14 +22,7 @@
2122

2223
using namespace swift;
2324

24-
/// Check whether the @asyncHandler attribute can be applied to the given
25-
/// function declaration.
26-
///
27-
/// \param diagnose Whether to emit a diagnostic when a problem is encountered.
28-
///
29-
/// \returns \c true if there was a problem with adding the attribute, \c false
30-
/// otherwise.
31-
static bool checkAsyncHandler(FuncDecl *func, bool diagnose) {
25+
bool swift::checkAsyncHandler(FuncDecl *func, bool diagnose) {
3226
if (!func->getResultInterfaceType()->isVoid()) {
3327
if (diagnose) {
3428
func->diagnose(diag::asynchandler_returns_value)
@@ -233,30 +227,6 @@ class IsolationRestriction {
233227

234228
explicit IsolationRestriction(Kind kind) : kind(kind) { }
235229

236-
/// Determine whether the given value is an instance member of an actor
237-
/// class that is isolated to the current actor instance.
238-
///
239-
/// \returns the type of the actor.
240-
static ClassDecl *getActorIsolatingInstanceMember(ValueDecl *value) {
241-
// Only instance members are isolated.
242-
if (!value->isInstanceMember())
243-
return nullptr;
244-
245-
// Are we within an actor class?
246-
auto classDecl = value->getDeclContext()->getSelfClassDecl();
247-
if (!classDecl || !classDecl->isActor())
248-
return nullptr;
249-
250-
// Functions that are an asynchronous context can be accessed from anywhere.
251-
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
252-
if (func->isAsyncContext())
253-
return nullptr;
254-
}
255-
256-
// This member is part of the isolated state.
257-
return classDecl;
258-
}
259-
260230
public:
261231
Kind getKind() const { return kind; }
262232

@@ -354,8 +324,7 @@ class IsolationRestriction {
354324
return forLocalCapture(decl->getDeclContext());
355325

356326
// Protected actor instance members can only be accessed on 'self'.
357-
if (auto actorClass = getActorIsolatingInstanceMember(
358-
cast<ValueDecl>(decl)))
327+
if (auto actorClass = getActorIsolatingMember(cast<ValueDecl>(decl)))
359328
return forActorSelf(actorClass);
360329

361330
// All other accesses are unsafe.
@@ -635,3 +604,23 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) {
635604
ActorIsolationWalker walker(dc);
636605
const_cast<Expr *>(expr)->walk(walker);
637606
}
607+
608+
ClassDecl *swift::getActorIsolatingMember(ValueDecl *value) {
609+
// Only instance members are isolated.
610+
if (!value->isInstanceMember())
611+
return nullptr;
612+
613+
// Are we within an actor class?
614+
auto classDecl = value->getDeclContext()->getSelfClassDecl();
615+
if (!classDecl || !classDecl->isActor())
616+
return nullptr;
617+
618+
// Functions that are an asynchronous context can be accessed from anywhere.
619+
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
620+
if (func->isAsyncContext())
621+
return nullptr;
622+
}
623+
624+
// This member is part of the isolated state.
625+
return classDecl;
626+
}

lib/Sema/TypeCheckConcurrency.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===--- TypeCheckConcurrency.h - Concurrency -------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file provides type checking support for Swift's concurrency model.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_SEMA_TYPECHECKCONCURRENCY_H
18+
#define SWIFT_SEMA_TYPECHECKCONCURRENCY_H
19+
20+
namespace swift {
21+
22+
class ClassDecl;
23+
class DeclContext;
24+
class Expr;
25+
class FuncDecl;
26+
class ValueDecl;
27+
28+
/// Check whether the @asyncHandler attribute can be applied to the given
29+
/// function declaration.
30+
///
31+
/// \param diagnose Whether to emit a diagnostic when a problem is encountered.
32+
///
33+
/// \returns \c true if there was a problem with adding the attribute, \c false
34+
/// otherwise.
35+
bool checkAsyncHandler(FuncDecl *func, bool diagnose);
36+
37+
/// Add notes suggesting the addition of 'async' or '@asyncHandler', as
38+
/// appropriate, to a diagnostic for a function that isn't an async context.
39+
void addAsyncNotes(FuncDecl *func);
40+
41+
/// Check actor isolation rules.
42+
void checkActorIsolation(const Expr *expr, const DeclContext *dc);
43+
44+
/// Determine whether the given value is an instance member of an actor
45+
/// class that is isolated to the current actor instance.
46+
///
47+
/// \returns the type of the actor.
48+
ClassDecl *getActorIsolatingMember(ValueDecl *value);
49+
50+
} // end namespace swift
51+
52+
#endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */

lib/Sema/TypeCheckDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
#include "DerivedConformances.h"
2020
#include "TypeChecker.h"
2121
#include "TypeCheckAccess.h"
22-
#include "TypeCheckDecl.h"
2322
#include "TypeCheckAvailability.h"
23+
#include "TypeCheckConcurrency.h"
24+
#include "TypeCheckDecl.h"
2425
#include "TypeCheckObjC.h"
2526
#include "TypeCheckType.h"
2627
#include "MiscDiagnostics.h"

lib/Sema/TypeCheckEffects.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "TypeChecker.h"
19+
#include "TypeCheckConcurrency.h"
1920
#include "swift/AST/ASTWalker.h"
2021
#include "swift/AST/DiagnosticsSema.h"
2122
#include "swift/AST/Initializer.h"

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "TypeAccessScopeChecker.h"
2222
#include "TypeCheckAccess.h"
2323
#include "TypeCheckAvailability.h"
24+
#include "TypeCheckConcurrency.h"
2425
#include "TypeCheckObjC.h"
2526
#include "swift/AST/ASTContext.h"
2627
#include "swift/AST/ASTMangler.h"
@@ -706,7 +707,6 @@ swift::matchWitness(
706707
!reqFnType->getExtInfo().isThrowing()) {
707708
return RequirementMatch(witness, MatchKind::ThrowsConflict);
708709
}
709-
710710
} else {
711711
auto reqTypeIsIUO = req->isImplicitlyUnwrappedOptional();
712712
auto witnessTypeIsIUO = witness->isImplicitlyUnwrappedOptional();
@@ -727,6 +727,10 @@ swift::matchWitness(
727727
}
728728
}
729729

730+
// Actor-isolated witnesses cannot conform to protocol requirements.
731+
if (getActorIsolatingMember(witness))
732+
return RequirementMatch(witness, MatchKind::ActorIsolatedWitness);
733+
730734
// Now finalize the match.
731735
auto result = finalize(anyRenaming, optionalAdjustments);
732736
// Check if the requirement's `@differentiable` attributes are satisfied by
@@ -2429,6 +2433,24 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance,
24292433
case MatchKind::NonObjC:
24302434
diags.diagnose(match.Witness, diag::protocol_witness_not_objc);
24312435
break;
2436+
case MatchKind::ActorIsolatedWitness: {
2437+
bool canBeAsyncHandler = false;
2438+
if (auto witnessFunc = dyn_cast<FuncDecl>(match.Witness)) {
2439+
canBeAsyncHandler = !witnessFunc->isAsyncHandler() &&
2440+
!checkAsyncHandler(witnessFunc, /*diagnose=*/false);
2441+
}
2442+
auto diag = match.Witness->diagnose(
2443+
canBeAsyncHandler ? diag::actor_isolated_witness_could_be_async_handler
2444+
: diag::actor_isolated_witness,
2445+
match.Witness->getDescriptiveKind(), match.Witness->getName());
2446+
2447+
if (canBeAsyncHandler) {
2448+
diag.fixItInsert(
2449+
match.Witness->getAttributeInsertionLoc(false), "@asyncHandler ");
2450+
}
2451+
break;
2452+
}
2453+
24322454
case MatchKind::MissingDifferentiableAttr: {
24332455
auto *witness = match.Witness;
24342456
// Emit a note and fix-it showing the missing requirement `@differentiable`

lib/Sema/TypeCheckProtocol.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ enum class MatchKind : uint8_t {
268268
/// The witness is explicitly @nonobjc but the requirement is @objc.
269269
NonObjC,
270270

271+
/// The witness is part of actor-isolated state.
272+
ActorIsolatedWitness,
273+
271274
/// The witness is missing a `@differentiable` attribute from the requirement.
272275
MissingDifferentiableAttr,
273276

@@ -500,6 +503,7 @@ struct RequirementMatch {
500503
case MatchKind::AsyncConflict:
501504
case MatchKind::ThrowsConflict:
502505
case MatchKind::NonObjC:
506+
case MatchKind::ActorIsolatedWitness:
503507
case MatchKind::MissingDifferentiableAttr:
504508
case MatchKind::EnumCaseWithAssociatedValues:
505509
return false;
@@ -532,6 +536,7 @@ struct RequirementMatch {
532536
case MatchKind::AsyncConflict:
533537
case MatchKind::ThrowsConflict:
534538
case MatchKind::NonObjC:
539+
case MatchKind::ActorIsolatedWitness:
535540
case MatchKind::MissingDifferentiableAttr:
536541
case MatchKind::EnumCaseWithAssociatedValues:
537542
return false;

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,13 +1384,6 @@ void checkUnknownAttrRestrictions(
13841384
/// it to later stages.
13851385
void bindSwitchCasePatternVars(DeclContext *dc, CaseStmt *stmt);
13861386

1387-
/// Add notes suggesting the addition of 'async' or '@asyncHandler', as
1388-
/// appropriate, to a diagnostic for a function that isn't an async context.
1389-
void addAsyncNotes(FuncDecl *func);
1390-
1391-
/// Check actor isolation rules.
1392-
void checkActorIsolation(const Expr *expr, const DeclContext *dc);
1393-
13941387
} // end namespace swift
13951388

13961389
#endif
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency
2+
3+
protocol AsyncProtocol {
4+
func asyncMethod() async -> Int
5+
}
6+
7+
actor class MyActor {
8+
}
9+
10+
// Actors conforming to asynchronous program.
11+
extension MyActor: AsyncProtocol {
12+
func asyncMethod() async -> Int { return 0 }
13+
}
14+
15+
// FIXME: "Do you want to add a stub?" diagnostics should be suppressed here.
16+
protocol SyncProtocol {
17+
var propertyA: Int { get }
18+
// expected-note@-1{{do you want to add a stub}}
19+
var propertyB: Int { get set }
20+
// expected-note@-1{{do you want to add a stub}}
21+
22+
func syncMethodA()
23+
// expected-note@-1{{do you want to add a stub}}
24+
25+
func syncMethodB()
26+
27+
subscript (index: Int) -> String { get }
28+
// expected-note@-1{{do you want to add a stub}}
29+
30+
static func staticMethod()
31+
static var staticProperty: Int { get }
32+
}
33+
34+
35+
actor class OtherActor: SyncProtocol { // expected-error{{type 'OtherActor' does not conform to protocol 'SyncProtocol'}}
36+
var propertyB: Int = 17
37+
// expected-note@-1{{actor-isolated property 'propertyB' cannot be used to satisfy a protocol requirement}}
38+
39+
var propertyA: Int { 17 }
40+
// expected-note@-1{{actor-isolated property 'propertyA' cannot be used to satisfy a protocol requirement}}
41+
42+
func syncMethodA() { }
43+
// expected-note@-1{{actor-isolated instance method 'syncMethodA()' cannot be used to satisfy a protocol requirement; did you mean to make it an asychronous handler?}}{{3-3=@asyncHandler }}
44+
45+
// Async handlers are okay.
46+
@asyncHandler
47+
func syncMethodB() { }
48+
49+
subscript (index: Int) -> String { "\(index)" }
50+
// expected-note@-1{{actor-isolated subscript 'subscript(_:)' cannot be used to satisfy a protocol requirement}}
51+
52+
// Static methods and properties are okay.
53+
static func staticMethod() { }
54+
static var staticProperty: Int = 17
55+
}

0 commit comments

Comments
 (0)