Skip to content

Commit 4b0d5e7

Browse files
authored
Merge pull request #2354 from swiftwasm/main
[pull] swiftwasm from main
2 parents af158aa + 7c78207 commit 4b0d5e7

File tree

10 files changed

+319
-16
lines changed

10 files changed

+319
-16
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4210,6 +4210,13 @@ ERROR(actor_isolated_self_independent_context,none,
42104210
"actor-isolated %0 %1 can not be referenced from an "
42114211
"'@actorIndependent' context",
42124212
(DescriptiveDeclKind, DeclName))
4213+
ERROR(actor_isolated_inout_state,none,
4214+
"actor-isolated %0 %1 cannot be passed 'inout' to"
4215+
"%select{| implicitly}2 'async' function call",
4216+
(DescriptiveDeclKind, DeclName, bool))
4217+
ERROR(actor_isolated_mutating_func,none,
4218+
"cannot call mutating async function %0 on actor-isolated %1 %2",
4219+
(DeclName, DescriptiveDeclKind, DeclName))
42134220
ERROR(actor_isolated_global_actor_context,none,
42144221
"actor-isolated %0 %1 can not be referenced from context of global "
42154222
"actor %2",

include/swift/AST/IRGenOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ struct PointerAuthOptions : clang::PointerAuthOptions {
143143

144144
/// The function to call to resume running in the parent context.
145145
PointerAuthSchema AsyncContextResume;
146+
147+
/// The resume function stored in AsyncTask.
148+
PointerAuthSchema TaskResumeFunction;
146149
};
147150

148151
enum class JITDebugArtifact : unsigned {

lib/IRGen/GenFunc.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2462,7 +2462,16 @@ IRGenFunction::createAsyncDispatchFn(const FunctionPointer &fnPtr,
24622462

24632463
void IRGenFunction::emitSuspensionPoint(llvm::Value *toExecutor,
24642464
llvm::Value *asyncResume) {
2465-
// TODO: pointerauth
2465+
2466+
llvm::Value *task = getAsyncTask();
2467+
llvm::Value *callableResume = asyncResume;
2468+
2469+
if (auto schema = IGM.getOptions().PointerAuth.TaskResumeFunction) {
2470+
auto *resumeAddr = Builder.CreateStructGEP(task, 4);
2471+
auto authInfo = PointerAuthInfo::emit(
2472+
*this, schema, resumeAddr, PointerAuthEntity());
2473+
callableResume = emitPointerAuthSign(*this, asyncResume, authInfo);
2474+
}
24662475

24672476
// Setup the suspend point.
24682477
SmallVector<llvm::Value *, 8> arguments;
@@ -2474,10 +2483,10 @@ void IRGenFunction::emitSuspensionPoint(llvm::Value *toExecutor,
24742483
arguments.push_back(
24752484
Builder.CreateBitOrPointerCast(suspendFn, IGM.Int8PtrTy));
24762485

2477-
arguments.push_back(asyncResume);
2486+
arguments.push_back(callableResume);
24782487
arguments.push_back(
24792488
Builder.CreateBitOrPointerCast(toExecutor, getAsyncExecutor()->getType()));
2480-
arguments.push_back(getAsyncTask());
2489+
arguments.push_back(task);
24812490
arguments.push_back(getAsyncExecutor());
24822491
arguments.push_back(getAsyncContext());
24832492

lib/IRGen/IRGen.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,10 @@ static void setPointerAuthOptions(PointerAuthOptions &opts,
699699
opts.AsyncContextResume =
700700
PointerAuthSchema(codeKey, /*address*/ true, Discrimination::Constant,
701701
SpecialPointerAuthDiscriminators::AsyncContextResume);
702+
703+
opts.TaskResumeFunction =
704+
PointerAuthSchema(codeKey, /*address*/ true, Discrimination::Constant,
705+
SpecialPointerAuthDiscriminators::TaskResumeFunction);
702706
}
703707

704708
std::unique_ptr<llvm::TargetMachine>

lib/Sema/CSBindings.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,9 @@ ConstraintSystem::getPotentialBindingForRelationalConstraint(
865865

866866
// If the type we'd be binding to is a dependent member, don't try to
867867
// resolve this type variable yet.
868-
if (type->is<DependentMemberType>()) {
868+
if (type->getWithoutSpecifierType()
869+
->lookThroughAllOptionalTypes()
870+
->is<DependentMemberType>()) {
869871
SmallVector<TypeVariableType *, 4> referencedVars;
870872
type->getTypeVariables(referencedVars);
871873

@@ -1210,15 +1212,19 @@ Optional<Type> ConstraintSystem::checkTypeOfBinding(TypeVariableType *typeVar,
12101212
}
12111213
}
12121214

1213-
// If the type is a type variable itself, don't permit the binding.
1214-
if (type->getRValueType()->is<TypeVariableType>())
1215-
return None;
1215+
{
1216+
auto objType = type->getWithoutSpecifierType();
12161217

1217-
// Don't bind to a dependent member type, even if it's currently
1218-
// wrapped in any number of optionals, because binding producer
1219-
// might unwrap and try to attempt it directly later.
1220-
if (type->lookThroughAllOptionalTypes()->is<DependentMemberType>())
1221-
return None;
1218+
// If the type is a type variable itself, don't permit the binding.
1219+
if (objType->is<TypeVariableType>())
1220+
return None;
1221+
1222+
// Don't bind to a dependent member type, even if it's currently
1223+
// wrapped in any number of optionals, because binding producer
1224+
// might unwrap and try to attempt it directly later.
1225+
if (objType->lookThroughAllOptionalTypes()->is<DependentMemberType>())
1226+
return None;
1227+
}
12221228

12231229
// Okay, allow the binding (with the simplified type).
12241230
return type;

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,24 @@ findMemberReference(Expr *expr) {
611611
return None;
612612
}
613613

614+
/// Return true if the callee of an ApplyExpr is async
615+
///
616+
/// Note that this must be called after the implicitlyAsync flag has been set,
617+
/// or implicitly async calls will not return the correct value.
618+
static bool isAsyncCall(const ApplyExpr *call) {
619+
if (call->implicitlyAsync())
620+
return true;
621+
622+
// Effectively the same as doing a
623+
// `cast_or_null<FunctionType>(call->getFn()->getType())`, check the
624+
// result of that and then checking `isAsync` if it's defined.
625+
Type funcTypeType = call->getFn()->getType();
626+
if (!funcTypeType)
627+
return false;
628+
FunctionType *funcType = funcTypeType->castTo<FunctionType>();
629+
return funcType->isAsync();
630+
}
631+
614632
namespace {
615633
/// Check for adherence to the actor isolation rules, emitting errors
616634
/// when actor-isolated declarations are used in an unsafe manner.
@@ -679,6 +697,11 @@ namespace {
679697
return { true, expr };
680698
}
681699

700+
if (auto inout = dyn_cast<InOutExpr>(expr)) {
701+
if (!applyStack.empty())
702+
diagnoseInOutArg(applyStack.back(), inout, false);
703+
}
704+
682705
if (auto lookup = dyn_cast<LookupExpr>(expr)) {
683706
checkMemberReference(lookup->getBase(), lookup->getMember(),
684707
lookup->getLoc());
@@ -720,11 +743,22 @@ namespace {
720743
Expr *fn = call->getFn()->getValueProvidingExpr();
721744
if (auto memberRef = findMemberReference(fn)) {
722745
checkMemberReference(
723-
call->getArg(), memberRef->first, memberRef->second,
746+
call->getArg(), memberRef->first, memberRef->second,
724747
/*isEscapingPartialApply=*/false, /*maybeImplicitAsync=*/true);
725748

726749
call->getArg()->walk(*this);
727750

751+
if (applyStack.size() >= 2) {
752+
ApplyExpr *outerCall = applyStack[applyStack.size() - 2];
753+
if (isAsyncCall(outerCall)) {
754+
// This call is a partial application within an async call.
755+
// If the partial application take a value inout, it is bad.
756+
if (InOutExpr *inoutArg = dyn_cast<InOutExpr>(
757+
call->getArg()->getSemanticsProvidingExpr()))
758+
diagnoseInOutArg(outerCall, inoutArg, true);
759+
}
760+
}
761+
728762
// manual clean-up since normal traversal is skipped
729763
assert(applyStack.back() == dyn_cast<ApplyExpr>(expr));
730764
applyStack.pop_back();
@@ -860,6 +894,59 @@ namespace {
860894
return true;
861895
}
862896

897+
/// Diagnose an inout argument passed into an async call
898+
///
899+
/// \returns true if we diagnosed the entity, \c false otherwise.
900+
bool diagnoseInOutArg(const ApplyExpr *call, const InOutExpr *arg,
901+
bool isPartialApply) {
902+
// check that the call is actually async
903+
if (!isAsyncCall(call))
904+
return false;
905+
906+
Expr *subArg = arg->getSubExpr();
907+
if (LookupExpr *baseArg = dyn_cast<LookupExpr>(subArg)) {
908+
while (LookupExpr *nextLayer = dyn_cast<LookupExpr>(baseArg->getBase()))
909+
baseArg = nextLayer;
910+
// subArg: the actual property being passed inout
911+
// baseArg: the property in the actor who's property is being passed
912+
// inout
913+
914+
auto memberDecl = baseArg->getMember().getDecl();
915+
auto isolation = ActorIsolationRestriction::forDeclaration(memberDecl);
916+
switch (isolation) {
917+
case ActorIsolationRestriction::Unrestricted:
918+
case ActorIsolationRestriction::LocalCapture:
919+
case ActorIsolationRestriction::Unsafe:
920+
case ActorIsolationRestriction::GlobalActor: // TODO: handle global
921+
// actors
922+
break;
923+
case ActorIsolationRestriction::ActorSelf: {
924+
if (isPartialApply) {
925+
// The partially applied InoutArg is a property of actor. This can
926+
// really only happen when the property is a struct with a mutating
927+
// async method.
928+
if (auto partialApply = dyn_cast<ApplyExpr>(call->getFn())) {
929+
ValueDecl *fnDecl =
930+
cast<DeclRefExpr>(partialApply->getFn())->getDecl();
931+
ctx.Diags.diagnose(
932+
call->getLoc(), diag::actor_isolated_mutating_func,
933+
fnDecl->getName(), memberDecl->getDescriptiveKind(),
934+
memberDecl->getName());
935+
return true;
936+
}
937+
} else {
938+
ctx.Diags.diagnose(
939+
subArg->getLoc(), diag::actor_isolated_inout_state,
940+
memberDecl->getDescriptiveKind(), memberDecl->getName(),
941+
call->implicitlyAsync());
942+
return true;
943+
}
944+
}
945+
}
946+
}
947+
return false;
948+
}
949+
863950
/// Get the actor isolation of the innermost relevant context.
864951
ActorIsolation getInnermostIsolatedContext(const DeclContext *constDC) {
865952
// Retrieve the actor isolation for a declaration somewhere in our
@@ -1196,7 +1283,9 @@ namespace {
11961283
llvm_unreachable("Locals cannot be referenced with member syntax");
11971284

11981285
case ActorIsolationRestriction::Unsafe:
1199-
return diagnoseReferenceToUnsafe(member, memberLoc);
1286+
// This case is hit when passing actor state inout to functions in some
1287+
// cases. The error is emitted by diagnoseInOutArg.
1288+
return false;
12001289
}
12011290
llvm_unreachable("unhandled actor isolation kind!");
12021291
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency
2+
// REQUIRES: concurrency
3+
4+
// Verify that we don't allow actor-isolated state to be passed via inout
5+
// Check:
6+
// - can't pass it into a normal async function
7+
// - can't pass it into a first-class async function as a value
8+
// - can't pass it into another actor method
9+
// - can't pass it into a curried/partially applied function
10+
// - can't pass it inout to a function that doesn't directly touch it
11+
// - can't pass it into a function that was passed into the calling method
12+
// - can't call async mutating function on actor isolated state
13+
14+
struct Point {
15+
var x: Int
16+
var y: Int
17+
18+
mutating func setComponents(x: inout Int, y: inout Int) async {
19+
defer { (x, y) = (self.x, self.y) }
20+
(self.x, self.y) = (x, y)
21+
}
22+
}
23+
24+
actor class TestActor {
25+
var position = Point(x: 0, y: 0)
26+
var nextPosition = Point(x: 0, y: 1)
27+
var value1: Int = 0
28+
var value2: Int = 1
29+
}
30+
31+
func modifyAsynchronously(_ foo: inout Int) async { foo += 1 }
32+
let modifyAsyncValue = modifyAsynchronously
33+
34+
// external function call
35+
extension TestActor {
36+
37+
// Can't pass actor-isolated primitive into a function
38+
func inoutAsyncFunctionCall() async {
39+
// expected-error@+1{{actor-isolated property 'value1' cannot be passed 'inout' to 'async' function call}}
40+
await modifyAsynchronously(&value1)
41+
}
42+
43+
func inoutAsyncClosureCall() async {
44+
// expected-error@+1{{actor-isolated property 'value1' cannot be passed 'inout' to 'async' function call}}
45+
await { (_ foo: inout Int) async in foo += 1 }(&value1)
46+
}
47+
48+
// Can't pass actor-isolated primitive into first-class function value
49+
func inoutAsyncValueCall() async {
50+
// expected-error@+1{{actor-isolated property 'value1' cannot be passed 'inout' to 'async' function call}}
51+
await modifyAsyncValue(&value1)
52+
}
53+
54+
// Can't pass property of actor-isolated state inout to async function
55+
func inoutPropertyStateValueCall() async {
56+
// expected-error@+1{{actor-isolated property 'position' cannot be passed 'inout' to 'async' function call}}
57+
await modifyAsynchronously(&position.x)
58+
}
59+
}
60+
61+
// internal method call
62+
extension TestActor {
63+
func modifyByValue(_ other: inout Int) async {
64+
other += value1
65+
}
66+
67+
func passStateIntoMethod() async {
68+
// expected-error@+1{{actor-isolated property 'value1' cannot be passed 'inout' to 'async' function call}}
69+
await modifyByValue(&value1)
70+
}
71+
}
72+
73+
// external class method call
74+
class NonAsyncClass {
75+
func modifyOtherAsync(_ other : inout Int) async {
76+
// ...
77+
}
78+
79+
func modifyOtherNotAsync(_ other: inout Int) {
80+
// ...
81+
}
82+
}
83+
84+
// Calling external class/struct async function
85+
extension TestActor {
86+
// Can't pass state into async method of another class
87+
88+
func passStateIntoDifferentClassMethod() async {
89+
let other = NonAsyncClass()
90+
let otherCurry = other.modifyOtherAsync
91+
// expected-error@+1{{actor-isolated property 'value2' cannot be passed 'inout' to 'async' function call}}
92+
await other.modifyOtherAsync(&value2)
93+
// expected-error@+1{{actor-isolated property 'value1' cannot be passed 'inout' to 'async' function call}}
94+
await otherCurry(&value1)
95+
other.modifyOtherNotAsync(&value2) // This is okay since it's not async!
96+
97+
}
98+
99+
func callMutatingFunctionOnStruct() async {
100+
// expected-error@+3:20{{cannot call mutating async function 'setComponents(x:y:)' on actor-isolated property 'position'}}
101+
// expected-error@+2:51{{actor-isolated property 'nextPosition' cannot be passed 'inout' to 'async' function call}}
102+
// expected-error@+1:71{{actor-isolated property 'nextPosition' cannot be passed 'inout' to 'async' function call}}
103+
await position.setComponents(x: &nextPosition.x, y: &nextPosition.y)
104+
105+
// expected-error@+3:20{{cannot call mutating async function 'setComponents(x:y:)' on actor-isolated property 'position'}}
106+
// expected-error@+2:38{{actor-isolated property 'value1' cannot be passed 'inout' to 'async' function call}}
107+
// expected-error@+1:50{{actor-isolated property 'value2' cannot be passed 'inout' to 'async' function call}}
108+
await position.setComponents(x: &value1, y: &value2)
109+
}
110+
}
111+
112+
// Check implicit async testing
113+
actor class DifferentActor {
114+
func modify(_ state: inout Int) {}
115+
}
116+
117+
extension TestActor {
118+
func modify(_ state: inout Int) {}
119+
120+
// Actor state passed inout to implicitly async function on an actor of the
121+
// same type
122+
func modifiedByOtherTestActor(_ other: TestActor) async {
123+
//expected-error@+1{{actor-isolated property 'value2' cannot be passed 'inout' to implicitly 'async' function call}}
124+
await other.modify(&value2)
125+
}
126+
127+
// Actor state passed inout to an implicitly async function on an actor of a
128+
// different type
129+
func modifiedByOther(_ other: DifferentActor) async {
130+
//expected-error@+1{{actor-isolated property 'value2' cannot be passed 'inout' to implicitly 'async' function call}}
131+
await other.modify(&value2)
132+
}
133+
}

test/DebugInfo/async-args.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %target-swift-frontend %s -emit-ir -g -o - \
22
// RUN: -module-name M -enable-experimental-concurrency | %FileCheck %s
3+
// REQUIRES: concurrency
34

45
func use<T>(_ t: T) {}
56
func forceSplit() async {

0 commit comments

Comments
 (0)