Add AsyncEntryPoint SILDeclRef type

The AsyncEntryPoint represents the thunk that is wrapped in a task. This
thunk is used to ensure that the main function explicitly calls "exit",
and to properly unwrap and report any unhandled errors returned from the
user-written main. The function takes on the name `@async_main` in the
emitted SIL.
This commit is contained in:
Evan Wilde
2021-07-29 17:00:27 -07:00
parent b10c2c89a9
commit 552ae0635a
9 changed files with 67 additions and 3 deletions

View File

@@ -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);

View File

@@ -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:

View File

@@ -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<FileUnit>(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<VarDecl>(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();

View File

@@ -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");

View File

@@ -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";

View File

@@ -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<ClassDecl>(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");
}

View File

@@ -1039,6 +1039,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
postEmitFunction(constant, f);
return;
}
case SILDeclRef::Kind::AsyncEntryPoint:
case SILDeclRef::Kind::EntryPoint: {
f->setBare(IsBare);

View File

@@ -141,6 +141,7 @@ DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) {
case SILDeclRef::Kind::EnumElement:
return getMagicFunctionName(cast<EnumElementDecl>(ref.getDecl())
->getDeclContext());
case SILDeclRef::Kind::AsyncEntryPoint:
case SILDeclRef::Kind::EntryPoint:
auto *file = ref.getDecl()->getDeclContext()->getParentSourceFile();
return getMagicFunctionName(file);

View File

@@ -638,6 +638,9 @@ public:
/// 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);