From c4aa3665924e443519d09d70171df8315ff76aab Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 22 Jul 2021 18:45:39 +0200 Subject: [PATCH] Use heuristic to determine the type to convert to for compile time types. Added parsing for `generic` --- src/compiler/ast.c | 17 +-- src/compiler/compiler.c | 1 - src/compiler/compiler_internal.h | 4 +- src/compiler/context.c | 11 +- src/compiler/copying.c | 4 +- src/compiler/enums.h | 5 +- src/compiler/parse_global.c | 7 +- src/compiler/sema_casts.c | 30 ++++- src/compiler/sema_decls.c | 80 +++-------- src/compiler/sema_expr.c | 126 +++++++++++++++--- src/compiler/sema_name_resolution.c | 2 +- src/compiler/sema_types.c | 43 +----- .../macros/macro_convert_literal.c3 | 12 ++ 13 files changed, 185 insertions(+), 157 deletions(-) create mode 100644 test/test_suite/macros/macro_convert_literal.c3 diff --git a/src/compiler/ast.c b/src/compiler/ast.c index eb4b5016a..d7865e0a4 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -87,8 +87,6 @@ const char *decl_to_name(Decl *decl) return "function"; case DECL_GENERIC: return "generic"; - case DECL_GENFUNC: - TODO case DECL_INTERFACE: return "interface"; case DECL_MACRO: @@ -180,7 +178,6 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: - case DECL_GENFUNC: case DECL_GENERIC: case DECL_CT_IF: case DECL_CT_ELSE: @@ -914,8 +911,8 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) indent--; DUMPAST(decl->macro_decl.body); DUMPEND(); - case DECL_GENFUNC: - DUMPF("(macro %s", decl->name); + case DECL_GENERIC: + DUMPF("(generic %s", decl->name); DUMPTI(decl->macro_decl.rtype); indent++; DUMP("(params"); @@ -963,16 +960,6 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) DUMPF("(enum-constant %s", decl->name); DUMPEXPR(decl->enum_constant.expr); DUMPEND(); - case DECL_GENERIC: - DUMPF("(generic %s\n", decl->name); - indent++; - DUMP("(params"); - DUMPDECLS(decl->generic_decl.parameters); - DUMP("(cases"); - DUMPASTS(decl->generic_decl.cases); - DUMPE(); - indent--; - DUMPEND(); case DECL_TYPEDEF: DUMPF("(typedef %s", decl->name); if (decl->typedef_decl.is_func) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 5b4d8b3b4..44fef6dfa 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -183,7 +183,6 @@ static void register_generic_decls(Module *module, Decl **decls) case DECL_DISTINCT: case DECL_ENUM: case DECL_GENERIC: - case DECL_GENFUNC: case DECL_INTERFACE: case DECL_ERR: case DECL_FUNC: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 1acbe4298..2199374c3 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -428,10 +428,10 @@ typedef struct { bool failable : 1; Decl **parameters; - Decl **body_parameters; TypeInfo *type_parent; // May be null TypeInfo *rtype; // May be null! struct Ast_ *body; + Decl **body_parameters; TokenId block_parameter; } MacroDecl; @@ -1236,6 +1236,7 @@ typedef struct Module_ Ast **files; // Asts Decl** method_extensions; + Decl** generic_cache; STable symbols; STable public_symbols; struct Context_ **contexts; @@ -1321,6 +1322,7 @@ typedef struct Context_ Decl **functions; Decl **macros; Decl **generics; + Decl **generic_methods; Decl **interfaces; Decl **templates; Decl **methods; diff --git a/src/compiler/context.c b/src/compiler/context.c index cc41f95e4..d3c728c2b 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -113,9 +113,16 @@ void context_register_global_decl(Context *context, Decl *decl) vec_add(context->interfaces, decl); decl_set_external_name(decl); break; - case DECL_GENFUNC: case DECL_GENERIC: - vec_add(context->generics, decl); + if (decl->macro_decl.type_parent) + { + vec_add(context->generic_methods, decl); + return; + } + else + { + vec_add(context->generics, decl); + } decl_set_external_name(decl); break; case DECL_MACRO: diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 0fffa0100..0a5e05e22 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -561,15 +561,13 @@ Decl *copy_decl(Decl *decl) break; case DECL_ARRAY_VALUE: TODO - case DECL_GENFUNC: + case DECL_GENERIC: case DECL_MACRO: MACRO_COPY_TYPE(decl->macro_decl.type_parent); MACRO_COPY_DECL_LIST(decl->macro_decl.parameters); MACRO_COPY_AST(decl->macro_decl.body); MACRO_COPY_TYPE(decl->macro_decl.rtype); break; - case DECL_GENERIC: - TODO case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 9f41e5a53..389dd3b5c 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -149,7 +149,6 @@ typedef enum DECL_INTERFACE, DECL_LABEL, DECL_MACRO, - DECL_GENFUNC, DECL_STRUCT, DECL_TYPEDEF, DECL_UNION, @@ -157,9 +156,9 @@ typedef enum } DeclKind; #define NON_TYPE_DECLS DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: \ - case DECL_GENERIC: case DECL_CT_IF: case DECL_CT_ELSE: case DECL_CT_ELIF: \ + case DECL_CT_IF: case DECL_CT_ELSE: case DECL_CT_ELIF: \ case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_LABEL: \ - case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENFUNC + case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC typedef enum { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index df50d4ede..c47d160d6 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1693,9 +1693,10 @@ static inline bool parse_func_macro_header(Context *context, bool rtype_is_optio */ static inline Decl *parse_macro_declaration(Context *context, Visibility visibility) { - advance_and_verify(context, TOKEN_MACRO); + DeclKind kind = try_consume(context, TOKEN_MACRO) ? DECL_MACRO : DECL_GENERIC; + if (kind == DECL_GENERIC) advance_and_verify(context, TOKEN_GENERIC); - Decl *decl = decl_new(DECL_MACRO, context->tok.id, visibility); + Decl *decl = decl_new(kind, context->tok.id, visibility); TypeInfo **rtype_ref = &decl->macro_decl.rtype; TypeInfo **method_type_ref = &decl->macro_decl.type_parent; bool failable; @@ -2236,8 +2237,6 @@ Decl *parse_top_level_statement(Context *context) decl = TRY_DECL_OR(parse_struct_declaration(context, visibility), poisoned_decl); break; case TOKEN_GENERIC: - TODO - break; case TOKEN_MACRO: decl = TRY_DECL_OR(parse_macro_declaration(context, visibility), poisoned_decl); break; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 5de0f799e..9a4093167 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -330,15 +330,41 @@ bool enum_to_pointer(Expr* expr, Type *from, Type *type) return int_to_pointer(expr, type); } +Type *type_by_expr_range(ExprConst *expr) +{ + if (expr->kind == TYPE_FXX) + { + return type_double; + } + assert(expr->kind == TYPE_IXX); + BigInt *b = &expr->i; + // 1. Does it fit in a C int? If so, that's the type. + Type *type = type_cint(); + if (!expr_const_will_overflow(expr, type->type_kind)) return type; + + int width_max = platform_target.int128 ? 128 : 64; + int current_width = platform_target.width_c_int * 2; + while (current_width <= width_max) + { + type = type_int_signed_by_bitsize(current_width); + if (!expr_const_will_overflow(expr, type->type_kind)) return type; + type = type_int_unsigned_by_bitsize(current_width); + if (!expr_const_will_overflow(expr, type->type_kind)) return type; + current_width *= width_max; + } + return NULL; +} + bool cast_implicitly_to_runtime(Expr *expr) { Type *canonical = expr->type->canonical; + Type *type; switch (canonical->type_kind) { case TYPE_IXX: - return cast(expr, type_long); case TYPE_FXX: - return cast(expr, type_double); + type = type_by_expr_range(&expr->const_expr); + return type && cast(expr, type); default: return true; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index b64d68172..fccb54f67 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -610,7 +610,7 @@ static inline const char *name_by_decl(Decl *method_like) return "macro method"; case DECL_FUNC: return "method"; - case DECL_GENFUNC: + case DECL_GENERIC: return "generic method"; default: UNREACHABLE @@ -1009,6 +1009,7 @@ static bool sema_analyse_macro_method(Context *context, Decl *decl) static inline bool sema_analyse_macro(Context *context, Decl *decl) { + bool is_generic = decl->decl_kind == DECL_GENERIC; TypeInfo *rtype = decl->macro_decl.rtype; if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return decl_poison(decl); VECEACH(decl->macro_decl.parameters, i) @@ -1018,10 +1019,16 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) assert(param->decl_kind == DECL_VAR); switch (param->var.kind) { - case VARDECL_PARAM: case VARDECL_PARAM_EXPR: case VARDECL_PARAM_CT: case VARDECL_PARAM_REF: + if (is_generic) + { + SEMA_ERROR(param, "Only regular parameters and type parameters are allowed for generic functions."); + return decl_poison(decl); + } + FALLTHROUGH; + case VARDECL_PARAM: if (param->var.type_info) { if (!sema_resolve_type_info(context, param->var.type_info)) return decl_poison(decl); @@ -1046,6 +1053,11 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) } param->resolve_status = RESOLVE_DONE; } + if (is_generic && vec_size(decl->macro_decl.body_parameters)) + { + SEMA_ERROR(decl->macro_decl.body_parameters[0], "Trailing block syntax is not allowed for generic functions."); + return decl_poison(decl); + } VECEACH(decl->macro_decl.body_parameters, i) { Decl *param = decl->macro_decl.body_parameters[i]; @@ -1195,64 +1207,6 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) } } -static inline bool sema_analyse_generic(Context *context, Decl *decl) -{ - // 1. If it has a return type, make sure it resolves. - if (decl->generic_decl.rtype && !sema_resolve_type_info(context, decl->generic_decl.rtype)) return false; - - unsigned param_count = vec_size(decl->generic_decl.parameters); - if (param_count < 1) - { - SEMA_ERROR(decl, "A generic function needs at least 1 parameter."); - return false; - } - Ast **cases = decl->generic_decl.cases; - - bool default_has_been_found = false; - VECEACH(cases, i) - { - Ast *generic_case = cases[i]; - if (generic_case->ast_kind == AST_CASE_STMT) - { - if (!generic_case->case_stmt.is_type) - { - SEMA_ERROR(generic_case->case_stmt.expr, "Expected a type as the argument."); - return false; - } - if (!generic_case->case_stmt.is_type_list) - { - TypeInfo **type_infos = VECNEW(TypeInfo *, 2); - vec_add(type_infos, generic_case->case_stmt.type_info); - generic_case->case_stmt.type_infos = type_infos; - generic_case->case_stmt.is_type_list = true; - } - TypeInfo **type_infos = generic_case->case_stmt.type_infos; - unsigned args = vec_size(type_infos); - for (unsigned j = 0; j < args; j++) - { - if (!sema_resolve_type_info(context, type_infos[j])) return false; - } - if (args != param_count) - { - if (param_count == 1) - { - SEMA_ERROR(type_infos[1], "Expected a single type as the argument."); - return false; - } - SEMA_ERROR(type_infos[args - 1], "Expected %d types in the case statement.", param_count); - return false; - } - continue; - } - assert(generic_case->ast_kind == AST_DEFAULT_STMT); - if (default_has_been_found) - { - SEMA_ERROR(generic_case, "More than one default statement found."); - return false; - } - } - return true; -} @@ -1458,9 +1412,8 @@ bool sema_analyse_decl(Context *context, Decl *decl) case DECL_FUNC: if (!sema_analyse_func(context, decl)) return decl_poison(decl); break; - case DECL_GENFUNC: - TODO case DECL_MACRO: + case DECL_GENERIC: if (!sema_analyse_macro(context, decl)) return decl_poison(decl); break; case DECL_VAR: @@ -1482,9 +1435,6 @@ bool sema_analyse_decl(Context *context, Decl *decl) if (!sema_analyse_error(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; - case DECL_GENERIC: - if (!sema_analyse_generic(context, decl)) return decl_poison(decl); - break; case DECL_DEFINE: if (!sema_analyse_define(context, decl)) return decl_poison(decl); break; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 0f4e1cd4f..4709e4d46 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -192,6 +192,9 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case DECL_MACRO: SEMA_ERROR(expr, "Expected macro followed by (...) or prefixed by '&'."); return expr_poison(expr); + case DECL_GENERIC: + SEMA_ERROR(expr, "Expected generic followed by (...) or prefixed by '&'."); + return expr_poison(expr); case DECL_ENUM_CONSTANT: expr_replace(expr, decl->enum_constant.expr); return true; @@ -223,8 +226,6 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case DECL_ARRAY_VALUE: UNREACHABLE case DECL_IMPORT: - case DECL_GENERIC: - case DECL_GENFUNC: case DECL_CT_IF: case DECL_CT_ELSE: case DECL_CT_ELIF: @@ -232,9 +233,8 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_CT_ASSERT: - UNREACHABLE case DECL_DEFINE: - TODO + UNREACHABLE } switch (decl->var.kind) { @@ -1031,22 +1031,6 @@ static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr * } -static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Decl *decl, Expr *expr) -{ - Expr **arguments = expr->call_expr.arguments; - Decl **parameter_list = decl->generic_decl.parameters; - if (vec_size(parameter_list) != vec_size(arguments)) - { - SEMA_ERROR(expr, "Expected %d parameter(s) to the generic function.", vec_size(parameter_list)); - return false; - } - VECEACH(arguments, i) - { - if (!sema_analyse_expr(context, NULL, arguments[i])) return false; - } - - TODO -}; @@ -1239,6 +1223,11 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr case VARDECL_PARAM: // foo if (!sema_analyse_expr_of_required_type(context, param->type, arg, false)) return false; + if (!param->type && !cast_implicitly_to_runtime(arg)) + { + SEMA_ERROR(arg, "Constant cannot implicitly be cast to a real type."); + return false; + } param->alignment = type_abi_alignment(param->type ? param->type : arg->type); break; case VARDECL_PARAM_EXPR: @@ -1422,7 +1411,102 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr context->macro_scope = old_macro_scope; return ok; } +static inline Decl *sema_generate_generic_function(Context *context, Expr *call_expr, Expr *struct_var, Decl *decl, const char *mangled_name) +{ + TODO + return NULL; +} +static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *call_expr, Expr *struct_var, Decl *decl) +{ + assert(decl->decl_kind == DECL_GENERIC); + + Expr **args = call_expr->call_expr.arguments; + Decl **func_params = decl->macro_decl.parameters; + + unsigned explicit_args = vec_size(args); + unsigned total_args = explicit_args; + + scratch_buffer_clear(); + scratch_buffer_append(decl->external_name); + + // TODO revisit name mangling + if (struct_var) + { + total_args++; + scratch_buffer_append_char('@'); + scratch_buffer_append(struct_var->type->canonical->name); + } + + if (total_args != vec_size(func_params)) + { + // TODO HANDLE VARARGS + SEMA_ERROR(call_expr, "Mismatch on number of arguments."); + return false; + } + + int offset = total_args - explicit_args; + for (unsigned i = 0; i < explicit_args; i++) + { + Expr *arg = args[i]; + Decl *param = func_params[i + offset]; + if (param->decl_kind == VARDECL_PARAM_CT_TYPE) + { + if (!sema_analyse_expr_value(context, NULL, arg)) return false; + if (arg->expr_kind != EXPR_TYPEINFO) + { + SEMA_ERROR(arg, "A type, like 'int' or 'double' was expected for the parameter '%s'.", param->name); + return false; + } + scratch_buffer_append_char(','); + scratch_buffer_append(arg->type_expr->type->canonical->name); + continue; + } + if (param->var.type_info) + { + if (!sema_analyse_expr_of_required_type(context, param->var.type_info->type, arg, true)) return false; + } + else + { + if (!sema_analyse_expr(context, NULL, arg)) return false; + if (!cast_implicitly_to_runtime(arg)) return false; + } + scratch_buffer_append_char(','); + scratch_buffer_append(arg->type->canonical->name); + } + const char *mangled_name = scratch_buffer_interned(); + Decl **generic_cache = decl->module->generic_cache; + Decl *found = NULL; + VECEACH(generic_cache, i) + { + if (generic_cache[i]->external_name == mangled_name) + { + found = generic_cache[i]; + break; + } + } + if (!found) + { + found = sema_generate_generic_function(context, call_expr, struct_var, decl, mangled_name); + } + // Remove type parameters from call. + for (unsigned i = 0; i < explicit_args; i++) + { + Decl *param = func_params[i + offset]; + if (param->decl_kind == VARDECL_PARAM_CT_TYPE) + { + for (unsigned j = i + 1; j < explicit_args; j++) + { + args[j - 1] = args[j]; + } + explicit_args--; + i--; + } + } + vec_resize(args, explicit_args); + // Perform the normal func call on the found declaration. + return sema_expr_analyse_func_call(context, to, call_expr, found, struct_var); +} static bool sema_analyse_body_expansion(Context *context, Expr *call) { @@ -1537,7 +1621,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr SEMA_ERROR(expr, "Macro declarations cannot be called without using '@' before the macro name."); return false; case DECL_GENERIC: - return sema_expr_analyse_generic_call(context, to, decl, expr); + return sema_expr_analyse_generic_call(context, to, expr, struct_var, decl); case DECL_POISONED: return false; default: diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 9f6d23ccc..dcb72215d 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -244,7 +244,7 @@ Decl *sema_find_extension_method_in_module(Module *module, Type *type, const cha if (extension->func_decl.type_parent->type == type) return extension; break; case DECL_MACRO: - case DECL_GENFUNC: + case DECL_GENERIC: if (extension->macro_decl.type_parent->type == type) return extension; break; default: diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index bfb00a2c9..dcb8b70ef 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -119,7 +119,6 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: - case DECL_GENFUNC: case DECL_GENERIC: case DECL_LABEL: SEMA_TOKID_ERROR(type_info->unresolved.name_loc, "This is not a type."); @@ -137,30 +136,6 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) } -Type *type_by_expr_range(ExprConst *expr) -{ - if (expr->kind == TYPE_FXX) - { - return type_double; - } - assert(expr->kind == TYPE_IXX); - BigInt *b = &expr->i; - // 1. Does it fit in a C int? If so, that's the type. - Type *type = type_cint(); - if (!expr_const_will_overflow(expr, type->type_kind)) return type; - - int width_max = platform_target.int128 ? 128 : 64; - int current_width = platform_target.width_c_int * 2; - while (current_width <= width_max) - { - type = type_int_signed_by_bitsize(current_width); - if (!expr_const_will_overflow(expr, type->type_kind)) return type; - type = type_int_unsigned_by_bitsize(current_width); - if (!expr_const_will_overflow(expr, type->type_kind)) return type; - current_width *= width_max; - } - return NULL; -} bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info, bool allow_inferred_type, bool in_shallow) { @@ -192,22 +167,12 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info, bool allow { return type_info_poison(type_info); } - type_info->type = expr->type; - switch (type_info->type->type_kind) + if (!cast_implicitly_to_runtime(expr)) { - case TYPE_FXX: - case TYPE_IXX: - assert(expr->expr_kind == EXPR_CONST); - type_info->type = type_by_expr_range(&expr->const_expr); - if (!type_info->type) - { - SEMA_ERROR(expr, "The expression does not fit any runtime type."); - return false; - } - break; - default: - break; + SEMA_ERROR(expr, "The expression does not fit any runtime type."); + return false; } + type_info->type = expr->type; type_info->resolve_status = RESOLVE_DONE; return true; } diff --git a/test/test_suite/macros/macro_convert_literal.c3 b/test/test_suite/macros/macro_convert_literal.c3 new file mode 100644 index 000000000..3f25ff9f8 --- /dev/null +++ b/test/test_suite/macros/macro_convert_literal.c3 @@ -0,0 +1,12 @@ +module foo; +import std::io; + +macro foo(y) +{ + return y * y; +} + +func void main() +{ + io::printf("%d\n", @foo(10)); +} \ No newline at end of file