diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index daf638f797480..bf6aeaf1e65dc 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -40,6 +40,7 @@ SWIFT_TYPEID_NAMED(ConstructorDecl *, ConstructorDecl) SWIFT_TYPEID_NAMED(CustomAttr *, CustomAttr) SWIFT_TYPEID_NAMED(Decl *, Decl) SWIFT_TYPEID_NAMED(EnumDecl *, EnumDecl) +SWIFT_TYPEID_NAMED(FuncDecl *, FuncDecl) SWIFT_TYPEID_NAMED(GenericParamList *, GenericParamList) SWIFT_TYPEID_NAMED(GenericTypeParamType *, GenericTypeParamType) SWIFT_TYPEID_NAMED(InfixOperatorDecl *, InfixOperatorDecl) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index b81fdc686b9ac..973f7dc2c9836 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -30,6 +30,7 @@ class ConstructorDecl; class CustomAttr; class Decl; class EnumDecl; +class FuncDecl; enum class FunctionBuilderBodyPreCheck : uint8_t; class GenericParamList; class GenericSignature; diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index b2866e493daf7..540b999668150 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2564,6 +2564,23 @@ class CustomAttrTypeRequest void cacheResult(Type value) const; }; +class SynthesizeMainFunctionRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + FuncDecl *evaluate(Evaluator &evaluator, Decl *) const; + +public: + bool isCached() const { return true; } +}; + // Allow AnyValue to compare two Type values, even though Type doesn't // support ==. template<> diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index f9cfdb060ecf9..f940dc811a05a 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -274,3 +274,5 @@ SWIFT_REQUEST(TypeChecker, LookupAllConformancesInContextRequest, Uncached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SimpleDidSetRequest, bool(AccessorDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, SynthesizeMainFunctionRequest, + FuncDecl *(Decl *), Cached, NoLocationInfo) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 147affbcbc40e..7375764e368ee 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1782,7 +1782,76 @@ void AttributeChecker::visitUIApplicationMainAttr(UIApplicationMainAttr *attr) { C.getIdentifier("UIApplicationMain")); } -void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) { +namespace { +struct MainTypeAttrParams { + FuncDecl *mainFunction; + MainTypeAttr *attr; +}; + +} +static std::pair +synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) { + ASTContext &context = fn->getASTContext(); + MainTypeAttrParams *params = (MainTypeAttrParams *) arg; + + FuncDecl *mainFunction = params->mainFunction; + auto location = params->attr->getLocation(); + NominalTypeDecl *nominal = fn->getDeclContext()->getSelfNominalTypeDecl(); + + auto *typeExpr = TypeExpr::createImplicit(nominal->getDeclaredType(), context); + + SubstitutionMap substitutionMap; + if (auto *environment = mainFunction->getGenericEnvironment()) { + substitutionMap = SubstitutionMap::get( + environment->getGenericSignature(), + [&](SubstitutableType *type) { return nominal->getDeclaredType(); }, + LookUpConformanceInModule(nominal->getModuleContext())); + } else { + substitutionMap = SubstitutionMap(); + } + + auto funcDeclRef = ConcreteDeclRef(mainFunction, substitutionMap); + + auto *memberRefExpr = new (context) MemberRefExpr( + typeExpr, SourceLoc(), funcDeclRef, DeclNameLoc(location), + /*Implicit*/ true); + memberRefExpr->setImplicit(true); + + auto *callExpr = CallExpr::createImplicit(context, memberRefExpr, {}, {}); + callExpr->setImplicit(true); + callExpr->setThrows(mainFunction->hasThrows()); + callExpr->setType(context.TheEmptyTupleType); + + Expr *returnedExpr; + + if (mainFunction->hasThrows()) { + auto *tryExpr = new (context) TryExpr( + SourceLoc(), callExpr, context.TheEmptyTupleType, /*implicit=*/true); + returnedExpr = tryExpr; + } else { + returnedExpr = callExpr; + } + + auto *returnStmt = + new (context) ReturnStmt(SourceLoc(), callExpr, /*Implicit=*/true); + + SmallVector stmts; + stmts.push_back(returnStmt); + auto *body = BraceStmt::create(context, SourceLoc(), stmts, + SourceLoc(), /*Implicit*/true); + + return std::make_pair(body, /*typechecked=*/false); +} + +FuncDecl * +SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, + Decl *D) const { + auto &context = D->getASTContext(); + + MainTypeAttr *attr = D->getAttrs().getAttribute(); + if (attr == nullptr) + return nullptr; + auto *extension = dyn_cast(D); IterableDeclContext *iterableDeclContext; @@ -1802,25 +1871,19 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) { braces = nominal->getBraces(); } - if (!nominal) { - assert(false && "Should have already recognized that the MainType decl " + assert(nominal && "Should have already recognized that the MainType decl " "isn't applicable to decls other than NominalTypeDecls"); - return; - } assert(iterableDeclContext); assert(declContext); // The type cannot be generic. if (nominal->isGenericContext()) { - diagnose(attr->getLocation(), - diag::attr_generic_ApplicationMain_not_supported, 2); + context.Diags.diagnose(attr->getLocation(), + diag::attr_generic_ApplicationMain_not_supported, 2); attr->setInvalid(); - return; + return nullptr; } - SourceFile *file = cast(declContext->getModuleScopeContext()); - assert(file); - // Create a function // // func $main() { @@ -1832,8 +1895,6 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) { // usual type-checking. The alternative would be to directly call // mainType.main() from the entry point, and that would require fully // type-checking the call to mainType.main(). - auto &context = D->getASTContext(); - auto location = attr->getLocation(); auto resolution = resolveValueMember( *declContext, nominal->getInterfaceType(), context.Id_main); @@ -1861,26 +1922,21 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) { } if (viableCandidates.size() != 1) { - diagnose(attr->getLocation(), diag::attr_MainType_without_main, - nominal->getBaseName()); + context.Diags.diagnose(attr->getLocation(), + diag::attr_MainType_without_main, + nominal->getBaseName()); attr->setInvalid(); - return; + return nullptr; } mainFunction = viableCandidates[0]; } - bool mainFunctionThrows = mainFunction->hasThrows(); - - auto voidToVoidFunctionType = - FunctionType::get({}, context.TheEmptyTupleType, - FunctionType::ExtInfo().withThrows(mainFunctionThrows)); - auto nominalToVoidToVoidFunctionType = FunctionType::get({AnyFunctionType::Param(nominal->getInterfaceType())}, voidToVoidFunctionType); auto *func = FuncDecl::create( context, /*StaticLoc*/ SourceLoc(), StaticSpellingKind::KeywordStatic, /*FuncLoc*/ SourceLoc(), DeclName(context, DeclBaseName(context.Id_MainEntryPoint), ParameterList::createEmpty(context)), - /*NameLoc*/ SourceLoc(), /*Throws=*/mainFunctionThrows, + /*NameLoc*/ SourceLoc(), /*Throws=*/mainFunction->hasThrows(), /*ThrowsLoc=*/SourceLoc(), /*GenericParams=*/nullptr, ParameterList::createEmpty(context), /*FnRetType=*/TypeLoc::withoutLoc(TupleType::getEmpty(context)), @@ -1888,80 +1944,30 @@ void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) { func->setImplicit(true); func->setSynthesized(true); - auto *typeExpr = TypeExpr::createImplicit(nominal->getDeclaredType(), context); - - SubstitutionMap substitutionMap; - if (auto *environment = mainFunction->getGenericEnvironment()) { - substitutionMap = SubstitutionMap::get( - environment->getGenericSignature(), - [&](SubstitutableType *type) { return nominal->getDeclaredType(); }, - LookUpConformanceInModule(nominal->getModuleContext())); - } else { - substitutionMap = SubstitutionMap(); - } - - auto funcDeclRef = ConcreteDeclRef(mainFunction, substitutionMap); - - auto *memberRefExpr = new (context) MemberRefExpr( - typeExpr, SourceLoc(), funcDeclRef, DeclNameLoc(location), - /*Implicit*/ true); - memberRefExpr->setImplicit(true); - - auto *callExpr = CallExpr::createImplicit(context, memberRefExpr, {}, {}); - callExpr->setImplicit(true); - callExpr->setThrows(mainFunctionThrows); - callExpr->setType(context.TheEmptyTupleType); - - Expr *returnedExpr; + auto *params = context.Allocate(); + params->mainFunction = mainFunction; + params->attr = attr; + func->setBodySynthesizer(synthesizeMainBody, params); - if (mainFunctionThrows) { - auto *tryExpr = new (context) TryExpr( - SourceLoc(), callExpr, context.TheEmptyTupleType, /*implicit=*/true); - returnedExpr = tryExpr; - } else { - returnedExpr = callExpr; - } + iterableDeclContext->addMember(func); - auto *returnStmt = - new (context) ReturnStmt(SourceLoc(), callExpr, /*Implicit=*/true); + return func; +} - SmallVector stmts; - stmts.push_back(returnStmt); - auto *body = BraceStmt::create(context, SourceLoc(), stmts, - SourceLoc(), /*Implicit*/true); - func->setBodyParsed(body); - func->setInterfaceType(nominalToVoidToVoidFunctionType); +void AttributeChecker::visitMainTypeAttr(MainTypeAttr *attr) { + auto &context = D->getASTContext(); - iterableDeclContext->addMember(func); + SourceFile *file = D->getDeclContext()->getParentSourceFile(); + assert(file); - // This function must be type-checked. Why? Consider the following scenario: - // - // protocol AlmostMainable {} - // protocol ReallyMainable {} - // extension AlmostMainable where Self : ReallyMainable { - // static func main() {} - // } - // @main struct Main : AlmostMainable {} - // - // Note in particular that Main does not conform to ReallyMainable. - // - // In this case, resolveValueMember will find the function main in the - // extension, and so, since there is one candidate, the function $main will - // accordingly be formed as usual: - // - // func $main() { - // return Main.main() - // } - // - // Of course, this function's body does not type-check. - file->DelayedFunctions.push_back(func); + auto *func = evaluateOrDefault(context.evaluator, + SynthesizeMainFunctionRequest{D}, + nullptr); // Register the func as the main decl in the module. If there are multiples // they will be diagnosed. - if (file->registerMainDecl(func, attr->getLocation())) { + if (file->registerMainDecl(func, attr->getLocation())) attr->setInvalid(); - return; - } } /// Determine whether the given context is an extension to an Objective-C class diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 92638c7f25699..f2af26aa9d614 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2523,6 +2523,12 @@ EmittedMembersRequest::evaluate(Evaluator &evaluator, forceConformance(Context.getProtocol(KnownProtocolKind::Hashable)); forceConformance(Context.getProtocol(KnownProtocolKind::Differentiable)); + // If the class has a @main attribute, we need to force synthesis of the + // $main function. + (void) evaluateOrDefault(Context.evaluator, + SynthesizeMainFunctionRequest{CD}, + nullptr); + for (auto *member : CD->getMembers()) { if (auto *var = dyn_cast(member)) { // The projected storage wrapper ($foo) might have dynamically-dispatched diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 30ef739271870..2550d986a61f1 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1797,13 +1797,13 @@ class DeclChecker : public DeclVisitor { checkGenericParams(ED); // Check for circular inheritance of the raw type. - (void)ED->hasCircularRawValue(); + (void) ED->hasCircularRawValue(); + + TypeChecker::checkDeclAttributes(ED); for (Decl *member : ED->getMembers()) visit(member); - TypeChecker::checkDeclAttributes(ED); - checkInheritanceClause(ED); checkAccessControl(ED); @@ -1845,13 +1845,13 @@ class DeclChecker : public DeclVisitor { installCodingKeysIfNecessary(SD); + TypeChecker::checkDeclAttributes(SD); + for (Decl *Member : SD->getMembers()) visit(Member); TypeChecker::checkPatternBindingCaptures(SD); - TypeChecker::checkDeclAttributes(SD); - checkInheritanceClause(SD); checkAccessControl(SD); @@ -1974,6 +1974,8 @@ class DeclChecker : public DeclVisitor { // Force creation of an implicit destructor, if any. (void) CD->getDestructor(); + TypeChecker::checkDeclAttributes(CD); + for (Decl *Member : CD->getEmittedMembers()) visit(Member); @@ -2084,8 +2086,6 @@ class DeclChecker : public DeclVisitor { } } - TypeChecker::checkDeclAttributes(CD); - checkInheritanceClause(CD); checkAccessControl(CD); @@ -2105,12 +2105,12 @@ class DeclChecker : public DeclVisitor { // Check for circular inheritance within the protocol. (void)PD->hasCircularInheritedProtocols(); + TypeChecker::checkDeclAttributes(PD); + // Check the members. for (auto Member : PD->getMembers()) visit(Member); - TypeChecker::checkDeclAttributes(PD); - checkAccessControl(PD); checkInheritanceClause(PD); @@ -2428,6 +2428,8 @@ class DeclChecker : public DeclVisitor { checkGenericParams(ED); + TypeChecker::checkDeclAttributes(ED); + for (Decl *Member : ED->getMembers()) visit(Member); @@ -2435,7 +2437,6 @@ class DeclChecker : public DeclVisitor { TypeChecker::checkConformancesInContext(ED); - TypeChecker::checkDeclAttributes(ED); checkAccessControl(ED); checkExplicitAvailability(ED); diff --git a/test/Compatibility/accessibility.swift b/test/Compatibility/accessibility.swift index e7402cd845c11..35b348b1b4e61 100644 --- a/test/Compatibility/accessibility.swift +++ b/test/Compatibility/accessibility.swift @@ -107,7 +107,7 @@ private extension PublicStruct { private func extImplPrivate() {} } public extension InternalStruct { // expected-error {{extension of internal struct cannot be declared public}} {{1-8=}} - public func extMemberPublic() {} // expected-warning {{'public' modifier is redundant for instance method declared in a public extension}} {{3-10=}} + public func extMemberPublic() {} fileprivate func extFuncPublic() {} private func extImplPublic() {} } @@ -127,12 +127,12 @@ private extension InternalStruct { private func extImplPrivate() {} } public extension FilePrivateStruct { // expected-error {{extension of fileprivate struct cannot be declared public}} {{1-8=}} - public func extMemberPublic() {} // expected-warning {{'public' modifier is redundant for instance method declared in a public extension}} {{3-10=}} + public func extMemberPublic() {} fileprivate func extFuncPublic() {} private func extImplPublic() {} } internal extension FilePrivateStruct { // expected-error {{extension of fileprivate struct cannot be declared internal}} {{1-10=}} - public func extMemberInternal() {} // expected-warning {{'public' modifier conflicts with extension's default access of 'internal'}} {{none}} + public func extMemberInternal() {} fileprivate func extFuncInternal() {} private func extImplInternal() {} } @@ -147,18 +147,18 @@ private extension FilePrivateStruct { private func extImplPrivate() {} } public extension PrivateStruct { // expected-error {{extension of private struct cannot be declared public}} {{1-8=}} - public func extMemberPublic() {} // expected-warning {{'public' modifier is redundant for instance method declared in a public extension}} {{3-10=}} + public func extMemberPublic() {} fileprivate func extFuncPublic() {} private func extImplPublic() {} } internal extension PrivateStruct { // expected-error {{extension of private struct cannot be declared internal}} {{1-10=}} - public func extMemberInternal() {} // expected-warning {{'public' modifier conflicts with extension's default access of 'internal'}} {{none}} + public func extMemberInternal() {} fileprivate func extFuncInternal() {} private func extImplInternal() {} } fileprivate extension PrivateStruct { // expected-error {{extension of private struct cannot be declared fileprivate}} {{1-13=}} - public func extMemberFilePrivate() {} // expected-warning {{'public' modifier conflicts with extension's default access of 'fileprivate'}} {{none}} - fileprivate func extFuncFilePrivate() {} // expected-warning {{'fileprivate' modifier is redundant for instance method declared in a fileprivate extension}} {{3-15=}} + public func extMemberFilePrivate() {} + fileprivate func extFuncFilePrivate() {} private func extImplFilePrivate() {} } private extension PrivateStruct { diff --git a/test/SILGen/attr_main_class.swift b/test/SILGen/attr_main_class.swift new file mode 100644 index 0000000000000..3e964ab8105ed --- /dev/null +++ b/test/SILGen/attr_main_class.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-emit-silgen -parse-as-library %s | %FileCheck %s + +@main class Horse { + static func main() {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s15attr_main_class5HorseC5$mainyyFZ : $@convention(method) (@thick Horse.Type) -> () { + +// CHECK-LABEL: sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +// CHECK: function_ref @$s15attr_main_class5HorseC5$mainyyFZ : $@convention(method) (@thick Horse.Type) -> () +// CHECK: } diff --git a/test/SILGen/attr_main_class_2.swift b/test/SILGen/attr_main_class_2.swift new file mode 100644 index 0000000000000..4af16cd7c3efd --- /dev/null +++ b/test/SILGen/attr_main_class_2.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-emit-silgen -parse-as-library %s | %FileCheck %s + +@main class Horse { + class func main() {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s17attr_main_class_25HorseC5$mainyyFZ : $@convention(method) (@thick Horse.Type) -> () { + +// CHECK-LABEL: sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +// CHECK: function_ref @$s17attr_main_class_25HorseC5$mainyyFZ : $@convention(method) (@thick Horse.Type) -> () +// CHECK: } diff --git a/test/SILGen/attr_main_enum.swift b/test/SILGen/attr_main_enum.swift new file mode 100644 index 0000000000000..900684a3dabc0 --- /dev/null +++ b/test/SILGen/attr_main_enum.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-emit-silgen -parse-as-library %s | %FileCheck %s + +@main enum Horse { + static func main() {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s14attr_main_enum5HorseO5$mainyyFZ : $@convention(method) (@thin Horse.Type) -> () { + +// CHECK-LABEL: sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +// CHECK: function_ref @$s14attr_main_enum5HorseO5$mainyyFZ : $@convention(method) (@thin Horse.Type) -> () +// CHECK: } diff --git a/test/SILGen/attr_main_struct.swift b/test/SILGen/attr_main_struct.swift new file mode 100644 index 0000000000000..1dc8e1fa45470 --- /dev/null +++ b/test/SILGen/attr_main_struct.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-emit-silgen -parse-as-library %s | %FileCheck %s + +@main struct Horse { + static func main() {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s16attr_main_struct5HorseV5$mainyyFZ : $@convention(method) (@thin Horse.Type) -> () { + +// CHECK-LABEL: sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer>>) -> Int32 { +// CHECK: function_ref @$s16attr_main_struct5HorseV5$mainyyFZ : $@convention(method) (@thin Horse.Type) -> () +// CHECK: } diff --git a/test/Sema/accessibility.swift b/test/Sema/accessibility.swift index b7e5e46e20f63..2e4d016488f2e 100644 --- a/test/Sema/accessibility.swift +++ b/test/Sema/accessibility.swift @@ -106,7 +106,7 @@ private extension PublicStruct { private func extImplPrivate() {} } public extension InternalStruct { // expected-error {{extension of internal struct cannot be declared public}} {{1-8=}} - public func extMemberPublic() {} // expected-warning {{'public' modifier is redundant for instance method declared in a public extension}} {{3-10=}} + public func extMemberPublic() {} fileprivate func extFuncPublic() {} private func extImplPublic() {} } @@ -126,12 +126,12 @@ private extension InternalStruct { private func extImplPrivate() {} } public extension FilePrivateStruct { // expected-error {{extension of fileprivate struct cannot be declared public}} {{1-8=}} - public func extMemberPublic() {} // expected-warning {{'public' modifier is redundant for instance method declared in a public extension}} {{3-10=}} + public func extMemberPublic() {} fileprivate func extFuncPublic() {} private func extImplPublic() {} } internal extension FilePrivateStruct { // expected-error {{extension of fileprivate struct cannot be declared internal}} {{1-10=}} - public func extMemberInternal() {} // expected-warning {{'public' modifier conflicts with extension's default access of 'internal'}} {{none}} + public func extMemberInternal() {} fileprivate func extFuncInternal() {} private func extImplInternal() {} } @@ -146,18 +146,18 @@ private extension FilePrivateStruct { private func extImplPrivate() {} } public extension PrivateStruct { // expected-error {{extension of private struct cannot be declared public}} {{1-8=}} - public func extMemberPublic() {} // expected-warning {{'public' modifier is redundant for instance method declared in a public extension}} {{3-10=}} + public func extMemberPublic() {} fileprivate func extFuncPublic() {} private func extImplPublic() {} } internal extension PrivateStruct { // expected-error {{extension of private struct cannot be declared internal}} {{1-10=}} - public func extMemberInternal() {} // expected-warning {{'public' modifier conflicts with extension's default access of 'internal'}} {{none}} + public func extMemberInternal() {} fileprivate func extFuncInternal() {} private func extImplInternal() {} } fileprivate extension PrivateStruct { // expected-error {{extension of private struct cannot be declared fileprivate}} {{1-13=}} - public func extMemberFilePrivate() {} // expected-warning {{'public' modifier conflicts with extension's default access of 'fileprivate'}} {{none}} - fileprivate func extFuncFilePrivate() {} // expected-warning {{'fileprivate' modifier is redundant for instance method declared in a fileprivate extension}} {{3-15=}} + public func extMemberFilePrivate() {} + fileprivate func extFuncFilePrivate() {} private func extImplFilePrivate() {} } private extension PrivateStruct {