Skip to content

Commit b2174da

Browse files
committed
[CodeCompletion] Update for actor isolation
For cross actor references: * Annotate with 'async' * "Not recommended" if there're non-'Sendable' arguments or return types rdar://72200120
1 parent eb17c2d commit b2174da

File tree

5 files changed

+263
-20
lines changed

5 files changed

+263
-20
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,17 @@ class raw_ostream;
2424
}
2525

2626
namespace swift {
27-
class ClassDecl;
27+
class DeclContext;
28+
class NominalTypeDecl;
2829
class SubstitutionMap;
29-
class Type;
3030

3131
/// Determine whether the given types are (canonically) equal, declared here
3232
/// to avoid having to include Types.h.
3333
bool areTypesEqual(Type type1, Type type2);
3434

35+
/// Determine whether the given type is suitable as a concurrent value type.
36+
bool isSendableType(const DeclContext *dc, Type type);
37+
3538
/// Describes the actor isolation of a given declaration, which determines
3639
/// the actors with which it can interact.
3740
class ActorIsolation {

include/swift/IDE/CodeCompletion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,7 @@ class CodeCompletionResult {
603603
Redundant,
604604
Deprecated,
605605
InvalidContext,
606+
CrossActorReference,
606607
NoReason,
607608
};
608609

lib/IDE/CodeCompletion.cpp

Lines changed: 141 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,7 +1761,7 @@ static bool hasTrivialTrailingClosure(const FuncDecl *FD,
17611761
}
17621762

17631763
/// Returns \c true if \p DC can handles async call.
1764-
static bool canDeclContextHandlesAsync(const DeclContext *DC) {
1764+
static bool canDeclContextHandleAsync(const DeclContext *DC) {
17651765
if (auto *func = dyn_cast<AbstractFunctionDecl>(DC))
17661766
return func->isAsyncContext();
17671767

@@ -1840,6 +1840,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
18401840
/// Expected types of the code completion expression.
18411841
ExpectedTypeContext expectedTypeContext;
18421842

1843+
bool CanCurrDeclContextHandleAsync = false;
18431844
bool HaveDot = false;
18441845
bool IsUnwrappedOptional = false;
18451846
SourceLoc DotLoc;
@@ -1855,6 +1856,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
18551856
bool IsSwiftKeyPathExpr = false;
18561857
bool IsAfterSwiftKeyPathRoot = false;
18571858
bool IsDynamicLookup = false;
1859+
bool IsCrossActorReference = false;
18581860
bool PreferFunctionReferencesToCalls = false;
18591861
bool HaveLeadingSpace = false;
18601862

@@ -1987,6 +1989,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
19871989
CurrentMethod = CurrDeclContext->getInnermostMethodContext();
19881990
if (auto *FD = dyn_cast_or_null<FuncDecl>(CurrentMethod))
19891991
InsideStaticMethod = FD->isStatic();
1992+
CanCurrDeclContextHandleAsync = canDeclContextHandleAsync(CurrDeclContext);
19901993
}
19911994
}
19921995

@@ -2530,11 +2533,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25302533
addValueBaseName(Builder, Name);
25312534
setClangDeclKeywords(VD, Pairs, Builder);
25322535

2536+
Optional<CodeCompletionResult::NotRecommendedReason> NotRecommended;
25332537
// "not recommended" in its own getter.
25342538
if (Kind == LookupKind::ValueInDeclContext) {
25352539
if (auto accessor = dyn_cast<AccessorDecl>(CurrDeclContext)) {
25362540
if (accessor->getStorage() == VD && accessor->isGetter())
2537-
Builder.setNotRecommended(CodeCompletionResult::NoReason);
2541+
NotRecommended = CodeCompletionResult::NoReason;
25382542
}
25392543
}
25402544

@@ -2543,6 +2547,37 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25432547

25442548
// Add a type annotation.
25452549
Type VarType = getTypeOfMember(VD, dynamicLookupInfo);
2550+
2551+
bool implicitlyAsync = false;
2552+
auto isolation = getActorIsolation(const_cast<VarDecl *>(VD));
2553+
switch (isolation.getKind()) {
2554+
case ActorIsolation::ActorInstance: {
2555+
if (IsCrossActorReference) {
2556+
implicitlyAsync = true;
2557+
if (!isSendableType(CurrDeclContext, VarType)) {
2558+
NotRecommended = CodeCompletionResult::CrossActorReference;
2559+
}
2560+
// TODO: 'NotRecommended' if this is a r-value reference.
2561+
}
2562+
break;
2563+
}
2564+
case ActorIsolation::GlobalActor:
2565+
case ActorIsolation::GlobalActorUnsafe:
2566+
// TODO: Implement.
2567+
break;
2568+
case ActorIsolation::Unspecified:
2569+
case ActorIsolation::Independent:
2570+
case ActorIsolation::IndependentUnsafe:
2571+
break;
2572+
}
2573+
2574+
if (!NotRecommended && implicitlyAsync && !CanCurrDeclContextHandleAsync) {
2575+
NotRecommended = CodeCompletionResult::InvalidContext;
2576+
}
2577+
2578+
if (NotRecommended)
2579+
Builder.setNotRecommended(*NotRecommended);
2580+
25462581
if (auto *PD = dyn_cast<ParamDecl>(VD)) {
25472582
if (Name != Ctx.Id_self && PD->isInOut()) {
25482583
// It is useful to show inout for function parameters.
@@ -2566,6 +2601,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25662601
else
25672602
addTypeAnnotation(Builder, VarType, genericSig);
25682603

2604+
if (implicitlyAsync)
2605+
Builder.addAnnotatedAsync();
2606+
25692607
if (isUnresolvedMemberIdealType(VarType))
25702608
Builder.setSemanticContext(SemanticContextKind::ExpressionSpecific);
25712609
}
@@ -2686,11 +2724,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26862724

26872725
static void addEffectsSpecifiers(CodeCompletionResultBuilder &Builder,
26882726
const AnyFunctionType *AFT,
2689-
const AbstractFunctionDecl *AFD) {
2727+
const AbstractFunctionDecl *AFD,
2728+
bool forceAsync = false) {
26902729
assert(AFT != nullptr);
26912730

26922731
// 'async'.
2693-
if ((AFD && AFD->hasAsync()) || AFT->isAsync())
2732+
if (forceAsync || (AFD && AFD->hasAsync()) || AFT->isAsync())
26942733
Builder.addAnnotatedAsync();
26952734

26962735
// 'throws' or 'rethrows'.
@@ -2861,8 +2900,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
28612900
else
28622901
addTypeAnnotation(Builder, AFT->getResult(), genericSig);
28632902

2864-
if (AFT->isAsync() &&
2865-
!canDeclContextHandlesAsync(CurrDeclContext)) {
2903+
if (AFT->isAsync() && !CanCurrDeclContextHandleAsync) {
28662904
Builder.setNotRecommended(
28672905
CodeCompletionResult::NotRecommendedReason::InvalidContext);
28682906
}
@@ -2973,6 +3011,47 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
29733011
if (AFT && !IsImplicitlyCurriedInstanceMethod)
29743012
trivialTrailingClosure = hasTrivialTrailingClosure(FD, AFT);
29753013

3014+
Optional<CodeCompletionResult::NotRecommendedReason> NotRecommended;
3015+
bool implictlyAsync = false;
3016+
auto isolation = getActorIsolation(const_cast<FuncDecl *>(FD));
3017+
switch (isolation.getKind()) {
3018+
case ActorIsolation::ActorInstance: {
3019+
if (IsCrossActorReference) {
3020+
implictlyAsync = true;
3021+
3022+
// Check if the result and the param types are all concurrent values.
3023+
if (AFT) {
3024+
if (!isSendableType(CurrDeclContext, AFT->getResult())) {
3025+
NotRecommended = CodeCompletionResult::CrossActorReference;
3026+
} else {
3027+
for (auto &param : AFT->getParams()) {
3028+
Type paramType = param.getPlainType();
3029+
if (!isSendableType(CurrDeclContext, paramType)) {
3030+
NotRecommended = CodeCompletionResult::CrossActorReference;
3031+
break;
3032+
}
3033+
}
3034+
}
3035+
}
3036+
}
3037+
break;
3038+
}
3039+
case ActorIsolation::GlobalActor:
3040+
case ActorIsolation::GlobalActorUnsafe:
3041+
// TODO: implement.
3042+
break;
3043+
case ActorIsolation::Unspecified:
3044+
case ActorIsolation::Independent:
3045+
case ActorIsolation::IndependentUnsafe:
3046+
break;
3047+
}
3048+
3049+
if (!NotRecommended && !IsImplicitlyCurriedInstanceMethod &&
3050+
((AFT && AFT->isAsync()) || implictlyAsync) &&
3051+
!CanCurrDeclContextHandleAsync) {
3052+
NotRecommended = CodeCompletionResult::InvalidContext;
3053+
}
3054+
29763055
// Add the method, possibly including any default arguments.
29773056
auto addMethodImpl = [&](bool includeDefaultArgs = true,
29783057
bool trivialTrailingClosure = false) {
@@ -2983,6 +3062,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
29833062
expectedTypeContext);
29843063
setClangDeclKeywords(FD, Pairs, Builder);
29853064
Builder.setAssociatedDecl(FD);
3065+
3066+
if (NotRecommended)
3067+
Builder.setNotRecommended(*NotRecommended);
3068+
29863069
addLeadingDot(Builder);
29873070
addValueBaseName(Builder, Name);
29883071
if (IsDynamicLookup)
@@ -3004,14 +3087,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
30043087
Builder.addRightParen();
30053088
} else if (trivialTrailingClosure) {
30063089
Builder.addBraceStmtWithCursor(" { code }");
3007-
addEffectsSpecifiers(Builder, AFT, FD);
3090+
addEffectsSpecifiers(Builder, AFT, FD, implictlyAsync);
30083091
} else {
30093092
Builder.addLeftParen();
30103093
addCallArgumentPatterns(Builder, AFT, FD->getParameters(),
30113094
FD->getGenericSignatureOfContext(),
30123095
includeDefaultArgs);
30133096
Builder.addRightParen();
3014-
addEffectsSpecifiers(Builder, AFT, FD);
3097+
addEffectsSpecifiers(Builder, AFT, FD, implictlyAsync);
30153098
}
30163099

30173100
// Build type annotation.
@@ -3076,13 +3159,6 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
30763159
ResultType->isVoid()) {
30773160
Builder.setExpectedTypeRelation(CodeCompletionResult::Invalid);
30783161
}
3079-
3080-
if (!IsImplicitlyCurriedInstanceMethod &&
3081-
AFT->isAsync() &&
3082-
!canDeclContextHandlesAsync(CurrDeclContext)) {
3083-
Builder.setNotRecommended(
3084-
CodeCompletionResult::NotRecommendedReason::InvalidContext);
3085-
}
30863162
};
30873163

30883164
if (!AFT || IsImplicitlyCurriedInstanceMethod) {
@@ -3170,8 +3246,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
31703246
addTypeAnnotation(Builder, *Result, CD->getGenericSignatureOfContext());
31713247
}
31723248

3173-
if (ConstructorType->isAsync() &&
3174-
!canDeclContextHandlesAsync(CurrDeclContext)) {
3249+
if (ConstructorType->isAsync() && !CanCurrDeclContextHandleAsync) {
31753250
Builder.setNotRecommended(
31763251
CodeCompletionResult::NotRecommendedReason::InvalidContext);
31773252
}
@@ -3223,13 +3298,53 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
32233298
if (!subscriptType)
32243299
return;
32253300

3301+
Optional<CodeCompletionResult::NotRecommendedReason> NotRecommended;
3302+
bool implictlyAsync = false;
3303+
auto isolation = getActorIsolation(const_cast<SubscriptDecl *>(SD));
3304+
switch (isolation.getKind()) {
3305+
case ActorIsolation::ActorInstance: {
3306+
if (IsCrossActorReference) {
3307+
implictlyAsync = true;
3308+
3309+
// Check if the result and the param types are all concurrent values.
3310+
if (!isSendableType(CurrDeclContext, subscriptType->getResult())) {
3311+
NotRecommended = CodeCompletionResult::CrossActorReference;
3312+
} else {
3313+
for (auto &param : subscriptType->getParams()) {
3314+
Type paramType = param.getPlainType();
3315+
if (!isSendableType(CurrDeclContext, paramType))
3316+
NotRecommended = CodeCompletionResult::CrossActorReference;
3317+
}
3318+
}
3319+
3320+
// TODO: 'NotRecommended' if this is a r-value reference.
3321+
}
3322+
break;
3323+
}
3324+
case ActorIsolation::GlobalActor:
3325+
case ActorIsolation::GlobalActorUnsafe:
3326+
// TODO: implement.
3327+
break;
3328+
case ActorIsolation::Unspecified:
3329+
case ActorIsolation::Independent:
3330+
case ActorIsolation::IndependentUnsafe:
3331+
break;
3332+
}
3333+
3334+
if (!NotRecommended && implictlyAsync && !CanCurrDeclContextHandleAsync) {
3335+
NotRecommended = CodeCompletionResult::InvalidContext;
3336+
}
3337+
32263338
CommandWordsPairs Pairs;
32273339
CodeCompletionResultBuilder Builder(
32283340
Sink, CodeCompletionResult::ResultKind::Declaration,
32293341
getSemanticContext(SD, Reason, dynamicLookupInfo), expectedTypeContext);
32303342
Builder.setAssociatedDecl(SD);
32313343
setClangDeclKeywords(SD, Pairs, Builder);
32323344

3345+
if (NotRecommended)
3346+
Builder.setNotRecommended(*NotRecommended);
3347+
32333348
// '\TyName#^TOKEN^#' requires leading dot.
32343349
if (!HaveDot && IsAfterSwiftKeyPathRoot)
32353350
Builder.addLeadingDot();
@@ -3251,6 +3366,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
32513366
// Optional<T> type.
32523367
resultTy = OptionalType::get(resultTy);
32533368
}
3369+
3370+
if (implictlyAsync)
3371+
Builder.addAnnotatedAsync();
3372+
32543373
addTypeAnnotation(Builder, resultTy, SD->getGenericSignatureOfContext());
32553374
}
32563375

@@ -3916,6 +4035,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
39164035
if (ExprType->isAnyExistentialType())
39174036
ExprType = OpenedArchetypeType::getAny(ExprType);
39184037

4038+
if (!IsSelfRefExpr && !IsSuperRefExpr && ExprType->getAnyNominal() &&
4039+
ExprType->getAnyNominal()->isActor()) {
4040+
IsCrossActorReference = true;
4041+
}
4042+
39194043
if (WasOptional)
39204044
ExprType = OptionalType::get(ExprType);
39214045

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ static bool isSendableClosure(const AbstractClosureExpr *closure) {
764764
}
765765

766766
/// Determine whether the given type is suitable as a concurrent value type.
767-
static bool isSendableType(const DeclContext *dc, Type type) {
767+
bool swift::isSendableType(const DeclContext *dc, Type type) {
768768
class IsSendable : public TypeVisitor<IsSendable, bool> {
769769
DeclContext *dc;
770770
ProtocolDecl *SendableProto;

0 commit comments

Comments
 (0)