Skip to content

Commit d376ee9

Browse files
committed
Run first thunk of Async main synchronously
This patch updates the asynchronous main function to run the first thunk of the function synchronously through a call to `swift_job_run`. The runloop is killed by exiting or aborting the task that it is running on. As such, we need to ensure that the task contains an async function that either calls exit explicitly or aborts. The AsyncEntryPoint, that contains this code, was added in the previous patch. This patch adds the pieces for the actual implementation of this behaviour as well as adding the necessary code to start the runloop. There are now four layers of main functions before hitting the "real" code. @main: This is the actual main entrypoint of the program. This constructs the task containing @async_main, grabs the main executor, runs swift_job_run to run the first part synchronously, and finally kicks off the runloop with a call to _asyncMainDrainQueue. This is generated in the call to `emitAsyncMainThreadStart`. @async_main: This thunk exists to ensure that the main function calls `exit` at some point so that the runloop stops. It also handles emitting an error if the user-written main function throws. e.g: ``` func async_main() async -> () { do { try await Main.$main() exit(0) } catch { _errorInMain(error) } } ``` Main.$main(): This still has the same behaviour as with the synchronous case. It just calls `try await Main.main()` and exists to simplify typechecking. Main.main(): This is the actual user-specified main. It serves the same purpose as in the synchronous, allowing the programmer to write code, but it's async! The control flow in `emitFunctionDefinition` is a little confusing (to me anyway), so here it is spelled out: If the main function is synchronous, the `constant.kind` will be a `SILDeclRef::Kind::EntryPoint`, but the `decl` won't be async, so it drops down to `emitArtificalTopLevel` anyway. If the main function is async and we're generating `@main`, the `constant.kind` will be `SILDeclRef::Kind::AsyncEntryPoint`, so we also call `emitArtificalTopLevel`. `emitArtificalTopLevel` is responsible for detecting whether the decl is async and deciding whether to emit code to extract the argc/argv variables that get passed into the actual main entrypoint to the program. If we're generating the `@async_main` body, the kind will be `SILDeclRef::Kind::EntryPoint` and the `decl` will be async, so we grab the mainEntryPoint decl and call `emitAsyncMainThreadStart` to generate the wrapping code. Note; there is a curious change in `SILLocation::getSourceLoc()` where instead of simply checking `isFilenameAndLocation()`, I change it to `getStorageKind() == FilenameAndLocationKind`. This is because the SILLocation returned is to a FilenameAndLocationKind, but the actual storage returns true for the call to `isNull()` inside of the `isFilenameAndLocation()` call. This results in us incorrectly falling through to the `getASTNode()` call below that, which asserts when asked to get the AST node of a location. I also did a little bit of refactoring in the SILGenModule for grabbing intrinsics. Previously, there was only a `getConcurrencyIntrinsic` function, which would only load FuncDecls out of the concurrency module. The `exit` function is in the concurrency shims module, so I refactored the load code to take a ModuleDecl to search from. The emitBuiltinCreateAsyncTask function symbol is exposed from SILGenBuiltin so that it is available from SILGenFunction. There is a fair bit of work involved going from what is available at the SGF to what is needed for actually calling the CreateAsyncTask builtin, so in order to avoid additional maintenance, it's good to re-use that.
1 parent 552ae06 commit d376ee9

File tree

8 files changed

+299
-65
lines changed

8 files changed

+299
-65
lines changed

lib/SIL/IR/SILLocation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ SourceLoc SILLocation::getSourceLoc() const {
4242

4343
// Don't crash if the location is a FilenameAndLocation.
4444
// TODO: this is a workaround until rdar://problem/25225083 is implemented.
45-
if (isFilenameAndLocation())
45+
if (getStorageKind() == FilenameAndLocationKind)
4646
return SourceLoc();
4747

4848
return getSourceLoc(getPrimaryASTNode());

lib/SILGen/SILGen.cpp

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,37 @@ SILGenModule::getCheckExpectedExecutor() {
440440
"_checkExpectedExecutor");
441441
}
442442

443+
FuncDecl *SILGenModule::getAsyncMainDrainQueue() {
444+
return lookupConcurrencyIntrinsic(getASTContext(), AsyncMainDrainQueue,
445+
"_asyncMainDrainQueue");
446+
}
447+
448+
FuncDecl *SILGenModule::getGetMainExecutor() {
449+
return lookupConcurrencyIntrinsic(getASTContext(), GetMainExecutor,
450+
"_getMainExecutor");
451+
}
452+
453+
FuncDecl *SILGenModule::getSwiftJobRun() {
454+
return lookupConcurrencyIntrinsic(getASTContext(), SwiftJobRun,
455+
"_swiftJobRun");
456+
}
457+
458+
FuncDecl *SILGenModule::getExit() {
459+
if (ExitFunc)
460+
return *ExitFunc;
461+
462+
ASTContext &C = getASTContext();
463+
ModuleDecl *concurrencyShims =
464+
C.getModuleByIdentifier(C.getIdentifier("_SwiftConcurrencyShims"));
465+
466+
if (!concurrencyShims) {
467+
ExitFunc = nullptr;
468+
return nullptr;
469+
}
470+
471+
return lookupIntrinsic(*concurrencyShims, ExitFunc, C.getIdentifier("exit"));
472+
}
473+
443474
ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {
444475
if (NSErrorConformanceToError)
445476
return *NSErrorConformanceToError;
@@ -1050,7 +1081,27 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
10501081
auto *decl = constant.getDecl();
10511082
auto *dc = decl->getDeclContext();
10521083
PrettyStackTraceSILFunction X("silgen emitArtificialTopLevel", f);
1053-
SILGenFunction(*this, *f, dc).emitArtificialTopLevel(decl);
1084+
// In all cases, a constant.kind == EntryPoint indicates the main entrypoint
1085+
// to the program, @main.
1086+
// In the synchronous case, the decl is not async, so emitArtificialTopLevel
1087+
// emits the error unwrapping and call to MainType.$main() into @main.
1088+
//
1089+
// In the async case, emitAsyncMainThreadStart is responsible for generating
1090+
// the contents of @main. This wraps @async_main in a task, passes that task
1091+
// to swift_job_run to execute the first thunk, and starts the runloop to
1092+
// run any additional continuations. The kind is EntryPoint, and the decl is
1093+
// async.
1094+
// When the kind is 'AsyncMain', we are generating @async_main. In this
1095+
// case, emitArtificialTopLevel emits the code for calling MaintType.$main,
1096+
// unwrapping errors, and calling exit(0) into @async_main to run the
1097+
// user-specified main function.
1098+
if (constant.kind == SILDeclRef::Kind::EntryPoint && isa<FuncDecl>(decl) &&
1099+
static_cast<FuncDecl *>(decl)->hasAsync()) {
1100+
SILDeclRef mainEntryPoint = SILDeclRef::getAsyncMainDeclEntryPoint(decl);
1101+
SILGenFunction(*this, *f, dc).emitAsyncMainThreadStart(mainEntryPoint);
1102+
} else {
1103+
SILGenFunction(*this, *f, dc).emitArtificialTopLevel(decl);
1104+
}
10541105
postEmitFunction(constant, f);
10551106
return;
10561107
}
@@ -2033,10 +2084,15 @@ class SILGenModuleRAII {
20332084

20342085
// If the source file contains an artificial main, emit the implicit
20352086
// top-level code.
2036-
if (auto *mainDecl = sf->getMainDecl())
2087+
if (auto *mainDecl = sf->getMainDecl()) {
2088+
if (isa<FuncDecl>(mainDecl) &&
2089+
static_cast<FuncDecl *>(mainDecl)->hasAsync())
2090+
emitSILFunctionDefinition(
2091+
SILDeclRef::getAsyncMainDeclEntryPoint(mainDecl));
20372092
emitSILFunctionDefinition(SILDeclRef::getMainDeclEntryPoint(mainDecl));
2093+
}
20382094
}
2039-
2095+
20402096
void emitSILFunctionDefinition(SILDeclRef ref) {
20412097
SGM.emitFunctionDefinition(ref, SGM.getFunction(ref, ForDefinition));
20422098
}

lib/SILGen/SILGen.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
131131
Optional<FuncDecl*> ResumeUnsafeThrowingContinuationWithError;
132132
Optional<FuncDecl*> CheckExpectedExecutor;
133133

134+
Optional<FuncDecl *> AsyncMainDrainQueue;
135+
Optional<FuncDecl *> GetMainExecutor;
136+
Optional<FuncDecl *> SwiftJobRun;
137+
Optional<FuncDecl *> ExitFunc;
138+
134139
public:
135140
SILGenModule(SILModule &M, ModuleDecl *SM);
136141

@@ -517,6 +522,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
517522
/// Retrieve the _Concurrency._checkExpectedExecutor intrinsic.
518523
FuncDecl *getCheckExpectedExecutor();
519524

525+
/// Retrieve the _Concurrency._asyncMainDrainQueue intrinsic.
526+
FuncDecl *getAsyncMainDrainQueue();
527+
/// Retrieve the _Concurrency._getMainExecutor intrinsic.
528+
FuncDecl *getGetMainExecutor();
529+
/// Retrieve the _Concurrency._swiftJobRun intrinsic.
530+
FuncDecl *getSwiftJobRun();
531+
// Retrieve the _SwiftConcurrencyShims.exit intrinsic.
532+
FuncDecl *getExit();
533+
520534
SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess,
521535
KeyPathTypeKind typeKind);
522536

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,9 +1402,10 @@ emitFunctionArgumentForAsyncTaskEntryPoint(SILGenFunction &SGF,
14021402
}
14031403

14041404
// Emit SIL for the named builtin: createAsyncTask.
1405-
static ManagedValue emitBuiltinCreateAsyncTask(
1406-
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
1407-
ArrayRef<ManagedValue> args, SGFContext C) {
1405+
ManagedValue emitBuiltinCreateAsyncTask(SILGenFunction &SGF, SILLocation loc,
1406+
SubstitutionMap subs,
1407+
ArrayRef<ManagedValue> args,
1408+
SGFContext C) {
14081409
ASTContext &ctx = SGF.getASTContext();
14091410
auto flags = args[0].forward(SGF);
14101411

lib/SILGen/SILGenFunction.cpp

Lines changed: 140 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "RValue.h"
2020
#include "SILGenFunctionBuilder.h"
2121
#include "Scope.h"
22+
#include "swift/ABI/MetadataValues.h"
2223
#include "swift/AST/ClangModuleLoader.h"
2324
#include "swift/AST/DiagnosticsSIL.h"
2425
#include "swift/AST/FileUnit.h"
@@ -563,14 +564,26 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) {
563564
emitEpilog(ace);
564565
}
565566

567+
ManagedValue emitBuiltinCreateAsyncTask(SILGenFunction &SGF, SILLocation loc,
568+
SubstitutionMap subs,
569+
ArrayRef<ManagedValue> args,
570+
SGFContext C);
571+
566572
void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
567573
// Create the argc and argv arguments.
568574
auto entry = B.getInsertionBB();
569575
auto paramTypeIter = F.getConventions()
570576
.getParameterSILTypes(getTypeExpansionContext())
571577
.begin();
572-
SILValue argc = entry->createFunctionArgument(*paramTypeIter);
573-
SILValue argv = entry->createFunctionArgument(*std::next(paramTypeIter));
578+
579+
SILValue argc;
580+
SILValue argv;
581+
const bool isAsyncFunc =
582+
isa<FuncDecl>(mainDecl) && static_cast<FuncDecl *>(mainDecl)->hasAsync();
583+
if (!isAsyncFunc) {
584+
argc = entry->createFunctionArgument(*paramTypeIter);
585+
argv = entry->createFunctionArgument(*std::next(paramTypeIter));
586+
}
574587

575588
switch (mainDecl->getArtificialMainKind()) {
576589
case ArtificialMainKind::UIApplicationMain: {
@@ -777,13 +790,32 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
777790
auto builtinInt32Type = SILType::getBuiltinIntegerType(32, getASTContext());
778791

779792
auto *exitBlock = createBasicBlock();
780-
B.setInsertionPoint(exitBlock);
781793
SILValue exitCode =
782794
exitBlock->createPhiArgument(builtinInt32Type, OwnershipKind::None);
783-
auto returnType = F.getConventions().getSingleSILResultType(B.getTypeExpansionContext());
784-
if (exitCode->getType() != returnType)
785-
exitCode = B.createStruct(moduleLoc, returnType, exitCode);
786-
B.createReturn(moduleLoc, exitCode);
795+
B.setInsertionPoint(exitBlock);
796+
797+
if (!mainFunc->hasAsync()) {
798+
auto returnType = F.getConventions().getSingleSILResultType(
799+
B.getTypeExpansionContext());
800+
if (exitCode->getType() != returnType)
801+
exitCode = B.createStruct(moduleLoc, returnType, exitCode);
802+
B.createReturn(moduleLoc, exitCode);
803+
} else {
804+
FuncDecl *exitFuncDecl = SGM.getExit();
805+
assert(exitFuncDecl && "Failed to find exit function declaration");
806+
SILFunction *exitSILFunc = SGM.getFunction(
807+
SILDeclRef(exitFuncDecl, SILDeclRef::Kind::Func, /*isForeign*/ true),
808+
NotForDefinition);
809+
810+
SILFunctionType &funcType =
811+
*exitSILFunc->getLoweredType().getAs<SILFunctionType>();
812+
SILType retType = SILType::getPrimitiveObjectType(
813+
funcType.getParameters().front().getInterfaceType());
814+
exitCode = B.createStruct(moduleLoc, retType, exitCode);
815+
SILValue exitCall = B.createFunctionRef(moduleLoc, exitSILFunc);
816+
B.createApply(moduleLoc, exitCall, {}, {exitCode});
817+
B.createUnreachable(moduleLoc);
818+
}
787819

788820
if (mainFunc->hasThrows()) {
789821
auto *successBlock = createBasicBlock();
@@ -821,6 +853,107 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
821853
}
822854
}
823855

856+
void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) {
857+
auto moduleLoc = RegularLocation::getModuleLocation();
858+
auto *entryBlock = B.getInsertionBB();
859+
auto paramTypeIter = F.getConventions()
860+
.getParameterSILTypes(getTypeExpansionContext())
861+
.begin();
862+
863+
entryBlock->createFunctionArgument(*paramTypeIter); // argc
864+
entryBlock->createFunctionArgument(*std::next(paramTypeIter)); // argv
865+
866+
// Lookup necessary functions
867+
swift::ASTContext &ctx = entryPoint.getDecl()->getASTContext();
868+
869+
B.setInsertionPoint(entryBlock);
870+
871+
/// Generates a reinterpret_cast for converting
872+
/// Builtin.Job -> UnownedJob
873+
/// Builtin.Executor -> UnownedSerialExecutor
874+
/// These are used by _swiftJobRun, which, ABI-wise, could take
875+
/// Builtin.Job or Builtin.Executor, but doesn't.
876+
auto createExplodyCastForCall =
877+
[this, &moduleLoc](SILValue originalValue, FuncDecl *jobRunFuncDecl,
878+
uint32_t paramIndex) -> SILValue {
879+
// The type coming from the _swiftJobRun function
880+
Type apiType = jobRunFuncDecl->getParameters()->get(paramIndex)->getType();
881+
SILType apiSILType =
882+
SILType::getPrimitiveObjectType(apiType->getCanonicalType());
883+
// If the types are the same, we don't need to do anything!
884+
if (apiSILType == originalValue->getType())
885+
return originalValue;
886+
return this->B.createUncheckedReinterpretCast(moduleLoc, originalValue,
887+
apiSILType);
888+
};
889+
890+
// Call CreateAsyncTask
891+
FuncDecl *builtinDecl = cast<FuncDecl>(getBuiltinValueDecl(
892+
getASTContext(),
893+
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CreateAsyncTask))));
894+
895+
auto subs = SubstitutionMap::get(builtinDecl->getGenericSignature(),
896+
{TupleType::getEmpty(ctx)},
897+
ArrayRef<ProtocolConformanceRef>{});
898+
899+
SILValue mainFunctionRef = emitGlobalFunctionRef(moduleLoc, entryPoint);
900+
901+
// Emit the CreateAsyncTask builtin
902+
TaskCreateFlags taskCreationFlagMask(0);
903+
SILValue taskFlags =
904+
emitWrapIntegerLiteral(moduleLoc, getLoweredType(ctx.getIntType()),
905+
taskCreationFlagMask.getOpaqueValue());
906+
907+
SILValue task =
908+
emitBuiltinCreateAsyncTask(*this, moduleLoc, subs,
909+
{ManagedValue::forUnmanaged(taskFlags),
910+
ManagedValue::forUnmanaged(mainFunctionRef)},
911+
{})
912+
.forward(*this);
913+
DestructureTupleInst *structure = B.createDestructureTuple(moduleLoc, task);
914+
task = structure->getResult(0);
915+
916+
// Get swiftJobRun
917+
FuncDecl *swiftJobRunFuncDecl = SGM.getSwiftJobRun();
918+
SILFunction *swiftJobRunSILFunc =
919+
SGM.getFunction(SILDeclRef(swiftJobRunFuncDecl, SILDeclRef::Kind::Func),
920+
NotForDefinition);
921+
SILValue swiftJobRunFunc =
922+
B.createFunctionRefFor(moduleLoc, swiftJobRunSILFunc);
923+
924+
// Convert task to job
925+
SILType JobType = SILType::getPrimitiveObjectType(
926+
getBuiltinType(ctx, "Job")->getCanonicalType());
927+
SILValue jobResult = B.createBuiltin(
928+
moduleLoc,
929+
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::ConvertTaskToJob)),
930+
JobType, {}, {task});
931+
jobResult = createExplodyCastForCall(jobResult, swiftJobRunFuncDecl, 0);
932+
933+
// Get main executor
934+
FuncDecl *getMainExecutorFuncDecl = SGM.getGetMainExecutor();
935+
SILFunction *getMainExeutorSILFunc = SGM.getFunction(
936+
SILDeclRef(getMainExecutorFuncDecl, SILDeclRef::Kind::Func),
937+
NotForDefinition);
938+
SILValue getMainExeutorFunc =
939+
B.createFunctionRefFor(moduleLoc, getMainExeutorSILFunc);
940+
SILValue mainExecutor = B.createApply(moduleLoc, getMainExeutorFunc, {}, {});
941+
mainExecutor = createExplodyCastForCall(mainExecutor, swiftJobRunFuncDecl, 1);
942+
943+
// Run first part synchronously
944+
B.createApply(moduleLoc, swiftJobRunFunc, {}, {jobResult, mainExecutor});
945+
946+
// Start Main loop!
947+
FuncDecl *drainQueueFuncDecl = SGM.getAsyncMainDrainQueue();
948+
SILFunction *drainQueueSILFunc = SGM.getFunction(
949+
SILDeclRef(drainQueueFuncDecl, SILDeclRef::Kind::Func), NotForDefinition);
950+
SILValue drainQueueFunc =
951+
B.createFunctionRefFor(moduleLoc, drainQueueSILFunc);
952+
B.createApply(moduleLoc, drainQueueFunc, {}, {});
953+
B.createUnreachable(moduleLoc);
954+
return;
955+
}
956+
824957
void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value,
825958
bool EmitProfilerIncrement) {
826959
auto *dc = function.getDecl()->getInnermostDeclContext();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,41 +1888,29 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) {
18881888
Expr *returnedExpr;
18891889

18901890
if (mainFunction->hasAsync()) {
1891-
// Pass main into _runAsyncMain(_ asyncFunc: () async throws -> ())
1892-
// Resulting $main looks like:
1893-
// $main() { _runAsyncMain(main) }
1891+
// Ensure that the concurrency module is loaded
18941892
auto *concurrencyModule = context.getLoadedModule(context.Id_Concurrency);
18951893
if (!concurrencyModule) {
18961894
context.Diags.diagnose(mainFunction->getAsyncLoc(),
18971895
diag::async_main_no_concurrency);
18981896
auto result = new (context) ErrorExpr(mainFunction->getSourceRange());
1899-
SmallVector<ASTNode, 1> stmts;
1900-
stmts.push_back(result);
1901-
auto body = BraceStmt::create(context, SourceLoc(), stmts,
1902-
SourceLoc(), /*Implicit*/true);
1903-
1897+
ASTNode stmts[] = {result};
1898+
auto body = BraceStmt::create(context, SourceLoc(), stmts, SourceLoc(),
1899+
/*Implicit*/ true);
19041900
return std::make_pair(body, /*typechecked*/true);
19051901
}
19061902

1907-
SmallVector<ValueDecl *, 1> decls;
1908-
concurrencyModule->lookupQualified(
1909-
concurrencyModule,
1910-
DeclNameRef(context.getIdentifier("_runAsyncMain")),
1911-
NL_QualifiedDefault | NL_IncludeUsableFromInline,
1912-
decls);
1913-
assert(!decls.empty() && "Failed to find _runAsyncMain");
1914-
FuncDecl *runner = cast<FuncDecl>(decls[0]);
1915-
1916-
auto asyncRunnerDeclRef = ConcreteDeclRef(runner, substitutionMap);
1917-
1918-
DeclRefExpr *funcExpr = new (context) DeclRefExpr(asyncRunnerDeclRef,
1919-
DeclNameLoc(),
1920-
/*implicit=*/true);
1921-
funcExpr->setType(runner->getInterfaceType());
1922-
auto *argList =
1923-
ArgumentList::forImplicitUnlabeled(context, {memberRefExpr});
1924-
auto *callExpr = CallExpr::createImplicit(context, funcExpr, argList);
1925-
returnedExpr = callExpr;
1903+
// $main() async { await main() }
1904+
Expr *awaitExpr =
1905+
new (context) AwaitExpr(callExpr->getLoc(), callExpr,
1906+
context.TheEmptyTupleType, /*implicit*/ true);
1907+
if (mainFunction->hasThrows()) {
1908+
// $main() async throws { try await main() }
1909+
awaitExpr =
1910+
new (context) TryExpr(callExpr->getLoc(), awaitExpr,
1911+
context.TheEmptyTupleType, /*implicit*/ true);
1912+
}
1913+
returnedExpr = awaitExpr;
19261914
} else if (mainFunction->hasThrows()) {
19271915
auto *tryExpr = new (context) TryExpr(
19281916
callExpr->getLoc(), callExpr, context.TheEmptyTupleType, /*implicit=*/true);
@@ -2039,7 +2027,7 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
20392027
DeclName(context, DeclBaseName(context.Id_MainEntryPoint),
20402028
ParameterList::createEmpty(context)),
20412029
/*NameLoc=*/SourceLoc(),
2042-
/*Async=*/false,
2030+
/*Async=*/mainFunction->hasAsync(),
20432031
/*Throws=*/mainFunction->hasThrows(),
20442032
/*GenericParams=*/nullptr, ParameterList::createEmpty(context),
20452033
/*FnRetType=*/TupleType::getEmpty(context), declContext);
@@ -2501,7 +2489,7 @@ static FuncDecl *findSimilarAccessor(DeclNameRef replacedVarName,
25012489
}
25022490

25032491
assert(!isa<FuncDecl>(results[0]));
2504-
2492+
25052493
auto *origStorage = cast<AbstractStorageDecl>(results[0]);
25062494
if (forDynamicReplacement && !origStorage->isDynamic()) {
25072495
Diags.diagnose(attr->getLocation(),

0 commit comments

Comments
 (0)