Skip to content

Commit 52c9440

Browse files
committed
@main: Allowed attribute on extensions.
Previously the @main attribute could only be applied to NominalTypeDecls. Here, that limitation is lifted so that the attribute can be applied to ExtensionDecls.
1 parent 5bd96a4 commit 52c9440

File tree

25 files changed

+261
-11
lines changed

25 files changed

+261
-11
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ SIMPLE_DECL_ATTR(dynamicCallable, DynamicCallable,
148148
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
149149
6)
150150
SIMPLE_DECL_ATTR(main, MainType,
151-
OnClass | OnStruct | OnEnum |
151+
OnClass | OnStruct | OnEnum | OnExtension |
152152
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
153153
7)
154154
SIMPLE_DECL_ATTR(_exported, Exported,

lib/SILGen/SILGenFunction.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,15 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
732732
SILFunction *mainFunction =
733733
SGM.getFunction(mainFunctionDeclRef, NotForDefinition);
734734

735-
NominalTypeDecl *mainType = cast<NominalTypeDecl>(mainFunc->getDeclContext());
735+
ExtensionDecl *mainExtension =
736+
dyn_cast<ExtensionDecl>(mainFunc->getDeclContext());
737+
738+
NominalTypeDecl *mainType;
739+
if (mainExtension) {
740+
mainType = mainExtension->getExtendedNominal();
741+
} else {
742+
mainType = cast<NominalTypeDecl>(mainFunc->getDeclContext());
743+
}
736744
auto metatype = B.createMetatype(mainType, getLoweredType(mainType->getInterfaceType()));
737745

738746
auto mainFunctionRef = B.createFunctionRef(mainFunc, mainFunction);

lib/Sema/TypeCheckAttr.cpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,13 +1700,32 @@ void AttributeChecker::visitUIApplicationMainAttr(UIApplicationMainAttr *attr) {
17001700
}
17011701

17021702
void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) {
1703-
auto *nominal = dyn_cast<NominalTypeDecl>(D);
1703+
auto *extension = dyn_cast<ExtensionDecl>(D);
1704+
1705+
IterableDeclContext *iterableDeclContext;
1706+
DeclContext *declContext;
1707+
NominalTypeDecl *nominal;
1708+
SourceRange braces;
1709+
1710+
if (extension) {
1711+
nominal = extension->getExtendedNominal();
1712+
iterableDeclContext = extension;
1713+
declContext = extension;
1714+
braces = extension->getBraces();
1715+
} else {
1716+
nominal = dyn_cast<NominalTypeDecl>(D);
1717+
iterableDeclContext = nominal;
1718+
declContext = nominal;
1719+
braces = nominal->getBraces();
1720+
}
17041721

17051722
if (!nominal) {
17061723
assert(false && "Should have already recognized that the MainType decl "
17071724
"isn't applicable to decls other than NominalTypeDecls");
17081725
return;
17091726
}
1727+
assert(iterableDeclContext);
1728+
assert(declContext);
17101729

17111730
// The type cannot be generic.
17121731
if (nominal->isGenericContext()) {
@@ -1716,7 +1735,8 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) {
17161735
return;
17171736
}
17181737

1719-
auto *file = cast<SourceFile>(nominal->getModuleScopeContext());
1738+
SourceFile *file = cast<SourceFile>(declContext->getModuleScopeContext());
1739+
assert(file);
17201740

17211741
// Create a function
17221742
//
@@ -1729,11 +1749,11 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) {
17291749
// usual type-checking. The alternative would be to directly call
17301750
// mainType.main() from the entry point, and that would require fully
17311751
// type-checking the call to mainType.main().
1732-
auto &context = nominal->getASTContext();
1752+
auto &context = D->getASTContext();
17331753
auto location = attr->getLocation();
17341754

1735-
auto resolution = resolveValueMember(*nominal, nominal->getInterfaceType(),
1736-
context.Id_main);
1755+
auto resolution = resolveValueMember(
1756+
*declContext, nominal->getInterfaceType(), context.Id_main);
17371757

17381758
FuncDecl *mainFunction = nullptr;
17391759

@@ -1769,14 +1789,14 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) {
17691789
auto voidToVoidFunctionType = FunctionType::get({}, context.TheEmptyTupleType);
17701790
auto nominalToVoidToVoidFunctionType = FunctionType::get({AnyFunctionType::Param(nominal->getInterfaceType())}, voidToVoidFunctionType);
17711791
auto *func = FuncDecl::create(
1772-
context, /*StaticLoc*/ nominal->getBraces().End, StaticSpellingKind::KeywordStatic,
1792+
context, /*StaticLoc*/ braces.End, StaticSpellingKind::KeywordStatic,
17731793
/*FuncLoc*/ location,
17741794
DeclName(context, DeclBaseName(context.Id_MainEntryPoint),
17751795
ParameterList::createEmpty(context)),
1776-
/*NameLoc*/ nominal->getBraces().End, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
1796+
/*NameLoc*/ braces.End, /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
17771797
/*GenericParams=*/nullptr, ParameterList::createEmpty(context),
17781798
/*FnRetType=*/TypeLoc::withoutLoc(TupleType::getEmpty(context)),
1779-
nominal);
1799+
declContext);
17801800
func->setImplicit(true);
17811801
func->setSynthesized(true);
17821802

@@ -1817,7 +1837,8 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) {
18171837
func->setBodyParsed(body);
18181838
func->setInterfaceType(nominalToVoidToVoidFunctionType);
18191839

1820-
nominal->addMember(func);
1840+
iterableDeclContext->addMember(func);
1841+
18211842
// This function must be type-checked. Why? Consider the following scenario:
18221843
//
18231844
// protocol AlmostMainable {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
2+
3+
class EntryPoint {
4+
}
5+
6+
@main
7+
extension EntryPoint {
8+
static func main() {
9+
}
10+
}
11+
12+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
2+
3+
class EntryPoint {
4+
}
5+
6+
@main
7+
extension EntryPoint {
8+
static func main() {
9+
}
10+
}
11+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// This file is a part of the multi-file test driven by 'main2.swift'.
2+
3+
// RUN: %target-swift-frontend -parse %s
4+
5+
public class Main {
6+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module -emit-library -module-name ModuleA -module-link-name ModuleA %S/A.swift -o %t/%target-library-name(ModuleA)
3+
// RUN: %target-swift-frontend -c -I %t -L %t -lModuleA -parse-as-library %s
4+
5+
import ModuleA
6+
7+
@main
8+
extension Main {
9+
static func main() {
10+
print("ok")
11+
}
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This file is a part of the multi-file test driven by 'main2.swift'.
2+
3+
// RUN: %target-swift-frontend -parse %s
4+
5+
public class Main {
6+
public class func main() {
7+
print("ok")
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module -emit-library -module-name ModuleA -module-link-name ModuleA %S/A.swift -o %t/%target-library-name(ModuleA)
3+
// RUN: %target-swift-frontend -c -I %t -L %t -lModuleA -parse-as-library %s
4+
5+
import ModuleA
6+
7+
@main
8+
extension Main {
9+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
2+
3+
enum EntryPoint {
4+
}
5+
6+
@main
7+
extension EntryPoint {
8+
static func main() {
9+
}
10+
}
11+
12+
13+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
2+
3+
enum EntryPoint {
4+
static func main() {
5+
}
6+
}
7+
8+
@main
9+
extension EntryPoint {
10+
}
11+
12+
13+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// This file is a part of the multi-file test driven by 'main2.swift'.
2+
3+
// RUN: %target-swift-frontend -parse %s
4+
5+
public enum Main {
6+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module -emit-library -module-name ModuleA -module-link-name ModuleA %S/A.swift -o %t/%target-library-name(ModuleA)
3+
// RUN: %target-swift-frontend -c -I %t -L %t -lModuleA -parse-as-library %s
4+
5+
import ModuleA
6+
7+
@main
8+
extension Main {
9+
static func main() {
10+
print("ok")
11+
}
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This file is a part of the multi-file test driven by 'main2.swift'.
2+
3+
// RUN: %target-swift-frontend -parse %s
4+
5+
public enum Main {
6+
public static func main() {
7+
print("ok")
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module -emit-library -module-name ModuleA -module-link-name ModuleA %S/A.swift -o %t/%target-library-name(ModuleA)
3+
// RUN: %target-swift-frontend -c -I %t -L %t -lModuleA -parse-as-library %s
4+
5+
import ModuleA
6+
7+
@main
8+
extension Main {
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This file is a part of the multi-file test driven by 'main2.swift'.
2+
3+
// RUN: %target-swift-frontend -parse %s
4+
5+
@main
6+
extension Main {
7+
static func main() {
8+
print("hello world")
9+
}
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s %S/main1.swift
2+
3+
// Serialized partial AST support:
4+
// RUN: %target-swift-frontend -module-name main -emit-module-path %t.swiftmodule -primary-file %s %S/main1.swift
5+
// RUN: %target-swift-frontend -module-name main -parse-as-library -typecheck %t.swiftmodule -primary-file %S/main1.swift -verify -verify-ignore-unknown
6+
7+
class Main {
8+
}
9+
10+
// FIXME: Remove -verify-ignore-unknown.
11+
// <unknown>:0: error: unexpected error produced: 'main' attribute can only apply to one class in a module
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
2+
3+
class EntryPoint {
4+
}
5+
6+
@main // expected-error{{'EntryPoint' is annotated with @main and must provide a main static function of type () -> ()}}
7+
extension EntryPoint {
8+
}
9+
10+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
2+
3+
struct EntryPoint {
4+
}
5+
6+
@main
7+
extension EntryPoint {
8+
static func main() {
9+
}
10+
}
11+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
2+
3+
struct EntryPoint {
4+
static func main() {
5+
}
6+
}
7+
8+
@main
9+
extension EntryPoint {
10+
}
11+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// This file is a part of the multi-file test driven by 'main2.swift'.
2+
3+
// RUN: %target-swift-frontend -parse %s
4+
5+
public struct Main {
6+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module -emit-library -module-name ModuleA -module-link-name ModuleA %S/A.swift -o %t/%target-library-name(ModuleA)
3+
// RUN: %target-swift-frontend -c -I %t -L %t -lModuleA -parse-as-library %s
4+
5+
import ModuleA
6+
7+
@main
8+
extension Main {
9+
static func main() {
10+
print("ok")
11+
}
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This file is a part of the multi-file test driven by 'main2.swift'.
2+
3+
// RUN: %target-swift-frontend -parse %s
4+
5+
public struct Main {
6+
public static func main() {
7+
print("ok")
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -emit-module -emit-library -module-name ModuleA -module-link-name ModuleA %S/A.swift -o %t/%target-library-name(ModuleA)
3+
// RUN: %target-swift-frontend -c -I %t -L %t -lModuleA -parse-as-library %s
4+
5+
import ModuleA
6+
7+
@main
8+
extension Main {
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend -typecheck -parse-as-library -verify %s
2+
3+
@main
4+
extension (Int, String) { // expected-error {{non-nominal type '(Int, String)' cannot be extended}}
5+
static func main() {
6+
}
7+
}
8+
9+
10+
11+

0 commit comments

Comments
 (0)