diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index b5d939eede36b..871549258c17a 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -156,8 +156,11 @@ struct SILDeclRef { /// The main entry-point function. This may reference a SourceFile for a /// top-level main, or a decl for e.g an @main decl. EntryPoint, + + /// The asynchronous main entry-point function. + AsyncEntryPoint, }; - + /// The AST node represented by this SILDeclRef. Loc loc; /// The Kind of this SILDeclRef. @@ -231,6 +234,9 @@ struct SILDeclRef { /// Produces a SILDeclRef for a synthetic main entry-point such as @main. static SILDeclRef getMainDeclEntryPoint(ValueDecl *decl); + /// Produces a SILDeclRef for the synthesized async main entry-point + static SILDeclRef getAsyncMainDeclEntryPoint(ValueDecl *decl); + /// Produces a SILDeclRef for the entry-point of a main FileUnit. static SILDeclRef getMainFileEntryPoint(FileUnit *file); diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index 74b9c638fd34d..3f860074818fd 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -474,6 +474,7 @@ namespace { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::EntryPoint: + case SILDeclRef::Kind::AsyncEntryPoint: llvm_unreachable("Method does not have a selector"); case SILDeclRef::Kind::Destroyer: diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index ba68115e8e92c..07a0ee1a62770 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -273,6 +273,8 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { // The main entry-point is public. if (kind == Kind::EntryPoint) return SILLinkage::Public; + if (kind == Kind::AsyncEntryPoint) + return SILLinkage::Hidden; // Add External to the linkage (e.g. Public -> PublicExternal) if this is a // declaration not a definition. @@ -448,6 +450,15 @@ SILDeclRef SILDeclRef::getMainDeclEntryPoint(ValueDecl *decl) { return result; } +SILDeclRef SILDeclRef::getAsyncMainDeclEntryPoint(ValueDecl *decl) { + auto *file = cast(decl->getDeclContext()->getModuleScopeContext()); + assert(file->getMainDecl() == decl); + SILDeclRef result; + result.loc = decl; + result.kind = Kind::AsyncEntryPoint; + return result; +} + SILDeclRef SILDeclRef::getMainFileEntryPoint(FileUnit *file) { assert(file->hasEntryPoint() && !file->getMainDecl()); SILDeclRef result; @@ -539,7 +550,7 @@ IsSerialized_t SILDeclRef::isSerialized() const { return IsNotSerialized; } - if (kind == Kind::EntryPoint) + if (kind == Kind::EntryPoint || kind == Kind::AsyncEntryPoint) return IsNotSerialized; if (isIVarInitializerOrDestroyer()) @@ -925,6 +936,9 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { return mangler.mangleInitFromProjectedValueEntity(cast(getDecl()), SKind); + case SILDeclRef::Kind::AsyncEntryPoint: { + return "async_Main"; + } case SILDeclRef::Kind::EntryPoint: { return getASTContext().getEntryPointFunctionName(); } @@ -1257,7 +1271,8 @@ unsigned SILDeclRef::getParameterListCount() const { return 1; // Always uncurried even if the underlying function is curried. - if (kind == Kind::DefaultArgGenerator || kind == Kind::EntryPoint) + if (kind == Kind::DefaultArgGenerator || kind == Kind::EntryPoint || + kind == Kind::AsyncEntryPoint) return 1; auto *vd = getDecl(); diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index c99f8aca8e30c..02e23458ceb12 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -2526,6 +2526,9 @@ static CanSILFunctionType getNativeSILFunctionType( case SILDeclRef::Kind::Deallocator: return getSILFunctionTypeForConventions(DeallocatorConventions()); + case SILDeclRef::Kind::AsyncEntryPoint: + return getSILFunctionTypeForConventions( + DefaultConventions(NormalParameterConvention::Guaranteed)); case SILDeclRef::Kind::EntryPoint: llvm_unreachable("Handled by getSILFunctionTypeForAbstractCFunction"); } @@ -3071,6 +3074,7 @@ static ObjCSelectorFamily getObjCSelectorFamily(SILDeclRef c) { case SILDeclRef::Kind::PropertyWrapperBackingInitializer: case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: case SILDeclRef::Kind::EntryPoint: + case SILDeclRef::Kind::AsyncEntryPoint: llvm_unreachable("Unexpected Kind of foreign SILDeclRef"); } @@ -3343,6 +3347,8 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) { case SILDeclRef::Kind::IVarDestroyer: return SILFunctionTypeRepresentation::Method; + case SILDeclRef::Kind::AsyncEntryPoint: + return SILFunctionTypeRepresentation::Thin; case SILDeclRef::Kind::EntryPoint: return SILFunctionTypeRepresentation::CFunctionPointer; } @@ -4170,6 +4176,7 @@ static AbstractFunctionDecl *getBridgedFunction(SILDeclRef declRef) { case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: case SILDeclRef::Kind::EntryPoint: + case SILDeclRef::Kind::AsyncEntryPoint: return nullptr; } llvm_unreachable("bad SILDeclRef kind"); diff --git a/lib/SIL/IR/SILLocation.cpp b/lib/SIL/IR/SILLocation.cpp index c662a49627d84..0185a1e6c8fd1 100644 --- a/lib/SIL/IR/SILLocation.cpp +++ b/lib/SIL/IR/SILLocation.cpp @@ -42,7 +42,7 @@ SourceLoc SILLocation::getSourceLoc() const { // Don't crash if the location is a FilenameAndLocation. // TODO: this is a workaround until rdar://problem/25225083 is implemented. - if (isFilenameAndLocation()) + if (getStorageKind() == FilenameAndLocationKind) return SourceLoc(); return getSourceLoc(getPrimaryASTNode()); diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index a44cc8ad7fb33..5b7faa4d0a395 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -316,6 +316,7 @@ void SILDeclRef::print(raw_ostream &OS) const { switch (kind) { case SILDeclRef::Kind::Func: case SILDeclRef::Kind::EntryPoint: + case SILDeclRef::Kind::AsyncEntryPoint: break; case SILDeclRef::Kind::Allocator: OS << "!allocator"; diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index ae83f68cec349..5743e1b543d46 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -2556,6 +2556,32 @@ getFunctionInterfaceTypeWithCaptures(TypeConverter &TC, innerExtInfo); } +static CanAnyFunctionType getAsyncEntryPoint(ASTContext &C) { + + // @main struct Main { + // static func main() async throws {} + // static func $main() async throws { try await main() } + // } + // + // func @async_main() async -> Void { + // do { + // try await Main.$main() + // exit(0) + // } catch { + // _emitErrorInMain(error) + // } + // } + // + // This generates the type signature for @async_main + // TODO: 'Never' return type would be more accurate. + + CanType returnType = C.getVoidType()->getCanonicalType(); + FunctionType::ExtInfo extInfo = + FunctionType::ExtInfoBuilder().withAsync(true).withThrows(false).build(); + return CanAnyFunctionType::get(/*genericSig*/ nullptr, {}, returnType, + extInfo); +} + static CanAnyFunctionType getEntryPointInterfaceType(ASTContext &C) { // Use standard library types if we have them; otherwise, fall back to // builtins. @@ -2675,6 +2701,8 @@ CanAnyFunctionType TypeConverter::makeConstantInterfaceType(SILDeclRef c) { case SILDeclRef::Kind::IVarDestroyer: return getIVarInitDestroyerInterfaceType(cast(vd), c.isForeign, true); + case SILDeclRef::Kind::AsyncEntryPoint: + return getAsyncEntryPoint(Context); case SILDeclRef::Kind::EntryPoint: return getEntryPointInterfaceType(Context); } @@ -2729,6 +2757,7 @@ TypeConverter::getConstantGenericSignature(SILDeclRef c) { case SILDeclRef::Kind::StoredPropertyInitializer: return vd->getDeclContext()->getGenericSignatureOfContext(); case SILDeclRef::Kind::EntryPoint: + case SILDeclRef::Kind::AsyncEntryPoint: llvm_unreachable("Doesn't have generic signature"); } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index dfcc2a7dc24b3..99ba13ffbbda4 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -338,24 +338,15 @@ SILGenModule::getConformanceToBridgedStoredNSError(SILLocation loc, Type type) { return SwiftModule->lookupConformance(type, proto); } -static FuncDecl *lookupConcurrencyIntrinsic(ASTContext &C, - Optional &cache, - StringRef name) { +static FuncDecl *lookupIntrinsic(ModuleDecl &module, + Optional &cache, Identifier name) { if (cache) return *cache; - - auto *module = C.getLoadedModule(C.Id_Concurrency); - if (!module) { - cache = nullptr; - return nullptr; - } - - SmallVector decls; - module->lookupQualified(module, - DeclNameRef(C.getIdentifier(name)), - NL_QualifiedDefault | NL_IncludeUsableFromInline, - decls); + SmallVector decls; + module.lookupQualified(&module, DeclNameRef(name), + NL_QualifiedDefault | NL_IncludeUsableFromInline, + decls); if (decls.size() != 1) { cache = nullptr; return nullptr; @@ -365,6 +356,18 @@ static FuncDecl *lookupConcurrencyIntrinsic(ASTContext &C, return func; } +static FuncDecl *lookupConcurrencyIntrinsic(ASTContext &C, + Optional &cache, + StringRef name) { + auto *module = C.getLoadedModule(C.Id_Concurrency); + if (!module) { + cache = nullptr; + return nullptr; + } + + return lookupIntrinsic(*module, cache, C.getIdentifier(name)); +} + FuncDecl * SILGenModule::getAsyncLetStart() { return lookupConcurrencyIntrinsic(getASTContext(), @@ -437,6 +440,37 @@ SILGenModule::getCheckExpectedExecutor() { "_checkExpectedExecutor"); } +FuncDecl *SILGenModule::getAsyncMainDrainQueue() { + return lookupConcurrencyIntrinsic(getASTContext(), AsyncMainDrainQueue, + "_asyncMainDrainQueue"); +} + +FuncDecl *SILGenModule::getGetMainExecutor() { + return lookupConcurrencyIntrinsic(getASTContext(), GetMainExecutor, + "_getMainExecutor"); +} + +FuncDecl *SILGenModule::getSwiftJobRun() { + return lookupConcurrencyIntrinsic(getASTContext(), SwiftJobRun, + "_swiftJobRun"); +} + +FuncDecl *SILGenModule::getExit() { + if (ExitFunc) + return *ExitFunc; + + ASTContext &C = getASTContext(); + ModuleDecl *concurrencyShims = + C.getModuleByIdentifier(C.getIdentifier("_SwiftConcurrencyShims")); + + if (!concurrencyShims) { + ExitFunc = nullptr; + return nullptr; + } + + return lookupIntrinsic(*concurrencyShims, ExitFunc, C.getIdentifier("exit")); +} + ProtocolConformance *SILGenModule::getNSErrorConformanceToError() { if (NSErrorConformanceToError) return *NSErrorConformanceToError; @@ -1036,6 +1070,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { postEmitFunction(constant, f); return; } + case SILDeclRef::Kind::AsyncEntryPoint: case SILDeclRef::Kind::EntryPoint: { f->setBare(IsBare); @@ -1046,7 +1081,27 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) { auto *decl = constant.getDecl(); auto *dc = decl->getDeclContext(); PrettyStackTraceSILFunction X("silgen emitArtificialTopLevel", f); - SILGenFunction(*this, *f, dc).emitArtificialTopLevel(decl); + // In all cases, a constant.kind == EntryPoint indicates the main entrypoint + // to the program, @main. + // In the synchronous case, the decl is not async, so emitArtificialTopLevel + // emits the error unwrapping and call to MainType.$main() into @main. + // + // In the async case, emitAsyncMainThreadStart is responsible for generating + // the contents of @main. This wraps @async_main in a task, passes that task + // to swift_job_run to execute the first thunk, and starts the runloop to + // run any additional continuations. The kind is EntryPoint, and the decl is + // async. + // When the kind is 'AsyncMain', we are generating @async_main. In this + // case, emitArtificialTopLevel emits the code for calling MaintType.$main, + // unwrapping errors, and calling exit(0) into @async_main to run the + // user-specified main function. + if (constant.kind == SILDeclRef::Kind::EntryPoint && isa(decl) && + static_cast(decl)->hasAsync()) { + SILDeclRef mainEntryPoint = SILDeclRef::getAsyncMainDeclEntryPoint(decl); + SILGenFunction(*this, *f, dc).emitAsyncMainThreadStart(mainEntryPoint); + } else { + SILGenFunction(*this, *f, dc).emitArtificialTopLevel(decl); + } postEmitFunction(constant, f); return; } @@ -2029,10 +2084,15 @@ class SILGenModuleRAII { // If the source file contains an artificial main, emit the implicit // top-level code. - if (auto *mainDecl = sf->getMainDecl()) + if (auto *mainDecl = sf->getMainDecl()) { + if (isa(mainDecl) && + static_cast(mainDecl)->hasAsync()) + emitSILFunctionDefinition( + SILDeclRef::getAsyncMainDeclEntryPoint(mainDecl)); emitSILFunctionDefinition(SILDeclRef::getMainDeclEntryPoint(mainDecl)); + } } - + void emitSILFunctionDefinition(SILDeclRef ref) { SGM.emitFunctionDefinition(ref, SGM.getFunction(ref, ForDefinition)); } diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 53a823a1b39b2..d4ad5926d9c62 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -131,6 +131,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { Optional ResumeUnsafeThrowingContinuationWithError; Optional CheckExpectedExecutor; + Optional AsyncMainDrainQueue; + Optional GetMainExecutor; + Optional SwiftJobRun; + Optional ExitFunc; + public: SILGenModule(SILModule &M, ModuleDecl *SM); @@ -517,6 +522,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// Retrieve the _Concurrency._checkExpectedExecutor intrinsic. FuncDecl *getCheckExpectedExecutor(); + /// Retrieve the _Concurrency._asyncMainDrainQueue intrinsic. + FuncDecl *getAsyncMainDrainQueue(); + /// Retrieve the _Concurrency._getMainExecutor intrinsic. + FuncDecl *getGetMainExecutor(); + /// Retrieve the _Concurrency._swiftJobRun intrinsic. + FuncDecl *getSwiftJobRun(); + // Retrieve the _SwiftConcurrencyShims.exit intrinsic. + FuncDecl *getExit(); + SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess, KeyPathTypeKind typeKind); diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 5b44ead47142c..d19e9ca57f621 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -1402,9 +1402,10 @@ emitFunctionArgumentForAsyncTaskEntryPoint(SILGenFunction &SGF, } // Emit SIL for the named builtin: createAsyncTask. -static ManagedValue emitBuiltinCreateAsyncTask( - SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, - ArrayRef args, SGFContext C) { +ManagedValue emitBuiltinCreateAsyncTask(SILGenFunction &SGF, SILLocation loc, + SubstitutionMap subs, + ArrayRef args, + SGFContext C) { ASTContext &ctx = SGF.getASTContext(); auto flags = args[0].forward(SGF); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index a15b221755cde..19f8cc553986c 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -19,6 +19,7 @@ #include "RValue.h" #include "SILGenFunctionBuilder.h" #include "Scope.h" +#include "swift/ABI/MetadataValues.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/FileUnit.h" @@ -141,6 +142,7 @@ DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) { case SILDeclRef::Kind::EnumElement: return getMagicFunctionName(cast(ref.getDecl()) ->getDeclContext()); + case SILDeclRef::Kind::AsyncEntryPoint: case SILDeclRef::Kind::EntryPoint: auto *file = ref.getDecl()->getDeclContext()->getParentSourceFile(); return getMagicFunctionName(file); @@ -562,14 +564,26 @@ void SILGenFunction::emitClosure(AbstractClosureExpr *ace) { emitEpilog(ace); } +ManagedValue emitBuiltinCreateAsyncTask(SILGenFunction &SGF, SILLocation loc, + SubstitutionMap subs, + ArrayRef args, + SGFContext C); + void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { // Create the argc and argv arguments. auto entry = B.getInsertionBB(); auto paramTypeIter = F.getConventions() .getParameterSILTypes(getTypeExpansionContext()) .begin(); - SILValue argc = entry->createFunctionArgument(*paramTypeIter); - SILValue argv = entry->createFunctionArgument(*std::next(paramTypeIter)); + + SILValue argc; + SILValue argv; + const bool isAsyncFunc = + isa(mainDecl) && static_cast(mainDecl)->hasAsync(); + if (!isAsyncFunc) { + argc = entry->createFunctionArgument(*paramTypeIter); + argv = entry->createFunctionArgument(*std::next(paramTypeIter)); + } switch (mainDecl->getArtificialMainKind()) { case ArtificialMainKind::UIApplicationMain: { @@ -776,13 +790,32 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { auto builtinInt32Type = SILType::getBuiltinIntegerType(32, getASTContext()); auto *exitBlock = createBasicBlock(); - B.setInsertionPoint(exitBlock); SILValue exitCode = exitBlock->createPhiArgument(builtinInt32Type, OwnershipKind::None); - auto returnType = F.getConventions().getSingleSILResultType(B.getTypeExpansionContext()); - if (exitCode->getType() != returnType) - exitCode = B.createStruct(moduleLoc, returnType, exitCode); - B.createReturn(moduleLoc, exitCode); + B.setInsertionPoint(exitBlock); + + if (!mainFunc->hasAsync()) { + auto returnType = F.getConventions().getSingleSILResultType( + B.getTypeExpansionContext()); + if (exitCode->getType() != returnType) + exitCode = B.createStruct(moduleLoc, returnType, exitCode); + B.createReturn(moduleLoc, exitCode); + } else { + FuncDecl *exitFuncDecl = SGM.getExit(); + assert(exitFuncDecl && "Failed to find exit function declaration"); + SILFunction *exitSILFunc = SGM.getFunction( + SILDeclRef(exitFuncDecl, SILDeclRef::Kind::Func, /*isForeign*/ true), + NotForDefinition); + + SILFunctionType &funcType = + *exitSILFunc->getLoweredType().getAs(); + SILType retType = SILType::getPrimitiveObjectType( + funcType.getParameters().front().getInterfaceType()); + exitCode = B.createStruct(moduleLoc, retType, exitCode); + SILValue exitCall = B.createFunctionRef(moduleLoc, exitSILFunc); + B.createApply(moduleLoc, exitCall, {}, {exitCode}); + B.createUnreachable(moduleLoc); + } if (mainFunc->hasThrows()) { auto *successBlock = createBasicBlock(); @@ -820,6 +853,99 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { } } +void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) { + auto moduleLoc = RegularLocation::getModuleLocation(); + auto *entryBlock = B.getInsertionBB(); + auto paramTypeIter = F.getConventions() + .getParameterSILTypes(getTypeExpansionContext()) + .begin(); + + entryBlock->createFunctionArgument(*paramTypeIter); // argc + entryBlock->createFunctionArgument(*std::next(paramTypeIter)); // argv + + // Lookup necessary functions + swift::ASTContext &ctx = entryPoint.getDecl()->getASTContext(); + + B.setInsertionPoint(entryBlock); + + auto wrapCallArgs = [this, &moduleLoc](SILValue originalValue, FuncDecl *fd, + uint32_t paramIndex) -> SILValue { + Type parameterType = fd->getParameters()->get(paramIndex)->getType(); + SILType paramSILType = SILType::getPrimitiveObjectType(parameterType->getCanonicalType()); + // If the types are the same, we don't need to do anything! + if (paramSILType == originalValue->getType()) + return originalValue; + return this->B.createStruct(moduleLoc, paramSILType, originalValue); + }; + + // Call CreateAsyncTask + FuncDecl *builtinDecl = cast(getBuiltinValueDecl( + getASTContext(), + ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CreateAsyncTask)))); + + auto subs = SubstitutionMap::get(builtinDecl->getGenericSignature(), + {TupleType::getEmpty(ctx)}, + ArrayRef{}); + + SILValue mainFunctionRef = emitGlobalFunctionRef(moduleLoc, entryPoint); + + // Emit the CreateAsyncTask builtin + TaskCreateFlags taskCreationFlagMask(0); + taskCreationFlagMask.setInheritContext(true); + SILValue taskFlags = + emitWrapIntegerLiteral(moduleLoc, getLoweredType(ctx.getIntType()), + taskCreationFlagMask.getOpaqueValue()); + + SILValue task = + emitBuiltinCreateAsyncTask(*this, moduleLoc, subs, + {ManagedValue::forUnmanaged(taskFlags), + ManagedValue::forUnmanaged(mainFunctionRef)}, + {}) + .forward(*this); + DestructureTupleInst *structure = B.createDestructureTuple(moduleLoc, task); + task = structure->getResult(0); + + // Get swiftJobRun + FuncDecl *swiftJobRunFuncDecl = SGM.getSwiftJobRun(); + SILFunction *swiftJobRunSILFunc = + SGM.getFunction(SILDeclRef(swiftJobRunFuncDecl, SILDeclRef::Kind::Func), + NotForDefinition); + SILValue swiftJobRunFunc = + B.createFunctionRefFor(moduleLoc, swiftJobRunSILFunc); + + // Convert task to job + SILType JobType = SILType::getPrimitiveObjectType( + getBuiltinType(ctx, "Job")->getCanonicalType()); + SILValue jobResult = B.createBuiltin( + moduleLoc, + ctx.getIdentifier(getBuiltinName(BuiltinValueKind::ConvertTaskToJob)), + JobType, {}, {task}); + jobResult = wrapCallArgs(jobResult, swiftJobRunFuncDecl, 0); + + // Get main executor + FuncDecl *getMainExecutorFuncDecl = SGM.getGetMainExecutor(); + SILFunction *getMainExeutorSILFunc = SGM.getFunction( + SILDeclRef(getMainExecutorFuncDecl, SILDeclRef::Kind::Func), + NotForDefinition); + SILValue getMainExeutorFunc = + B.createFunctionRefFor(moduleLoc, getMainExeutorSILFunc); + SILValue mainExecutor = B.createApply(moduleLoc, getMainExeutorFunc, {}, {}); + mainExecutor = wrapCallArgs(mainExecutor, swiftJobRunFuncDecl, 1); + + // Run first part synchronously + B.createApply(moduleLoc, swiftJobRunFunc, {}, {jobResult, mainExecutor}); + + // Start Main loop! + FuncDecl *drainQueueFuncDecl = SGM.getAsyncMainDrainQueue(); + SILFunction *drainQueueSILFunc = SGM.getFunction( + SILDeclRef(drainQueueFuncDecl, SILDeclRef::Kind::Func), NotForDefinition); + SILValue drainQueueFunc = + B.createFunctionRefFor(moduleLoc, drainQueueSILFunc); + B.createApply(moduleLoc, drainQueueFunc, {}, {}); + B.createUnreachable(moduleLoc); + return; +} + void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value, bool EmitProfilerIncrement) { auto *dc = function.getDecl()->getInnermostDeclContext(); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index e5b7b9ec2900f..78d3bc8dc8eb6 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -638,6 +638,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// application based on a main type and optionally a main type. void emitArtificialTopLevel(Decl *mainDecl); + /// Generate code into @main for starting the async main on the main thread. + void emitAsyncMainThreadStart(SILDeclRef entryPoint); + /// Generates code for a class deallocating destructor. This /// calls the destroying destructor and then deallocates 'self'. void emitDeallocatingDestructor(DestructorDecl *dd); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index eb5fd9ac37c49..325d9d35c3a16 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1888,41 +1888,29 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) { Expr *returnedExpr; if (mainFunction->hasAsync()) { - // Pass main into _runAsyncMain(_ asyncFunc: () async throws -> ()) - // Resulting $main looks like: - // $main() { _runAsyncMain(main) } + // Ensure that the concurrency module is loaded auto *concurrencyModule = context.getLoadedModule(context.Id_Concurrency); if (!concurrencyModule) { context.Diags.diagnose(mainFunction->getAsyncLoc(), diag::async_main_no_concurrency); auto result = new (context) ErrorExpr(mainFunction->getSourceRange()); - SmallVector stmts; - stmts.push_back(result); - auto body = BraceStmt::create(context, SourceLoc(), stmts, - SourceLoc(), /*Implicit*/true); - + ASTNode stmts[] = {result}; + auto body = BraceStmt::create(context, SourceLoc(), stmts, SourceLoc(), + /*Implicit*/ true); return std::make_pair(body, /*typechecked*/true); } - SmallVector decls; - concurrencyModule->lookupQualified( - concurrencyModule, - DeclNameRef(context.getIdentifier("_runAsyncMain")), - NL_QualifiedDefault | NL_IncludeUsableFromInline, - decls); - assert(!decls.empty() && "Failed to find _runAsyncMain"); - FuncDecl *runner = cast(decls[0]); - - auto asyncRunnerDeclRef = ConcreteDeclRef(runner, substitutionMap); - - DeclRefExpr *funcExpr = new (context) DeclRefExpr(asyncRunnerDeclRef, - DeclNameLoc(), - /*implicit=*/true); - funcExpr->setType(runner->getInterfaceType()); - auto *argList = - ArgumentList::forImplicitUnlabeled(context, {memberRefExpr}); - auto *callExpr = CallExpr::createImplicit(context, funcExpr, argList); - returnedExpr = callExpr; + // $main() async { await main() } + Expr *awaitExpr = + new (context) AwaitExpr(callExpr->getLoc(), callExpr, + context.TheEmptyTupleType, /*implicit*/ true); + if (mainFunction->hasThrows()) { + // $main() async throws { try await main() } + awaitExpr = + new (context) TryExpr(callExpr->getLoc(), awaitExpr, + context.TheEmptyTupleType, /*implicit*/ true); + } + returnedExpr = awaitExpr; } else if (mainFunction->hasThrows()) { auto *tryExpr = new (context) TryExpr( callExpr->getLoc(), callExpr, context.TheEmptyTupleType, /*implicit=*/true); @@ -2039,7 +2027,7 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, DeclName(context, DeclBaseName(context.Id_MainEntryPoint), ParameterList::createEmpty(context)), /*NameLoc=*/SourceLoc(), - /*Async=*/false, + /*Async=*/mainFunction->hasAsync(), /*Throws=*/mainFunction->hasThrows(), /*GenericParams=*/nullptr, ParameterList::createEmpty(context), /*FnRetType=*/TupleType::getEmpty(context), declContext); @@ -2501,7 +2489,7 @@ static FuncDecl *findSimilarAccessor(DeclNameRef replacedVarName, } assert(!isa(results[0])); - + auto *origStorage = cast(results[0]); if (forDynamicReplacement && !origStorage->isDynamic()) { Diags.diagnose(attr->getLocation(), diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index ac5447b64f6de..1d75b04d21259 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -753,7 +753,11 @@ func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: Builtin.Job) @available(SwiftStdlib 5.5, *) @_silgen_name("swift_task_asyncMainDrainQueue") -public func _asyncMainDrainQueue() -> Never +internal func _asyncMainDrainQueue() -> Never + +@available(SwiftStdlib 5.5, *) +@_silgen_name("swift_task_getMainExecutor") +internal func _getMainExecutor() -> Builtin.Executor @available(SwiftStdlib 5.5, *) public func _runAsyncMain(@_unsafeSendable _ asyncFun: @escaping () async throws -> ()) { diff --git a/test/Concurrency/Runtime/async_task_priority_current.swift b/test/Concurrency/Runtime/async_task_priority_current.swift index 1ab95c82d59b8..8c39b4a9b703a 100644 --- a/test/Concurrency/Runtime/async_task_priority_current.swift +++ b/test/Concurrency/Runtime/async_task_priority_current.swift @@ -17,10 +17,19 @@ extension TaskPriority: CustomStringConvertible { } } +@available(SwiftStdlib 5.5, *) +@main struct Main { + static func main() async { + print("main priority: \(Task.currentPriority)") // CHECK: main priority: TaskPriority(rawValue: [[#MAIN_PRIORITY:]]) + await test_detach() + await test_multiple_lo_indirectly_escalated() + } +} + @available(SwiftStdlib 5.5, *) func test_detach() async { let a1 = Task.currentPriority - print("a1: \(a1)") // CHECK: TaskPriority(rawValue: 21) + print("a1: \(a1)") // CHECK: a1: TaskPriority(rawValue: [[#MAIN_PRIORITY]]) // Note: remember to detach using a higher priority, otherwise a lower one // might be escalated by the get() and we could see `default` in the detached @@ -30,8 +39,15 @@ func test_detach() async { print("a2: \(a2)") // CHECK: a2: TaskPriority(rawValue: 25) }.get() - let a3 = Task.currentPriority - print("a3: \(a3)") // CHECK: a3: TaskPriority(rawValue: 21) + await detach(priority: .default) { + let a3 = Task.currentPriority + // The priority of 'a3' may either be 21 (default) or elevated to that of + // the main function, whichever is greater. + print("a3: \(a3)") // CHECK: a3: TaskPriority(rawValue: [[#max(MAIN_PRIORITY,21)]] + }.get() + + let a4 = Task.currentPriority + print("a4: \(a4)") // CHECK: a4: TaskPriority(rawValue: [[#MAIN_PRIORITY]]) } @available(SwiftStdlib 5.5, *) @@ -66,10 +82,4 @@ func test_multiple_lo_indirectly_escalated() async { print("default done") // CHECK: default done } -@available(SwiftStdlib 5.5, *) -@main struct Main { - static func main() async { - await test_detach() - await test_multiple_lo_indirectly_escalated() - } -} + diff --git a/test/Concurrency/async_main.swift b/test/Concurrency/async_main.swift index b14fc3d52da6b..8001fdc8f2843 100644 --- a/test/Concurrency/async_main.swift +++ b/test/Concurrency/async_main.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -dump-ast -disable-availability-checking -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-AST +// RUN: %target-swift-frontend -emit-sil -disable-availability-checking -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-SIL // RUN: %target-build-swift -Xfrontend -disable-availability-checking -Xfrontend -parse-as-library %s -o %t_binary // RUN: %target-run %t_binary | %FileCheck %s --check-prefix=CHECK-EXEC @@ -15,29 +15,67 @@ func asyncFunc() async { } @main struct MyProgram { - static func main() async { + static func main() async throws { await asyncFunc() } } // CHECK-EXEC: Hello World! -// CHECK-AST-LABEL: "main()" interface -// CHECK-AST: (await_expr type='()' -// CHECK-AST-NEXT: (call_expr type='()' -// CHECK-AST-NEXT: (declref_expr type='() async -> ()' -// CHECK-AST-SAME: decl=async_main.(file).asyncFunc()@ - -// CHECK-AST-LABEL: (func_decl implicit "$main()" interface -// CHECK-AST: (brace_stmt -// CHECK-AST-NEXT: (return_stmt implicit -// CHECK-AST-NEXT: (call_expr implicit type='()' -// CHECK-AST-NEXT: (declref_expr implicit -// CHECK-AST-SAME: type='(@escaping () async throws -> ()) -> ()' -// CHECK-AST-SAME: decl=_Concurrency.(file)._runAsyncMain -// CHECK-AST-SAME: function_ref=single -// CHECK-AST-NEXT: (argument_list -// CHECK-AST-NEXT: (argument -// CHECK-AST-NEXT: (function_conversion_expr implicit type='() async throws -> ()' -// CHECK-AST-NEXT: (dot_syntax_call_expr -// CHECK-AST-NEXT: (autoclosure_expr implicit type='(MyProgram.Type) -> () async -> ()' +// static MyProgram.main() +// CHECK-SIL-LABEL: sil hidden @$s10async_main9MyProgramV0B0yyYaKFZ : $@convention(method) @async (@thin MyProgram.Type) -> @error Error + + +// static MyProgram.$main() +// CHECK-SIL-LABEL: sil hidden @$s10async_main9MyProgramV5$mainyyYaKFZ : $@convention(method) @async (@thin MyProgram.Type) -> @error Error + + +// async_Main +// CHECK-SIL_LABEL: sil hidden @async_Main : $@convention(thin) @async () -> () { +// call main +// CHECK-SIL: %0 = metatype $@thin MyProgram.Type // user: %2 +// CHECK-SIL-NEXT: // function_ref static MyProgram.$main() +// CHECK-SIL-NEXT: %1 = function_ref @$s10async_main9MyProgramV5$mainyyYaKFZ : $@convention(method) @async (@thin MyProgram.Type) -> @error Error // user: %2 +// CHECK-SIL-NEXT: try_apply %1(%0) : $@convention(method) @async (@thin MyProgram.Type) -> @error Error, normal bb1, error bb2 // id: %2 + +// unwrap error and exit or explode +// CHECK-SIL: bb1(%3 : $()): +// CHECK-SIL-NEXT: %4 = integer_literal $Builtin.Int32, 0 +// CHECK-SIL-NEXT: %5 = struct $Int32 (%4 : $Builtin.Int32) +// CHECK-SIL-NEXT: // function_ref exit +// CHECK-SIL-NEXT: %6 = function_ref @exit : $@convention(c) (Int32) -> Never +// CHECK-SIL-NEXT: %7 = apply %6(%5) : $@convention(c) (Int32) -> Never +// CHECK-SIL-NEXT: unreachable + +// CHECK-SIL: bb2(%9 : $Error): +// CHECK-SIL-NEXT: %10 = builtin "errorInMain"(%9 : $Error) : $() +// CHECK-SIL-NEXT: unreachable + +// main +// CHECK-SIL-LABEL: sil @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { + +// CHECK-SIL: // function_ref async_Main +// CHECK-SIL-NEXT: %2 = function_ref @async_Main : $@convention(thin) @async () -> () +// CHECK-SIL-NEXT: %3 = integer_literal $Builtin.Int64, 2048 +// CHECK-SIL-NEXT: %4 = struct $Int (%3 : $Builtin.Int64) +// CHECK-SIL-NEXT: %5 = metatype $@thick ().Type +// CHECK-SIL-NEXT: %6 = init_existential_metatype %5 : $@thick ().Type, $@thick Any.Type +// CHECK-SIL-NEXT: // function_ref thunk for @escaping @convention(thin) @async () -> () +// CHECK-SIL-NEXT: %7 = function_ref @$sIetH_yts5Error_pIegHrzo_TR : $@convention(thin) @async (@convention(thin) @async () -> ()) -> (@out (), @error Error) +// CHECK-SIL-NEXT: %8 = partial_apply [callee_guaranteed] %7(%2) : $@convention(thin) @async (@convention(thin) @async () -> ()) -> (@out (), @error Error) +// CHECK-SIL-NEXT: %9 = convert_function %8 : $@async @callee_guaranteed () -> (@out (), @error Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()> +// CHECK-SIL-NEXT: %10 = builtin "createAsyncTask"<()>(%4 : $Int, %6 : $@thick Any.Type, %9 : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-SIL-NEXT: %11 = tuple_extract %10 : $(Builtin.NativeObject, Builtin.RawPointer), 0 +// CHECK-SIL-NEXT: // function_ref swift_job_run +// CHECK-SIL-NEXT: %12 = function_ref @swift_job_run : $@convention(thin) (UnownedJob, UnownedSerialExecutor) -> () +// CHECK-SIL-NEXT: %13 = builtin "convertTaskToJob"(%11 : $Builtin.NativeObject) : $Builtin.Job +// CHECK-SIL-NEXT: %14 = struct $UnownedJob (%13 : $Builtin.Job) +// CHECK-SIL-NEXT: // function_ref swift_task_getMainExecutor +// CHECK-SIL-NEXT: %15 = function_ref @swift_task_getMainExecutor : $@convention(thin) () -> Builtin.Executor +// CHECK-SIL-NEXT: %16 = apply %15() : $@convention(thin) () -> Builtin.Executor +// CHECK-SIL-NEXT: %17 = struct $UnownedSerialExecutor (%16 : $Builtin.Executor) +// CHECK-SIL-NEXT: %18 = apply %12(%14, %17) : $@convention(thin) (UnownedJob, UnownedSerialExecutor) -> () +// CHECK-SIL-NEXT: // function_ref swift_task_asyncMainDrainQueue +// CHECK-SIL-NEXT: %19 = function_ref @swift_task_asyncMainDrainQueue : $@convention(thin) () -> Never +// CHECK-SIL-NEXT: %20 = apply %19() : $@convention(thin) () -> Never +// CHECK-SIL-NEXT: unreachable