Make Windows use wWinMain always. Support /SUBSYSTEM:WINDOWS

This commit is contained in:
Christoffer Lerno
2023-01-28 21:04:25 +01:00
committed by Christoffer Lerno
parent f89bf9ea2f
commit 5ea15eb708
18 changed files with 512 additions and 138 deletions

View File

@@ -0,0 +1,156 @@
module std::core::main_stub;
private macro usz _strlen(ptr)
{
usz len = 0;
while (ptr[len]) len++;
return len;
}
macro int @main_to_err_main(#m, int, char**) => catch(#m()) ? 1 : 0;
macro int @main_to_int_main(#m, int, char**) => #m();
macro int @main_to_void_main(#m, int, char**)
{
#m();
return 0;
}
private macro String[] args_to_strings(int argc, char** argv)
{
String *list = malloc(String.sizeof * argc);
for (int i = 0; i < argc; i++)
{
char* arg = argv[i];
usz len = 0;
list[i] = arg[:_strlen(arg)];
}
return list[:argc];
}
macro int @main_to_err_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
return catch(#m(list)) ? 1 : 0;
}
macro int @main_to_int_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
return #m(list);
}
macro int @main_to_void_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
#m(list);
return 0;
}
$if (env::OS_TYPE == OsType.WIN32):
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extname("CommandLineToArgvW");
private macro String[] win_command_line_to_strings(ushort* cmd_line)
{
int argc;
Char16** argv = _win_command_line_to_argv_w(cmd_line, &argc);
return wargs_strings(argc, argv);
}
private macro String[] wargs_strings(int argc, Char16** argv)
{
String *list = malloc(String.sizeof * argc);
for (int i = 0; i < argc; i++)
{
Char16* arg = argv[i];
Char16[] argstring = arg[:_strlen(arg)];
list[i] = str::utf16to8(argstring) ?? str::copy("?");
}
return list[:argc];
}
private macro void release_wargs(String[] list)
{
foreach (s : list) free(s.ptr);
free(list.ptr);
}
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => catch(#m()) ? 1 : 0;
macro int @win_to_int_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => #m();
macro int @win_to_void_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
{
#m();
return 0;
}
macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return catch(#m(args)) ? 1 : 0;
}
macro int @win_to_int_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(args);
}
macro int @win_to_void_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(args);
return 0;
}
macro int @win_to_err_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return catch(#m(handle, args, show_cmd)) ? 1 : 0;
}
macro int @win_to_int_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(handle, args, show_cmd);
}
macro int @win_to_void_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(handle, args, show_cmd);
return 0;
}
macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return catch(#m(args)) ? 1 : 0;
}
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return #m(args);
}
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
#m(args);
return 0;
}
$endif;

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved. // Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license // Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::runtime; module std::core::runtime;
struct VirtualAny struct VirtualAny
{ {

View File

@@ -87,7 +87,7 @@ fn void random_fasta(String symb, double[] probability, int n)
if (i % LINELEN != 0) io::putchar('\n'); if (i % LINELEN != 0) io::putchar('\n');
} }
fn void main(int argc, char **argv) fn int main(int argc, char **argv)
{ {
int n = 1000; int n = 1000;
if (argc > 1) n = libc::atoi(argv[1]); if (argc > 1) n = libc::atoi(argv[1]);
@@ -101,4 +101,5 @@ fn void main(int argc, char **argv)
io::printf(">THREE Homo sapiens frequency\n"); io::printf(">THREE Homo sapiens frequency\n");
random_fasta(homosapiens, homosapiens_p, n * 5); random_fasta(homosapiens, homosapiens_p, n * 5);
return 0;
} }

View File

@@ -4,7 +4,7 @@ extern fn int atoi(char *s);
extern fn int printf(char *s, ...); extern fn int printf(char *s, ...);
extern fn void putchar(int c); extern fn void putchar(int c);
fn void main(int argc, char **argv) fn int main(int argc, char **argv)
{ {
int w = atoi(argv[1]); int w = atoi(argv[1]);
int h = w; int h = w;
@@ -55,4 +55,5 @@ fn void main(int argc, char **argv)
} }
} }
} }
return 0;
} }

View File

@@ -129,6 +129,7 @@ static void usage(void)
OUTPUT(" -z <argument> - Send the <argument> as a parameter to the linker."); OUTPUT(" -z <argument> - Send the <argument> as a parameter to the linker.");
OUTPUT(" --forcelinker - Force built in linker usage when doing non-cross linking."); OUTPUT(" --forcelinker - Force built in linker usage when doing non-cross linking.");
OUTPUT(""); OUTPUT("");
OUTPUT(" --gui - Build a 'gui' variant of the executable (e.g. '/SUBSYSTEM:WINDOWS' on Win32).");
OUTPUT(" --reloc=<option> - Relocation model: none, pic, PIC, pie, PIE."); OUTPUT(" --reloc=<option> - Relocation model: none, pic, PIC, pie, PIE.");
OUTPUT(" --x86vec=<option> - Set max level of vector instructions: none, native, mmx, sse, avx, avx512."); OUTPUT(" --x86vec=<option> - Set max level of vector instructions: none, native, mmx, sse, avx, avx512.");
OUTPUT(" --memory-env=<option> - Set the memory environment: normal, small, tiny, none."); OUTPUT(" --memory-env=<option> - Set the memory environment: normal, small, tiny, none.");
@@ -532,6 +533,11 @@ static void parse_option(BuildOptions *options)
options->symtab_size = next_highest_power_of_2(symtab); options->symtab_size = next_highest_power_of_2(symtab);
return; return;
} }
if (match_longopt("gui"))
{
options->gui = true;
return;
}
if (match_longopt("forcelinker")) if (match_longopt("forcelinker"))
{ {
options->force_linker = true; options->force_linker = true;

View File

@@ -290,6 +290,7 @@ typedef struct BuildOptions_
bool no_stdlib; bool no_stdlib;
bool no_entry; bool no_entry;
bool no_libc; bool no_libc;
bool gui;
bool force_linker; bool force_linker;
bool read_stdin; bool read_stdin;
bool print_output; bool print_output;
@@ -374,6 +375,7 @@ typedef struct
bool emit_asm; bool emit_asm;
bool no_stdlib; bool no_stdlib;
bool no_libc; bool no_libc;
bool gui;
bool emit_object_files; bool emit_object_files;
bool force_linker; bool force_linker;
bool benchmarking; bool benchmarking;

View File

@@ -233,6 +233,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
target->panicfn = options->panicfn; target->panicfn = options->panicfn;
target->benchmarking = options->benchmarking; target->benchmarking = options->benchmarking;
target->testing = options->testing; target->testing = options->testing;
if (options->gui) target->gui = true;
if (options->macos.sdk) target->macos.sdk = options->macos.sdk; if (options->macos.sdk) target->macos.sdk = options->macos.sdk;
if (options->win.sdk) target->win.sdk = options->win.sdk; if (options->win.sdk) target->win.sdk = options->win.sdk;
if (options->macos.min_version) target->macos.min_version = options->macos.min_version; if (options->macos.min_version) target->macos.min_version = options->macos.min_version;

View File

@@ -362,6 +362,9 @@ static void load_into_build_target(JSONObject *json, const char *type, BuildTarg
// nolibc // nolibc
target->no_libc = get_valid_bool(json, "nolibc", type, target->no_libc); target->no_libc = get_valid_bool(json, "nolibc", type, target->no_libc);
// gui
target->gui = get_valid_bool(json, "gui", type, target->gui);
// no-entry // no-entry
target->no_entry = get_valid_bool(json, "no-entry", type, target->no_entry); target->no_entry = get_valid_bool(json, "no-entry", type, target->no_entry);

View File

@@ -1902,6 +1902,8 @@ extern const char *kw_pure;
extern const char *kw_return; extern const char *kw_return;
extern const char *kw_std; extern const char *kw_std;
extern const char *kw_type; extern const char *kw_type;
extern const char *kw_winmain;
extern const char *kw_wmain;
extern ArchOsTarget default_target; extern ArchOsTarget default_target;
ARENA_DEF(chars, char) ARENA_DEF(chars, char)

View File

@@ -992,3 +992,13 @@ typedef enum
COND_TYPE_UNWRAP, COND_TYPE_UNWRAP,
COND_TYPE_EVALTYPE_VALUE, COND_TYPE_EVALTYPE_VALUE,
} CondType; } CondType;
typedef enum
{
MAIN_TYPE_ERROR,
MAIN_TYPE_RAW,
MAIN_TYPE_NO_ARGS,
MAIN_TYPE_ARGS,
MAIN_TYPE_WIN,
} MainType;

View File

@@ -77,6 +77,7 @@ static const char *string_esc(const char *str)
} }
static void linker_setup_windows(const char ***args_ref, LinkerType linker_type) static void linker_setup_windows(const char ***args_ref, LinkerType linker_type)
{ {
add_arg(active_target.gui ? "/SUBSYSTEM:WINDOWS" : "/SUBSYSTEM:CONSOLE");
if (linker_type == LINKER_CC) return; if (linker_type == LINKER_CC) return;
//add_arg("/MACHINE:X64"); //add_arg("/MACHINE:X64");
bool is_debug = false; bool is_debug = false;
@@ -145,6 +146,7 @@ static void linker_setup_windows(const char ***args_ref, LinkerType linker_type)
add_arg("kernel32.lib"); add_arg("kernel32.lib");
add_arg("ntdll.lib"); add_arg("ntdll.lib");
add_arg("user32.lib"); add_arg("user32.lib");
add_arg("shell32.lib");
add_arg("legacy_stdio_definitions.lib"); add_arg("legacy_stdio_definitions.lib");
if (active_target.win.crt_linking == WIN_CRT_STATIC) if (active_target.win.crt_linking == WIN_CRT_STATIC)

View File

@@ -1881,6 +1881,285 @@ ADDED:;
return true; return true;
} }
static inline MainType sema_find_main_type(SemaContext *context, Signature *sig)
{
Decl **params = sig->params;
unsigned param_count = vec_size(params);
bool is_win32 = platform_target.os == OS_TYPE_WIN32;
Type *arg_type, *arg_type2;
switch (param_count)
{
case 0:
return MAIN_TYPE_NO_ARGS;
break;
case 1:
arg_type = type_flatten_distinct(params[0]->type);
if (arg_type == type_get_subarray(type_get_subarray(type_char))) return MAIN_TYPE_ARGS;
SEMA_ERROR(params[0], "Expected a parameter of type 'String[]'.");
return MAIN_TYPE_ERROR;
case 2:
arg_type = type_flatten_distinct(params[0]->type);
arg_type2 = type_flatten_distinct(params[1]->type);
if (arg_type != type_cint)
{
SEMA_ERROR(params[0],
"Expected a parameter of type %s for a C-style main.",
type_quoted_error_string(type_cint));
return MAIN_TYPE_ERROR;
}
if (arg_type2 != type_get_ptr(type_get_ptr(type_char)))
{
SEMA_ERROR(params[1], "Expected a parameter of type 'char**' for a C-style main.");
return MAIN_TYPE_ERROR;
}
return MAIN_TYPE_RAW;
case 3:
if (!is_win32) break;
arg_type = type_flatten_distinct(params[0]->type);
arg_type2 = type_flatten_distinct(params[1]->type);
if (arg_type != type_voidptr)
{
SEMA_ERROR(params[0], "Expected a parameter of type 'void*' (HINSTANCE)");
return MAIN_TYPE_ERROR;
}
if (arg_type2 != type_get_subarray(type_get_subarray(type_char)))
{
SEMA_ERROR(params[1], "Expected a parameter of type 'String[]'.");
return MAIN_TYPE_ERROR;
}
if (type_flatten_distinct(params[2]->type) != type_cint)
{
SEMA_ERROR(params[2], "Expected a parameter of type %s for the 'showCmd' parameter.",
type_quoted_error_string(type_cint));
return MAIN_TYPE_ERROR;
}
return MAIN_TYPE_WIN;
default:
break;
}
SEMA_ERROR(params[0], is_win32
? "Expected zero, 1 or 3 parameters for main."
: "Expected zero or 1 parameters for main.");
return MAIN_TYPE_ERROR;
}
static inline Decl *sema_create_synthetic_main(SemaContext *context, Decl *decl, MainType main, bool int_return, bool err_return)
{
Decl *function = decl_new(DECL_FUNC, NULL, decl->span, VISIBLE_EXTERN);
function->name = kw_mainstub;
function->unit = decl->unit;
function->extname = kw_main;
function->has_extname = true;
function->func_decl.signature.rtype = type_infoid(type_info_new_base(type_cint, decl->span));
function->func_decl.signature.vararg_index = 2;
Decl *param1 = decl_new_generated_var(type_cint, VARDECL_PARAM, decl->span);
Decl *param2 = decl_new_generated_var(type_get_ptr(type_get_ptr(type_char)), VARDECL_PARAM, decl->span);
Decl **main_params = NULL;
vec_add(main_params, param1);
vec_add(main_params, param2);
function->func_decl.signature.params = main_params;
Ast *body = new_ast(AST_COMPOUND_STMT, decl->span);
AstId *next = &body->compound_stmt.first_stmt;
Ast *ret_stmt = new_ast(AST_RETURN_STMT, decl->span);
int type = int_return ? 1 : (err_return ? 2 : 0);
const char *main_invoker;
switch (main)
{
case MAIN_TYPE_ARGS:
switch (type)
{
case 0 : main_invoker = "@main_to_void_main_args"; goto NEXT;
case 1 : main_invoker = "@main_to_int_main_args"; goto NEXT;
case 2 : main_invoker = "@main_to_err_main_args"; goto NEXT;
default: UNREACHABLE
}
case MAIN_TYPE_NO_ARGS:
switch (type)
{
case 0 : main_invoker = "@main_to_void_main"; goto NEXT;
case 1 : main_invoker = "@main_to_int_main"; goto NEXT;
case 2 : main_invoker = "@main_to_err_main"; goto NEXT;
default: UNREACHABLE
}
default:
UNREACHABLE;
}
NEXT:;
const char *kw_main_invoker = symtab_preset(main_invoker, TOKEN_AT_IDENT);
Decl *d = sema_find_symbol(context, kw_main_invoker);
if (!d)
{
SEMA_ERROR(decl, "Missing main forwarding function '%s'.", kw_main_invoker);
return poisoned_decl;
}
Expr *invoker = expr_new(EXPR_IDENTIFIER, decl->span);
invoker->identifier_expr.decl = d;
invoker->resolve_status = RESOLVE_DONE;
invoker->type = decl->type;
Expr *call = expr_new(EXPR_CALL, decl->span);
Expr *fn_ref = expr_variable(decl);
vec_add(call->call_expr.arguments, fn_ref);
vec_add(call->call_expr.arguments, expr_variable(param1));
vec_add(call->call_expr.arguments, expr_variable(param2));
call->call_expr.function = exprid(invoker);
param1->resolve_status = RESOLVE_NOT_DONE;
param2->resolve_status = RESOLVE_NOT_DONE;
ast_append(&next, ret_stmt);
ret_stmt->return_stmt.expr = call;
function->func_decl.body = astid(body);
function->is_synthetic = true;
return function;
}
static inline Decl *sema_create_synthetic_wmain(SemaContext *context, Decl *decl, MainType main, bool int_return, bool err_return)
{
Decl *function = decl_new(DECL_FUNC, NULL, decl->span, VISIBLE_EXTERN);
function->name = kw_mainstub;
function->unit = decl->unit;
function->extname = kw_wmain;
function->has_extname = true;
function->func_decl.signature.rtype = type_infoid(type_info_new_base(type_cint, decl->span));
function->func_decl.signature.vararg_index = 2;
Decl *param1 = decl_new_generated_var(type_cint, VARDECL_PARAM, decl->span);
Decl *param2 = decl_new_generated_var(type_get_ptr(type_get_ptr(type_ushort)), VARDECL_PARAM, decl->span);
Decl **main_params = NULL;
vec_add(main_params, param1);
vec_add(main_params, param2);
function->func_decl.signature.params = main_params;
Ast *body = new_ast(AST_COMPOUND_STMT, decl->span);
AstId *next = &body->compound_stmt.first_stmt;
Ast *ret_stmt = new_ast(AST_RETURN_STMT, decl->span);
int type = int_return ? 1 : (err_return ? 2 : 0);
const char *main_invoker;
switch (main)
{
case MAIN_TYPE_ARGS:
switch (type)
{
case 0 : main_invoker = "@wmain_to_void_main_args"; goto NEXT;
case 1 : main_invoker = "@wmain_to_int_main_args"; goto NEXT;
case 2 : main_invoker = "@wmain_to_err_main_args"; goto NEXT;
default: UNREACHABLE
}
case MAIN_TYPE_NO_ARGS:
switch (type)
{
case 0 : main_invoker = "@main_to_void_main"; goto NEXT;
case 1 : main_invoker = "@main_to_int_main"; goto NEXT;
case 2 : main_invoker = "@main_to_err_main"; goto NEXT;
default: UNREACHABLE
}
default:
UNREACHABLE;
}
NEXT:;
const char *kw_main_invoker = symtab_preset(main_invoker, TOKEN_AT_IDENT);
Decl *d = sema_find_symbol(context, kw_main_invoker);
if (!d)
{
SEMA_ERROR(decl, "Missing main forwarding function '%s'.", kw_main_invoker);
return poisoned_decl;
}
Expr *invoker = expr_new(EXPR_IDENTIFIER, decl->span);
invoker->identifier_expr.decl = d;
invoker->resolve_status = RESOLVE_DONE;
invoker->type = decl->type;
Expr *call = expr_new(EXPR_CALL, decl->span);
Expr *fn_ref = expr_variable(decl);
vec_add(call->call_expr.arguments, fn_ref);
vec_add(call->call_expr.arguments, expr_variable(param1));
vec_add(call->call_expr.arguments, expr_variable(param2));
call->call_expr.function = exprid(invoker);
param1->resolve_status = RESOLVE_NOT_DONE;
param2->resolve_status = RESOLVE_NOT_DONE;
ast_append(&next, ret_stmt);
ret_stmt->return_stmt.expr = call;
function->func_decl.body = astid(body);
function->is_synthetic = true;
return function;
}
static inline Decl *sema_create_synthetic_win_main(SemaContext *context, Decl *decl, MainType main, bool int_return, bool err_return)
{
Decl *function = decl_new(DECL_FUNC, NULL, decl->span, VISIBLE_EXTERN);
function->name = kw_mainstub;
function->unit = decl->unit;
function->extname = kw_winmain;
function->has_extname = true;
function->func_decl.signature.rtype = type_infoid(type_info_new_base(type_cint, decl->span));
function->func_decl.signature.vararg_index = 3;
Decl *param1 = decl_new_generated_var(type_voidptr, VARDECL_PARAM, decl->span);
Decl *param2 = decl_new_generated_var(type_get_ptr(type_ushort), VARDECL_PARAM, decl->span);
Decl *param3 = decl_new_generated_var(type_cint, VARDECL_PARAM, decl->span);
Decl **main_params = NULL;
vec_add(main_params, param1);
vec_add(main_params, param2);
vec_add(main_params, param3);
function->func_decl.signature.params = main_params;
Ast *body = new_ast(AST_COMPOUND_STMT, decl->span);
AstId *next = &body->compound_stmt.first_stmt;
Ast *ret_stmt = new_ast(AST_RETURN_STMT, decl->span);
int type = int_return ? 1 : (err_return ? 2 : 0);
const char *main_invoker;
switch (main)
{
case MAIN_TYPE_ARGS:
switch (type)
{
case 0 : main_invoker = "@win_to_void_main_args"; goto NEXT;
case 1 : main_invoker = "@win_to_int_main_args"; goto NEXT;
case 2 : main_invoker = "@win_to_err_main_args"; goto NEXT;
default: UNREACHABLE
}
case MAIN_TYPE_NO_ARGS:
switch (type)
{
case 0 : main_invoker = "@win_to_void_main_noargs"; goto NEXT;
case 1 : main_invoker = "@win_to_int_main_noargs"; goto NEXT;
case 2 : main_invoker = "@win_to_err_main_noargs"; goto NEXT;
default: UNREACHABLE
}
case MAIN_TYPE_WIN:
switch (type)
{
case 0 : main_invoker = "@win_to_void_main"; goto NEXT;
case 1 : main_invoker = "@win_to_int_main"; goto NEXT;
case 2 : main_invoker = "@win_to_err_main"; goto NEXT;
default: UNREACHABLE
}
default:
UNREACHABLE;
}
NEXT:;
const char *kw_main_invoker = symtab_preset(main_invoker, TOKEN_AT_IDENT);
Decl *d = sema_find_symbol(context, kw_main_invoker);
if (!d)
{
SEMA_ERROR(decl, "Missing main forwarding macro '%s'.", kw_main_invoker);
return poisoned_decl;
}
Expr *invoker = expr_new(EXPR_IDENTIFIER, decl->span);
invoker->identifier_expr.decl = d;
invoker->resolve_status = RESOLVE_DONE;
invoker->type = decl->type;
Expr *call = expr_new(EXPR_CALL, decl->span);
Expr *fn_ref = expr_variable(decl);
vec_add(call->call_expr.arguments, fn_ref);
vec_add(call->call_expr.arguments, expr_variable(param1));
vec_add(call->call_expr.arguments, expr_variable(param2));
vec_add(call->call_expr.arguments, expr_variable(param3));
call->call_expr.function = exprid(invoker);
param1->resolve_status = RESOLVE_NOT_DONE;
param2->resolve_status = RESOLVE_NOT_DONE;
param3->resolve_status = RESOLVE_NOT_DONE;
ast_append(&next, ret_stmt);
ret_stmt->return_stmt.expr = call;
function->func_decl.body = astid(body);
function->is_synthetic = true;
return function;
}
static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl)
{ {
assert(decl != context->unit->main_function); assert(decl != context->unit->main_function);
@@ -1914,40 +2193,8 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl)
return false; return false;
} }
// At this point the style is either MAIN_INT_VOID, MAIN_VOID_VOID or MAIN_ERR_VOID // At this point the style is either MAIN_INT_VOID, MAIN_VOID_VOID or MAIN_ERR_VOID
Decl **params = signature->params; MainType type = sema_find_main_type(context, signature);
unsigned param_count = vec_size(params); if (type == MAIN_TYPE_ERROR) return false;
bool subarray_param = false;
bool cparam = false;
switch (param_count)
{
case 0:
// This is the default style already set
break;
case 1:
if (type_flatten_distinct(params[0]->type) != type_get_subarray(type_get_subarray(type_char)))
{
SEMA_ERROR(params[0], "Expected a parameter of type 'char[][]'");
return false;
}
subarray_param = true;
break;
case 2:
if (type_flatten_distinct(params[0]->type) != type_cint)
{
SEMA_ERROR(params[0], "Expected a parameter of type %s for a C-style main.", type_quoted_error_string(type_cint));
return false;
}
if (type_flatten_distinct(params[1]->type) != type_get_ptr(type_get_ptr(type_char)))
{
SEMA_ERROR(params[1], "Expected a parameter of type 'char**' for a C-style main.");
return false;
}
cparam = true;
break;
default:
SEMA_ERROR(params[0], "Expected zero, 1 or 2 parameters for main.");
return false;
}
if (active_target.type == TARGET_TYPE_TEST) return true; if (active_target.type == TARGET_TYPE_TEST) return true;
Decl *function; Decl *function;
if (active_target.no_entry) if (active_target.no_entry)
@@ -1955,73 +2202,29 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl)
function = decl; function = decl;
goto REGISTER_MAIN; goto REGISTER_MAIN;
} }
if (!subarray_param && is_int_return) if (type == MAIN_TYPE_RAW && !is_int_return)
{
SEMA_ERROR(rtype_info, "Int return is required for C style main.");
return false;
}
if ((type == MAIN_TYPE_RAW || type == MAIN_TYPE_NO_ARGS) && is_int_return && !active_target.gui)
{ {
// Int return is pass-through at the moment. // Int return is pass-through at the moment.
decl->visibility = VISIBLE_EXTERN; decl->visibility = VISIBLE_EXTERN;
function = decl; function = decl;
goto REGISTER_MAIN; goto REGISTER_MAIN;
} }
function = decl_new(DECL_FUNC, NULL, decl->span, VISIBLE_EXTERN); if (platform_target.os == OS_TYPE_WIN32)
function->name = kw_mainstub;
function->unit = decl->unit;
function->extname = kw_main;
function->has_extname = true;
function->func_decl.signature.rtype = type_infoid(type_info_new_base(type_cint, decl->span));
function->func_decl.signature.vararg_index = 2;
Decl *param1 = decl_new_generated_var(type_cint, VARDECL_PARAM, decl->span);
Decl *param2 = decl_new_generated_var(type_get_ptr(type_get_ptr(type_char)), VARDECL_PARAM, decl->span);
Decl **main_params = NULL;
vec_add(main_params, param1);
vec_add(main_params, param2);
function->func_decl.signature.params = main_params;
Ast *body = new_ast(AST_COMPOUND_STMT, decl->span);
AstId *next = &body->compound_stmt.first_stmt;
Ast *ret_stmt = new_ast(AST_RETURN_STMT, decl->span);
Expr *call = expr_new(EXPR_CALL, decl->span);
call->call_expr.function = exprid(expr_variable(decl));
if (subarray_param)
{ {
Expr *subarray = expr_new(EXPR_ARGV_TO_SUBARRAY, decl->span); function = active_target.gui
subarray->argv_expr.argc = param1; ? sema_create_synthetic_win_main(context, decl, type, is_int_return, is_err_return)
subarray->argv_expr.argv = param2; : sema_create_synthetic_wmain(context, decl, type, is_int_return, is_err_return);
vec_add(call->call_expr.arguments, subarray);
}
else if (cparam)
{
vec_add(call->call_expr.arguments, expr_variable(param1));
vec_add(call->call_expr.arguments, expr_variable(param2));
}
// Unresolve them or the params cannot be resolved later.
param1->resolve_status = RESOLVE_NOT_DONE;
param2->resolve_status = RESOLVE_NOT_DONE;
if (is_int_return)
{
ret_stmt->return_stmt.expr = call;
}
else if (is_err_return)
{
Expr *try_expr = expr_new(EXPR_TRY, decl->span);
try_expr->inner_expr = call;
Expr *not_expr = expr_new(EXPR_UNARY, decl->span);
not_expr->unary_expr.expr = try_expr;
not_expr->unary_expr.operator = UNARYOP_NOT;
Expr *cast_expr = expr_new(EXPR_CAST, decl->span);
cast_expr->cast_expr.expr = exprid(not_expr);
cast_expr->cast_expr.type_info = type_infoid(type_info_new_base(type_cint, decl->span));
ret_stmt->return_stmt.expr = cast_expr;
} }
else else
{ {
Ast *stmt = new_ast(AST_EXPR_STMT, decl->span); function = sema_create_synthetic_main(context, decl, type, is_int_return, is_err_return);
stmt->expr_stmt = call;
ast_append(&next, stmt);
ret_stmt->expr_stmt = expr_new_const_int(decl->span, type_cint, 0, true);
} }
ast_append(&next, ret_stmt); if (!decl_ok(function)) return false;
assert(body);
function->func_decl.body = astid(body);
function->is_synthetic = true;
REGISTER_MAIN: REGISTER_MAIN:
context->unit->main_function = function; context->unit->main_function = function;
if (global_context.main) if (global_context.main)

View File

@@ -82,6 +82,8 @@ const char *kw_std__core__types;
const char *kw___run_default_test_runner; const char *kw___run_default_test_runner;
const char *kw_type; const char *kw_type;
const char *kw_typekind; const char *kw_typekind;
const char *kw_winmain;
const char *kw_wmain;
void symtab_destroy() void symtab_destroy()
{ {
@@ -162,6 +164,8 @@ void symtab_init(uint32_t capacity)
kw_std__core__types = KW_DEF("std::core::types"); kw_std__core__types = KW_DEF("std::core::types");
kw___run_default_test_runner = KW_DEF("__run_default_test_runner"); kw___run_default_test_runner = KW_DEF("__run_default_test_runner");
kw_type = KW_DEF("type"); kw_type = KW_DEF("type");
kw_winmain = KW_DEF("wWinMain");
kw_wmain = KW_DEF("wmain");
type_property_list[TYPE_PROPERTY_MAX] = builtin_list[BUILTIN_MAX] = KW_DEF("max"); type_property_list[TYPE_PROPERTY_MAX] = builtin_list[BUILTIN_MAX] = KW_DEF("max");
type_property_list[TYPE_PROPERTY_MIN] = builtin_list[BUILTIN_MIN] = KW_DEF("min"); type_property_list[TYPE_PROPERTY_MIN] = builtin_list[BUILTIN_MIN] = KW_DEF("min");

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.28" #define COMPILER_VERSION "0.4.29"

View File

@@ -89,7 +89,7 @@ fn void random_fasta(char[] symb, double[] probability, int n)
if (i % LINELEN != 0) putchar('\n'); if (i % LINELEN != 0) putchar('\n');
} }
fn void main(int argc, char **argv) fn int main(int argc, char **argv)
{ {
int n = 1000; int n = 1000;
if (argc > 1) n = atoi(argv[1]); if (argc > 1) n = atoi(argv[1]);
@@ -103,6 +103,7 @@ fn void main(int argc, char **argv)
printf(">THREE Homo sapiens frequency\n"); printf(">THREE Homo sapiens frequency\n");
random_fasta(homosapiens, homosapiens_p, n * 5); random_fasta(homosapiens, homosapiens_p, n * 5);
return 0;
} }
/* #expect: fasta.ll /* #expect: fasta.ll
@@ -326,7 +327,7 @@ if.exit14: ; preds = %if.then13, %loop.ex
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define void @fasta_main(i32 %0, ptr %1) #0 { define i32 @main(i32 %0, ptr %1) #0 {
entry: entry:
%n = alloca i32, align 4 %n = alloca i32, align 4
store i32 1000, ptr %n, align 4 store i32 1000, ptr %n, align 4
@@ -363,11 +364,5 @@ if.exit: ; preds = %if.then, %entry
%9 = load i32, ptr %n, align 4 %9 = load i32, ptr %n, align 4
%mul10 = mul i32 %9, 5 %mul10 = mul i32 %9, 5
call void @fasta_random_fasta(ptr %lo6, i64 %hi7, ptr %lo8, i64 %hi9, i32 %mul10) call void @fasta_random_fasta(ptr %lo6, i64 %hi7, ptr %lo8, i64 %hi9, i32 %mul10)
ret void
}
define i32 @main(i32 %0, ptr %1) #0 {
entry:
call void @fasta_main(i32 %0, ptr %1)
ret i32 0 ret i32 0
} }

View File

@@ -106,21 +106,18 @@ define i32 @main(i32 %0, ptr %1) #0 {
entry: entry:
%2 = call i64 @foo_main() %2 = call i64 @foo_main()
%not_err = icmp eq i64 %2, 0 %not_err = icmp eq i64 %2, 0
br i1 %not_err, label %after_check, label %error_block br i1 %not_err, label %after_check, label %assign_optional
assign_optional: ; preds = %entry
store i64 %2, ptr %error_var, align 8
br label %noerr_block
after_check: ; preds = %entry after_check: ; preds = %entry
br label %noerr_block br label %noerr_block
noerr_block: ; preds = %after_check noerr_block: ; preds = %after_check, %assign_optional
br label %phi_trycatch_block %3 = load i64, ptr %error_var, align 8
%neq = icmp ne i64 %3, 0
error_block: ; preds = %entry %ternary = select i1 %neq, i32 1, i32 0
br label %phi_trycatch_block ret i32 %ternary
}
phi_trycatch_block: ; preds = %error_block, %noerr_block
%val = phi i8 [ 1, %noerr_block ], [ 0, %error_block ]
%3 = trunc i8 %val to i1
%not = xor i1 %3, true
%boolsi = zext i1 %not to i32
ret i32 %boolsi
}

View File

@@ -89,7 +89,7 @@ fn void random_fasta(char[] symb, double[] probability, int n)
if (i % LINELEN != 0) putchar('\n'); if (i % LINELEN != 0) putchar('\n');
} }
fn void main(int argc, char **argv) fn int main(int argc, char **argv)
{ {
int n = 1000; int n = 1000;
if (argc > 1) n = atoi(argv[1]); if (argc > 1) n = atoi(argv[1]);
@@ -102,7 +102,7 @@ fn void main(int argc, char **argv)
printf(">THREE Homo sapiens frequency\n"); printf(">THREE Homo sapiens frequency\n");
random_fasta(homosapiens, homosapiens_p, n * 5); random_fasta(homosapiens, homosapiens_p, n * 5);
return 0;
} }
/* #expect: fasta.ll /* #expect: fasta.ll
@@ -329,7 +329,7 @@ if.exit15: ; preds = %if.then14, %loop.ex
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define void @fasta_main(i32 %0, i8** %1) #0 { define i32 @main(i32 %0, i8** %1) #0 {
entry: entry:
%n = alloca i32, align 4 %n = alloca i32, align 4
store i32 1000, i32* %n, align 4 store i32 1000, i32* %n, align 4
@@ -366,11 +366,5 @@ if.exit: ; preds = %if.then, %entry
%9 = load i32, i32* %n, align 4 %9 = load i32, i32* %n, align 4
%mul10 = mul i32 %9, 5 %mul10 = mul i32 %9, 5
call void @fasta_random_fasta(i8* %lo6, i64 %hi7, i8* %lo8, i64 %hi9, i32 %mul10) call void @fasta_random_fasta(i8* %lo6, i64 %hi7, i8* %lo8, i64 %hi9, i32 %mul10)
ret void
}
define i32 @main(i32 %0, i8** %1) #0 {
entry:
call void @fasta_main(i32 %0, i8** %1)
ret i32 0 ret i32 0
} }

View File

@@ -106,21 +106,18 @@ define i32 @main(i32 %0, i8** %1) #0 {
entry: entry:
%2 = call i64 @foo_main() %2 = call i64 @foo_main()
%not_err = icmp eq i64 %2, 0 %not_err = icmp eq i64 %2, 0
br i1 %not_err, label %after_check, label %error_block br i1 %not_err, label %after_check, label %assign_optional
assign_optional: ; preds = %entry
store i64 %2, i64* %error_var, align 8
br label %noerr_block
after_check: ; preds = %entry after_check: ; preds = %entry
br label %noerr_block br label %noerr_block
noerr_block: ; preds = %after_check noerr_block: ; preds = %after_check, %assign_optional
br label %phi_trycatch_block %3 = load i64, i64* %error_var, align 8
%neq = icmp ne i64 %3, 0
error_block: ; preds = %entry %ternary = select i1 %neq, i32 1, i32 0
br label %phi_trycatch_block ret i32 %ternary
phi_trycatch_block: ; preds = %error_block, %noerr_block
%val = phi i8 [ 1, %noerr_block ], [ 0, %error_block ]
%3 = trunc i8 %val to i1
%not = xor i1 %3, true
%boolsi = zext i1 %not to i32
ret i32 %boolsi
} }