Skip to content

Allow the main function for @main types to use typed throws #71207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/AST/KnownDecls.def
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ FUNC_DECL(ConvertToAnyHashable, "_convertToAnyHashable")

FUNC_DECL(DiagnoseUnexpectedError, "_unexpectedError")
FUNC_DECL(DiagnoseUnexpectedErrorTyped, "_unexpectedErrorTyped")
FUNC_DECL(ErrorInMainTyped, "_errorInMainTyped")
FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional")
FUNC_DECL(DiagnoseUnexpectedEnumCase, "_diagnoseUnexpectedEnumCase")
FUNC_DECL(DiagnoseUnexpectedEnumCaseValue, "_diagnoseUnexpectedEnumCaseValue")
Expand Down
18 changes: 17 additions & 1 deletion lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,21 @@ FuncDecl *ASTContext::getAsyncIteratorNext() const {
return nullptr;
}

namespace {

template<typename DeclClass>
DeclClass *synthesizeBuiltinDecl(const ASTContext &ctx, StringRef name) {
if (name == "Never") {
auto never = new (ctx) EnumDecl(SourceLoc(), ctx.getIdentifier(name),
SourceLoc(), { }, nullptr,
ctx.MainModule);
return (DeclClass *)never;
}
return nullptr;
}

}

#define KNOWN_STDLIB_TYPE_DECL(NAME, DECL_CLASS, NUM_GENERIC_PARAMS) \
DECL_CLASS *ASTContext::get##NAME##Decl() const { \
if (getImpl().NAME##Decl) \
Expand All @@ -980,7 +995,8 @@ DECL_CLASS *ASTContext::get##NAME##Decl() const { \
} \
} \
} \
return nullptr; \
getImpl().NAME##Decl = synthesizeBuiltinDecl<DECL_CLASS>(*this, #NAME); \
return getImpl().NAME##Decl; \
} \
\
Type ASTContext::get##NAME##Type() const { \
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5700,7 +5700,7 @@ llvm::Optional<Type> AnyFunctionType::getEffectiveThrownErrorType() const {
return getASTContext().getErrorExistentialType();

// If the thrown interface type is "Never", this function does not throw.
if (thrownError->isEqual(getASTContext().getNeverType()))
if (thrownError->isNever())
return llvm::None;

// Otherwise, return the typed error.
Expand Down
6 changes: 0 additions & 6 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,6 @@ void SILGenFunction::ForceTryEmission::finish() {
}, LookUpConformanceInModule(SGF.getModule().getSwiftModule()));

// Generic errors are passed indirectly.
#if true
if (!error.getType().isAddress()) {
auto *tmp = SGF.B.createAllocStack(Loc,
error.getType().getObjectType(),
Expand All @@ -1218,11 +1217,6 @@ void SILGenFunction::ForceTryEmission::finish() {

tmpBuffer = tmp;
}
#else
error = SGF.emitSubstToOrigValue(Loc, error,
AbstractionPattern::getOpaque(),
error.getType().getASTType());
#endif
}

SGF.emitApplyOfLibraryIntrinsic(
Expand Down
84 changes: 1 addition & 83 deletions lib/SILGen/SILGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,89 +1319,7 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {

case ArtificialMainKind::TypeMain: {
// Emit a call to the main static function.
// return Module.$main();
auto *mainFunc = cast<FuncDecl>(mainDecl);
auto moduleLoc = SILLocation(mainDecl);
auto *entryBlock = B.getInsertionBB();

SILDeclRef mainFunctionDeclRef(mainFunc, SILDeclRef::Kind::Func);
SILFunction *mainFunction =
SGM.getFunction(mainFunctionDeclRef, NotForDefinition);

ExtensionDecl *mainExtension =
dyn_cast<ExtensionDecl>(mainFunc->getDeclContext());

NominalTypeDecl *mainType;
if (mainExtension) {
mainType = mainExtension->getExtendedNominal();
} else {
mainType = cast<NominalTypeDecl>(mainFunc->getDeclContext());
}
auto metatype = B.createMetatype(mainType, getLoweredType(mainType->getInterfaceType()));

auto mainFunctionRef = B.createFunctionRef(moduleLoc, mainFunction);

auto builtinInt32Type = SILType::getBuiltinIntegerType(32, getASTContext());

auto *exitBlock = createBasicBlock();
SILValue exitCode =
exitBlock->createPhiArgument(builtinInt32Type, OwnershipKind::None);
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<SILFunctionType>();
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();
B.setInsertionPoint(successBlock);
successBlock->createPhiArgument(SGM.Types.getEmptyTupleType(),
OwnershipKind::None);
SILValue zeroReturnValue =
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 0);
B.createBranch(moduleLoc, exitBlock, {zeroReturnValue});

auto *failureBlock = createBasicBlock();
B.setInsertionPoint(failureBlock);
SILValue error = failureBlock->createPhiArgument(
SILType::getExceptionType(getASTContext()), OwnershipKind::Owned);
// Log the error.
B.createBuiltin(moduleLoc, getASTContext().getIdentifier("errorInMain"),
SGM.Types.getEmptyTupleType(), {}, {error});
B.createEndLifetime(moduleLoc, error);
SILValue oneReturnValue =
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 1);
B.createBranch(moduleLoc, exitBlock, {oneReturnValue});

B.setInsertionPoint(entryBlock);
B.createTryApply(moduleLoc, mainFunctionRef, SubstitutionMap(),
{metatype}, successBlock, failureBlock);
} else {
B.setInsertionPoint(entryBlock);
B.createApply(moduleLoc, mainFunctionRef, SubstitutionMap(), {metatype});
SILValue returnValue =
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 0);
B.createBranch(moduleLoc, exitBlock, {returnValue});
}
emitCallToMain(cast<FuncDecl>(mainDecl));
return;
}
}
Expand Down
3 changes: 3 additions & 0 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
/// application based on a main type and optionally a main type.
void emitArtificialTopLevel(Decl *mainDecl);

/// Generate code for calling the given main function.
void emitCallToMain(FuncDecl *mainDecl);

/// Generate code into @main for starting the async main on the main thread.
void emitAsyncMainThreadStart(SILDeclRef entryPoint);

Expand Down
134 changes: 134 additions & 0 deletions lib/SILGen/SILGenTopLevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,140 @@ void SILGenModule::emitEntryPoint(SourceFile *SF, SILFunction *TopLevel) {
emitLazyConformancesForFunction(&toplevel);
}

/// Generate code for calling the given main function.
void SILGenFunction::emitCallToMain(FuncDecl *mainFunc) {
// This function is effectively emitting SIL for:
// return try await TheType.$main();
auto loc = SILLocation(mainFunc);
auto *entryBlock = B.getInsertionBB();

SILDeclRef mainFunctionDeclRef(mainFunc, SILDeclRef::Kind::Func);
SILFunction *mainFunction =
SGM.getFunction(mainFunctionDeclRef, NotForDefinition);

NominalTypeDecl *mainType =
mainFunc->getDeclContext()->getSelfNominalTypeDecl();
auto metatype = B.createMetatype(mainType, getLoweredType(mainType->getInterfaceType()));

auto mainFunctionRef = B.createFunctionRef(loc, mainFunction);

auto builtinInt32Type = SILType::getBuiltinIntegerType(
32, getASTContext());

// Set up the exit block, which will either return the exit value
// (for synchronous main()) or call exit() with the return value (for
// asynchronous main()).
auto *exitBlock = createBasicBlock();
SILValue exitCode =
exitBlock->createPhiArgument(builtinInt32Type, OwnershipKind::None);
B.setInsertionPoint(exitBlock);

if (!mainFunc->hasAsync()) {
auto returnType = F.getConventions().getSingleSILResultType(
B.getTypeExpansionContext());
if (exitCode->getType() != returnType)
exitCode = B.createStruct(loc, returnType, exitCode);
B.createReturn(loc, 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<SILFunctionType>();
SILType retType = SILType::getPrimitiveObjectType(
funcType.getParameters().front().getInterfaceType());
exitCode = B.createStruct(loc, retType, exitCode);
SILValue exitCall = B.createFunctionRef(loc, exitSILFunc);
B.createApply(loc, exitCall, {}, {exitCode});
B.createUnreachable(loc);
}

// Form a call to the main function.
CanSILFunctionType mainFnType = mainFunction->getConventions().funcTy;
ASTContext &ctx = getASTContext();
if (mainFnType->hasErrorResult()) {
auto *successBlock = createBasicBlock();
B.setInsertionPoint(successBlock);
successBlock->createPhiArgument(SGM.Types.getEmptyTupleType(),
OwnershipKind::None);
SILValue zeroReturnValue =
B.createIntegerLiteral(loc, builtinInt32Type, 0);
B.createBranch(loc, exitBlock, {zeroReturnValue});

SILResultInfo errorResult = mainFnType->getErrorResult();
SILType errorType = errorResult.getSILStorageInterfaceType();

auto *failureBlock = createBasicBlock();
B.setInsertionPoint(failureBlock);
SILValue error;
if (IndirectErrorResult) {
error = IndirectErrorResult;
} else {
error = failureBlock->createPhiArgument(
errorType, OwnershipKind::Owned);
}

// Log the error.
if (errorType.getASTType()->isErrorExistentialType()) {
// Load the indirect error, if needed.
if (IndirectErrorResult) {
const TypeLowering &errorExistentialTL = getTypeLowering(errorType);

error = emitLoad(
loc, IndirectErrorResult, errorExistentialTL, SGFContext(),
IsTake).forward(*this);
}

// Call the errorInMain entrypoint, which takes an existential
// error.
B.createBuiltin(loc, ctx.getIdentifier("errorInMain"),
SGM.Types.getEmptyTupleType(), {}, {error});
} else {
// Call the _errorInMainTyped entrypoint, which handles
// arbitrary error types.
SILValue tmpBuffer;

FuncDecl *entrypoint = ctx.getErrorInMainTyped();
auto genericSig = entrypoint->getGenericSignature();
SubstitutionMap subMap = SubstitutionMap::get(
genericSig, [&](SubstitutableType *dependentType) {
return errorType.getASTType();
}, LookUpConformanceInModule(getModule().getSwiftModule()));

// Generic errors are passed indirectly.
if (!error->getType().isAddress()) {
auto *tmp = B.createAllocStack(loc,
error->getType().getObjectType(),
llvm::None);
emitSemanticStore(
loc, error, tmp,
getTypeLowering(tmp->getType()), IsInitialization);
tmpBuffer = tmp;
error = tmp;
}

emitApplyOfLibraryIntrinsic(
loc, entrypoint, subMap,
{ ManagedValue::forForwardedRValue(*this, error) },
SGFContext());
}
B.createUnreachable(loc);

B.setInsertionPoint(entryBlock);
B.createTryApply(loc, mainFunctionRef, SubstitutionMap(),
{metatype}, successBlock, failureBlock);
} else {
B.setInsertionPoint(entryBlock);
B.createApply(loc, mainFunctionRef, SubstitutionMap(), {metatype});
SILValue returnValue =
B.createIntegerLiteral(loc, builtinInt32Type, 0);
B.createBranch(loc, exitBlock, {returnValue});
}
}

void SILGenModule::emitEntryPoint(SourceFile *SF) {
assert(!M.lookUpFunction(getASTContext().getEntryPointFunctionName()) &&
"already emitted toplevel?!");
Expand Down
39 changes: 14 additions & 25 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2749,47 +2749,36 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
ConstraintSystemFlags::IgnoreAsyncSyncMismatch);
ConstraintLocator *locator =
CS.getConstraintLocator({}, ConstraintLocator::Member);
auto throwsTypeVar = CS.createTypeVariable(locator, 0);

// Allowed main function types
// `() -> Void`
// `() async -> Void`
// `() throws -> Void`
// `() async throws -> Void`
// `@MainActor () -> Void`
// `@MainActor () async -> Void`
// `@MainActor () throws -> Void`
// `@MainActor () async throws -> Void`
// `() throws(E) -> Void`
// `() async throws(E) -> Void`
// `@MainActor () throws(E) -> Void`
// `@MainActor () async throws(E) -> Void`
{
llvm::SmallVector<Type, 8> mainTypes = {

FunctionType::get(/*params*/ {}, context.TheEmptyTupleType,
ASTExtInfo()),
FunctionType::get(
/*params*/ {}, context.TheEmptyTupleType,
ASTExtInfoBuilder().withAsync().build()),

llvm::SmallVector<Type, 4> mainTypes = {
FunctionType::get(/*params*/ {}, context.TheEmptyTupleType,
ASTExtInfoBuilder().withThrows().build()),
ASTExtInfoBuilder().withThrows(
true, throwsTypeVar
).build()),

FunctionType::get(
/*params*/ {}, context.TheEmptyTupleType,
ASTExtInfoBuilder().withAsync().withThrows().build())};
ASTExtInfoBuilder().withAsync()
.withThrows(true, throwsTypeVar).build())};

Type mainActor = context.getMainActorType();
if (mainActor) {
auto extInfo = ASTExtInfoBuilder().withIsolation(
FunctionTypeIsolation::forGlobalActor(mainActor));
FunctionTypeIsolation::forGlobalActor(mainActor))
.withThrows(true, throwsTypeVar);
mainTypes.push_back(FunctionType::get(
/*params*/ {}, context.TheEmptyTupleType,
extInfo.build()));
mainTypes.push_back(FunctionType::get(
/*params*/ {}, context.TheEmptyTupleType,
extInfo.withAsync().build()));
mainTypes.push_back(FunctionType::get(
/*params*/ {}, context.TheEmptyTupleType,
extInfo.withThrows().build()));
mainTypes.push_back(FunctionType::get(
/*params*/ {}, context.TheEmptyTupleType,
extInfo.withAsync().withThrows().build()));
}
TypeVariableType *mainType =
CS.createTypeVariable(locator, /*options=*/0);
Expand Down
10 changes: 10 additions & 0 deletions stdlib/public/core/ErrorType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,16 @@ public func _errorInMain(_ error: Error) {
#endif
}

/// Invoked by the compiler when code at top level throws an uncaught, typed error.
@_alwaysEmitIntoClient
public func _errorInMainTyped<Failure: Error>(_ error: Failure) -> Never {
#if !$Embedded
fatalError("Error raised at top level: \(String(reflecting: error))")
#else
Builtin.int_trap()
#endif
}

/// Runtime function to determine the default code for an Error-conforming type.
/// Called by the Foundation overlay.
@_silgen_name("_swift_stdlib_getDefaultErrorCode")
Expand Down
Loading