Skip to content

Commit da9eaad

Browse files
committed
@main: Enable main function to throw.
SE-0281 was accepted with the modification that the main function should be allowed to be throwing. Here support for enabling that is added. Support is implemented in two steps: (1) The $main wrapper function is modified to be throwing static func $main() throws { return try main() } whenever the main function is throwing (it remains non-throwing when $main is not throwing). (2) The @main entry point is modified to be sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 { entry(%argc : $Int32, %argv : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>): %the_main_type = metatype $@thin TheMainType.Type %the_main_func = function_ref @`TheMainType.main()` : $@convention(method) (@thin TheMainType.Type) -> @error Error try_apply %the_main_func(%the_main_type) : $@convention(method) (@thin TheMainType.Type) -> @error Error, normal success, error failure success(%_ : $()): %success_code_builtin_int32 = integer_literal $Builtin.Int32, 0 br bb1(%success_code_builtin_int32 : $Builtin.Int32) failure(%error : @owned $Error): %_ = builtin "errorInMain"(%error : $Error) : $() end_lifetime %error : $Error %error_code_builtin_int32 = integer_literal $Builtin.Int32, 1 br bb1(%error_code_builtin_int32 : $Builtin.Int32) exit(%code_builtin_int32 : $Builtin.Int32): %code = struct $Int32 (%code_builtin_int32 : $Builtin.Int32) return %code : $Int32 } whenever the main function is throwing (and consequently $main also is). In the non-throwing case, (a) the try_apply instruction is replaced with an apply instruction, (b) the body of the success block is appended to the entry block, and (c) the success and failure blocks are removed.
1 parent 52c9440 commit da9eaad

17 files changed

+174
-27
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2913,7 +2913,7 @@ NOTE(attr_ApplicationMain_script_here,none,
29132913
())
29142914

29152915
ERROR(attr_MainType_without_main,none,
2916-
"%0 is annotated with @main and must provide a main static function of type () -> ().",
2916+
"%0 is annotated with @main and must provide a main static function of type () -> Void or () throws -> Void.",
29172917
(DeclName))
29182918

29192919
#undef SELECT_APPLICATION_MAIN

include/swift/AST/SourceFile.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,6 @@ class SourceFile final : public FileUnit {
155155
/// The source location of the main type.
156156
SourceLoc MainDeclDiagLoc;
157157

158-
/// The main function in the type marked @main.
159-
FuncDecl *MainFunc = nullptr;
160-
161158
/// A hash of all interface-contributing tokens that have been lexed for
162159
/// this source file so far.
163160
/// We only collect interface hash for primary input files.

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7332,7 +7332,7 @@ bool FuncDecl::isCallAsFunctionMethod() const {
73327332
bool FuncDecl::isMainTypeMainMethod() const {
73337333
return (getBaseIdentifier() == getASTContext().Id_main) &&
73347334
!isInstanceMember() && getResultInterfaceType()->isVoid() &&
7335-
getParameters()->size() == 0 && !hasThrows();
7335+
getParameters()->size() == 0;
73367336
}
73377337

73387338
ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc,

lib/SILGen/SILGenFunction.cpp

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -725,8 +725,8 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
725725
// Emit a call to the main static function.
726726
// return Module.$main();
727727
auto *mainFunc = cast<FuncDecl>(mainDecl);
728-
729-
SILGenFunctionBuilder builder(SGM);
728+
auto moduleLoc = RegularLocation::getModuleLocation();
729+
auto *entryBlock = B.getInsertionBB();
730730

731731
SILDeclRef mainFunctionDeclRef(mainFunc, SILDeclRef::Kind::Func);
732732
SILFunction *mainFunction =
@@ -743,16 +743,51 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
743743
}
744744
auto metatype = B.createMetatype(mainType, getLoweredType(mainType->getInterfaceType()));
745745

746-
auto mainFunctionRef = B.createFunctionRef(mainFunc, mainFunction);
746+
auto mainFunctionRef = B.createFunctionRef(moduleLoc, mainFunction);
747747

748-
B.createApply(mainFunc, mainFunctionRef, SubstitutionMap(), {metatype});
748+
auto builtinInt32Type = SILType::getBuiltinIntegerType(32, getASTContext());
749749

750-
SILValue returnValue = B.createIntegerLiteral(
751-
mainFunc, SILType::getBuiltinIntegerType(32, getASTContext()), 0);
750+
auto *exitBlock = createBasicBlock();
751+
B.setInsertionPoint(exitBlock);
752+
SILValue exitCode = exitBlock->createPhiArgument(builtinInt32Type,
753+
ValueOwnershipKind::None);
752754
auto returnType = F.getConventions().getSingleSILResultType();
753-
if (returnValue->getType() != returnType)
754-
returnValue = B.createStruct(mainFunc, returnType, returnValue);
755-
B.createReturn(mainFunc, returnValue);
755+
if (exitCode->getType() != returnType)
756+
exitCode = B.createStruct(moduleLoc, returnType, exitCode);
757+
B.createReturn(moduleLoc, exitCode);
758+
759+
if (mainFunc->hasThrows()) {
760+
auto *successBlock = createBasicBlock();
761+
B.setInsertionPoint(successBlock);
762+
successBlock->createPhiArgument(SGM.Types.getEmptyTupleType(),
763+
ValueOwnershipKind::None);
764+
SILValue zeroReturnValue =
765+
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 0);
766+
B.createBranch(moduleLoc, exitBlock, {zeroReturnValue});
767+
768+
auto *failureBlock = createBasicBlock();
769+
B.setInsertionPoint(failureBlock);
770+
SILValue error = failureBlock->createPhiArgument(
771+
SILType::getExceptionType(getASTContext()),
772+
ValueOwnershipKind::Owned);
773+
// Log the error.
774+
B.createBuiltin(moduleLoc, getASTContext().getIdentifier("errorInMain"),
775+
SGM.Types.getEmptyTupleType(), {}, {error});
776+
B.createEndLifetime(moduleLoc, error);
777+
SILValue oneReturnValue =
778+
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 1);
779+
B.createBranch(moduleLoc, exitBlock, {oneReturnValue});
780+
781+
B.setInsertionPoint(entryBlock);
782+
B.createTryApply(moduleLoc, mainFunctionRef, SubstitutionMap(),
783+
{metatype}, successBlock, failureBlock);
784+
} else {
785+
B.setInsertionPoint(entryBlock);
786+
B.createApply(moduleLoc, mainFunctionRef, SubstitutionMap(), {metatype});
787+
SILValue returnValue =
788+
B.createIntegerLiteral(moduleLoc, builtinInt32Type, 0);
789+
B.createBranch(moduleLoc, exitBlock, {returnValue});
790+
}
756791
return;
757792
}
758793
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,14 +1786,19 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) {
17861786
mainFunction = viableCandidates[0];
17871787
}
17881788

1789-
auto voidToVoidFunctionType = FunctionType::get({}, context.TheEmptyTupleType);
1789+
bool mainFunctionThrows = mainFunction->hasThrows();
1790+
1791+
auto voidToVoidFunctionType =
1792+
FunctionType::get({}, context.TheEmptyTupleType,
1793+
FunctionType::ExtInfo().withThrows(mainFunctionThrows));
17901794
auto nominalToVoidToVoidFunctionType = FunctionType::get({AnyFunctionType::Param(nominal->getInterfaceType())}, voidToVoidFunctionType);
17911795
auto *func = FuncDecl::create(
1792-
context, /*StaticLoc*/ braces.End, StaticSpellingKind::KeywordStatic,
1793-
/*FuncLoc*/ location,
1796+
context, /*StaticLoc*/ SourceLoc(), StaticSpellingKind::KeywordStatic,
1797+
/*FuncLoc*/ SourceLoc(),
17941798
DeclName(context, DeclBaseName(context.Id_MainEntryPoint),
17951799
ParameterList::createEmpty(context)),
1796-
/*NameLoc*/ braces.End, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
1800+
/*NameLoc*/ SourceLoc(), /*Throws=*/mainFunctionThrows,
1801+
/*ThrowsLoc=*/SourceLoc(),
17971802
/*GenericParams=*/nullptr, ParameterList::createEmpty(context),
17981803
/*FnRetType=*/TypeLoc::withoutLoc(TupleType::getEmpty(context)),
17991804
declContext);
@@ -1821,12 +1826,23 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) {
18211826
auto *dotSyntaxCallExpr = new (context) DotSyntaxCallExpr(
18221827
funcDeclRefExpr, /*DotLoc*/ SourceLoc(), typeExpr, voidToVoidFunctionType);
18231828
dotSyntaxCallExpr->setImplicit(true);
1824-
dotSyntaxCallExpr->setThrows(false);
1829+
dotSyntaxCallExpr->setThrows(mainFunctionThrows);
18251830

18261831
auto *callExpr = CallExpr::createImplicit(context, dotSyntaxCallExpr, {}, {});
18271832
callExpr->setImplicit(true);
1828-
callExpr->setThrows(false);
1833+
callExpr->setThrows(mainFunctionThrows);
18291834
callExpr->setType(context.TheEmptyTupleType);
1835+
1836+
Expr *returnedExpr;
1837+
1838+
if (mainFunctionThrows) {
1839+
auto *tryExpr = new (context) TryExpr(
1840+
SourceLoc(), callExpr, context.TheEmptyTupleType, /*implicit=*/true);
1841+
returnedExpr = tryExpr;
1842+
} else {
1843+
returnedExpr = callExpr;
1844+
}
1845+
18301846
auto *returnStmt =
18311847
new (context) ReturnStmt(SourceLoc(), callExpr, /*Implicit=*/true);
18321848

test/attr/ApplicationMain/attr_main_arguments.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

3-
@main // expected-error{{'MyBase' is annotated with @main and must provide a main static function of type () -> ().}}
3+
@main // expected-error{{'MyBase' is annotated with @main and must provide a main static function}}
44
struct MyBase {
55
static func main(_ argc: Int, _ argv: [String]) {
66
}

test/attr/ApplicationMain/attr_main_dynamicCallable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

3-
@main // expected-error{{'Foo' is annotated with @main and must provide a main static function of type () -> ().}}
3+
@main // expected-error{{'Foo' is annotated with @main and must provide a main static function}}
44
struct Foo {
55
@dynamicCallable
66
struct main {

test/attr/ApplicationMain/attr_main_dynamicMemberLookup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

3-
@main @dynamicMemberLookup // expected-error{{'Main' is annotated with @main and must provide a main static function of type () -> ().}}
3+
@main @dynamicMemberLookup // expected-error{{'Main' is annotated with @main and must provide a main static function}}
44
struct Main {
55
subscript(dynamicMember member: String) -> () -> Void {
66
return {

test/attr/ApplicationMain/attr_main_extension_nofunc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
class EntryPoint {
44
}
55

6-
@main // expected-error{{'EntryPoint' is annotated with @main and must provide a main static function of type () -> ()}}
6+
@main // expected-error{{'EntryPoint' is annotated with @main and must provide a main static function}}
77
extension EntryPoint {
88
}
99

test/attr/ApplicationMain/attr_main_instance.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

3-
@main // expected-error{{'MyBase' is annotated with @main and must provide a main static function of type () -> ().}}
3+
@main // expected-error{{'MyBase' is annotated with @main and must provide a main static function}}
44
class MyBase {
55
func main() {
66
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-run-simple-swift(-parse-as-library) | %FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
protocol P {
5+
}
6+
extension P {
7+
static func main() throws {
8+
print("P.main")
9+
}
10+
}
11+
struct S {
12+
static func main() {
13+
print("S.main")
14+
}
15+
}
16+
17+
// CHECK: S.main
18+
@main
19+
extension S : P {}
20+
21+
22+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %target-run-simple-swift(-parse-as-library) | %FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
protocol P {
5+
}
6+
extension P {
7+
static func main() {
8+
print("P.main")
9+
}
10+
}
11+
struct S {
12+
static func main() {
13+
print("S.main though neither throw")
14+
}
15+
}
16+
17+
// CHECK: S.main though neither throw
18+
@main
19+
extension S : P {}
20+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %target-run-simple-swift(-parse-as-library) | %FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
protocol P {
5+
}
6+
extension P {
7+
static func main() {
8+
print("P.main")
9+
}
10+
}
11+
struct S {
12+
static func main() throws {
13+
print("S.main though throwing")
14+
}
15+
}
16+
17+
// CHECK: S.main though throwing
18+
@main
19+
extension S : P {}
20+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-run-simple-swift(-parse-as-library) | %FileCheck %s
2+
// REQUIRES: executable_test
3+
4+
protocol P {
5+
}
6+
extension P {
7+
static func main() throws {
8+
print("P.main")
9+
}
10+
}
11+
struct S {
12+
static func main() throws {
13+
print("S.main though both throw")
14+
}
15+
}
16+
17+
// CHECK: S.main though both throw
18+
@main
19+
extension S : P {}
20+
21+

test/attr/ApplicationMain/attr_main_return.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

3-
@main // expected-error{{'MyBase' is annotated with @main and must provide a main static function of type () -> ().}}
3+
@main // expected-error{{'MyBase' is annotated with @main and must provide a main static function}}
44
struct MyBase {
55
static func main() -> Int {
66
}

test/attr/ApplicationMain/attr_main_throws.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
22

3-
@main // expected-error{{'MyBase' is annotated with @main and must provide a main static function of type () -> ().}}
3+
@main
44
struct MyBase {
55
static func main() throws {
66
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -parse-as-library %s -o %t/main
3+
// RUN: %target-codesign %t/main
4+
// RUN: not --crash %t/main 2>&1 | %FileCheck %s
5+
// REQUIRES: executable_test
6+
// REQUIRES: OS=macosx
7+
8+
enum Err : Error { case or }
9+
10+
// CHECK: Fatal error: Error raised at top level: main.Err.or:
11+
@main
12+
struct S {
13+
static func main() throws {
14+
throw Err.or
15+
}
16+
}

0 commit comments

Comments
 (0)