mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Splat/unsplat/typed varargs
This commit is contained in:
committed by
Christoffer Lerno
parent
12fbaf476b
commit
21d4ecf9b4
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 <type>[], so we get the <type>
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user