diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 7fe0cfb40..a28f64ad1 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -857,11 +857,7 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) DUMPF("(generic %s\n", decl->name); indent++; DUMP("(params"); - VECEACH(decl->generic_decl.parameters, i) - { - DUMPFI("%s", TOKSTR(decl->generic_decl.parameters[i])); - } - DUMPE(); + DUMPDECLS(decl->generic_decl.parameters); DUMP("(cases"); DUMPASTS(decl->generic_decl.cases); DUMPE(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 12bfe467d..1ad7513b3 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -288,6 +288,7 @@ typedef struct _VarDecl bool constant : 1; bool failable : 1; bool unwrap : 1; + bool vararg : 1; TypeInfo *type_info; union { @@ -347,6 +348,7 @@ typedef struct _FunctionSignature bool variadic : 1; bool has_default : 1; bool failable : 1; + bool typed_variadic : 1; TypeInfo *rtype; struct ABIArgInfo_ *ret_abi_info; struct ABIArgInfo_ *failable_abi_info; @@ -422,7 +424,7 @@ typedef struct typedef struct { struct _Ast **cases; - TokenId *parameters; + Decl **parameters; TypeInfo *rtype; // May be null! Path *path; // For redefinition } GenericDecl; @@ -570,6 +572,7 @@ typedef struct { bool is_type_method : 1; bool is_pointer_call : 1; + bool unsplat_last : 1; Expr *function; Expr **arguments; } ExprCall; diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 3cea87e9b..7e028226b 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -569,7 +569,8 @@ unsigned intrinsic_id_ctlz; unsigned intrinsic_id_cttz; unsigned intrinsic_id_convert_from_fp16; unsigned intrinsic_id_convert_to_fp16; - +unsigned intrinsic_id_lifetime_start; +unsigned intrinsic_id_lifetime_end; @@ -595,10 +596,8 @@ void llvm_codegen_setup() intrinsic_id_trap = lookup_intrinsic("llvm.trap"); intrinsic_id_assume = lookup_intrinsic("llvm.assume"); - - - - + intrinsic_id_lifetime_start = lookup_intrinsic("llvm.lifetime.start"); + intrinsic_id_lifetime_end = lookup_intrinsic("llvm.lifetime.end"); intrinsic_id_ssub_overflow = lookup_intrinsic("llvm.ssub.with.overflow"); intrinsic_id_ssub_sat = lookup_intrinsic("llvm.ssub.sat"); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 6eb512969..55089e267 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2319,6 +2319,7 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM } } + LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment) { *alignment = type_min_alignment(offset, struct_alignment); @@ -2326,6 +2327,25 @@ LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLV return addr; } +void llvm_emit_subarray_len(GenContext *c, BEValue *subarray, BEValue *len) +{ + llvm_value_addr(c, subarray); + unsigned alignment = 0; + LLVMValueRef len_addr = llvm_emit_struct_gep_raw(c, subarray->value, llvm_get_type(c, subarray->type), 1, subarray->alignment, + type_abi_alignment(type_voidptr), &alignment); + llvm_value_set_address_align(len, len_addr, type_usize, alignment); +} + +void llvm_emit_subarray_pointer(GenContext *c, BEValue *subarray, BEValue *pointer) +{ + llvm_value_addr(c, subarray); + unsigned alignment = 0; + LLVMValueRef len_addr = llvm_emit_struct_gep_raw(c, subarray->value, llvm_get_type(c, subarray->type), 0, subarray->alignment, + 0, &alignment); + llvm_value_set_address_align(pointer, len_addr, type_get_ptr(subarray->type->array.base), alignment); + +} + void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index) { llvm_value_fold_failable(c, struct_pointer); @@ -2498,6 +2518,39 @@ void llvm_emit_parameter(GenContext *context, LLVMValueRef **args, ABIArgInfo *i } } +static void llvm_emit_unpacked_variadic_arg(GenContext *c, Expr *expr, BEValue *subarray) +{ + BEValue value; + llvm_emit_expr(c, &value, expr); + BEValue len_addr; + BEValue pointer_addr; + llvm_emit_subarray_len(c, subarray, &len_addr); + llvm_emit_subarray_pointer(c, subarray, &pointer_addr); + Type *type = expr->type->canonical; + switch (type->type_kind) + { + case TYPE_ARRAY: + { + llvm_store_bevalue_raw(c, &len_addr, llvm_const_int(c, type_usize, type->array.len)); + llvm_value_addr(c, &value); + llvm_store_bevalue_raw(c, &pointer_addr, llvm_emit_bitcast(c, value.value, type_get_ptr(type->array.base))); + return; + } + case TYPE_POINTER: + // Load the pointer + llvm_value_rvalue(c, &value); + llvm_store_bevalue_raw(c, &len_addr, llvm_const_int(c, type_usize, type->pointer->array.len)); + llvm_store_bevalue_raw(c, &pointer_addr, llvm_emit_bitcast(c, value.value, type_get_ptr(type->array.base))); + return; + case TYPE_SUBARRAY: + *subarray = value; + return; + case TYPE_VARARRAY: + TODO + default: + UNREACHABLE + } +} void llvm_emit_call_expr(GenContext *c, BEValue *be_value, Expr *expr) { Expr *function = expr->call_expr.function; @@ -2615,8 +2668,10 @@ void llvm_emit_call_expr(GenContext *c, BEValue *be_value, Expr *expr) // 8. Add all other arguments. unsigned arguments = vec_size(expr->call_expr.arguments); - assert(arguments >= vec_size(signature->params)); - VECEACH(signature->params, i) + unsigned non_variadic_params = vec_size(signature->params); + if (signature->typed_variadic) non_variadic_params--; + assert(arguments >= non_variadic_params); + for (unsigned i = 0; i < non_variadic_params; i++) { // 8a. Evaluate the expression. Expr *arg_expr = expr->call_expr.arguments[i]; @@ -2628,13 +2683,71 @@ void llvm_emit_call_expr(GenContext *c, BEValue *be_value, Expr *expr) llvm_emit_parameter(c, &values, info, be_value, param->type); } - // 9. Emit varargs. - for (unsigned i = vec_size(signature->params); i < arguments; i++) + // 9. Typed varargs + if (signature->typed_variadic) { - Expr *arg_expr = expr->call_expr.arguments[i]; - llvm_emit_expr(c, be_value, arg_expr); - printf("TODO: varargs should be expanded correctly\n"); - vec_add(values, llvm_value_rvalue_store(c, be_value)); + printf("All varargs should be called with non-alias!\n"); + Decl *vararg_param = signature->params[non_variadic_params]; + + BEValue subarray; + + llvm_value_set_address(&subarray, llvm_emit_alloca_aligned(c, vararg_param->type, "vararg"), vararg_param->type); + + // 9a. Special case, empty argument + if (arguments == non_variadic_params + 1 && !expr->call_expr.arguments[non_variadic_params]) + { + // Just set the size to zero. + BEValue len_addr; + llvm_emit_subarray_len(c, &subarray, &len_addr); + llvm_store_bevalue_raw(c, &len_addr, llvm_get_zero(c, type_usize)); + } + else if (arguments == non_variadic_params + 1 && expr->call_expr.unsplat_last) + { + // 9b. We unpack the last type which is either a slice, an array or a dynamic array. + llvm_emit_unpacked_variadic_arg(c, expr->call_expr.arguments[non_variadic_params], &subarray); + } + else + { + // 9b. Otherwise we also need to allocate memory for the arguments: + Type *pointee_type = vararg_param->type->pointer; + LLVMTypeRef llvm_pointee = llvm_get_type(c, pointee_type); + Type *array = type_get_array(pointee_type, arguments - non_variadic_params); + LLVMTypeRef llvm_array_type = llvm_get_type(c, array); + LLVMValueRef array_ref = llvm_emit_alloca_aligned(c, array, "varargslots"); + LLVMValueRef zero = llvm_get_zero(c, type_usize); + LLVMValueRef indices[2] = { + zero, + zero, + }; + for (unsigned i = non_variadic_params; i < arguments; i++) + { + Expr *arg_expr = expr->call_expr.arguments[i]; + llvm_emit_expr(c, be_value, arg_expr); + indices[1] = llvm_const_int(c, type_usize, i - non_variadic_params); + LLVMValueRef slot = LLVMBuildInBoundsGEP2(c->builder, llvm_array_type, array_ref, indices, 2, ""); + llvm_store_bevalue_aligned(c, slot, be_value, 0); + } + BEValue len_addr; + llvm_emit_subarray_len(c, &subarray, &len_addr); + llvm_store_bevalue_raw(c, &len_addr, llvm_const_int(c, type_usize, arguments - non_variadic_params)); + BEValue pointer_addr; + llvm_emit_subarray_pointer(c, &subarray, &pointer_addr); + Type *array_as_pointer_type = type_get_ptr(pointee_type); + llvm_store_bevalue_raw(c, &pointer_addr, llvm_emit_bitcast(c, array_ref, array_as_pointer_type)); + } + ABIArgInfo *info = vararg_param->var.abi_info; + llvm_emit_parameter(c, &values, info, &subarray, vararg_param->type); + } + else + { + // 9. Emit varargs. + for (unsigned i = vec_size(signature->params); i < arguments; i++) + { + Expr *arg_expr = expr->call_expr.arguments[i]; + llvm_emit_expr(c, be_value, arg_expr); + printf("TODO: varargs should be expanded correctly\n"); + vec_add(values, llvm_value_rvalue_store(c, be_value)); + } } // 10. Create the actual call diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 236e6dc7e..3993964f9 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -156,6 +156,8 @@ extern unsigned intrinsic_id_ctlz; extern unsigned intrinsic_id_cttz; extern unsigned intrinsic_id_convert_from_fp16; extern unsigned intrinsic_id_convert_to_fp16; +extern unsigned intrinsic_id_lifetime_start; +extern unsigned intrinsic_id_lifetime_end; // LLVM Attributes @@ -246,6 +248,8 @@ void llvm_emit_ptr_from_array(GenContext *c, BEValue *value); void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable); void llvm_emit_return_implicit(GenContext *c); LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment); +void llvm_emit_subarray_len(GenContext *context, BEValue *subarray, BEValue *len); +void llvm_emit_subarray_pointer(GenContext *context, BEValue *subarray, BEValue *pointer); LLVMValueRef llvm_get_next_param(GenContext *context, unsigned *index); LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info); static inline LLVMBasicBlockRef llvm_get_current_block_if_in_use(GenContext *context); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 8357f50db..673639166 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -131,20 +131,14 @@ static bool parse_param_path(Context *context, DesignatorElement ***path) } } /** - * param_list - * : parameter - * | parameter ',' parameters - * ; - * - * parameter - * : expr - * | param_path '=' expr - * ; + * param_list ::= ('...' parameter | parameter (',' parameter)*)? * + * parameter ::= (param_path '=')? expr */ -bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenType param_end) +bool parse_param_list(Context *context, Expr ***result, TokenType param_end, bool *unsplat) { *result = NULL; + if (unsplat) *unsplat = false; while (1) { Expr *expr = NULL; @@ -166,7 +160,11 @@ bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenTy } else { - expr = parse_expr_or_initializer_list(context); + if (unsplat) + { + *unsplat = try_consume(context, TOKEN_ELLIPSIS); + } + expr = TRY_EXPR_OR(parse_expr_or_initializer_list(context), false); } vec_add(*result, expr); if (!try_consume(context, TOKEN_COMMA)) @@ -174,6 +172,11 @@ bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenTy return true; } if (TOKEN_IS(param_end)) return true; + if (unsplat && *unsplat) + { + SEMA_TOKEN_ERROR(context->tok, "'...' is only allowed on the last argument in a call."); + return false; + } } } @@ -215,7 +218,13 @@ static inline Expr* parse_non_assign_expr(Context *context) Expr *parse_expression_list(Context *context) { Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok); - if (!parse_param_list(context, &expr_list->expression_list, false, TOKEN_INVALID_TOKEN)) return poisoned_expr; + while (1) + { + Expr *expr = NULL; + expr = TRY_EXPR_OR(parse_expr_or_initializer_list(context), poisoned_expr); + vec_add(expr_list->expression_list, expr); + if (!try_consume(context, TOKEN_COMMA)) break; + } return expr_list; } @@ -376,7 +385,7 @@ Expr *parse_initializer_list(Context *context) CONSUME_OR(TOKEN_LBRACE, poisoned_expr); if (!try_consume(context, TOKEN_RBRACE)) { - if (!parse_param_list(context, &initializer_list->initializer_expr.initializer_expr, false, TOKEN_RBRACE)) return poisoned_expr; + if (!parse_param_list(context, &initializer_list->initializer_expr.initializer_expr, TOKEN_RBRACE, NULL)) return poisoned_expr; CONSUME_OR(TOKEN_RBRACE, poisoned_expr); } RANGE_EXTEND_PREV(initializer_list); @@ -449,15 +458,17 @@ static Expr *parse_call_expr(Context *context, Expr *left) Expr **params = NULL; advance_and_verify(context, TOKEN_LPAREN); + bool unsplat; if (!TOKEN_IS(TOKEN_RPAREN)) { - if (!parse_param_list(context, ¶ms, 0, TOKEN_RPAREN)) return poisoned_expr; + if (!parse_param_list(context, ¶ms, TOKEN_RPAREN, &unsplat)) return poisoned_expr; } TRY_CONSUME_OR(TOKEN_RPAREN, "Expected the ending ')' here", poisoned_expr); Expr *call = EXPR_NEW_EXPR(EXPR_CALL, left); call->call_expr.function = left; call->call_expr.arguments = params; + call->call_expr.unsplat_last = unsplat; RANGE_EXTEND_PREV(call); return call; } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 2cb5580f5..2f812ef29 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -3,6 +3,7 @@ static Decl *parse_const_declaration(Context *context, Visibility visibility); + /** * Walk forward through the token stream to identify a type on the format: foo::bar::Type * @@ -48,6 +49,16 @@ static bool context_next_is_type_with_path_prefix(Context *context) } } +static bool context_next_is_type_and_not_ident(Context *context) +{ + if (context->tok.type == TOKEN_IDENT) + { + if (context->next_tok.type != TOKEN_COLON) return false; + return context_next_is_type_with_path_prefix(context); + } + return true; +} + /** * Walk until we find the first top level construct, the current heuristic is this: @@ -529,7 +540,7 @@ static inline TypeInfo *parse_base_type(Context *context) TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER, range); type_info->unresolved.path = path; type_info->unresolved.name_loc = context->tok.id; - if (!consume_type_name(context, "types")) return poisoned_type_info; + if (!consume_type_name(context, "type")) return poisoned_type_info; RANGE_EXTEND_PREV(type_info); return type_info; } @@ -1049,22 +1060,21 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl) /** - * param_declaration - * : type_expression - * | type_expression IDENT - * | type_expression IDENT '=' initializer + * param_declaration ::= type_expression '...'?) (IDENT ('=' initializer)?)? * ; */ static inline bool parse_param_decl(Context *context, Visibility parent_visibility, Decl*** parameters, bool require_name) { + TokenId first = context->tok.id; TypeInfo *type = TRY_TYPE_OR(parse_type(context), false); + bool vararg = try_consume(context, TOKEN_ELLIPSIS); Decl *param = decl_new_var(context->tok.id, type, VARDECL_PARAM, parent_visibility); - param->span = type->span; + param->span = (SourceSpan) { first, context->tok.id }; + param->var.vararg = vararg; if (!try_consume(context, TOKEN_IDENT)) { param->name = NULL; } - const char *name = param->name; if (!name && require_name) @@ -1116,7 +1126,7 @@ static inline bool parse_opt_parameter_type_list(Context *context, Visibility pa CONSUME_OR(TOKEN_LPAREN, false); while (!try_consume(context, TOKEN_RPAREN)) { - if (signature->variadic) + if (signature->variadic || signature->typed_variadic) { SEMA_TOKEN_ERROR(context->tok, "Variadic arguments should be the last in a parameter list."); return false; @@ -1128,6 +1138,7 @@ static inline bool parse_opt_parameter_type_list(Context *context, Visibility pa else { if (!parse_param_decl(context, parent_visibility, &(signature->params), false)) return false; + signature->typed_variadic = VECLAST(signature->params)->var.vararg; } if (!try_consume(context, TOKEN_COMMA)) { @@ -1299,7 +1310,78 @@ static inline Ast *parse_generics_statements(Context *context) return ast; } - +static bool parse_macro_arguments(Context *context, Visibility visibility, Decl ***params_ref) +{ + CONSUME_OR(TOKEN_LPAREN, false); + *params_ref = NULL; + bool vararg = false; + while (!try_consume(context, TOKEN_RPAREN)) + { + TypeInfo *parm_type = NULL; + VarDeclKind param_kind; + TEST_TYPE: + switch (context->tok.type) + { + // normal foo + case TOKEN_IDENT: + param_kind = VARDECL_PARAM; + break; + // ct_var $foo + case TOKEN_CT_IDENT: + param_kind = VARDECL_PARAM_CT; + break; + // reference &foo + case TOKEN_AMP: + advance(context); + if (!TOKEN_IS(TOKEN_IDENT)) + { + SEMA_TOKEN_ERROR(context->tok, "Only normal variables may be passed by reference."); + return false; + } + param_kind = VARDECL_PARAM_REF; + break; + // #Foo (not allowed) + case TOKEN_HASH_TYPE_IDENT: + SEMA_TOKEN_ERROR(context->tok, "An unevaluated expression can never be a type, did you mean to use $Type?"); + return false; + // expression #foo + case TOKEN_HASH_IDENT: + // Note that the HASH_TYPE_IDENT will be an error later on. + param_kind = VARDECL_PARAM_EXPR; + break; + // Compile time type $Type + case TOKEN_CT_TYPE_IDENT: + param_kind = VARDECL_PARAM_CT_TYPE; + break; + default: + if (parm_type || vararg) + { + SEMA_TOKEN_ERROR(context->tok, "Expected a macro parameter"); + return false; + } + // We either have "... var" or "int... var" + if (try_consume(context, TOKEN_ELLIPSIS)) + { + vararg = true; + } + else + { + parm_type = TRY_TYPE_OR(parse_type(context), false); + if (try_consume(context, TOKEN_ELLIPSIS)) + { + vararg = true; + } + } + goto TEST_TYPE; + } + Decl *param = decl_new_var(context->tok.id, parm_type, param_kind, visibility); + param->var.vararg = vararg; + advance(context); + vec_add(*params_ref, param); + COMMA_RPAREN_OR(false); + } + return true; +} /** * generics_declaration * : GENERIC opt_path IDENT '(' macro_argument_list ')' '{' generics_body '}' @@ -1318,7 +1400,7 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi { advance_and_verify(context, TOKEN_GENERIC); TypeInfo *rtype = NULL; - if (!TOKEN_IS(TOKEN_IDENT)) + if (context_next_is_type_and_not_ident(context)) { rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); } @@ -1329,23 +1411,10 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi decl->generic_decl.path = path; if (!consume_ident(context, "generic function name")) return poisoned_decl; decl->generic_decl.rtype = rtype; - TokenId *parameters = NULL; - CONSUME_OR(TOKEN_LPAREN, poisoned_decl); - while (!try_consume(context, TOKEN_RPAREN)) - { - if (!TOKEN_IS(TOKEN_IDENT)) - { - SEMA_TOKEN_ERROR(context->tok, "Expected an identifier."); - return false; - } - parameters = VECADD(parameters, context->tok.id); - advance(context); - COMMA_RPAREN_OR(poisoned_decl); - } + if (!parse_macro_arguments(context, visibility, &decl->generic_decl.parameters)) return poisoned_decl; Ast **cases = NULL; if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT, true)) return poisoned_decl; decl->generic_decl.cases = cases; - decl->generic_decl.parameters = parameters; return decl; } @@ -1524,10 +1593,6 @@ static inline Decl *parse_typedef_declaration(Context *context, Visibility visib return decl; } -static bool next_is_type_and_not_ident(Context *context) -{ - return context->tok.type != TOKEN_IDENT || context->next_tok.type == TOKEN_COLON; -} /** * macro ::= MACRO (type '!'?)? identifier '!'? '(' macro_params ')' compound_statement */ @@ -1539,7 +1604,7 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil bool failable = false; // 1. Return type? - if (next_is_type_and_not_ident(context)) + if (context_next_is_type_and_not_ident(context)) { rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); failable = try_consume(context, TOKEN_BANG); @@ -1555,64 +1620,7 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here.", poisoned_decl); - CONSUME_OR(TOKEN_LPAREN, poisoned_decl); - Decl **params = NULL; - while (!try_consume(context, TOKEN_RPAREN)) - { - TypeInfo *parm_type = NULL; - VarDeclKind param_kind; - TEST_TYPE: - switch (context->tok.type) - { - // normal foo - case TOKEN_IDENT: - param_kind = VARDECL_PARAM; - break; - // ct_var $foo - case TOKEN_CT_IDENT: - param_kind = VARDECL_PARAM_CT; - break; - // reference &foo - case TOKEN_AMP: - advance(context); - if (!TOKEN_IS(TOKEN_IDENT)) - { - SEMA_TOKEN_ERROR(context->tok, "Only normal variables may be passed by reference."); - return poisoned_decl; - } - param_kind = VARDECL_PARAM_REF; - break; - // #Foo (not allowed) - case TOKEN_HASH_TYPE_IDENT: - SEMA_TOKEN_ERROR(context->tok, "An unevaluated expression can never be a type, did you mean to use $Type?"); - return poisoned_decl; - // expression #foo - case TOKEN_HASH_IDENT: - // Note that the HASH_TYPE_IDENT will be an error later on. - param_kind = VARDECL_PARAM_EXPR; - break; - // Compile time type $Type - case TOKEN_CT_TYPE_IDENT: - param_kind = VARDECL_PARAM_CT_TYPE; - break; - case TOKEN_ELLIPSIS: - // varargs - TODO - default: - if (parm_type) - { - SEMA_TOKEN_ERROR(context->tok, "Expected a macro parameter"); - return poisoned_decl; - } - parm_type = TRY_TYPE_OR(parse_type(context), poisoned_decl); - goto TEST_TYPE; - } - Decl *param = decl_new_var(context->tok.id, parm_type, param_kind, visibility); - advance(context); - params = VECADD(params, param); - COMMA_RPAREN_OR(poisoned_decl); - } - decl->macro_decl.parameters = params; + if (!parse_macro_arguments(context, visibility, &decl->macro_decl.parameters)) return poisoned_decl; decl->macro_decl.body = TRY_AST_OR(parse_stmt(context), poisoned_decl); return decl; } @@ -1666,6 +1674,11 @@ static inline bool parse_enum_spec(Context *context, TypeInfo **type_ref, Decl** while (!try_consume(context, TOKEN_RPAREN)) { if (!parse_param_decl(context, parent_visibility, parameters_ref, true)) return false; + if (VECLAST(*parameters_ref)->var.vararg) + { + SEMA_TOKID_ERROR(context->prev_tok, "Vararg parameters are not allowed as enum parameters."); + return false; + } if (!try_consume(context, TOKEN_COMMA)) { EXPECT_OR(TOKEN_RPAREN, false); @@ -1735,7 +1748,7 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili if (try_consume(context, TOKEN_LPAREN)) { Expr **result = NULL; - if (!parse_param_list(context, &result, true, TOKEN_RPAREN)) return poisoned_decl; + if (!parse_param_list(context, &result, TOKEN_RPAREN, NULL)) return poisoned_decl; enum_const->enum_constant.args = result; CONSUME_OR(TOKEN_RPAREN, poisoned_decl); } diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 8a2a6330c..b5bce5a90 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -52,7 +52,8 @@ bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, Toke bool allow_multiple_values); Expr *parse_expression_list(Context *context); Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type); -bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenType end_type); + +bool parse_param_list(Context *context, Expr ***result, TokenType param_end, bool *unsplat); Expr *parse_type_compound_literal_expr_after_type(Context *context, TypeInfo *type_info); Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info); bool parse_next_is_decl(Context *context); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 038adf181..6f82f5321 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -331,6 +331,10 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo { return false; } + if (param->var.vararg) + { + param->var.type_info->type = type_get_subarray(param->var.type_info->type); + } param->type = param->var.type_info->type; if (param->var.init_expr && !is_function) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 5720b444f..c2012b723 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -813,8 +813,26 @@ static inline bool sema_expr_analyse_intrinsic_invocation(Context *context, Expr UNREACHABLE } +static inline bool expr_may_unpack_as_vararg(Expr *expr, Type *variadic_base_type) +{ + Type *base_type = variadic_base_type->canonical; + Type *canonical = expr->type->canonical; + switch (canonical->type_kind) + { + case TYPE_ARRAY: + case TYPE_SUBARRAY: + case TYPE_VARARRAY: + return canonical->array.base == base_type; + case TYPE_POINTER: + if (canonical->pointer->type_kind == TYPE_ARRAY) return canonical->pointer->array.base == base_type; + return false; + default: + return false; + } +} static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionSignature *signature, Expr *expr, Decl *decl, Type *to, Expr *struct_var) { + // 1. Builtin? We handle that elsewhere. if (decl->func.is_builtin) { assert(!struct_var); @@ -822,92 +840,200 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS } Decl **func_params = signature->params; Expr **args = expr->call_expr.arguments; + + // 2. If this is a type call, then we have an implicit first argument. unsigned struct_args = struct_var == NULL ? 0 : 1; - unsigned func_param_count = vec_size(func_params); + + // 3. There might be unpacking for typed varargs. unsigned num_args = vec_size(args) + struct_args; + bool unsplat = expr->call_expr.unsplat_last; + if (unsplat) + { + // 4. Is this *not* a naked vararg or typed vararg? That's an error. + if (!signature->typed_variadic && !signature->variadic) + { + SEMA_ERROR(VECLAST(expr->call_expr.arguments), "Unpacking is only allowed for functions with variable parameters."); + return false; + } + } + + // 4. Zero out all argument slots. + unsigned func_param_count = vec_size(func_params); + // 4a. We need at least as many function locations as we have parameters. unsigned entries_needed = func_param_count > num_args ? func_param_count : num_args; Expr **actual_args = VECNEW(Expr*, entries_needed); for (unsigned i = 0; i < entries_needed; i++) vec_add(actual_args, NULL); memset(actual_args, 0, entries_needed * sizeof(Expr*)); - bool uses_named_parameters = false; + // 5. We might have a typed variadic call e.g. foo(int, double...) + // get that type. + Type *variadic_type = NULL; + if (signature->typed_variadic) + { + // 5a. The parameter type is [], so we get the + assert(func_params[func_param_count - 1]->type->type_kind == TYPE_SUBARRAY); + variadic_type = func_params[func_param_count - 1]->type->array.base; + // 5b. The last function parameter is implicit, so remove it. + func_param_count--; + } + + // 6. Loop through the parameters. + bool uses_named_parameters = false; for (unsigned i = 0; i < num_args; i++) { + // 7. We might pick up the argument as the type if this is a type + // method. bool is_implicit = i < struct_args; Expr *arg = is_implicit ? struct_var : args[i - struct_args]; - // Named parameters + + // 8. Handle named parameters if (arg->expr_kind == EXPR_DESIGNATOR) { + // 8a. We have named parameters, that will add some restrictions. uses_named_parameters = true; + + // 8b. Find the location of the parameter. int index = find_index_of_named_parameter(func_params, arg); + + // 8c. If it's not found then this is an error. if (index < 0) return false; + + // 8d. We might actually be finding the typed vararg at the end, + // this is an error. + if (func_params[index]->var.vararg) + { + SEMA_ERROR(arg, "Vararg parameters may not be named parameters, use normal parameters instead.", func_params[index]->name); + return false; + } + + // 8e. We might have already set this parameter, that is not allowed. if (actual_args[index]) { SEMA_ERROR(arg, "The parameter '%s' was already set once.", func_params[index]->name); return false; } - if (!sema_analyse_expr_of_required_type(context, func_params[index]->type, arg->binary_expr.right, 0)) return false; - actual_args[index] = arg->binary_expr.right; - expr->failable |= arg->binary_expr.right->failable; - continue; - } - - if (i >= func_param_count) - { - if (!signature->variadic) - { - SEMA_ERROR(expr, "Too many parameters for this function."); - return false; - } - if (!sema_analyse_expr(context, NULL, arg)) return false; - // In the case of a compile time variable we cast to c_int / double. - - Type *arg_type = arg->type->canonical; - if (type_is_ct(arg_type)) - { - // Pick double / CInt - Type *target_type = type_is_any_integer(arg_type) ? type_c_int->canonical : type_double; - if (!cast_implicit(arg, target_type)) return false; - arg_type = target_type; - } - // Promote any integer or bool to at least CInt - if (type_is_promotable_integer(arg_type) || arg_type == type_bool) - { - cast(arg, type_c_int->canonical); - arg_type = type_c_int->canonical; - } - if (type_is_promotable_float(arg->type)) - { - cast(arg, type_double); - arg_type = type_double; - } - actual_args[i] = arg; - expr->failable |= arg->failable; + + // 8f. Check the parameter + if (!sema_analyse_expr_of_required_type(context, func_params[index]->type, arg->designator_expr.value, true)) return false; + + // 8g. Set the parameter and update failability. + actual_args[index] = arg->designator_expr.value; + expr->failable |= arg->designator_expr.value->failable; continue; } + // 9. Check for previous use of named parameters, this is not allowed + // like foo(.a = 1, 3) => error. if (uses_named_parameters) { SEMA_ERROR(expr, "A regular parameter cannot follow a named parameter."); return false; } + + // 10. If we exceed the function parameter count (remember we reduced this by one + // in the case of typed vararg) we're now in a variadic list. + if (i >= func_param_count) + { + // 11. We might have a typed variadic argument. + if (variadic_type) + { + // 11a. Look if we did an unsplat + if (expr->call_expr.unsplat_last) + { + // 11b. Is this the last argument, or did we get some before the unpack? + if (i < num_args - 1) + { + SEMA_ERROR(arg, "This looks like a variable argument before an unpacked variable which isn't allowed. Did you add too many arguments?"); + return false; + } + // 11c. Analyse the expression. We don't use any type inference here since + // foo(...{ 1, 2, 3 }) is a fairly worthless thing. + if (!sema_analyse_expr(context, NULL, arg)) return false; + + // 11d. If it is allowed. + if (!expr_may_unpack_as_vararg(arg, variadic_type)) + { + SEMA_ERROR(arg, "It's not possible to unpack %s as vararg of type %s", + type_quoted_error_string(arg->type), + type_quoted_error_string(variadic_type)); + return false; + } + } + else + { + // 11e. A simple variadic value: + if (!sema_analyse_expr_of_required_type(context, variadic_type, arg, true)) return false; + } + // Set the argument at the location. + actual_args[i] = arg; + expr->failable |= arg->failable; + continue; + } + // 12. We might have a naked variadic argument + if (signature->variadic) + { + // 12a. Analyse the expression. + if (!sema_analyse_expr(context, NULL, arg)) return false; + + // 12b. In the case of a compile time variable we cast to c_int / double. + Type *arg_type = arg->type->canonical; + if (type_is_ct(arg_type)) + { + // 12c. Pick double / CInt + Type *target_type = type_is_any_integer(arg_type) ? type_c_int->canonical : type_double; + if (!cast_implicit(arg, target_type)) return false; + arg_type = target_type; + } + // 12d. Promote any integer or bool to at least CInt + if (type_is_promotable_integer(arg_type) || arg_type == type_bool) + { + cast(arg, type_c_int->canonical); + arg_type = type_c_int->canonical; + } + // 12e. Promote any float to at least Double + if (type_is_promotable_float(arg->type)) + { + cast(arg, type_double); + arg_type = type_double; + } + // Set the argument at the location. + actual_args[i] = arg; + expr->failable |= arg->failable; + continue; + } + // 13. We have too many parameters... + SEMA_ERROR(expr, "Too many parameters for this function."); + return false; + } + + // 14. Analyse a regular argument. if (!sema_analyse_expr_of_required_type(context, func_params[i]->type, arg, true)) return false; expr->failable |= arg->failable; actual_args[i] = arg; } + + // 15. Set default values. for (unsigned i = 0; i < entries_needed; i++) { + // 15a. Assigned a value - skip if (actual_args[i]) continue; + // 15b. Set the init expression. if (func_params[i]->var.init_expr) { + assert(func_params[i]->var.init_expr->resolve_status == RESOLVE_DONE); actual_args[i] = func_params[i]->var.init_expr; continue; } + // 15c. Vararg not set? That's fine. + if (func_params[i]->var.vararg) continue; + + // 15d. Argument missing, that's bad. SEMA_ERROR(expr, "Parameter '%s' was not set.", func_params[i]->name); return false; } + // 16. Set the type and failable. expr_set_type(expr, signature->rtype->type); expr->call_expr.arguments = actual_args; expr->failable |= signature->failable; @@ -935,7 +1061,7 @@ 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; - TokenId *parameter_list = decl->generic_decl.parameters; + 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)); @@ -1115,6 +1241,7 @@ 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; + param->alignment = type_abi_alignment(param->type ? param->type : arg->type); break; case VARDECL_PARAM_EXPR: // #foo