Skip to content

Commit b416a1a

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 45680e0 commit b416a1a

File tree

8 files changed

+300
-63
lines changed

8 files changed

+300
-63
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
@@ -434,6 +434,37 @@ SILGenModule::getCheckExpectedExecutor() {
434434
"_checkExpectedExecutor");
435435
}
436436

437+
FuncDecl *SILGenModule::getAsyncMainDrainQueue() {
438+
return lookupConcurrencyIntrinsic(getASTContext(), AsyncMainDrainQueue,
439+
"_asyncMainDrainQueue");
440+
}
441+
442+
FuncDecl *SILGenModule::getGetMainExecutor() {
443+
return lookupConcurrencyIntrinsic(getASTContext(), GetMainExecutor,
444+
"_getMainExecutor");
445+
}
446+
447+
FuncDecl *SILGenModule::getSwiftJobRun() {
448+
return lookupConcurrencyIntrinsic(getASTContext(), SwiftJobRun,
449+
"_swiftJobRun");
450+
}
451+
452+
FuncDecl *SILGenModule::getExit() {
453+
if (ExitFunc)
454+
return *ExitFunc;
455+
456+
ASTContext &C = getASTContext();
457+
ModuleDecl *concurrencyShims =
458+
C.getModuleByIdentifier(C.getIdentifier("_SwiftConcurrencyShims"));
459+
460+
if (!concurrencyShims) {
461+
ExitFunc = nullptr;
462+
return nullptr;
463+
}
464+
465+
return lookupIntrinsic(*concurrencyShims, ExitFunc, C.getIdentifier("exit"));
466+
}
467+
437468
ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {
438469
if (NSErrorConformanceToError)
439470
return *NSErrorConformanceToError;
@@ -1027,7 +1058,27 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
10271058
auto *decl = constant.getDecl();
10281059
auto *dc = decl->getDeclContext();
10291060
PrettyStackTraceSILFunction X("silgen emitArtificialTopLevel", f);
1030-
SILGenFunction(*this, *f, dc).emitArtificialTopLevel(decl);
1061+
// In all cases, a constant.kind == EntryPoint indicates the main entrypoint
1062+
// to the program, @main.
1063+
// In the synchronous case, the decl is not async, so emitArtificialTopLevel
1064+
// emits the error unwrapping and call to MainType.$main() into @main.
1065+
//
1066+
// In the async case, emitAsyncMainThreadStart is responsible for generating
1067+
// the contents of @main. This wraps @async_main in a task, passes that task
1068+
// to swift_job_run to execute the first thunk, and starts the runloop to
1069+
// run any additional continuations. The kind is EntryPoint, and the decl is
1070+
// async.
1071+
// When the kind is 'AsyncMain', we are generating @async_main. In this
1072+
// case, emitArtificialTopLevel emits the code for calling MaintType.$main,
1073+
// unwrapping errors, and calling exit(0) into @async_main to run the
1074+
// user-specified main function.
1075+
if (constant.kind == SILDeclRef::Kind::EntryPoint && isa<FuncDecl>(decl) &&
1076+
static_cast<FuncDecl *>(decl)->hasAsync()) {
1077+
SILDeclRef mainEntryPoint = SILDeclRef::getAsyncMainDeclEntryPoint(decl);
1078+
SILGenFunction(*this, *f, dc).emitAsyncMainThreadStart(mainEntryPoint);
1079+
} else {
1080+
SILGenFunction(*this, *f, dc).emitArtificialTopLevel(decl);
1081+
}
10311082
postEmitFunction(constant, f);
10321083
return;
10331084
}
@@ -2005,10 +2056,15 @@ class SILGenModuleRAII {
20052056

20062057
// If the source file contains an artificial main, emit the implicit
20072058
// top-level code.
2008-
if (auto *mainDecl = sf->getMainDecl())
2059+
if (auto *mainDecl = sf->getMainDecl()) {
2060+
if (isa<FuncDecl>(mainDecl) &&
2061+
static_cast<FuncDecl *>(mainDecl)->hasAsync())
2062+
emitSILFunctionDefinition(
2063+
SILDeclRef::getAsyncMainDeclEntryPoint(mainDecl));
20092064
emitSILFunctionDefinition(SILDeclRef::getMainDeclEntryPoint(mainDecl));
2065+
}
20102066
}
2011-
2067+
20122068
void emitSILFunctionDefinition(SILDeclRef ref) {
20132069
SGM.emitFunctionDefinition(ref, SGM.getFunction(ref, ForDefinition));
20142070
}

lib/SILGen/SILGen.h

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

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

@@ -513,6 +518,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
513518
/// Retrieve the _Concurrency._checkExpectedExecutor intrinsic.
514519
FuncDecl *getCheckExpectedExecutor();
515520

521+
/// Retrieve the _Concurrency._asyncMainDrainQueue intrinsic.
522+
FuncDecl *getAsyncMainDrainQueue();
523+
/// Retrieve the _Concurrency._getMainExecutor intrinsic.
524+
FuncDecl *getGetMainExecutor();
525+
/// Retrieve the _Concurrency._swiftJobRun intrinsic.
526+
FuncDecl *getSwiftJobRun();
527+
// Retrieve the _SwiftConcurrencyShims.exit intrinsic.
528+
FuncDecl *getExit();
529+
516530
SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess,
517531
KeyPathTypeKind typeKind);
518532

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"
@@ -609,14 +610,26 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) {
609610
emitEpilog(ace);
610611
}
611612

613+
ManagedValue emitBuiltinCreateAsyncTask(SILGenFunction &SGF, SILLocation loc,
614+
SubstitutionMap subs,
615+
ArrayRef<ManagedValue> args,
616+
SGFContext C);
617+
612618
void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
613619
// Create the argc and argv arguments.
614620
auto entry = B.getInsertionBB();
615621
auto paramTypeIter = F.getConventions()
616622
.getParameterSILTypes(getTypeExpansionContext())
617623
.begin();
618-
SILValue argc = entry->createFunctionArgument(*paramTypeIter);
619-
SILValue argv = entry->createFunctionArgument(*std::next(paramTypeIter));
624+
625+
SILValue argc;
626+
SILValue argv;
627+
const bool isAsyncFunc =
628+
isa<FuncDecl>(mainDecl) && static_cast<FuncDecl *>(mainDecl)->hasAsync();
629+
if (!isAsyncFunc) {
630+
argc = entry->createFunctionArgument(*paramTypeIter);
631+
argv = entry->createFunctionArgument(*std::next(paramTypeIter));
632+
}
620633

621634
switch (mainDecl->getArtificialMainKind()) {
622635
case ArtificialMainKind::UIApplicationMain: {
@@ -823,13 +836,32 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
823836
auto builtinInt32Type = SILType::getBuiltinIntegerType(32, getASTContext());
824837

825838
auto *exitBlock = createBasicBlock();
826-
B.setInsertionPoint(exitBlock);
827839
SILValue exitCode =
828840
exitBlock->createPhiArgument(builtinInt32Type, OwnershipKind::None);
829-
auto returnType = F.getConventions().getSingleSILResultType(B.getTypeExpansionContext());
830-
if (exitCode->getType() != returnType)
831-
exitCode = B.createStruct(moduleLoc, returnType, exitCode);
832-
B.createReturn(moduleLoc, exitCode);
841+
B.setInsertionPoint(exitBlock);
842+
843+
if (!mainFunc->hasAsync()) {
844+
auto returnType = F.getConventions().getSingleSILResultType(
845+
B.getTypeExpansionContext());
846+
if (exitCode->getType() != returnType)
847+
exitCode = B.createStruct(moduleLoc, returnType, exitCode);
848+
B.createReturn(moduleLoc, exitCode);
849+
} else {
850+
FuncDecl *exitFuncDecl = SGM.getExit();
851+
assert(exitFuncDecl && "Failed to find exit function declaration");
852+
SILFunction *exitSILFunc = SGM.getFunction(
853+
SILDeclRef(exitFuncDecl, SILDeclRef::Kind::Func, /*isForeign*/ true),
854+
NotForDefinition);
855+
856+
SILFunctionType &funcType =
857+
*exitSILFunc->getLoweredType().getAs<SILFunctionType>();
858+
SILType retType = SILType::getPrimitiveObjectType(
859+
funcType.getParameters().front().getInterfaceType());
860+
exitCode = B.createStruct(moduleLoc, retType, exitCode);
861+
SILValue exitCall = B.createFunctionRef(moduleLoc, exitSILFunc);
862+
B.createApply(moduleLoc, exitCall, {}, {exitCode});
863+
B.createUnreachable(moduleLoc);
864+
}
833865

834866
if (mainFunc->hasThrows()) {
835867
auto *successBlock = createBasicBlock();
@@ -867,6 +899,107 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
867899
}
868900
}
869901

902+
void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) {
903+
auto moduleLoc = RegularLocation::getModuleLocation();
904+
auto *entryBlock = B.getInsertionBB();
905+
auto paramTypeIter = F.getConventions()
906+
.getParameterSILTypes(getTypeExpansionContext())
907+
.begin();
908+
909+
entryBlock->createFunctionArgument(*paramTypeIter); // argc
910+
entryBlock->createFunctionArgument(*std::next(paramTypeIter)); // argv
911+
912+
// Lookup necessary functions
913+
swift::ASTContext &ctx = entryPoint.getDecl()->getASTContext();
914+
915+
B.setInsertionPoint(entryBlock);
916+
917+
/// Generates a reinterpret_cast for converting
918+
/// Builtin.Job -> UnownedJob
919+
/// Builtin.Executor -> UnownedSerialExecutor
920+
/// These are used by _swiftJobRun, which, ABI-wise, could take
921+
/// Builtin.Job or Builtin.Executor, but doesn't.
922+
auto createExplodyCastForCall =
923+
[this, &moduleLoc](SILValue originalValue, FuncDecl *jobRunFuncDecl,
924+
uint32_t paramIndex) -> SILValue {
925+
// The type coming from the _swiftJobRun function
926+
Type apiType = jobRunFuncDecl->getParameters()->get(paramIndex)->getType();
927+
SILType apiSILType =
928+
SILType::getPrimitiveObjectType(apiType->getCanonicalType());
929+
// If the types are the same, we don't need to do anything!
930+
if (apiSILType == originalValue->getType())
931+
return originalValue;
932+
return this->B.createUncheckedReinterpretCast(moduleLoc, originalValue,
933+
apiSILType);
934+
};
935+
936+
// Call CreateAsyncTask
937+
FuncDecl *builtinDecl = cast<FuncDecl>(getBuiltinValueDecl(
938+
getASTContext(),
939+
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CreateAsyncTask))));
940+
941+
auto subs = SubstitutionMap::get(builtinDecl->getGenericSignature(),
942+
{TupleType::getEmpty(ctx)},
943+
ArrayRef<ProtocolConformanceRef>{});
944+
945+
SILValue mainFunctionRef = emitGlobalFunctionRef(moduleLoc, entryPoint);
946+
947+
// Emit the CreateAsyncTask builtin
948+
TaskCreateFlags taskCreationFlagMask(0);
949+
SILValue taskFlags =
950+
emitWrapIntegerLiteral(moduleLoc, getLoweredType(ctx.getIntType()),
951+
taskCreationFlagMask.getOpaqueValue());
952+
953+
SILValue task =
954+
emitBuiltinCreateAsyncTask(*this, moduleLoc, subs,
955+
{ManagedValue::forUnmanaged(taskFlags),
956+
ManagedValue::forUnmanaged(mainFunctionRef)},
957+
{})
958+
.forward(*this);
959+
DestructureTupleInst *structure = B.createDestructureTuple(moduleLoc, task);
960+
task = structure->getResult(0);
961+
962+
// Get swiftJobRun
963+
FuncDecl *swiftJobRunFuncDecl = SGM.getSwiftJobRun();
964+
SILFunction *swiftJobRunSILFunc =
965+
SGM.getFunction(SILDeclRef(swiftJobRunFuncDecl, SILDeclRef::Kind::Func),
966+
NotForDefinition);
967+
SILValue swiftJobRunFunc =
968+
B.createFunctionRefFor(moduleLoc, swiftJobRunSILFunc);
969+
970+
// Convert task to job
971+
SILType JobType = SILType::getPrimitiveObjectType(
972+
getBuiltinType(ctx, "Job")->getCanonicalType());
973+
SILValue jobResult = B.createBuiltin(
974+
moduleLoc,
975+
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::ConvertTaskToJob)),
976+
JobType, {}, {task});
977+
jobResult = createExplodyCastForCall(jobResult, swiftJobRunFuncDecl, 0);
978+
979+
// Get main executor
980+
FuncDecl *getMainExecutorFuncDecl = SGM.getGetMainExecutor();
981+
SILFunction *getMainExeutorSILFunc = SGM.getFunction(
982+
SILDeclRef(getMainExecutorFuncDecl, SILDeclRef::Kind::Func),
983+
NotForDefinition);
984+
SILValue getMainExeutorFunc =
985+
B.createFunctionRefFor(moduleLoc, getMainExeutorSILFunc);
986+
SILValue mainExecutor = B.createApply(moduleLoc, getMainExeutorFunc, {}, {});
987+
mainExecutor = createExplodyCastForCall(mainExecutor, swiftJobRunFuncDecl, 1);
988+
989+
// Run first part synchronously
990+
B.createApply(moduleLoc, swiftJobRunFunc, {}, {jobResult, mainExecutor});
991+
992+
// Start Main loop!
993+
FuncDecl *drainQueueFuncDecl = SGM.getAsyncMainDrainQueue();
994+
SILFunction *drainQueueSILFunc = SGM.getFunction(
995+
SILDeclRef(drainQueueFuncDecl, SILDeclRef::Kind::Func), NotForDefinition);
996+
SILValue drainQueueFunc =
997+
B.createFunctionRefFor(moduleLoc, drainQueueSILFunc);
998+
B.createApply(moduleLoc, drainQueueFunc, {}, {});
999+
B.createUnreachable(moduleLoc);
1000+
return;
1001+
}
1002+
8701003
void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value,
8711004
bool EmitProfilerIncrement) {
8721005
auto *dc = function.getDecl()->getInnermostDeclContext();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,39 +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 *callExpr = CallExpr::createImplicit(context, funcExpr, memberRefExpr, {});
1923-
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;
19241914
} else if (mainFunction->hasThrows()) {
19251915
auto *tryExpr = new (context) TryExpr(
19261916
callExpr->getLoc(), callExpr, context.TheEmptyTupleType, /*implicit=*/true);
@@ -2037,7 +2027,7 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
20372027
DeclName(context, DeclBaseName(context.Id_MainEntryPoint),
20382028
ParameterList::createEmpty(context)),
20392029
/*NameLoc=*/SourceLoc(),
2040-
/*Async=*/false,
2030+
/*Async=*/mainFunction->hasAsync(),
20412031
/*Throws=*/mainFunction->hasThrows(),
20422032
/*GenericParams=*/nullptr, ParameterList::createEmpty(context),
20432033
/*FnRetType=*/TupleType::getEmpty(context), declContext);
@@ -2498,7 +2488,7 @@ static FuncDecl *findSimilarAccessor(DeclNameRef replacedVarName,
24982488
}
24992489

25002490
assert(!isa<FuncDecl>(results[0]));
2501-
2491+
25022492
auto *origStorage = cast<AbstractStorageDecl>(results[0]);
25032493
if (forDynamicReplacement && !origStorage->isDynamic()) {
25042494
Diags.diagnose(attr->getLocation(),

0 commit comments

Comments
 (0)