Skip to content

Commit 3a3aaf1

Browse files
committed
Use forEachChild for traversal instead of ad-hoc traversal
Replacing ad-hoc expression tree with forEachChild traversal and matching the desired expression. This should avoid crashes, but may accept invalid code in the future.
1 parent 3f68fa1 commit 3a3aaf1

File tree

2 files changed

+59
-85
lines changed

2 files changed

+59
-85
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 57 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -637,42 +637,6 @@ static bool isAsyncCall(const ApplyExpr *call) {
637637
return funcType->isAsync();
638638
}
639639

640-
/// Extract the correct expression for diagnosing actor state being passed inout
641-
///
642-
/// This unwraps most expression types, working toward an offending DeclRefExpr
643-
/// or to the LookupExpr before the DeclRef.
644-
///
645-
/// The return will either be a LookupExpr or a DeclRefExpr.
646-
/// If part of the sub-expression isn't interesting, returns nullptr
647-
static const Expr *getInoutTopExpr(const InOutExpr *arg) {
648-
const Expr * currentExpr = arg->getSubExpr();
649-
while (currentExpr) {
650-
if (const InOutExpr *inout = dyn_cast<InOutExpr>(currentExpr)) {
651-
return nullptr; // The AST walker will hit this again
652-
} else if (const LookupExpr *baseArg = dyn_cast<LookupExpr>(currentExpr)) {
653-
if (isa<DeclRefExpr>(baseArg->getBase()))
654-
return baseArg;
655-
currentExpr = baseArg->getBase();
656-
} else if (const DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(currentExpr))
657-
return declRef;
658-
else if (const BindOptionalExpr *bindingOp =
659-
dyn_cast<BindOptionalExpr>(currentExpr))
660-
currentExpr = bindingOp->getSubExpr();
661-
else if (const ForceValueExpr *forceOp =
662-
dyn_cast<ForceValueExpr>(currentExpr))
663-
currentExpr = forceOp->getSubExpr();
664-
else if (const ImplicitConversionExpr *convExpr =
665-
dyn_cast<ImplicitConversionExpr>(currentExpr))
666-
currentExpr = convExpr->getSubExpr();
667-
else if (const IdentityExpr *identExpr =
668-
dyn_cast<IdentityExpr>(currentExpr))
669-
currentExpr = identExpr->getSubExpr();
670-
else
671-
return currentExpr;
672-
}
673-
llvm_unreachable("The current expression is a nullptr!");
674-
}
675-
676640
/// Determine whether we should diagnose data races within the current context.
677641
///
678642
/// By default, we do this only in code that makes use of concurrency
@@ -998,55 +962,65 @@ namespace {
998962
if (!isAsyncCall(call))
999963
return false;
1000964

1001-
const Expr *subArg = getInoutTopExpr(arg);
1002-
if (subArg == nullptr) // Subexpression isn't interesting
1003-
return false;
1004-
ValueDecl *valueDecl = nullptr;
1005-
if (const DeclRefExpr *declExpr = dyn_cast<DeclRefExpr>(subArg))
1006-
valueDecl = declExpr->getDecl();
1007-
else if (const LookupExpr * member = dyn_cast<LookupExpr>(subArg))
1008-
valueDecl = member->getMember().getDecl();
1009-
else
1010-
llvm_unreachable("Unhandled expression type");
1011-
auto isolation = ActorIsolationRestriction::forDeclaration(valueDecl);
1012-
switch (isolation) {
1013-
case ActorIsolationRestriction::Unrestricted:
1014-
case ActorIsolationRestriction::LocalCapture:
1015-
case ActorIsolationRestriction::Unsafe:
1016-
break;
1017-
case ActorIsolationRestriction::GlobalActor: {
1018-
ctx.Diags.diagnose(call->getLoc(),
1019-
diag::actor_isolated_inout_state,
1020-
valueDecl->getDescriptiveKind(),
1021-
valueDecl->getName(),
1022-
call->implicitlyAsync());
1023-
valueDecl->diagnose(diag::kind_declared_here,
1024-
valueDecl->getDescriptiveKind());
1025-
return true;
1026-
}
1027-
case ActorIsolationRestriction::ActorSelf: {
1028-
if (isPartialApply) {
1029-
// The partially applied InoutArg is a property of actor. This can
1030-
// really only happen when the property is a struct with a mutating
1031-
// async method.
1032-
if (auto partialApply = dyn_cast<ApplyExpr>(call->getFn())) {
1033-
ValueDecl *fnDecl =
1034-
cast<DeclRefExpr>(partialApply->getFn())->getDecl();
1035-
ctx.Diags.diagnose(
1036-
call->getLoc(), diag::actor_isolated_mutating_func,
1037-
fnDecl->getName(), valueDecl->getDescriptiveKind(),
1038-
valueDecl->getName());
1039-
return true;
965+
bool result = false;
966+
auto checkDiagnostic = [this, call, isPartialApply,
967+
&result](ValueDecl *decl, SourceLoc argLoc) {
968+
auto isolation = ActorIsolationRestriction::forDeclaration(decl);
969+
switch (isolation) {
970+
case ActorIsolationRestriction::Unrestricted:
971+
case ActorIsolationRestriction::LocalCapture:
972+
case ActorIsolationRestriction::Unsafe:
973+
break;
974+
case ActorIsolationRestriction::GlobalActor: {
975+
ctx.Diags.diagnose(argLoc, diag::actor_isolated_inout_state,
976+
decl->getDescriptiveKind(), decl->getName(),
977+
call->implicitlyAsync());
978+
decl->diagnose(diag::kind_declared_here, decl->getDescriptiveKind());
979+
result = true;
980+
break;
981+
}
982+
case ActorIsolationRestriction::ActorSelf: {
983+
if (isPartialApply) {
984+
// The partially applied InoutArg is a property of actor. This
985+
// can really only happen when the property is a struct with a
986+
// mutating async method.
987+
if (auto partialApply = dyn_cast<ApplyExpr>(call->getFn())) {
988+
ValueDecl *fnDecl =
989+
cast<DeclRefExpr>(partialApply->getFn())->getDecl();
990+
ctx.Diags.diagnose(call->getLoc(),
991+
diag::actor_isolated_mutating_func,
992+
fnDecl->getName(), decl->getDescriptiveKind(),
993+
decl->getName());
994+
result = true;
995+
}
996+
} else {
997+
ctx.Diags.diagnose(argLoc, diag::actor_isolated_inout_state,
998+
decl->getDescriptiveKind(), decl->getName(),
999+
call->implicitlyAsync());
1000+
result = true;
10401001
}
1041-
} else {
1042-
ctx.Diags.diagnose(
1043-
subArg->getLoc(), diag::actor_isolated_inout_state,
1044-
valueDecl->getDescriptiveKind(), valueDecl->getName(), call->implicitlyAsync());
1045-
return true;
1002+
break;
10461003
}
1047-
}
1048-
}
1049-
return false;
1004+
}
1005+
};
1006+
auto expressionWalker = [baseArg = arg->getSubExpr(),
1007+
checkDiagnostic](Expr *expr) -> Expr * {
1008+
if (isa<InOutExpr>(expr))
1009+
return nullptr; // AST walker will hit this again
1010+
if (LookupExpr *lookup = dyn_cast<LookupExpr>(expr)) {
1011+
if (isa<DeclRefExpr>(lookup->getBase())) {
1012+
checkDiagnostic(lookup->getMember().getDecl(), baseArg->getLoc());
1013+
return nullptr; // Diagnosed. Don't keep walking
1014+
}
1015+
}
1016+
if (DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(expr)) {
1017+
checkDiagnostic(declRef->getDecl(), baseArg->getLoc());
1018+
return nullptr; // Diagnosed. Don't keep walking
1019+
}
1020+
return expr;
1021+
};
1022+
arg->getSubExpr()->forEachChildExpr(expressionWalker);
1023+
return result;
10501024
}
10511025

10521026
/// Get the actor isolation of the innermost relevant context.

test/Concurrency/actor_inout_isolation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ extension TestActor {
110110

111111
func callMutatingFunctionOnStruct() async {
112112
// expected-error@+3:20{{cannot call mutating async function 'setComponents(x:y:)' on actor-isolated property 'position'}}
113-
// expected-error@+2:38{{actor-isolated property 'nextPosition' cannot be passed 'inout' to 'async' function call}}
114-
// expected-error@+1:58{{actor-isolated property 'nextPosition' cannot be passed 'inout' to 'async' function call}}
113+
// expected-error@+2:51{{actor-isolated property 'nextPosition' cannot be passed 'inout' to 'async' function call}}
114+
// expected-error@+1:71{{actor-isolated property 'nextPosition' cannot be passed 'inout' to 'async' function call}}
115115
await position.setComponents(x: &nextPosition.x, y: &nextPosition.y)
116116

117117
// expected-error@+3:20{{cannot call mutating async function 'setComponents(x:y:)' on actor-isolated property 'position'}}

0 commit comments

Comments
 (0)