Skip to content

Commit ba3d450

Browse files
authored
Merge pull request swiftlang#36563 from rintaro/ide-completion-actorisolation
[CodeCompletion] Update for actor isolation
2 parents c1bc470 + 3434463 commit ba3d450

File tree

5 files changed

+239
-29
lines changed

5 files changed

+239
-29
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: 117 additions & 26 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

@@ -2513,6 +2516,54 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25132516
return Type();
25142517
}
25152518

2519+
void analyzeActorIsolation(
2520+
const ValueDecl *VD, Type T, bool &implicitlyAsync,
2521+
Optional<CodeCompletionResult::NotRecommendedReason> &NotRecommended) {
2522+
auto isolation = getActorIsolation(const_cast<ValueDecl *>(VD));
2523+
2524+
switch (isolation.getKind()) {
2525+
case ActorIsolation::ActorInstance: {
2526+
if (IsCrossActorReference) {
2527+
implicitlyAsync = true;
2528+
// TODO: 'NotRecommended' if this is a r-value reference.
2529+
}
2530+
break;
2531+
}
2532+
case ActorIsolation::GlobalActor:
2533+
case ActorIsolation::GlobalActorUnsafe:
2534+
// TODO: Implement.
2535+
break;
2536+
case ActorIsolation::Unspecified:
2537+
case ActorIsolation::Independent:
2538+
case ActorIsolation::IndependentUnsafe:
2539+
return;
2540+
}
2541+
2542+
// If the reference is 'async', all types must be 'Sendable'.
2543+
if (implicitlyAsync && T) {
2544+
if (isa<VarDecl>(VD)) {
2545+
if (!isSendableType(CurrDeclContext, T)) {
2546+
NotRecommended = CodeCompletionResult::CrossActorReference;
2547+
}
2548+
} else {
2549+
assert(isa<FuncDecl>(VD) || isa<SubscriptDecl>(VD));
2550+
// Check if the result and the param types are all 'Sendable'.
2551+
auto *AFT = T->castTo<AnyFunctionType>();
2552+
if (!isSendableType(CurrDeclContext, AFT->getResult())) {
2553+
NotRecommended = CodeCompletionResult::CrossActorReference;
2554+
} else {
2555+
for (auto &param : AFT->getParams()) {
2556+
Type paramType = param.getPlainType();
2557+
if (!isSendableType(CurrDeclContext, paramType)) {
2558+
NotRecommended = CodeCompletionResult::CrossActorReference;
2559+
break;
2560+
}
2561+
}
2562+
}
2563+
}
2564+
}
2565+
}
2566+
25162567
void addVarDeclRef(const VarDecl *VD, DeclVisibilityKind Reason,
25172568
DynamicLookupInfo dynamicLookupInfo) {
25182569
if (!VD->hasName())
@@ -2521,6 +2572,24 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25212572
const Identifier Name = VD->getName();
25222573
assert(!Name.empty() && "name should not be empty");
25232574

2575+
Type VarType;
2576+
if (VD->hasInterfaceType())
2577+
VarType = getTypeOfMember(VD, dynamicLookupInfo);
2578+
2579+
Optional<CodeCompletionResult::NotRecommendedReason> NotRecommended;
2580+
// "not recommended" in its own getter.
2581+
if (Kind == LookupKind::ValueInDeclContext) {
2582+
if (auto accessor = dyn_cast<AccessorDecl>(CurrDeclContext)) {
2583+
if (accessor->getStorage() == VD && accessor->isGetter())
2584+
NotRecommended = CodeCompletionResult::NoReason;
2585+
}
2586+
}
2587+
bool implicitlyAsync = false;
2588+
analyzeActorIsolation(VD, VarType, implicitlyAsync, NotRecommended);
2589+
if (!NotRecommended && implicitlyAsync && !CanCurrDeclContextHandleAsync) {
2590+
NotRecommended = CodeCompletionResult::InvalidContext;
2591+
}
2592+
25242593
CommandWordsPairs Pairs;
25252594
CodeCompletionResultBuilder Builder(
25262595
Sink, CodeCompletionResult::ResultKind::Declaration,
@@ -2530,19 +2599,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25302599
addValueBaseName(Builder, Name);
25312600
setClangDeclKeywords(VD, Pairs, Builder);
25322601

2533-
// "not recommended" in its own getter.
2534-
if (Kind == LookupKind::ValueInDeclContext) {
2535-
if (auto accessor = dyn_cast<AccessorDecl>(CurrDeclContext)) {
2536-
if (accessor->getStorage() == VD && accessor->isGetter())
2537-
Builder.setNotRecommended(CodeCompletionResult::NoReason);
2538-
}
2539-
}
2602+
if (NotRecommended)
2603+
Builder.setNotRecommended(*NotRecommended);
25402604

2541-
if (!VD->hasInterfaceType())
2605+
if (!VarType)
25422606
return;
25432607

2544-
// Add a type annotation.
2545-
Type VarType = getTypeOfMember(VD, dynamicLookupInfo);
25462608
if (auto *PD = dyn_cast<ParamDecl>(VD)) {
25472609
if (Name != Ctx.Id_self && PD->isInOut()) {
25482610
// It is useful to show inout for function parameters.
@@ -2566,6 +2628,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25662628
else
25672629
addTypeAnnotation(Builder, VarType, genericSig);
25682630

2631+
if (implicitlyAsync)
2632+
Builder.addAnnotatedAsync();
2633+
25692634
if (isUnresolvedMemberIdealType(VarType))
25702635
Builder.setSemanticContext(SemanticContextKind::ExpressionSpecific);
25712636
}
@@ -2686,11 +2751,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26862751

26872752
static void addEffectsSpecifiers(CodeCompletionResultBuilder &Builder,
26882753
const AnyFunctionType *AFT,
2689-
const AbstractFunctionDecl *AFD) {
2754+
const AbstractFunctionDecl *AFD,
2755+
bool forceAsync = false) {
26902756
assert(AFT != nullptr);
26912757

26922758
// 'async'.
2693-
if ((AFD && AFD->hasAsync()) || AFT->isAsync())
2759+
if (forceAsync || (AFD && AFD->hasAsync()) || AFT->isAsync())
26942760
Builder.addAnnotatedAsync();
26952761

26962762
// 'throws' or 'rethrows'.
@@ -2861,8 +2927,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
28612927
else
28622928
addTypeAnnotation(Builder, AFT->getResult(), genericSig);
28632929

2864-
if (AFT->isAsync() &&
2865-
!canDeclContextHandlesAsync(CurrDeclContext)) {
2930+
if (AFT->isAsync() && !CanCurrDeclContextHandleAsync) {
28662931
Builder.setNotRecommended(
28672932
CodeCompletionResult::NotRecommendedReason::InvalidContext);
28682933
}
@@ -2973,6 +3038,16 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
29733038
if (AFT && !IsImplicitlyCurriedInstanceMethod)
29743039
trivialTrailingClosure = hasTrivialTrailingClosure(FD, AFT);
29753040

3041+
Optional<CodeCompletionResult::NotRecommendedReason> NotRecommended;
3042+
bool implictlyAsync = false;
3043+
analyzeActorIsolation(FD, AFT, implictlyAsync, NotRecommended);
3044+
3045+
if (!NotRecommended && !IsImplicitlyCurriedInstanceMethod &&
3046+
((AFT && AFT->isAsync()) || implictlyAsync) &&
3047+
!CanCurrDeclContextHandleAsync) {
3048+
NotRecommended = CodeCompletionResult::InvalidContext;
3049+
}
3050+
29763051
// Add the method, possibly including any default arguments.
29773052
auto addMethodImpl = [&](bool includeDefaultArgs = true,
29783053
bool trivialTrailingClosure = false) {
@@ -2983,6 +3058,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
29833058
expectedTypeContext);
29843059
setClangDeclKeywords(FD, Pairs, Builder);
29853060
Builder.setAssociatedDecl(FD);
3061+
3062+
if (NotRecommended)
3063+
Builder.setNotRecommended(*NotRecommended);
3064+
29863065
addLeadingDot(Builder);
29873066
addValueBaseName(Builder, Name);
29883067
if (IsDynamicLookup)
@@ -3004,14 +3083,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
30043083
Builder.addRightParen();
30053084
} else if (trivialTrailingClosure) {
30063085
Builder.addBraceStmtWithCursor(" { code }");
3007-
addEffectsSpecifiers(Builder, AFT, FD);
3086+
addEffectsSpecifiers(Builder, AFT, FD, implictlyAsync);
30083087
} else {
30093088
Builder.addLeftParen();
30103089
addCallArgumentPatterns(Builder, AFT, FD->getParameters(),
30113090
FD->getGenericSignatureOfContext(),
30123091
includeDefaultArgs);
30133092
Builder.addRightParen();
3014-
addEffectsSpecifiers(Builder, AFT, FD);
3093+
addEffectsSpecifiers(Builder, AFT, FD, implictlyAsync);
30153094
}
30163095

30173096
// Build type annotation.
@@ -3076,13 +3155,6 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
30763155
ResultType->isVoid()) {
30773156
Builder.setExpectedTypeRelation(CodeCompletionResult::Invalid);
30783157
}
3079-
3080-
if (!IsImplicitlyCurriedInstanceMethod &&
3081-
AFT->isAsync() &&
3082-
!canDeclContextHandlesAsync(CurrDeclContext)) {
3083-
Builder.setNotRecommended(
3084-
CodeCompletionResult::NotRecommendedReason::InvalidContext);
3085-
}
30863158
};
30873159

30883160
if (!AFT || IsImplicitlyCurriedInstanceMethod) {
@@ -3170,8 +3242,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
31703242
addTypeAnnotation(Builder, *Result, CD->getGenericSignatureOfContext());
31713243
}
31723244

3173-
if (ConstructorType->isAsync() &&
3174-
!canDeclContextHandlesAsync(CurrDeclContext)) {
3245+
if (ConstructorType->isAsync() && !CanCurrDeclContextHandleAsync) {
31753246
Builder.setNotRecommended(
31763247
CodeCompletionResult::NotRecommendedReason::InvalidContext);
31773248
}
@@ -3223,13 +3294,24 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
32233294
if (!subscriptType)
32243295
return;
32253296

3297+
Optional<CodeCompletionResult::NotRecommendedReason> NotRecommended;
3298+
bool implictlyAsync = false;
3299+
analyzeActorIsolation(SD, subscriptType, implictlyAsync, NotRecommended);
3300+
3301+
if (!NotRecommended && implictlyAsync && !CanCurrDeclContextHandleAsync) {
3302+
NotRecommended = CodeCompletionResult::InvalidContext;
3303+
}
3304+
32263305
CommandWordsPairs Pairs;
32273306
CodeCompletionResultBuilder Builder(
32283307
Sink, CodeCompletionResult::ResultKind::Declaration,
32293308
getSemanticContext(SD, Reason, dynamicLookupInfo), expectedTypeContext);
32303309
Builder.setAssociatedDecl(SD);
32313310
setClangDeclKeywords(SD, Pairs, Builder);
32323311

3312+
if (NotRecommended)
3313+
Builder.setNotRecommended(*NotRecommended);
3314+
32333315
// '\TyName#^TOKEN^#' requires leading dot.
32343316
if (!HaveDot && IsAfterSwiftKeyPathRoot)
32353317
Builder.addLeadingDot();
@@ -3251,6 +3333,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
32513333
// Optional<T> type.
32523334
resultTy = OptionalType::get(resultTy);
32533335
}
3336+
3337+
if (implictlyAsync)
3338+
Builder.addAnnotatedAsync();
3339+
32543340
addTypeAnnotation(Builder, resultTy, SD->getGenericSignatureOfContext());
32553341
}
32563342

@@ -3916,6 +4002,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
39164002
if (ExprType->isAnyExistentialType())
39174003
ExprType = OpenedArchetypeType::getAny(ExprType);
39184004

4005+
if (!IsSelfRefExpr && !IsSuperRefExpr && ExprType->getAnyNominal() &&
4006+
ExprType->getAnyNominal()->isActor()) {
4007+
IsCrossActorReference = true;
4008+
}
4009+
39194010
if (WasOptional)
39204011
ExprType = OptionalType::get(ExprType);
39214012

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)