Skip to content

Fixing async main resolution #42142

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 4 commits into from
Apr 4, 2022
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
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ namespace swift {
/// Emit a remark after loading a module.
bool EnableModuleLoadingRemarks = false;

/// Resolve main function as though it were called from an async context
bool EnableAsyncMainResolution = false;

///
/// Support for alternate usage modes
///
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ def pch_output_dir: Separate<["-"], "pch-output-dir">,
Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>,
HelpText<"Directory to persist automatically created precompiled bridging headers">;

def async_main: Flag<["-"], "async-main">,
Flags<[FrontendOption]>,
HelpText<"Resolve main function as if it were called from an asynchronous context">;

// FIXME: Unhide this once it doesn't depend on an output file map.
def incremental : Flag<["-"], "incremental">,
Flags<[NoInteractiveOption, HelpHidden, DoesNotAffectIncrementalBuild]>,
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,10 @@ enum class ConstraintSystemFlags {
/// calling conventions, say due to Clang attributes such as
/// `__attribute__((ns_consumed))`.
UseClangFunctionTypes = 0x80,

/// When set, nominal typedecl contexts are asynchronous contexts.
/// This is set while searching for the main function
ConsiderNominalTypeContextsAsync = 0x100,
};

/// Options that affect the constraint system as a whole.
Expand Down
8 changes: 6 additions & 2 deletions include/swift/Sema/IDETypeChecking.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "swift/AST/Identifier.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/Basic/OptionSet.h"
#include <memory>
#include <tuple>

Expand Down Expand Up @@ -50,6 +51,8 @@ namespace swift {
class ConstraintSystem;
class Solution;
class SolutionApplicationTarget;
enum class ConstraintSystemFlags;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this makes me slightly uncomfortable, if there is ever a size mismatch between the forward declaration and the full definition with enum members, there is a compile-time error.

e.g.

swift/include/swift/Sema/IDETypeChecking.h:54:14: error: enumeration redeclared with different underlying type 'long' (was 'int')
  enum class ConstraintSystemFlags : long;
             ^
swift/include/swift/Sema/ConstraintSystem.h:1453:12: note: previous declaration is here
enum class ConstraintSystemFlags {
           ^

using ConstraintSystemOptions = OptionSet<ConstraintSystemFlags>;
}

/// Typecheck binding initializer at \p bindingIndex.
Expand Down Expand Up @@ -93,8 +96,9 @@ namespace swift {
/// Unlike other member lookup functions, \c swift::resolveValueMember()
/// should be used when you want to look up declarations with the same name as
/// one you already have.
ResolvedMemberResult resolveValueMember(DeclContext &DC, Type BaseTy,
DeclName Name);
ResolvedMemberResult
resolveValueMember(DeclContext &DC, Type BaseTy, DeclName Name,
constraints::ConstraintSystemOptions Options = {});

/// Given a type and an extension to the original type decl of that type,
/// decide if the extension has been applied, i.e. if the requirements of the
Expand Down
1 change: 1 addition & 0 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
inputArgs.AddLastArg(arguments, options::OPT_access_notes_path);
inputArgs.AddLastArg(arguments, options::OPT_library_level);
inputArgs.AddLastArg(arguments, options::OPT_enable_regex_literals);
inputArgs.AddLastArg(arguments, options::OPT_async_main);

// Pass on any build config options
inputArgs.AddAllArgs(arguments, options::OPT_D);
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Diags.diagnose(SourceLoc(), diag::warn_flag_deprecated,
"-enable-experimental-async-top-level");

Opts.EnableAsyncMainResolution = Args.hasArg(OPT_async_main);

Opts.DiagnoseInvalidEphemeralnessAsError |=
Args.hasArg(OPT_enable_invalid_ephemeralness_as_error);

Expand Down
5 changes: 3 additions & 2 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4421,9 +4421,10 @@ getMemberDecls(InterestedMemberKind Kind) {
}

ResolvedMemberResult
swift::resolveValueMember(DeclContext &DC, Type BaseTy, DeclName Name) {
swift::resolveValueMember(DeclContext &DC, Type BaseTy, DeclName Name,
ConstraintSystemOptions Options) {
ResolvedMemberResult Result;
ConstraintSystem CS(&DC, None);
ConstraintSystem CS(&DC, Options);

// Look up all members of BaseTy with the given Name.
MemberLookupResult LookupResult = CS.performMemberLookup(
Expand Down
5 changes: 5 additions & 0 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2894,6 +2894,11 @@ bool ConstraintSystem::isAsynchronousContext(DeclContext *dc) {
FunctionType::ExtInfo()).isAsync();
}

if (Options.contains(
ConstraintSystemFlags::ConsiderNominalTypeContextsAsync) &&
isa<NominalTypeDecl>(dc))
return true;

return false;
}

Expand Down
8 changes: 7 additions & 1 deletion lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2171,8 +2171,14 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator,
// mainType.main() from the entry point, and that would require fully
// type-checking the call to mainType.main().

constraints::ConstraintSystemOptions lookupOptions;
if (context.LangOpts.EnableAsyncMainResolution)
lookupOptions |=
constraints::ConstraintSystemFlags::ConsiderNominalTypeContextsAsync;

auto resolution = resolveValueMember(
*declContext, nominal->getInterfaceType(), context.Id_main);
*declContext, nominal->getInterfaceType(), context.Id_main,
lookupOptions);
FuncDecl *mainFunction =
resolveMainFunctionDecl(declContext, resolution, context);
if (!mainFunction) {
Expand Down
44 changes: 24 additions & 20 deletions test/Concurrency/async_main_resolution.swift
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
// Non-apple platforms don't need to worry about the version number as much
// because they can pull in the concurrency libraries with the swift
// installation.

// async main is nested deeper in protocols than sync, use sync
// sync main is nested deeper in protocols than async, use async
// async and sync are same level, use async

// REQUIRES: rdar89500797

// REQUIRES: concurrency
// UNSUPPORTED: VENDOR=apple

// Async is deeper in the protocol chain from `MyMain`, use sync
// RUN: %target-swift-frontend -DASYNC_NESTED -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC

// Sync is deeper in the protocol chain from `MyMain`, use async
// RUN: %target-swift-frontend -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC

// Async and sync are the same level, use async
// RUN: %target-swift-frontend -DBOTH -DINHERIT_SYNC -typecheck -dump-ast -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
// ASYNC_NESTED: async is nested more deeply than sync main in protocol chain
// NO_ASYNC: no async main function
// NO_SYNC: no sync main function
// BOTH: MainProtocol has both sync and async main
// INHERIT_SYNC: main type directly conforms to synchronous main protocol

// | async flag | has async main | has sync main | both | inherits sync | nested async | Result | Run |
// | | | | | | | Error | RUN: not %target-swift-frontend -disable-availability-checking -DNO_ASYNC -DNO_SYNC -parse-as-library -typecheck -dump-ast %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR
// | | x | | | | | Async Main | RUN: %target-swift-frontend -disable-availability-checking -DNO_SYNC -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
// | x | | x | | | | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -DNO_ASYNC -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
// | x | x | x | | | | Async Main | RUN: %target-swift-frontend -disable-availability-checking -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
// | | x | x | | | | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
// | | x | x | | | x | Async Main | RUN: %target-swift-frontend -disable-availability-checking -DASYNC_NESTED -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
// | | x | x | | x | x | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -DINHERIT_SYNC -DASYNC_NESTED -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
// | x | x | x | | x | x | Async Main | RUN: %target-swift-frontend -disable-availability-checking -DINHERIT_SYNC -DASYNC_NESTED -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC
// | x | | x | | x | x | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -DNO_ASYNC -DINHERIT_SYNC -DASYNC_NESTED -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
// | | | x | x | | | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -DBOTH -DNO_ASYNC -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC
// | x | | x | x | | | Async Main | RUN: %target-swift-frontend -disable-availability-checking -DBOTH -DNO_ASYNC -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC

// tldr;
// If async flag is set, will pick an asynchronous main function if one is available and related. If none exist, will fall back on synchronous main.
// If async flag is not set, will pick a asynchronous main function if one is available and related. If none exist, will fall back on an asynchronous main
// If neither are available; error

#if ASYNC_NESTED
protocol AsyncMainProtocol { }
Expand Down Expand Up @@ -55,8 +63,6 @@ extension MainProtocol {
@main struct MyMain : AsyncMainProtocol {}
#endif



// CHECK-IS-SYNC-LABEL: "MyMain" interface type='MyMain.Type'
// CHECK-IS-SYNC: (func_decl implicit "$main()" interface type='(MyMain.Type) -> () -> ()'
// CHECK-IS-SYNC: (declref_expr implicit type='(MyMain.Type) -> () -> ()'
Expand All @@ -65,6 +71,4 @@ extension MainProtocol {
// CHECK-IS-ASYNC: (func_decl implicit "$main()" interface type='(MyMain.Type) -> () async -> ()'
// CHECK-IS-ASYNC: (declref_expr implicit type='(MyMain.Type) -> () async -> ()'

// CHECK-IS-ERROR: error: 'MyMain' is annotated with @main and must provide a main static function of type () -> Void or () throws -> Void

// CHECK-IS-ERROR-ASYNC: error: 'MyMain' is annotated with @main and must provide a main static function of type () -> Void, () throws -> Void, () async -> Void, or () async throws -> Void
// CHECK-IS-ERROR: error: 'MyMain' is annotated with @main and must provide a main static function of type {{\(\) -> Void or \(\) throws -> Void|\(\) -> Void, \(\) throws -> Void, \(\) async -> Void, or \(\) async throws -> Void}}
95 changes: 0 additions & 95 deletions test/Concurrency/async_main_resolution_macos.swift

This file was deleted.

Loading