Support varargs for macros. Bugfix member access. Support macro varargs.

This commit is contained in:
Christoffer Lerno
2022-08-25 18:10:50 +02:00
committed by Christoffer Lerno
parent 52bcf4654a
commit 6b1370ba76
33 changed files with 1959 additions and 610 deletions

View File

@@ -46,7 +46,9 @@ typedef struct Expr_ Expr;
typedef struct Module_ Module;
typedef struct Type_ Type;
typedef Type CanonicalType;
typedef struct Signature_ Signature;
typedef struct ConstInitializer_ ConstInitializer;
typedef struct CompilationUnit_ CompilationUnit;
typedef unsigned AstId;
typedef unsigned ExprId;
typedef unsigned DeclId;
@@ -88,32 +90,32 @@ typedef enum
typedef struct ConstInitializer_
struct ConstInitializer_
{
ConstInitType kind;
// Type initialized
Type *type;
union
{
struct ConstInitializer_ **init_struct;
ConstInitializer **init_struct;
Expr *init_value;
struct
{
struct ConstInitializer_ *element;
ConstInitializer *element;
MemberIndex index;
} init_union;
struct
{
struct ConstInitializer_ **elements;
ConstInitializer **elements;
} init_array;
struct ConstInitializer_ **init_array_full;
ConstInitializer **init_array_full;
struct
{
struct ConstInitializer_ *element;
ConstInitializer *element;
MemberIndex index;
} init_array_value;
};
} ConstInitializer;
};
typedef struct
{
@@ -169,8 +171,6 @@ typedef union
static_assert(sizeof(SourceSpan) == 8, "Expected 8 bytes");
typedef struct
{
const char *key;
@@ -225,14 +225,15 @@ typedef struct
typedef struct
{
bool nodiscard : 1;
bool maydiscard: 1;
} FunctionAttributes;
bool maydiscard : 1;
bool is_pure : 1;
bool noreturn : 1;
} CalleeAttributes;
typedef struct
{
FunctionAttributes attrs;
Module *module;
Decl** params;
Signature *signature;
struct FunctionPrototype_ *prototype;
} TypeFunction;
@@ -443,36 +444,53 @@ typedef enum
typedef struct FunctionSignature_
struct Signature_
{
FunctionAttributes attrs;
CalleeAttributes attrs;
bool is_macro : 1;
bool is_at_macro : 1;
Variadic variadic : 3;
unsigned vararg_index : 10;
bool use_win64 : 1;
bool is_pure : 1;
CallABI abi : 8;
TypeInfoId returntype;
unsigned vararg_index;
TypeInfoId rtype;
Decl** params;
} FunctionSignature;
};
typedef struct
{
struct
{
bool attr_noreturn : 1;
bool attr_inline : 1;
bool attr_noinline : 1;
bool attr_extname : 1;
bool attr_naked : 1;
};
TypeInfoId type_parent;
FunctionSignature function_signature;
Signature signature;
AstId body;
AstId docs;
union
{
struct
{
bool attr_inline : 1;
bool attr_noinline : 1;
bool attr_extname : 1;
bool attr_naked : 1;
};
struct
{
DeclId body_param;
CompilationUnit *unit;
};
};
} FuncDecl;
typedef struct
{
TypeInfoId type_parent; // May be 0
Signature signature;
AstId body;
DeclId body_param;
CompilationUnit *unit;
AstId docs;
} MacroDecl;
typedef struct
{
Decl **params;
@@ -485,7 +503,7 @@ typedef struct
bool is_distinct : 1;
union
{
FunctionSignature function_signature;
Signature function_signature;
TypeInfo *type_info;
};
} TypedefDecl;
@@ -500,33 +518,7 @@ typedef struct
};
} DistinctDecl;
typedef struct
{
struct
{
unsigned vararg_index : 10;
Variadic variadic : 3;
bool attr_noreturn : 1;
bool attr_nodiscard : 1;
bool attr_maydiscard : 1;
};
TypeInfoId type_parent; // May be 0
TypeInfoId rtype; // May be 0
AstId body;
DeclId body_param;
Decl **parameters;
struct CompilationUnit_ *unit;
AstId docs;
} MacroDecl;
typedef struct
{
struct Ast_ **cases;
Decl **parameters;
Decl **body_parameters;
TypeInfoId rtype; // May be 0!
Path *path; // For redefinition
} GenericDecl;
typedef enum
{
@@ -645,8 +637,6 @@ typedef struct Decl_
FuncDecl func_decl;
AttrDecl attr_decl;
TypedefDecl typedef_decl;
MacroDecl macro_decl;
GenericDecl generic_decl;
DefineDecl define_decl;
CtIfDecl ct_if_decl;
CtIfDecl ct_elif_decl;
@@ -845,7 +835,7 @@ typedef struct
typedef struct
{
TokenType type : 8;
TokenType type : 16;
ExprId arg;
} ExprCtArg;
@@ -883,7 +873,6 @@ typedef struct
typedef struct
{
AstId first_stmt;
Expr **args;
Decl **params;
BlockExit **block_exit;
} ExprMacroBlock;
@@ -1418,7 +1407,7 @@ typedef struct
DeclId *entries;
} DeclTable;
typedef struct CompilationUnit_
struct CompilationUnit_
{
Module *module;
File* file;
@@ -1449,7 +1438,7 @@ typedef struct CompilationUnit_
void *debug_file;
void *debug_compile_unit;
} llvm;
} CompilationUnit;
};
typedef struct ParseContext_
{
@@ -1494,6 +1483,7 @@ typedef struct SemaContext_
// Reusable returns cache.
Ast **returns_cache;
Expr **macro_varargs;
Decl **macro_params;
};
Type *rtype;
struct SemaContext_ *yield_context;
@@ -2137,7 +2127,7 @@ void type_func_prototype_init(uint32_t capacity);
bool type_is_subtype(Type *type, Type *possible_subtype);
bool type_is_abi_aggregate(Type *type);
bool type_is_int128(Type *type);
Type *type_get_func(FunctionSignature *signature, CallABI abi);
Type *type_get_func(Signature *signature, CallABI abi);
Type *type_from_token(TokenType type);
bool type_is_user_defined(Type *type);
bool type_is_structurally_equivalent(Type *type1, Type *type);

View File

@@ -175,7 +175,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
break;
case DECL_GENERIC:
assert(decl->name);
if (decl->macro_decl.type_parent)
if (decl->func_decl.type_parent)
{
vec_add(unit->generic_methods, decl);
return;
@@ -189,7 +189,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
break;
case DECL_MACRO:
assert(decl->name);
if (decl->macro_decl.type_parent)
if (decl->func_decl.type_parent)
{
vec_add(unit->macro_methods, decl);
return;

View File

@@ -545,6 +545,7 @@ TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source)
return copy;
case TYPE_INFO_EVALTYPE:
case TYPE_INFO_EXPRESSION:
case TYPE_INFO_VAARG_TYPE:
assert(source->resolve_status == RESOLVE_NOT_DONE);
copy->unresolved_type_expr = copy_expr(c, source->unresolved_type_expr);
return copy;
@@ -566,10 +567,10 @@ TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source)
UNREACHABLE
}
static void copy_function_signature_deep(CopyStruct *c, FunctionSignature *signature)
static void copy_signature_deep(CopyStruct *c, Signature *signature)
{
MACRO_COPY_DECL_LIST(signature->params);
MACRO_COPY_TYPEID(signature->returntype);
MACRO_COPY_TYPEID(signature->rtype);
}
void copy_decl_type(Decl *decl)
{
@@ -647,7 +648,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
copy_decl_type(copy);
MACRO_COPY_TYPEID(copy->func_decl.type_parent);
MACRO_COPY_ASTID(copy->func_decl.docs);
copy_function_signature_deep(c, &copy->func_decl.function_signature);
copy_signature_deep(c, &copy->func_decl.signature);
MACRO_COPY_ASTID(copy->func_decl.body);
break;
case DECL_VAR:
@@ -673,7 +674,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
copy_decl_type(copy);
if (copy->typedef_decl.is_func)
{
copy_function_signature_deep(c, &copy->typedef_decl.function_signature);
copy_signature_deep(c, &copy->typedef_decl.function_signature);
break;
}
MACRO_COPY_TYPE(copy->typedef_decl.type_info);
@@ -683,7 +684,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
MACRO_COPY_DECL_LIST(copy->methods);
if (copy->distinct_decl.typedef_decl.is_func)
{
copy_function_signature_deep(c, &copy->distinct_decl.typedef_decl.function_signature);
copy_signature_deep(c, &copy->distinct_decl.typedef_decl.function_signature);
break;
}
MACRO_COPY_TYPE(copy->distinct_decl.typedef_decl.type_info);
@@ -708,12 +709,11 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
break;
case DECL_GENERIC:
case DECL_MACRO:
MACRO_COPY_ASTID(copy->macro_decl.docs);
MACRO_COPY_TYPEID(decl->macro_decl.type_parent);
MACRO_COPY_DECL_LIST(decl->macro_decl.parameters);
MACRO_COPY_ASTID(decl->macro_decl.body);
MACRO_COPY_TYPEID(decl->macro_decl.rtype);
MACRO_COPY_DECLID(decl->macro_decl.body_param);
MACRO_COPY_ASTID(copy->func_decl.docs);
MACRO_COPY_TYPEID(decl->func_decl.type_parent);
copy_signature_deep(c, &copy->func_decl.signature);
MACRO_COPY_ASTID(decl->func_decl.body);
MACRO_COPY_DECLID(decl->func_decl.body_param);
break;
case DECL_CT_SWITCH:
MACRO_COPY_DECL_LIST(decl->ct_switch_decl.cases);

View File

@@ -324,6 +324,7 @@ typedef enum
TYPE_INFO_IDENTIFIER,
TYPE_INFO_CT_IDENTIFIER,
TYPE_INFO_EXPRESSION,
TYPE_INFO_VAARG_TYPE,
TYPE_INFO_EVALTYPE,
TYPE_INFO_ARRAY,
TYPE_INFO_VECTOR,
@@ -554,7 +555,8 @@ typedef enum
case TOKEN_FLOAT128: case TOKEN_TYPEID: case TOKEN_ANYERR: case TOKEN_VARIANT
#define TYPE_TOKENS NON_VOID_TYPE_TOKENS: case TOKEN_VOID
#define TYPELIKE_TOKENS TYPE_TOKENS: case TOKEN_TYPE_IDENT: \
case TOKEN_CT_TYPE_IDENT: case TOKEN_CT_TYPEOF
case TOKEN_CT_TYPE_IDENT: case TOKEN_CT_TYPEOF: case TOKEN_CT_EVALTYPE: \
case TOKEN_CT_VAARG_GET_TYPE
// Note that ordering matters here. If ordering is changed,
// So must type_find_max_type and friends.

View File

@@ -321,10 +321,11 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl)
LLVMValueRef init_value;
Type *var_type = type_lowering(decl->type);
AlignSize alignment = type_alloca_alignment(var_type);
Expr *init_expr = decl->var.init_expr;
if (init_expr && init_expr->expr_kind == EXPR_IDENTIFIER && init_expr->identifier_expr.is_const)
while (init_expr && init_expr->expr_kind == EXPR_IDENTIFIER
&& init_expr->identifier_expr.decl->decl_kind == DECL_VAR
&& init_expr->identifier_expr.decl->var.kind == VARDECL_CONST)
{
init_expr = init_expr->identifier_expr.decl->var.init_expr;
}

View File

@@ -124,7 +124,7 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl)
UNREACHABLE
}
flags |= LLVMDIFlagPrototyped;
if (decl->func_decl.attr_noreturn) flags |= LLVMDIFlagNoReturn;
if (decl->func_decl.signature.attrs.noreturn) flags |= LLVMDIFlagNoReturn;
uint32_t row = decl->span.row;
if (!row) row = 1;

View File

@@ -5163,11 +5163,12 @@ static inline void llvm_emit_expr_block(GenContext *context, BEValue *be_value,
static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, Expr *expr)
{
VECEACH(expr->macro_block.params, i)
foreach(Decl *, expr->macro_block.params)
{
// Skip vararg
if (!val) continue;
// In case we have a constant, we never do an emit. The value is already folded.
Decl *decl = expr->macro_block.params[i];
switch (decl->var.kind)
switch (val->var.kind)
{
case VARDECL_CONST:
case VARDECL_GLOBAL:
@@ -5183,8 +5184,8 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value,
case VARDECL_PARAM_REF:
{
BEValue addr;
llvm_emit_expr(context, &addr, decl->var.init_expr);
decl->backend_ref = addr.value;
llvm_emit_expr(context, &addr, val->var.init_expr);
val->backend_ref = addr.value;
continue;
}
case VARDECL_PARAM_CT:
@@ -5194,11 +5195,11 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value,
case VARDECL_PARAM:
break;
}
llvm_emit_and_set_decl_alloca(context, decl);
llvm_emit_and_set_decl_alloca(context, val);
BEValue value;
llvm_emit_expr(context, &value, expr->macro_block.args[i]);
llvm_store_decl(context, decl, &value);
llvm_emit_expr(context, &value, val->var.init_expr);
llvm_store_decl(context, val, &value);
}
llvm_emit_return_block(context, be_value, expr->type, expr->macro_block.first_stmt, expr->macro_block.block_exit);

View File

@@ -506,9 +506,9 @@ void llvm_emit_function_body(GenContext *c, Decl *decl)
if (!decl->func_decl.attr_naked)
{
// Generate LLVMValueRef's for all parameters, so we can use them as local vars in code
VECEACH(decl->func_decl.function_signature.params, i)
VECEACH(decl->func_decl.signature.params, i)
{
llvm_emit_parameter(c, decl->func_decl.function_signature.params[i], prototype->abi_args[i], &arg, i);
llvm_emit_parameter(c, decl->func_decl.signature.params[i], prototype->abi_args[i], &arg, i);
}
}
@@ -633,7 +633,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl)
{
llvm_attribute_add(c, function, attribute_id.noinline, -1);
}
if (decl->func_decl.attr_noreturn)
if (decl->func_decl.signature.attrs.noreturn)
{
llvm_attribute_add(c, function, attribute_id.noreturn, -1);
}

View File

@@ -912,11 +912,13 @@ static Expr *parse_ct_call(ParseContext *c, Expr *left)
return expr;
}
static Expr *parse_ct_arg(ParseContext *c, Expr *left)
{
assert(!left && "Unexpected left hand side");
Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_ARG);
TokenType type = expr->ct_arg_expr.type = c->tok;
assert(type != TOKEN_CT_VAARG_GET_TYPE);
advance(c);
CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr);
if (type != TOKEN_CT_VAARG_COUNT)
@@ -1753,5 +1755,10 @@ ParseRule rules[TOKEN_EOF + 1] = {
[TOKEN_CT_CONVERTIBLE] = { parse_ct_conv, NULL, PREC_NONE },
[TOKEN_CT_CASTABLE] = { parse_ct_conv, NULL, PREC_NONE },
[TOKEN_LBRACE] = { parse_initializer_list, NULL, PREC_NONE },
[TOKEN_CT_VAARG_COUNT] = { parse_ct_arg, NULL, PREC_NONE }
[TOKEN_CT_VAARG_COUNT] = { parse_ct_arg, NULL, PREC_NONE },
[TOKEN_CT_VAARG_GET_ARG] = { parse_ct_arg, NULL, PREC_NONE },
[TOKEN_CT_VAARG_GET_REF] = { parse_ct_arg, NULL, PREC_NONE },
[TOKEN_CT_VAARG_GET_TYPE] = { parse_type_expr, NULL, PREC_NONE },
[TOKEN_CT_VAARG_GET_EXPR] = { parse_ct_arg, NULL, PREC_NONE },
[TOKEN_CT_VAARG_GET_CONST] = { parse_ct_arg, NULL, PREC_NONE },
};

View File

@@ -491,6 +491,15 @@ static inline TypeInfo *parse_base_type(ParseContext *c)
RANGE_EXTEND_PREV(type_info);
return type_info;
}
if (try_consume(c, TOKEN_CT_VAARG_GET_TYPE))
{
TypeInfo *type_info = type_info_new(TYPE_INFO_VAARG_TYPE, c->prev_span);
CONSUME_OR_RET(TOKEN_LPAREN, poisoned_type_info);
ASSIGN_EXPR_OR_RET(type_info->unresolved_type_expr, parse_expr(c), poisoned_type_info);
CONSUME_OR_RET(TOKEN_RPAREN, poisoned_type_info);
RANGE_EXTEND_PREV(type_info);
return type_info;
}
if (try_consume(c, TOKEN_CT_EVALTYPE))
{
TypeInfo *type_info = type_info_new(TYPE_INFO_EVALTYPE, c->prev_span);
@@ -1066,11 +1075,10 @@ INLINE bool is_end_of_param_list(ParseContext *c)
* | ELLIPSIS (CT_TYPE_IDENT | non_type_ident ('=' expr)?)?
*/
bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref, Decl **body_params,
Variadic *variadic, unsigned *vararg_index_ref)
Variadic *variadic, int *vararg_index_ref)
{
Decl** params = NULL;
bool var_arg_found = false;
while (!is_end_of_param_list(c))
{
bool ellipsis = try_consume(c, TOKEN_ELLIPSIS);
@@ -1280,16 +1288,15 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref
*
* parameter_type_list ::= '(' parameters ')'
*/
static inline bool parse_fn_parameter_list(ParseContext *c, Visibility parent_visibility, FunctionSignature *signature, bool is_interface)
static inline bool parse_fn_parameter_list(ParseContext *c, Visibility parent_visibility, Signature *signature, bool is_interface)
{
Decl **decls = NULL;
CONSUME_OR_RET(TOKEN_LPAREN, false);
Variadic variadic = VARIADIC_NONE;
unsigned vararg_index = 0;
int vararg_index = -1;
if (!parse_parameters(c, parent_visibility, &decls, NULL, &variadic, &vararg_index)) return false;
CONSUME_OR_RET(TOKEN_RPAREN, false);
signature->vararg_index = vararg_index;
signature->vararg_index = vararg_index < 0 ? vec_size(decls) : vararg_index;
signature->params = decls;
signature->variadic = variadic;
@@ -1529,12 +1536,12 @@ static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl *
// Parse the regular parameters.
Variadic variadic = VARIADIC_NONE;
unsigned vararg_index = 0;
int vararg_index = -1;
Decl **params = NULL;
if (!parse_parameters(c, visibility, &params, NULL, &variadic, &vararg_index)) return false;
macro->macro_decl.parameters = params;
macro->macro_decl.vararg_index = vararg_index;
macro->macro_decl.variadic = variadic;
macro->func_decl.signature.params = params;
macro->func_decl.signature.vararg_index = vararg_index < 0 ? vec_size(params) : vararg_index;
macro->func_decl.signature.variadic = variadic;
// Do we have trailing block parameters?
if (try_consume(c, TOKEN_EOS))
@@ -1547,11 +1554,11 @@ static bool parse_macro_arguments(ParseContext *c, Visibility visibility, Decl *
if (!parse_parameters(c, visibility, &body_param->body_params, NULL, NULL, NULL)) return false;
CONSUME_OR_RET(TOKEN_RPAREN, false);
}
macro->macro_decl.body_param = declid(body_param);
macro->func_decl.body_param = declid(body_param);
}
else
{
macro->macro_decl.body_param = 0;
macro->func_decl.body_param = 0;
}
CONSUME_OR_RET(TOKEN_RPAREN, false);
return true;
@@ -1606,7 +1613,7 @@ static inline Decl *parse_define_type(ParseContext *c, Visibility visibility)
decl->typedef_decl.is_func = true;
decl->typedef_decl.is_distinct = distinct;
ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_optional_type(c), poisoned_decl);
decl->typedef_decl.function_signature.returntype = type_infoid(type_info);
decl->typedef_decl.function_signature.rtype = type_infoid(type_info);
if (!parse_fn_parameter_list(c, decl->visibility, &(decl->typedef_decl.function_signature), true))
{
return poisoned_decl;
@@ -1794,13 +1801,13 @@ static inline bool parse_is_macro_name(ParseContext *c)
* func_header ::= type '!'? (type '.')? (IDENT | MACRO_IDENT)
* macro_header ::= (type '!'?)? (type '.')? (IDENT | MACRO_IDENT)
*/
static inline bool parse_func_macro_header(ParseContext *c, bool is_macro,
TypeInfoId *rtype_ref, TypeInfoId *method_type_ref,
const char **name_ref, SourceSpan *name_span)
static inline bool parse_func_macro_header(ParseContext *c, Decl *decl)
{
TypeInfo *rtype = NULL;
TypeInfo *method_type = NULL;
bool is_macro = decl->decl_kind == DECL_MACRO;
// 1. If we have a macro and see the name, we're done.
if (is_macro && parse_is_macro_name(c))
{
@@ -1839,8 +1846,8 @@ static inline bool parse_func_macro_header(ParseContext *c, bool is_macro,
return false;
}
RESULT:
*name_ref = symstr(c);
*name_span = c->span;
decl->name = symstr(c);
decl->span = c->span;
if (is_macro && c->tok != TOKEN_IDENT && c->tok != TOKEN_AT_IDENT)
{
sema_error_at(c->span, "Expected a macro name here, e.g. '@someName' or 'someName'.");
@@ -1852,8 +1859,10 @@ static inline bool parse_func_macro_header(ParseContext *c, bool is_macro,
return false;
}
advance(c);
*rtype_ref = rtype ? type_infoid(rtype) : 0;
*method_type_ref = method_type ? type_infoid(method_type) : 0;
decl->func_decl.signature.rtype = rtype ? type_infoid(rtype) : 0;
decl->func_decl.signature.is_macro = is_macro;
decl->func_decl.signature.is_at_macro = decl->name[0] == '@';
decl->func_decl.type_parent = method_type ? type_infoid(method_type) : 0;
return true;
}
@@ -1869,15 +1878,13 @@ static inline Decl *parse_macro_declaration(ParseContext *c, Visibility visibili
Decl *decl = decl_calloc();
decl->decl_kind = kind;
decl->visibility = visibility;
decl->macro_decl.docs = docs;
TypeInfoId *rtype_ref = &decl->macro_decl.rtype;
TypeInfoId *method_type_ref = &decl->macro_decl.type_parent;
if (!parse_func_macro_header(c, true, rtype_ref, method_type_ref, &decl->name, &decl->span)) return poisoned_decl;
decl->func_decl.docs = docs;
if (!parse_func_macro_header(c, decl)) return poisoned_decl;
const char *block_parameter = NULL;
if (!parse_macro_arguments(c, visibility, decl)) return poisoned_decl;
if (!parse_attributes(c, &decl->attributes)) return poisoned_decl;
ASSIGN_ASTID_OR_RET(decl->macro_decl.body, parse_stmt(c), poisoned_decl);
ASSIGN_ASTID_OR_RET(decl->func_decl.body, parse_stmt(c), poisoned_decl);
return decl;
}
@@ -2073,15 +2080,13 @@ static inline Decl *parse_func_definition(ParseContext *c, Visibility visibility
func->decl_kind = DECL_FUNC;
func->visibility = visibility;
func->func_decl.docs = docs;
TypeInfoId *rtype_id_ref = &func->func_decl.function_signature.returntype;
TypeInfoId *method_type_ref = &func->func_decl.type_parent;
if (!parse_func_macro_header(c, false, rtype_id_ref, method_type_ref, &func->name, &func->span)) return poisoned_decl;
if (!parse_func_macro_header(c, func)) return poisoned_decl;
if (func->name[0] == '@')
{
SEMA_ERROR(func, "Function names may not use '@'.");
return false;
}
if (!parse_fn_parameter_list(c, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl;
if (!parse_fn_parameter_list(c, visibility, &(func->func_decl.signature), is_interface)) return poisoned_decl;
if (!parse_attributes(c, &func->attributes)) return poisoned_decl;
// TODO remove

View File

@@ -856,7 +856,6 @@ Ast *parse_stmt(ParseContext *c)
case TOKEN_HASH_IDENT:
case TOKEN_IDENT:
case TOKEN_CONST_IDENT:
case TOKEN_CT_EVALTYPE:
return parse_decl_or_expr_stmt(c);
case TOKEN_VAR:
return parse_var_stmt(c);
@@ -964,7 +963,6 @@ Ast *parse_stmt(ParseContext *c)
case TOKEN_CT_VAARG_GET_EXPR:
case TOKEN_CT_VAARG_GET_CONST:
case TOKEN_CT_VAARG_GET_REF:
case TOKEN_CT_VAARG_GET_TYPE:
return parse_expr_stmt(c);
case TOKEN_ASSERT:
return parse_assert_stmt(c);

View File

@@ -46,7 +46,7 @@ Decl *parse_var_decl(ParseContext *c);
bool
parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref, Decl **body_params, Variadic *variadic,
unsigned *vararg_index_ref);
int *vararg_index_ref);
bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat);
Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info);

View File

@@ -69,7 +69,7 @@ static inline bool sema_check_param_uniqueness_and_type(Decl **decls, Decl *curr
{
if (name == decls[i]->name)
{
SEMA_ERROR(current, "Duplicate parameter name %s.", name);
SEMA_ERROR(current, "Duplicate parameter name '%s'.", name);
SEMA_NOTE(decls[i], "Previous use of the name was here.");
decl_poison(decls[i]);
decl_poison(current);
@@ -581,51 +581,40 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl)
}
static inline bool sema_analyse_function_param(SemaContext *context, Decl *param)
static inline bool sema_analyse_signature(SemaContext *context, Signature *sig)
{
param->unit = context->unit;
assert(param->decl_kind == DECL_VAR);
// We need to check that the parameters are not typeless nor are of any macro parameter kind.
if (param->var.kind != VARDECL_PARAM)
{
SEMA_ERROR(param, "Only regular parameters are allowed for functions.");
return false;
}
Variadic variadic_type = sig->variadic;
Decl **params = sig->params;
unsigned param_count = vec_size(params);
unsigned vararg_index = sig->vararg_index;
bool is_fn = !sig->is_macro;
bool is_macro_at_name = sig->is_at_macro;
if (!sema_analyse_attributes_for_var(context, param)) return false;
if (!param->var.type_info)
// Check return type
assert(sig->rtype || sig->is_macro);
Type *rtype = NULL;
if (sig->rtype)
{
SEMA_ERROR(param, "Only typed parameters are allowed for functions.");
return false;
}
if (!sema_resolve_type_info(context, param->var.type_info)) 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)
{
Expr *expr = param->var.init_expr;
if (expr->expr_kind == EXPR_CONST)
TypeInfo *rtype_info = type_infoptr(sig->rtype);
if (!sema_resolve_type_info(context, type_infoptr(sig->rtype))) return false;
rtype = rtype_info->type;
if (sig->attrs.nodiscard)
{
if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return false;
if (rtype == type_void)
{
SEMA_ERROR(rtype_info, "@nodiscard cannot be used on %s returning 'void'.", is_fn ? "functions" : "macros");
return false;
}
}
if (sig->attrs.maydiscard)
{
if (!type_is_optional(rtype))
{
SEMA_ERROR(rtype_info, "@maydiscard can only be used on %s returning optional values.", is_fn ? "functions" : "macros");
return false;
}
}
}
param->alignment = type_abi_alignment(param->type);
return true;
}
static inline Type *sema_analyse_function_signature(SemaContext *context, Decl *parent, CallABI abi, FunctionSignature *signature, bool is_real_function)
{
// Get param count and variadic type
Variadic variadic_type = signature->variadic;
Decl **params = signature->params;
unsigned param_count = vec_size(params);
bool all_ok = sema_resolve_type_info(context, type_infoptr(signature->returntype));
// We don't support more than MAX_PARAMS number of params. This makes everything sane.
if (param_count > MAX_PARAMS)
@@ -633,39 +622,28 @@ static inline Type *sema_analyse_function_signature(SemaContext *context, Decl *
if (variadic_type != VARIADIC_NONE)
{
SEMA_ERROR(params[MAX_PARAMS], "The number of params exceeded the max of %d.", MAX_PARAMS);
return NULL;
return false;
}
SEMA_ERROR(params[MAX_PARAMS], "The number of params exceeded the max of %d. To accept more arguments, consider using varargs.", MAX_PARAMS);
return NULL;
return false;
}
Type **types = NULL;
int vararg_index = -1;
// Check parameters
for (unsigned i = 0; i < param_count; i++)
{
Decl *param = params[i];
// We might run into a raw vararg
if (!param)
{
assert(variadic_type == VARIADIC_RAW);
vararg_index = i;
assert(i == vararg_index);
continue;
}
assert(param->resolve_status == RESOLVE_NOT_DONE && "The param shouldn't have been resolved yet.");
param->resolve_status = RESOLVE_RUNNING;
// Analyse the param
if (!sema_analyse_function_param(context, param))
if (vararg_index < i)
{
decl_poison(param);
all_ok = false;
continue;
}
if (vararg_index > -1)
{
if (variadic_type == VARIADIC_RAW)
if (is_fn && variadic_type == VARIADIC_RAW)
{
SEMA_ERROR(param, "C-style varargs cannot be followed by regular parameters.");
return NULL;
return decl_poison(param);
}
// If we already reached a vararg (as a previous argument) and we have a
@@ -673,48 +651,149 @@ static inline Type *sema_analyse_function_signature(SemaContext *context, Decl *
if (!param->name)
{
SEMA_ERROR(param, "A parameter name was expected, as parameters after varargs must always be named.");
decl_poison(param);
all_ok = false;
continue;
return decl_poison(param);
}
}
// Disallow "void" args and repeating the same parameter name.
// This must be done after checking raw varargs
if (!sema_check_param_uniqueness_and_type(params, param, i, param_count))
assert(param->resolve_status == RESOLVE_NOT_DONE && "The param shouldn't have been resolved yet.");
param->resolve_status = RESOLVE_RUNNING;
param->unit = context->unit;
assert(param->decl_kind == DECL_VAR);
VarDeclKind var_kind = param->var.kind;
switch (var_kind)
{
all_ok = false;
case VARDECL_PARAM_EXPR:
case VARDECL_PARAM_REF:
if (is_fn)
{
SEMA_ERROR(param, "Only regular parameters are allowed for functions.");
return decl_poison(param);
}
if (!is_macro_at_name)
{
SEMA_ERROR(param, "Ref and expression parameters are not allowed in function-like macros. Prefix the macro name with '@'.");
return decl_poison(param);
}
FALLTHROUGH;
case VARDECL_PARAM_CT:
if (is_fn)
{
SEMA_ERROR(param, "Only regular parameters are allowed for functions.");
return decl_poison(param);
}
FALLTHROUGH;
case VARDECL_PARAM:
if (param->var.type_info)
{
if (!sema_resolve_type_info(context, param->var.type_info)) return decl_poison(param);
param->type = param->var.type_info->type;
}
else if (is_fn)
{
SEMA_ERROR(param, "Only typed parameters are allowed for functions.");
return false;
}
if (!sema_analyse_attributes_for_var(context, param)) return false;
break;
case VARDECL_PARAM_CT_TYPE:
if (is_fn)
{
SEMA_ERROR(param, "Only regular parameters are allowed for functions.");
return decl_poison(param);
}
if (param->var.type_info)
{
SEMA_ERROR(param->var.type_info, "A compile time type parameter cannot have a type itself.");
return decl_poison(param);
}
break;
case VARDECL_CONST:
case VARDECL_GLOBAL:
case VARDECL_LOCAL:
case VARDECL_MEMBER:
case VARDECL_BITMEMBER:
case VARDECL_LOCAL_CT:
case VARDECL_LOCAL_CT_TYPE:
case VARDECL_UNWRAPPED:
case VARDECL_REWRAPPED:
case VARDECL_ERASE:
UNREACHABLE
}
// Update whether this was a vararg, and update "default" for the signature.
if (param->var.vararg)
{
if (vararg_index > -1)
if (var_kind != VARDECL_PARAM)
{
SEMA_ERROR(param, "A function may not have more than one vararg.");
all_ok = false;
SEMA_ERROR(param, "Only regular parameters may be vararg.");
return decl_poison(param);
}
vararg_index = i;
if (!param->var.type_info)
{
SEMA_ERROR(param, "Only typed parameters may be vararg.");
return decl_poison(param);
}
// Update whether this was a vararg, and update "default" for the signature.
if (param->var.vararg)
{
if (vararg_index != i)
{
SEMA_ERROR(param, "A %s may not have more than one vararg.", is_fn ? "function" : "macro");
return decl_poison(param);
}
}
param->var.type_info->type = type_get_subarray(param->var.type_info->type);
}
// Resolution is done.
if (param->var.type_info)
{
param->type = param->var.type_info->type;
param->alignment = type_abi_alignment(param->type);
}
if (param->var.init_expr)
{
Expr *expr = param->var.init_expr;
if (expr->expr_kind == EXPR_CONST)
{
if (!sema_analyse_expr_rhs(context, param->type, expr, true)) return decl_poison(param);
}
}
if (!sema_check_param_uniqueness_and_type(params, param, i, param_count)) return decl_poison(param);
param->resolve_status = RESOLVE_DONE;
// Add it to the type for the type signature.
vec_add(types, param->type);
}
return true;
}
static inline Type *sema_analyse_function_signature(SemaContext *context, Decl *parent, CallABI abi, Signature *signature, bool is_real_function)
{
// Get param count and variadic type
Decl **params = signature->params;
if (!sema_analyse_signature(context, signature)) return NULL;
Variadic variadic_type = signature->variadic;
// Remove the last empty value.
if (variadic_type == VARIADIC_RAW)
{
assert(vararg_index >= 0);
assert(!params[vararg_index] && "The last parameter must have been a raw variadic.");
assert(vararg_index == param_count - 1);
assert(!params[signature->vararg_index] && "The last parameter must have been a raw variadic.");
assert(signature->vararg_index == vec_size(params) - 1);
vec_pop(params);
}
Type **types = NULL;
bool all_ok = true;
unsigned param_count = vec_size(params);
for (unsigned i = 0; i < param_count; i++)
{
vec_add(types, params[i]->type);
}
if (!all_ok) return NULL;
Type *raw_type = type_get_func(signature, abi);
Type *type = type_new(TYPE_FUNC, parent->name);
type->canonical = type;
type->function.attrs = signature->attrs;
type->function.signature = signature;
type->function.module = parent->unit->module;
type->function.params = signature->params;
type->function.prototype = raw_type->function.prototype;
return type;
}
@@ -1007,33 +1086,11 @@ static inline const char *name_by_decl(Decl *method_like)
}
static inline void find_operator_parameters(Decl *method, TypeInfoId *rtype_ptr, Decl ***params_ptr)
{
switch (method->decl_kind)
{
case DECL_GENERIC:
*params_ptr = method->generic_decl.parameters;
*rtype_ptr = method->generic_decl.rtype;
break;
case DECL_MACRO:
*params_ptr = method->macro_decl.parameters;
*rtype_ptr = method->macro_decl.rtype;
break;
case DECL_FUNC:
*params_ptr = method->func_decl.function_signature.params;
*rtype_ptr = method->func_decl.function_signature.returntype;
break;
default:
UNREACHABLE
}
}
static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Decl ***params_ptr, uint32_t parameters)
{
TypeInfoId rtype_id;
find_operator_parameters(method, &rtype_id, params_ptr);
Decl **params = *params_ptr;
Signature *signature = &method->func_decl.signature;
Decl **params = *params_ptr = signature->params;
uint32_t param_count = vec_size(params);
if (param_count > parameters)
{
@@ -1046,7 +1103,7 @@ static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Dec
return false;
}
if (!rtype_id)
if (!signature->rtype)
{
SEMA_ERROR(method, "The return value must be explicitly typed for '%s'.", method->name);
return false;
@@ -1060,7 +1117,7 @@ static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Dec
return false;
}
}
*rtype_ptr = type_infoptr(rtype_id);
*rtype_ptr = type_infoptr(signature->rtype);
return true;
}
@@ -1233,7 +1290,7 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl)
type_to_error_string(parent_type->type));
return false;
}
Decl **params = decl->func_decl.function_signature.params;
Decl **params = decl->func_decl.signature.params;
if (!vec_size(params))
{
SEMA_ERROR(decl, "A method must start with a parameter of type %s or %s.",
@@ -1330,21 +1387,21 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr,
switch (type)
{
case ATTRIBUTE_CDECL:
decl->func_decl.function_signature.abi = CALL_C;
decl->func_decl.signature.abi = CALL_C;
break;
case ATTRIBUTE_VECCALL:
switch (platform_target.arch)
{
case ARCH_TYPE_X86_64:
case ARCH_TYPE_X86:
decl->func_decl.function_signature.abi = CALL_X86_VECTOR;
decl->func_decl.signature.abi = CALL_X86_VECTOR;
break;
case ARCH_TYPE_ARM:
case ARCH_TYPE_ARMB:
case ARCH_TYPE_AARCH64:
case ARCH_TYPE_AARCH64_32:
case ARCH_TYPE_AARCH64_BE:
decl->func_decl.function_signature.abi = CALL_AAPCS_VFP;
decl->func_decl.signature.abi = CALL_AAPCS_VFP;
break;
default:
break;
@@ -1354,25 +1411,25 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr,
assert(decl->decl_kind == DECL_FUNC);
if (platform_target.arch == ARCH_TYPE_X86 || platform_target.arch == ARCH_TYPE_X86_64)
{
decl->func_decl.function_signature.abi = CALL_X86_STD;
decl->func_decl.signature.abi = CALL_X86_STD;
}
else if (platform_target.arch == ARCH_TYPE_ARM || platform_target.arch == ARCH_TYPE_ARMB)
{
decl->func_decl.function_signature.abi = CALL_AAPCS;
decl->func_decl.signature.abi = CALL_AAPCS;
}
break; // Check args
case ATTRIBUTE_FASTCALL:
if (platform_target.arch == ARCH_TYPE_X86 ||
(platform_target.arch == ARCH_TYPE_X86_64 && platform_target.os == OS_TYPE_WIN32))
{
decl->func_decl.function_signature.abi = CALL_X86_FAST;
decl->func_decl.signature.abi = CALL_X86_FAST;
}
break;
case ATTRIBUTE_REGCALL:
if (platform_target.arch == ARCH_TYPE_X86 ||
(platform_target.arch == ARCH_TYPE_X86_64 && platform_target.os == OS_TYPE_WIN32))
{
decl->func_decl.function_signature.abi = CALL_X86_REG;
decl->func_decl.signature.abi = CALL_X86_REG;
}
break;
case ATTRIBUTE_OPERATOR:
@@ -1476,33 +1533,17 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr,
decl->func_decl.attr_inline = false;
break;
case ATTRIBUTE_NODISCARD:
if (domain == ATTR_MACRO)
{
decl->macro_decl.attr_nodiscard = true;
break;
}
decl->func_decl.function_signature.attrs.nodiscard = true;
decl->func_decl.signature.attrs.nodiscard = true;
break;
case ATTRIBUTE_MAYDISCARD:
if (domain == ATTR_MACRO)
{
decl->macro_decl.attr_maydiscard = true;
break;
}
decl->func_decl.function_signature.attrs.maydiscard = true;
decl->func_decl.signature.attrs.maydiscard = true;
break;
case ATTRIBUTE_INLINE:
decl->func_decl.attr_inline = true;
decl->func_decl.attr_noinline = false;
break;
case ATTRIBUTE_NORETURN:
if (domain == ATTR_MACRO)
{
decl->macro_decl.attr_noreturn = true;
break;
}
assert(domain == ATTR_FUNC);
decl->func_decl.attr_noreturn = true;
decl->func_decl.signature.attrs.noreturn = true;
break;
case ATTRIBUTE_WEAK:
decl->is_weak = true;
@@ -1736,8 +1777,8 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl)
SEMA_ERROR(decl, "A main function may not have local visibility.");
return false;
}
FunctionSignature *signature = &decl->func_decl.function_signature;
TypeInfo *rtype_info = type_infoptr(signature->returntype);
Signature *signature = &decl->func_decl.signature;
TypeInfo *rtype_info = type_infoptr(signature->rtype);
Type *rtype = type_flatten_distinct(rtype_info->type);
bool is_int_return = true;
bool is_err_return = false;
@@ -1807,13 +1848,14 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl)
function->unit = decl->unit;
function->extname = kw_main;
function->has_extname = true;
function->func_decl.function_signature.returntype = type_infoid(type_info_new_base(type_cint, decl->span));
function->func_decl.signature.rtype = type_infoid(type_info_new_base(type_cint, decl->span));
function->func_decl.signature.vararg_index = 2;
Decl *param1 = decl_new_generated_var(type_cint, VARDECL_PARAM, decl->span);
Decl *param2 = decl_new_generated_var(type_get_ptr(type_get_ptr(type_char)), VARDECL_PARAM, decl->span);
Decl **main_params = NULL;
vec_add(main_params, param1);
vec_add(main_params, param2);
function->func_decl.function_signature.params = main_params;
function->func_decl.signature.params = main_params;
Ast *body = new_ast(AST_COMPOUND_STMT, decl->span);
AstId *next = &body->compound_stmt.first_stmt;
Ast *ret_stmt = new_ast(AST_RETURN_STMT, decl->span);
@@ -1877,19 +1919,24 @@ REGISTER_MAIN:
return true;
}
static inline bool sema_analyse_func_macro(SemaContext *context, Decl *decl, bool is_func)
{
if (!sema_analyse_attributes(context, decl, decl->attributes, is_func ? ATTR_FUNC : ATTR_MACRO)) return decl_poison(decl);
return true;
}
static inline bool sema_analyse_func(SemaContext *context, Decl *decl)
{
DEBUG_LOG("----Analysing function %s", decl->name);
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_FUNC)) return decl_poison(decl);
if (!sema_analyse_func_macro(context, decl, true)) return false;
Type *func_type = sema_analyse_function_signature(context, decl, decl->func_decl.function_signature.abi, &decl->func_decl.function_signature, true);
Type *func_type = sema_analyse_function_signature(context, decl, decl->func_decl.signature.abi, &decl->func_decl.signature, true);
decl->type = func_type;
if (!func_type) return decl_poison(decl);
TypeInfo *rtype_info = type_infoptr(decl->func_decl.function_signature.returntype);
TypeInfo *rtype_info = type_infoptr(decl->func_decl.signature.rtype);
assert(rtype_info);
Type *rtype = rtype_info->type->canonical;
if (decl->func_decl.function_signature.attrs.nodiscard)
if (decl->func_decl.signature.attrs.nodiscard)
{
if (rtype == type_void)
{
@@ -1897,7 +1944,7 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl)
return decl_poison(decl);
}
}
if (decl->func_decl.function_signature.attrs.maydiscard)
if (decl->func_decl.signature.attrs.maydiscard)
{
if (!type_is_optional(rtype))
{
@@ -1918,8 +1965,8 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl)
decl_set_external_name(decl);
}
bool pure = false;
if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.function_signature.params, NULL, &pure)) return decl_poison(decl);
decl->func_decl.function_signature.is_pure = pure;
if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.signature.params, NULL, &pure)) return decl_poison(decl);
decl->func_decl.signature.attrs.is_pure = pure;
decl->alignment = type_alloca_alignment(decl->type);
DEBUG_LOG("Function analysis done.");
return true;
@@ -1945,7 +1992,7 @@ ERROR:
static bool sema_analyse_macro_method(SemaContext *context, Decl *decl)
{
TypeInfo *parent_type_info = type_infoptr(decl->macro_decl.type_parent);
TypeInfo *parent_type_info = type_infoptr(decl->func_decl.type_parent);
if (!sema_resolve_type_info(context, parent_type_info)) return false;
Type *parent_type = parent_type_info->type;
if (!type_may_have_sub_elements(parent_type))
@@ -1955,12 +2002,12 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl)
type_to_error_string(parent_type));
return false;
}
if (!vec_size(decl->macro_decl.parameters))
if (!vec_size(decl->func_decl.signature.params))
{
SEMA_ERROR(decl, "Expected at least one parameter - of type %s.", type_to_error_string(parent_type));
return false;
}
Decl *first_param = decl->macro_decl.parameters[0];
Decl *first_param = decl->func_decl.signature.params[0];
if (!sema_is_valid_method_param(context, first_param, parent_type->canonical)) return false;
@@ -1975,91 +2022,21 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl)
static inline bool sema_analyse_macro(SemaContext *context, Decl *decl)
{
bool is_generic = decl->decl_kind == DECL_GENERIC;
decl->func_decl.unit = context->unit;
if (!sema_analyse_attributes(context, decl, decl->attributes, ATTR_MACRO)) return decl_poison(decl);
if (!sema_analyse_func_macro(context, decl, false)) return false;
if (!sema_analyse_signature(context, &decl->func_decl.signature)) return decl_poison(decl);
if (decl->macro_decl.rtype)
if (!decl->func_decl.signature.is_at_macro && decl->func_decl.body_param)
{
TypeInfo *rtype_info = type_infoptr(decl->macro_decl.rtype);
if (!sema_resolve_type_info(context, rtype_info)) return decl_poison(decl);
Type *rtype = rtype_info->type;
if (decl->macro_decl.attr_nodiscard)
{
if (rtype == type_void)
{
SEMA_ERROR(rtype_info, "@nodiscard cannot be used on macros returning 'void'.");
return decl_poison(decl);
}
}
if (decl->macro_decl.attr_maydiscard)
{
if (!type_is_optional(rtype))
{
SEMA_ERROR(rtype_info, "@maydiscard can only be used on macros returning optional values.");
return decl_poison(decl);
}
}
}
decl->macro_decl.unit = context->unit;
Decl **parameters = decl->macro_decl.parameters;
unsigned param_count = vec_size(parameters);
bool is_function_like = true;
for (unsigned i = 0; i < param_count; i++)
{
Decl *param = parameters[i];
param->unit = context->unit;
param->resolve_status = RESOLVE_RUNNING;
assert(param->decl_kind == DECL_VAR);
switch (param->var.kind)
{
case VARDECL_PARAM_EXPR:
case VARDECL_PARAM_REF:
is_function_like = false;
FALLTHROUGH;
case VARDECL_PARAM_CT:
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);
param->type = param->var.type_info->type;
}
break;
case VARDECL_PARAM_CT_TYPE:
if (param->var.type_info)
{
SEMA_ERROR(param->var.type_info, "A compile time type parameter cannot have a type itself.");
return decl_poison(decl);
}
break;
case VARDECL_CONST:
case VARDECL_GLOBAL:
case VARDECL_LOCAL:
case VARDECL_MEMBER:
case VARDECL_BITMEMBER:
case VARDECL_LOCAL_CT:
case VARDECL_LOCAL_CT_TYPE:
case VARDECL_UNWRAPPED:
case VARDECL_REWRAPPED:
case VARDECL_ERASE:
UNREACHABLE
}
if (!sema_check_param_uniqueness_and_type(parameters, param, i, param_count)) return decl_poison(decl);
param->resolve_status = RESOLVE_DONE;
}
DeclId body_param = decl->macro_decl.body_param;
if (body_param) is_function_like = false;
if (is_generic && body_param)
{
SEMA_ERROR(declptr(body_param), "Trailing block syntax is not allowed for generic functions.");
SEMA_ERROR(decl, "Names of macros with a trailing body must start with '@'.");
return decl_poison(decl);
}
Decl **parameters = decl->func_decl.signature.params;
unsigned param_count = vec_size(parameters);
DeclId body_param = decl->func_decl.body_param;
Decl **body_parameters = body_param ? declptr(body_param)->body_params : NULL;
unsigned body_param_count = vec_size(body_parameters);
for (unsigned i = 0; i < body_param_count; i++)
@@ -2094,13 +2071,9 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl)
param->resolve_status = RESOLVE_DONE;
}
bool pure = false;
if (!sema_analyse_doc_header(decl->macro_decl.docs, decl->macro_decl.parameters, body_parameters, &pure)) return decl_poison(decl);
if (decl->name[0] != '@' && !is_function_like)
{
SEMA_ERROR(decl, "Names of non-function like macros (i.e. a macro with a trailing body, ref or expression parameters, must start with '@'.");
return false;
}
if (decl->macro_decl.type_parent)
if (!sema_analyse_doc_header(decl->func_decl.docs, decl->func_decl.signature.params, body_parameters, &pure)) return decl_poison(decl);
if (decl->func_decl.type_parent)
{
if (!sema_analyse_macro_method(context, decl)) return decl_poison(decl);
}
@@ -2520,7 +2493,7 @@ static inline bool sema_analyse_attribute_decl(SemaContext *c, Decl *decl)
{
if (param[j].name == param->name)
{
SEMA_ERROR(param, "Duplicate parameter name.");
SEMA_ERROR(param, "Duplicate parameter name '%s'.", param->name);
return false;
}
}

View File

@@ -71,6 +71,37 @@ void expr_rewrite_to_builtin_access(SemaContext *context, Expr *expr, Expr *pare
expr->resolve_status = RESOLVE_DONE;
}
Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr)
{
unsigned args = vec_size(context->macro_varargs);
uint64_t index;
Decl *param = NULL;
if (!sema_analyse_expr(context, index_expr)) return poisoned_expr;
if (!type_is_integer(index_expr->type))
{
SEMA_ERROR(index_expr, "Expected the argument index here, but found a value of type %s.", type_quoted_error_string(index_expr->type));
return poisoned_expr;
}
if (!expr_is_const(index_expr))
{
SEMA_ERROR(index_expr, "Vararg functions need a constant argument, but this is a runtime value.");
return poisoned_expr;
}
Int index_val = index_expr->const_expr.ixx;
if (int_is_neg(index_val))
{
SEMA_ERROR(index_expr, "The index cannot be negative.");
return poisoned_expr;
}
Int int_max = { .i = { .low = args }, .type = TYPE_U32 };
if (int_comp(index_val, int_max, BINARYOP_GE))
{
SEMA_ERROR(index_expr, "Only %u vararg%s exist.", args, args == 1 ? "" : "s");
return poisoned_expr;
}
return context->macro_varargs[(size_t)index_val.i.low];
}
const char *ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing)
{
if (!sema_analyse_expr(c, inner)) return false;
@@ -149,7 +180,7 @@ void expr_insert_addr(Expr *original)
original->expr_kind = EXPR_UNARY;
Type *inner_type = inner->type;
bool failable = type_is_optional(inner->type);
original->type = type_add_optional(type_get_ptr(type_no_optional(inner->type)), failable);
original->type = type_add_optional(type_get_ptr(type_no_optional(inner_type)), failable);
original->unary_expr.operator = UNARYOP_ADDR;
original->unary_expr.expr = inner;
}
@@ -380,7 +411,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
return false;
case EXPR_IDENTIFIER:
if (expr->identifier_expr.decl->decl_kind != DECL_VAR) return true;
if (expr->identifier_expr.is_const)
if (expr->identifier_expr.decl->var.kind == VARDECL_CONST)
{
expr = expr->identifier_expr.decl->var.init_expr;
goto RETRY;
@@ -570,17 +601,17 @@ static bool sema_check_expr_lvalue(Expr *top_expr, Expr *expr)
return true;
case EXPR_IDENTIFIER:
{
if (expr->identifier_expr.is_const)
{
SEMA_ERROR(top_expr, "You cannot assign to a constant.");
return false;
}
Decl *decl = expr->identifier_expr.decl;
if (decl->decl_kind != DECL_VAR)
{
SEMA_ERROR(top_expr, "You cannot assign a value to %s.", decl_to_a_name(decl));
return false;
}
if (decl->var.kind == VARDECL_CONST)
{
SEMA_ERROR(top_expr, "You cannot assign to a constant.");
return false;
}
decl = decl_raw(decl);
switch (decl->var.kind)
{
@@ -1100,8 +1131,9 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to,
assert(expr && expr->identifier_expr.ident);
DEBUG_LOG("Resolving identifier '%s'", expr->identifier_expr.ident);
assert(expr->resolve_status != RESOLVE_DONE);
DeclId body_param;
if (!expr->identifier_expr.path && context->current_macro && (body_param = context->current_macro->macro_decl.body_param))
if (!expr->identifier_expr.path && context->current_macro && (body_param = context->current_macro->func_decl.body_param))
{
if (expr->identifier_expr.ident == declptr(body_param)->name)
{
@@ -1375,15 +1407,11 @@ static inline bool expr_may_splat_as_vararg(Expr *expr, Type *variadic_base_type
typedef struct
{
bool macro;
bool func_pointer;
const char *name;
const char *block_parameter;
Decl **params;
Type **param_types;
Expr *struct_var;
unsigned param_count;
Variadic variadic;
unsigned vararg_index;
Signature *signature;
} CalledDecl;
static inline bool expr_promote_vararg(SemaContext *context, Expr *arg)
@@ -1463,7 +1491,6 @@ INLINE bool sema_expand_call_arguments(SemaContext *context, CalledDecl *callee,
{
unsigned num_args = vec_size(args);
Decl **params = callee->params;
bool is_func_ptr = callee->func_pointer;
Expr **actual_args = VECNEW(Expr*, func_param_count);
for (unsigned i = 0; i < func_param_count; i++) vec_add(actual_args, NULL);
@@ -1628,8 +1655,10 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
// 2. Pick out all the arguments and parameters.
Expr **args = call->call_expr.arguments;
Signature *sig = callee.signature;
unsigned vararg_index = sig->vararg_index;
Variadic variadic = sig->variadic;
Decl **decl_params = callee.params;
Type **param_types = callee.param_types;
unsigned num_args = vec_size(args);
// 3. If this is a type call, then we have an implicit first argument.
@@ -1655,7 +1684,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
if (splat)
{
// 4a. Is this *not* a variadic function/macro? - Then that's an error.
if (callee.variadic == VARIADIC_NONE)
if (variadic == VARIADIC_NONE)
{
SEMA_ERROR(call->call_expr.arguments[num_args - 1],
"Using the splat operator is only allowed on vararg parameters.");
@@ -1664,15 +1693,15 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
}
// 5. Zero out all argument slots.
unsigned func_param_count = callee.param_count;
unsigned param_count = vec_size(decl_params);
// 6. We might have a typed variadic call e.g. foo(int, double...)
// get that type.
Type *variadic_type = NULL;
if (callee.variadic == VARIADIC_TYPED || callee.variadic == VARIADIC_ANY)
if (variadic == VARIADIC_TYPED || variadic == VARIADIC_ANY)
{
// 7a. The parameter type is <type>[], so we get the <type>
Type *vararg_slot_type = callee.macro ? callee.params[callee.vararg_index]->type : callee.param_types[callee.vararg_index];
Type *vararg_slot_type = decl_params[vararg_index]->type;
assert(vararg_slot_type->type_kind == TYPE_SUBARRAY);
variadic_type = vararg_slot_type->array.base;
}
@@ -1683,9 +1712,9 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
&callee,
call,
args,
func_param_count,
callee.variadic,
callee.vararg_index,
param_count,
variadic,
vararg_index,
failable, &varargs, &vararg_splat)) return false;
args = call->call_expr.arguments;
@@ -1694,18 +1723,22 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
call->call_expr.varargs = NULL;
if (varargs)
{
if (callee.variadic == VARIADIC_RAW)
if (variadic == VARIADIC_RAW)
{
foreach(Expr*, varargs)
{
// 12a. Analyse the expression.
if (!sema_analyse_expr(context, val)) return false;
// 12b. In the case of a compile time variable non macros we cast to c_int / double.
if (!callee.macro)
if (callee.macro)
{
// Just keep as lvalues
if (!sema_analyse_expr_lvalue_fold_const(context, val)) return false;
}
else
{
if (!sema_analyse_expr(context, val)) return false;
if (!expr_promote_vararg(context, val)) return false;
}
// Set the argument at the location.
*failable |= IS_OPTIONAL(val);
}
@@ -1746,27 +1779,15 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
// 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 == callee.vararg_index && callee.variadic != VARIADIC_NONE)
if (i == vararg_index && variadic != VARIADIC_NONE)
{
assert(arg == NULL);
continue;
}
Decl *param;
VarDeclKind kind;
Type *type;
if (decl_params)
{
param = decl_params[i];
kind = param->var.kind;
type = param->type;
}
else
{
param = NULL;
kind = VARDECL_PARAM;
type = param_types[i];
}
Decl *param = decl_params[i];
VarDeclKind kind = param->var.kind;
Type *type = param->type;
// 16. Analyse a regular argument.
switch (kind)
@@ -1801,8 +1822,9 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
SEMA_ERROR(arg, "An untyped list can only be passed as a compile time parameter.");
return false;
}
if (param && !param->alignment)
if (!param->alignment)
{
assert(callee.macro && "Only in the macro case should we need to insert the alignment.");
param->alignment = type_alloca_alignment(arg->type);
}
break;
@@ -1846,23 +1868,19 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr
}
return true;
}
static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Type *type,
FunctionSignature *sig, Expr *expr, Decl *decl,
Expr *struct_var, bool failable, const char *name)
static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Type *type, Expr *expr, Expr *struct_var,
bool failable, const char *name)
{
Signature *sig = type->function.signature;
CalledDecl callee = {
.macro = false,
.name = name,
.func_pointer = sig ? 0 : 1,
.block_parameter = NULL,
.struct_var = struct_var,
.params = type->function.params,
.param_types = type->function.prototype->param_types,
.param_count = vec_size(type->function.prototype->param_types),
.variadic = type->function.prototype->variadic,
.vararg_index = type->function.prototype->vararg_index
.params = sig->params,
.signature = sig,
};
if (context->current_function_pure && (!sig || !sig->is_pure) && !expr->call_expr.attr_pure)
if (context->current_function_pure && !sig->attrs.is_pure && !expr->call_expr.attr_pure)
{
SEMA_ERROR(expr, "Only '@pure' functions may be called, you can override this with an attribute.");
return false;
@@ -1873,16 +1891,16 @@ static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Type
Type *rtype = type->function.prototype->rtype;
if (is_unused && rtype != type_void && decl && decl->decl_kind == DECL_FUNC)
if (is_unused && rtype != type_void)
{
if (decl->func_decl.function_signature.attrs.nodiscard)
if (sig->attrs.nodiscard)
{
SEMA_ERROR(expr, "The result of the function must be used.");
return false;
}
if (type_is_optional(rtype) && !decl->func_decl.function_signature.attrs.maydiscard)
if (type_is_optional(rtype) && !sig->attrs.maydiscard)
{
SEMA_ERROR(expr, "The optional result of the macro must be used.");
SEMA_ERROR(expr, "The optional result of the function must be used.");
return false;
}
}
@@ -1895,20 +1913,14 @@ static inline bool sema_expr_analyse_func_invocation(SemaContext *context, Type
static inline bool sema_expr_analyse_var_call(SemaContext *context, Expr *expr, Type *func_ptr_type, bool failable)
{
Decl *decl = NULL;
if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC) goto ERR;
if (func_ptr_type->type_kind != TYPE_POINTER || func_ptr_type->pointer->type_kind != TYPE_FUNC)
{
SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(func_ptr_type));
return false;
}
Type *pointee = func_ptr_type->pointer;
expr->call_expr.is_pointer_call = true;
return sema_expr_analyse_func_invocation(context,
pointee,
NULL,
expr,
decl,
NULL,
failable,
func_ptr_type->pointer->name);
ERR:
SEMA_ERROR(expr, "Only macros, functions and function pointers maybe invoked, this is of type '%s'.", type_to_error_string(func_ptr_type));
return false;
return sema_expr_analyse_func_invocation(context, pointee, expr, NULL, failable, func_ptr_type->pointer->name);
}
// Unify returns in a macro or expression block.
@@ -2008,9 +2020,7 @@ static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr,
expr->call_expr.is_pointer_call = false;
return sema_expr_analyse_func_invocation(context,
decl->type,
&decl->func_decl.function_signature,
expr,
decl,
expr,
struct_var,
failable,
decl->name);
@@ -2068,24 +2078,43 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
{
assert(decl->decl_kind == DECL_MACRO);
Decl **params = decl_copy_list(decl->macro_decl.parameters);
Decl **params = decl_copy_list(decl->func_decl.signature.params);
CalledDecl callee = {
.macro = true,
.name = decl->name,
.block_parameter = decl->macro_decl.body_param ? declptr(decl->macro_decl.body_param)->name : NULL,
.params = params,
.variadic = decl->macro_decl.variadic,
.vararg_index = decl->macro_decl.vararg_index,
.param_count = vec_size(params),
.block_parameter = decl->func_decl.body_param ? declptr(decl->func_decl.body_param)->name : NULL,
.signature = &decl->func_decl.signature,
.struct_var = struct_var
};
if (!sema_expr_analyse_call_invocation(context, call_expr, callee, &failable)) return false;
unsigned vararg_index = decl->func_decl.signature.vararg_index;
Expr **args = call_expr->call_expr.arguments;
VECEACH(params, i)
{
Decl *param = params[i];
if (i == vararg_index)
{
if (!param) continue;
// Splat? That's the simple case.
if (call_expr->call_expr.splat_vararg)
{
if (!sema_analyse_expr(context, args[i] = call_expr->call_expr.splat)) return false;
}
else
{
Expr **exprs = call_expr->call_expr.varargs;
Expr *literal = expr_new(EXPR_COMPOUND_LITERAL, exprs ? exprs[0]->span : param->span);
Expr *initializer_list = expr_new_expr(EXPR_INITIALIZER_LIST, literal);
initializer_list->initializer_list = exprs;
literal->expr_compound_literal.type_info = param->var.type_info;
literal->expr_compound_literal.initializer = initializer_list;
if (!sema_analyse_expr(context, args[i] = literal)) return false;
}
}
param->var.init_expr = args[i];
VarDeclKind kind = param->var.kind;
if (kind == VARDECL_PARAM_CT_TYPE || kind == VARDECL_PARAM_CT)
@@ -2096,7 +2125,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
Decl **body_params = call_expr->call_expr.body_arguments;
unsigned body_params_count = vec_size(body_params);
Decl **macro_body_params = decl->macro_decl.body_param ? declptr(decl->macro_decl.body_param)->body_params : NULL;
Decl **macro_body_params = decl->func_decl.body_param ? declptr(decl->func_decl.body_param)->body_params : NULL;
unsigned expected_body_params = vec_size(macro_body_params);
if (expected_body_params > body_params_count)
{
@@ -2134,7 +2163,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
if (!body_arg->alignment) body_arg->alignment = type_alloca_alignment(body_arg->type);
}
Ast *body = ast_macro_copy(astptr(decl->macro_decl.body));
Ast *body = ast_macro_copy(astptr(decl->func_decl.body));
DynamicScope old_scope = context->active_scope;
context_change_scope_with_flags(context, SCOPE_NONE);
@@ -2142,10 +2171,10 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
SemaContext macro_context;
Type *rtype = NULL;
sema_context_init(&macro_context, decl->macro_decl.unit);
sema_context_init(&macro_context, decl->unit);
macro_context.compilation_unit = context->unit;
macro_context.current_function = context->current_function;
rtype = decl->macro_decl.rtype ? type_infoptr(decl->macro_decl.rtype)->type : NULL;
rtype = decl->func_decl.signature.rtype ? type_infoptr(decl->func_decl.signature.rtype)->type : NULL;
macro_context.expected_block_type = rtype;
bool may_failable = true;
if (rtype)
@@ -2170,26 +2199,30 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
macro_context.yield_body = body_id ? astptr(body_id) : NULL;
macro_context.yield_params = body_params;
macro_context.yield_context = context;
macro_context.macro_varargs = call_expr->call_expr.varargs;
macro_context.original_inline_line = context->original_inline_line ? context->original_inline_line : call_expr->span.row;
macro_context.macro_params = params;
BlockExit** block_exit_ref = MALLOCS(BlockExit*);
macro_context.block_exit_ref = block_exit_ref;
VECEACH(params, i)
{
Decl *param = params[i];
// Skip raw vararg
if (!param) continue;
if (!sema_add_local(&macro_context, param)) goto EXIT_FAIL;
}
AstId assert_first = 0;
AstId* next = &assert_first;
if (!sema_analyse_contracts(&macro_context, decl->macro_decl.docs, &next)) return false;
if (!sema_analyse_contracts(&macro_context, decl->func_decl.docs, &next)) return false;
sema_append_contract_asserts(assert_first, body);
if (!sema_analyse_statement(&macro_context, body)) goto EXIT_FAIL;
bool is_no_return = decl->macro_decl.attr_noreturn;
params = macro_context.macro_params;
bool is_no_return = decl->func_decl.signature.attrs.noreturn;
if (!vec_size(macro_context.returns))
{
@@ -2249,12 +2282,12 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
Type *type = call_expr->type;
if (type != type_void)
{
if (decl->macro_decl.attr_nodiscard)
if (decl->func_decl.signature.attrs.nodiscard)
{
SEMA_ERROR(call_expr, "The result of the macro must be used.");
return SCOPE_POP_ERROR();
}
if (type_is_optional(type) && !decl->macro_decl.attr_maydiscard)
if (type_is_optional(type) && !decl->func_decl.signature.attrs.maydiscard)
{
SEMA_ERROR(call_expr, "The optional result of the macro must be used.");
return SCOPE_POP_ERROR();
@@ -2285,7 +2318,6 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
call_expr->expr_kind = EXPR_MACRO_BLOCK;
call_expr->macro_block.first_stmt = body->compound_stmt.first_stmt;
call_expr->macro_block.params = params;
call_expr->macro_block.args = args;
call_expr->macro_block.block_exit = block_exit_ref;
EXIT:
assert(context->active_scope.defer_last == context->active_scope.defer_start);
@@ -2301,102 +2333,11 @@ static inline Decl *sema_generate_generic_function(SemaContext *context, Expr *c
return NULL;
}
static inline bool sema_expr_analyse_generic_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable)
{
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->extname);
// 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;
}
unsigned 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->var.kind == VARDECL_PARAM_CT_TYPE)
{
if (!sema_analyse_expr_lvalue_fold_const(context, 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_rhs(context, param->var.type_info->type, arg, true)) return false;
}
else
{
if (!sema_analyse_expr(context, 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->unit->module->generic_cache;
Decl *found = NULL;
VECEACH(generic_cache, i)
{
TODO
if (generic_cache[i]->extname == 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->var.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, call_expr, found, struct_var, failable);
}
static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call)
{
Decl *macro = macro_context->current_macro;
assert(macro);
DeclId body_param = macro->macro_decl.body_param;
DeclId body_param = macro->func_decl.body_param;
assert(body_param);
ExprCall *call_expr = &call->call_expr;
@@ -2431,7 +2372,6 @@ static bool sema_analyse_body_expansion(SemaContext *macro_context, Expr *call)
Ast *first_defer = NULL;
SemaContext *context = macro_context->yield_context;
Decl **params = macro_context->yield_params;
Expr *func_expr = exprptr(call_expr->function);
assert(func_expr->expr_kind == EXPR_MACRO_BODY_EXPANSION);
expr_replace(call, func_expr);
@@ -2499,7 +2439,7 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl
case DECL_GENERIC:
expr->call_expr.func_ref = declid(decl);
expr->call_expr.is_func_ref = true;
return sema_expr_analyse_generic_call(context, expr, struct_var, decl, failable);
TODO // Maybe generics won't happen
case DECL_POISONED:
return false;
default:
@@ -2820,14 +2760,8 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr)
switch (decl->decl_kind)
{
case DECL_MACRO:
if (decl->macro_decl.parameters[0]->type->type_kind == TYPE_POINTER)
{
expr_insert_addr(func_expr->access_expr.parent);
}
struct_var = func_expr->access_expr.parent;
break;
case DECL_FUNC:
if (decl->func_decl.function_signature.params[0]->type->type_kind == TYPE_POINTER)
if (decl->func_decl.signature.params[0]->type->type_kind == TYPE_POINTER)
{
expr_insert_addr(func_expr->access_expr.parent);
}
@@ -3512,6 +3446,7 @@ static void add_members_to_context(SemaContext *context, Decl *decl)
static Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child, bool *missing)
{
RETRY:
assert(child->resolve_status != RESOLVE_DONE);
switch (child->expr_kind)
{
case EXPR_IDENTIFIER:
@@ -4272,15 +4207,18 @@ CHECK_DEEPER:
return false;
}
// Transform bitstruct access to expr_bitaccess.
if (member->var.kind == VARDECL_BITMEMBER)
if (member->decl_kind == DECL_VAR)
{
expr->expr_kind = EXPR_BITACCESS;
}
if (member->var.kind == VARDECL_MEMBER && expr_is_const_list(current_parent))
{
sema_expr_fold_to_member(expr, current_parent, member);
return true;
if (member->var.kind == VARDECL_BITMEMBER)
{
// Transform bitstruct access to expr_bitaccess.
expr->expr_kind = EXPR_BITACCESS;
}
else if (member->var.kind == VARDECL_MEMBER && expr_is_const_list(current_parent))
{
sema_expr_fold_to_member(expr, current_parent, member);
return true;
}
}
// 13. Copy properties.
expr->access_expr.parent = current_parent;
@@ -7615,6 +7553,9 @@ RETRY:
if (!decl_ok(decl)) return poisoned_type;
return decl->type->canonical;
}
case TYPE_INFO_VAARG_TYPE:
if (!sema_resolve_type_info(context, type_info)) return poisoned_type;
return type_info->type->canonical;
case TYPE_INFO_EXPRESSION:
if (!sema_resolve_type_info(context, type_info)) return poisoned_type;
return type_info->type;
@@ -7771,18 +7712,104 @@ static inline bool sema_expr_analyse_variant(SemaContext *context, Expr *expr)
static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Expr *expr)
{
assert(expr->resolve_status == RESOLVE_RUNNING);
TokenType type = expr->ct_arg_expr.type;
if (!context->current_macro)
{
SEMA_ERROR(expr, "'%s' can only be used inside of a macro.", token_type_to_string(expr->ct_arg_expr.type));
SEMA_ERROR(expr, "'%s' can only be used inside of a macro.", token_type_to_string(type));
return false;
}
switch (expr->ct_arg_expr.type)
switch (type)
{
case TOKEN_CT_VAARG_COUNT:
expr_rewrite_const_int(expr, type_usize, vec_size(context->macro_varargs), true);
return true;
case TOKEN_CT_VAARG_GET_ARG:
{
// A normal argument, this means we only evaluate it once.
ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg)), false);
Decl *decl = NULL;
// Try to find the original param.
foreach(Decl*, context->macro_params)
{
if (!val) continue;
if (val->var.init_expr == arg_expr)
{
decl = val;
break;
}
}
// Not found, so generate a new.
if (!decl)
{
decl = decl_new_generated_var(arg_expr->type, VARDECL_PARAM, arg_expr->span);
decl->var.init_expr = arg_expr;
vec_add(context->macro_params, decl);
}
// Replace with the identifier.
expr->expr_kind = EXPR_IDENTIFIER;
expr->identifier_expr = (ExprIdentifier) { .decl = decl };
expr->resolve_status = RESOLVE_DONE;
expr->type = decl->type;
return true;
}
case TOKEN_CT_VAARG_GET_EXPR:
{
// An expr argument, this means we copy and evaluate.
ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg)), false);
expr_replace(expr, expr_macro_copy(arg_expr));
return true;
}
case TOKEN_CT_VAARG_GET_CONST:
{
// An expr argument, this means we copy and evaluate.
ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg)), false);
arg_expr = expr_macro_copy(arg_expr);
if (!expr_is_constant_eval(arg_expr, CONSTANT_EVAL_CONSTANT_VALUE))
{
SEMA_ERROR(arg_expr, "This argument needs to be a compile time constant.");
return false;
}
expr_replace(expr, arg_expr);
return true;
}
case TOKEN_CT_VAARG_GET_REF:
{
// A normal argument, this means we only evaluate it once.
ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg)), false);
if (!sema_check_expr_lvalue(arg_expr, arg_expr)) return false;
Decl *decl = NULL;
// Try to find the original param.
foreach(Decl*, context->macro_params)
{
if (!val) continue;
if (val->var.init_expr == arg_expr)
{
decl = val;
decl->var.kind = VARDECL_PARAM_REF;
break;
}
}
// Not found, so generate a new.
if (!decl)
{
decl = decl_new_generated_var(arg_expr->type, VARDECL_PARAM_REF, arg_expr->span);
decl->var.init_expr = arg_expr;
vec_add(context->macro_params, decl);
}
// Replace with the identifier.
expr->expr_kind = EXPR_IDENTIFIER;
expr->identifier_expr = (ExprIdentifier) { .decl = decl };
expr->resolve_status = RESOLVE_DONE;
expr->type = decl->type;
return true;
}
case TOKEN_CT_VAARG_GET_TYPE:
default:
TODO;
UNREACHABLE;
}
}
@@ -8133,7 +8160,7 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr)
case EXPR_MACRO_BODY_EXPANSION:
if (!expr->body_expansion_expr.ast)
{
SEMA_ERROR(expr, "'@%s' must be followed by ().", declptr(context->current_macro->macro_decl.body_param)->name);
SEMA_ERROR(expr, "'@%s' must be followed by ().", declptr(context->current_macro->func_decl.body_param)->name);
return false;
}
break;

View File

@@ -77,6 +77,7 @@ bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr);
bool sema_analyse_expr_lvalue_fold_const(SemaContext *context, Expr *expr);
bool sema_analyse_ct_expr(SemaContext *context, Expr *expr);
bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable);
Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr);
void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string);
const char *ct_eval_expr(SemaContext *c, const char *expr_type, Expr *inner, TokenType *type, Path **path_ref, bool report_missing);
extern const char *ct_eval_error;

View File

@@ -482,18 +482,7 @@ Decl *sema_find_extension_method_in_module(Module *module, Type *type, const cha
{
Decl *extension = extensions[i];
if (extension->name != method_name) continue;
switch (extension->decl_kind)
{
case DECL_FUNC:
if (type_infoptr(extension->func_decl.type_parent)->type->canonical == type) return extension;
break;
case DECL_MACRO:
case DECL_GENERIC:
if (type_infoptr(extension->macro_decl.type_parent)->type->canonical == type) return extension;
break;
default:
UNREACHABLE
}
if (type_infoptr(extension->func_decl.type_parent)->type->canonical == type) return extension;
}
return NULL;
}

View File

@@ -324,6 +324,7 @@ static inline bool sema_analyse_try_unwrap(SemaContext *context, Expr *expr)
return false;
}
assert(ident->resolve_status != RESOLVE_DONE);
if (ident->identifier_expr.path)
{
SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path.");
@@ -445,6 +446,7 @@ static inline bool sema_analyse_catch_unwrap(SemaContext *context, Expr *expr)
return false;
}
assert(ident->resolve_status != RESOLVE_DONE);
if (ident->identifier_expr.path)
{
SEMA_ERROR(ident->identifier_expr.path, "The variable may not have a path.");
@@ -1048,8 +1050,8 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen
return false;
}
index_macro = value_by_ref ? by_ref : by_val;
index_type = index_macro->macro_decl.parameters[1]->type;
TypeInfoId rtype = index_macro->macro_decl.rtype;
index_type = index_macro->func_decl.signature.params[1]->type;
TypeInfoId rtype = index_macro->func_decl.signature.rtype;
value_type = rtype ? type_infoptr(rtype)->type : NULL;
}
@@ -2244,8 +2246,6 @@ static bool sema_analyse_compound_stmt(SemaContext *context, Ast *statement)
static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement)
{
bool success = false;
// Enter for scope
SCOPE_OUTER_START
ExprId init;
if ((init = statement->for_stmt.init))
@@ -2262,16 +2262,16 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement
if (decl->decl_kind != DECL_VAR || (decl->var.kind != VARDECL_LOCAL_CT && decl->var.kind != VARDECL_LOCAL_CT_TYPE))
{
SEMA_ERROR(expr, "Only 'var $foo' and 'var $Type' declarations are allowed in a '$for'");
goto EXIT_ERROR;
return false;
}
if (!sema_analyse_var_decl_ct(context, decl)) goto EXIT_ERROR;
if (!sema_analyse_var_decl_ct(context, decl)) return false;
continue;
}
if (!sema_analyse_expr(context, expr)) goto EXIT_ERROR;
if (!sema_analyse_expr(context, expr)) return false;
if (!expr_is_constant_eval(expr, CONSTANT_EVAL_CONSTANT_VALUE))
{
SEMA_ERROR(expr, "Only constant expressions are allowed.");
goto EXIT_ERROR;
return false;
}
}
}
@@ -2284,16 +2284,16 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement
for (int i = 0; i < MAX_MACRO_ITERATIONS; i++)
{
Expr *copy = expr_macro_copy(exprptr(condition));
if (!sema_analyse_cond_expr(context, copy)) goto EXIT_ERROR;
if (!sema_analyse_cond_expr(context, copy)) return false;
if (copy->expr_kind != EXPR_CONST)
{
SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time.");
goto EXIT_ERROR;
return false;
}
if (!copy->const_expr.b) break;
Ast *compound_stmt = ast_macro_copy(body);
if (!sema_analyse_compound_stmt(context, compound_stmt)) goto EXIT_ERROR;
if (!sema_analyse_compound_statement_no_scope(context, compound_stmt)) return false;
*current = astid(compound_stmt);
current = &compound_stmt->next;
@@ -2303,21 +2303,18 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement
VECEACH(exprs, j)
{
copy = expr_macro_copy(exprs[j]);
if (!sema_analyse_expr(context, copy)) goto EXIT_ERROR;
if (!sema_analyse_expr(context, copy)) return false;
if (copy->expr_kind != EXPR_CONST)
{
SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time.");
goto EXIT_ERROR;
return false;
}
}
}
}
statement->ast_kind = AST_COMPOUND_STMT;
statement->compound_stmt.first_stmt = start;
success = true;
EXIT_ERROR:
SCOPE_OUTER_END;
return success;
return true;
}
@@ -2544,10 +2541,10 @@ bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts)
bool sema_analyse_function_body(SemaContext *context, Decl *func)
{
if (!decl_ok(func)) return false;
FunctionSignature *signature = &func->func_decl.function_signature;
Signature *signature = &func->func_decl.signature;
FunctionPrototype *prototype = func->type->function.prototype;
context->current_function = func;
context->current_function_pure = func->func_decl.function_signature.is_pure;
context->current_function_pure = func->func_decl.signature.attrs.is_pure;
context->rtype = prototype->rtype;
context->active_scope = (DynamicScope) {
.scope_id = 0,

View File

@@ -254,6 +254,21 @@ RETRY:
{
case TYPE_INFO_POISON:
UNREACHABLE
case TYPE_INFO_VAARG_TYPE:
if (context->current_macro)
{
ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, type_info->unresolved_type_expr), false);
if (arg_expr->expr_kind != EXPR_TYPEINFO)
{
SEMA_ERROR(arg_expr, "The argument was not a type.");
return false;
}
*type_info = *arg_expr->type_expr;
assert(type_info->resolve_status == RESOLVE_DONE);
return true;
}
SEMA_ERROR(type_info, "'%s' can only be used inside of a macro.", token_type_to_string(TOKEN_CT_VAARG_GET_TYPE));
return false;
case TYPE_INFO_CT_IDENTIFIER:
case TYPE_INFO_IDENTIFIER:
if (!sema_resolve_type_identifier(context, type_info)) return false;

View File

@@ -321,12 +321,12 @@ void sema_analysis_run(void)
{
error_exit("'%s::%s' is not a function.", path->module, ident);
}
Decl **params = decl->func_decl.function_signature.params;
Decl **params = decl->func_decl.signature.params;
if (vec_size(params) != 4 || params[0]->type != type_get_ptr(type_char)
|| params[1]->type != type_get_ptr(type_char)
|| params[2]->type != type_get_ptr(type_char)
|| params[3]->type != type_uint
|| typeinfotype(decl->func_decl.function_signature.returntype) != type_void)
|| typeinfotype(decl->func_decl.signature.rtype) != type_void)
{
error_exit("Expected panic function to have the signature fn void(char*, char*, uint, uint).");
}

View File

@@ -401,6 +401,6 @@ const char *token_type_to_string(TokenType type)
bool token_is_any_type(TokenType type)
{
return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT;
return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT || type == TOKEN_CT_VAARG_GET_TYPE;
}

View File

@@ -1080,10 +1080,10 @@ void type_func_prototype_init(uint32_t capacity)
map.max_load = (uint32_t)(TABLE_MAX_LOAD * capacity);
}
static uint32_t hash_function(FunctionSignature *sig)
static uint32_t hash_function(Signature *sig)
{
uintptr_t hash = (unsigned)sig->variadic;
hash = hash * 31 + (uintptr_t)type_infoptr(sig->returntype)->type->canonical;
hash = hash * 31 + (uintptr_t)type_infoptr(sig->rtype)->type->canonical;
Decl **params = sig->params;
VECEACH(params, i)
{
@@ -1093,14 +1093,14 @@ static uint32_t hash_function(FunctionSignature *sig)
return (uint32_t)((hash >> 16) ^ hash);
}
static int compare_function(FunctionSignature *sig, FunctionPrototype *proto)
static int compare_function(Signature *sig, FunctionPrototype *proto)
{
if (sig->variadic != proto->variadic) return -1;
Decl **params = sig->params;
Type **other_params = proto->param_types;
unsigned param_count = vec_size(params);
if (param_count != vec_size(other_params)) return -1;
if (type_infoptr(sig->returntype)->type->canonical != proto->rtype->canonical) return -1;
if (type_infoptr(sig->rtype)->type->canonical != proto->rtype->canonical) return -1;
VECEACH(params, i)
{
Decl *param = params[i];
@@ -1111,13 +1111,13 @@ static int compare_function(FunctionSignature *sig, FunctionPrototype *proto)
}
static inline Type *func_create_new_func_proto(FunctionSignature *sig, CallABI abi, uint32_t hash, FuncTypeEntry *entry)
static inline Type *func_create_new_func_proto(Signature *sig, CallABI abi, uint32_t hash, FuncTypeEntry *entry)
{
unsigned param_count = vec_size(sig->params);
FunctionPrototype *proto = CALLOCS(FunctionPrototype);
proto->variadic = sig->variadic;
proto->vararg_index = sig->vararg_index;
Type *rtype = type_infoptr(sig->returntype)->type;
Type *rtype = type_infoptr(sig->rtype)->type;
proto->rtype = rtype;
if (type_is_optional(rtype))
{
@@ -1163,10 +1163,14 @@ static inline Type *func_create_new_func_proto(FunctionSignature *sig, CallABI a
}
scratch_buffer_append_char(')');
Type *type = type_new(TYPE_FUNC, scratch_buffer_interned());
Signature *copy_sig = CALLOCS(Signature);
*copy_sig = *sig;
copy_sig->attrs = (CalleeAttributes) { .nodiscard = false };
copy_sig->params = proto->param_copy;
proto->raw_type = type;
type->function.prototype = proto;
type->function.module = NULL;
type->function.params = proto->param_copy;
type->function.signature = copy_sig;
type->canonical = type;
entry->key = hash;
entry->value = type;
@@ -1202,7 +1206,7 @@ static inline Type *func_create_new_func_proto(FunctionSignature *sig, CallABI a
return type;
}
Type *type_get_func(FunctionSignature *signature, CallABI abi)
Type *type_get_func(Signature *signature, CallABI abi)
{
uint32_t hash = hash_function(signature);
uint32_t mask = map.capacity - 1;

View File

@@ -11,7 +11,7 @@ fn void main()
printf("Foo %d\n", $i);
$endfor;
$for (var $i = 0, var $j = 100; $i < 4;):
$for ($i = 0, var $j = 100; $i < 4;):
printf("Foo %d %d\n", $i++, $j--);
$endfor;

View File

@@ -7,4 +7,6 @@ fn void foo3(bar) { } // #error: Only typed parameters are allowed for functions
fn void foo4($Type) { } // #error: Only regular parameters are allowed for functions.
fn void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions.
fn void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions.
fn void foo9(int x, int x) {} // #error: Duplicate parameter name 'x'.

View File

@@ -2,10 +2,10 @@ macro foo(a, $b, $Type) {}
macro @foo2(a, $b, $Type) {}
macro bar(&x) // #error: non-function
macro bar(&x) // #error: Ref and expression parameters
{}
macro baz(#y) {} // #error: non-function
macro baz(#y) {} // #error: Ref and expression parameters
macro baz2(a; @body()) {} // #error: non-function
macro baz2(a; @body()) {} // #error: Names of macros

View File

@@ -0,0 +1,210 @@
// #target: macos-x64
module test;
import std::io;
macro foo(int... x)
{
foreach (i : x)
{
io::printfln("%d", i);
}
}
macro foo2(x...)
{
foreach (i : x)
{
io::printfln("%d", *(int*)i);
}
}
fn void main()
{
foo(1, -1, 3141, 999 + 1);
foo2(1, -1, 3141, 999 + 1);
}
/* #expect: test.ll
define void @test_main() #0 {
entry:
%x = alloca %"int[]", align 8
%literal = alloca [4 x i32], align 16
%.anon = alloca i64, align 8
%.anon1 = alloca i64, align 8
%i = alloca i32, align 4
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%x4 = alloca %"variant[]", align 8
%literal5 = alloca [4 x %variant], align 16
%taddr6 = alloca i32, align 4
%taddr7 = alloca i32, align 4
%taddr8 = alloca i32, align 4
%taddr9 = alloca i32, align 4
%.anon10 = alloca i64, align 8
%.anon11 = alloca i64, align 8
%i15 = alloca %variant, align 8
%retparam17 = alloca i64, align 8
%taddr18 = alloca %"char[]", align 8
%vararg21 = alloca %"variant[]", align 8
%varargslots22 = alloca [1 x %variant], align 16
%0 = getelementptr inbounds [4 x i32], [4 x i32]* %literal, i64 0, i64 0
store i32 1, i32* %0, align 4
%1 = getelementptr inbounds [4 x i32], [4 x i32]* %literal, i64 0, i64 1
store i32 -1, i32* %1, align 4
%2 = getelementptr inbounds [4 x i32], [4 x i32]* %literal, i64 0, i64 2
store i32 3141, i32* %2, align 4
%3 = getelementptr inbounds [4 x i32], [4 x i32]* %literal, i64 0, i64 3
store i32 1000, i32* %3, align 4
%4 = bitcast [4 x i32]* %literal to i32*
%5 = insertvalue %"int[]" undef, i32* %4, 0
%6 = insertvalue %"int[]" %5, i64 4, 1
store %"int[]" %6, %"int[]"* %x, align 8
%7 = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 1
%8 = load i64, i64* %7, align 8
store i64 %8, i64* %.anon, align 8
store i64 0, i64* %.anon1, align 8
br label %loop.cond
loop.cond: ; preds = %voiderr, %entry
%9 = load i64, i64* %.anon1, align 8
%10 = load i64, i64* %.anon, align 8
%lt = icmp ult i64 %9, %10
br i1 %lt, label %loop.body, label %loop.exit
loop.body: ; preds = %loop.cond
%11 = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 0
%12 = load i32*, i32** %11, align 8
%13 = load i64, i64* %.anon1, align 8
%ptroffset = getelementptr inbounds i32, i32* %12, i64 %13
%14 = load i32, i32* %ptroffset, align 4
store i32 %14, i32* %i, align 4
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i64 2 }, %"char[]"* %taddr, align 8
%15 = bitcast %"char[]"* %taddr to { i8*, i64 }*
%16 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %15, i32 0, i32 0
%lo = load i8*, i8** %16, align 8
%17 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %15, i32 0, i32 1
%hi = load i64, i64* %17, align 8
%18 = bitcast i32* %i to i8*
%19 = insertvalue %variant undef, i8* %18, 0
%20 = insertvalue %variant %19, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%21 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0
store %variant %20, %variant* %21, align 16
%22 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1
store i64 1, i64* %22, align 8
%23 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0
%24 = bitcast [1 x %variant]* %varargslots to %variant*
store %variant* %24, %variant** %23, align 8
%25 = bitcast %"variant[]"* %vararg to { i8*, i64 }*
%26 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %25, i32 0, i32 0
%lo2 = load i8*, i8** %26, align 8
%27 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %25, i32 0, i32 1
%hi3 = load i64, i64* %27, align 8
%28 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo2, i64 %hi3)
%not_err = icmp eq i64 %28, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %loop.body
br label %voiderr
voiderr: ; preds = %after_check, %loop.body
%29 = load i64, i64* %.anon1, align 8
%add = add i64 %29, 1
store i64 %add, i64* %.anon1, align 8
br label %loop.cond
loop.exit: ; preds = %loop.cond
%30 = getelementptr inbounds [4 x %variant], [4 x %variant]* %literal5, i64 0, i64 0
store i32 1, i32* %taddr6, align 4
%31 = bitcast i32* %taddr6 to i8*
%32 = insertvalue %variant undef, i8* %31, 0
%33 = insertvalue %variant %32, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
store %variant %33, %variant* %30, align 8
%34 = getelementptr inbounds [4 x %variant], [4 x %variant]* %literal5, i64 0, i64 1
store i32 -1, i32* %taddr7, align 4
%35 = bitcast i32* %taddr7 to i8*
%36 = insertvalue %variant undef, i8* %35, 0
%37 = insertvalue %variant %36, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
store %variant %37, %variant* %34, align 8
%38 = getelementptr inbounds [4 x %variant], [4 x %variant]* %literal5, i64 0, i64 2
store i32 3141, i32* %taddr8, align 4
%39 = bitcast i32* %taddr8 to i8*
%40 = insertvalue %variant undef, i8* %39, 0
%41 = insertvalue %variant %40, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
store %variant %41, %variant* %38, align 8
%42 = getelementptr inbounds [4 x %variant], [4 x %variant]* %literal5, i64 0, i64 3
store i32 1000, i32* %taddr9, align 4
%43 = bitcast i32* %taddr9 to i8*
%44 = insertvalue %variant undef, i8* %43, 0
%45 = insertvalue %variant %44, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
store %variant %45, %variant* %42, align 8
%46 = bitcast [4 x %variant]* %literal5 to %variant*
%47 = insertvalue %"variant[]" undef, %variant* %46, 0
%48 = insertvalue %"variant[]" %47, i64 4, 1
store %"variant[]" %48, %"variant[]"* %x4, align 8
%49 = getelementptr inbounds %"variant[]", %"variant[]"* %x4, i32 0, i32 1
%50 = load i64, i64* %49, align 8
store i64 %50, i64* %.anon10, align 8
store i64 0, i64* %.anon11, align 8
br label %loop.cond12
loop.cond12: ; preds = %voiderr27, %loop.exit
%51 = load i64, i64* %.anon11, align 8
%52 = load i64, i64* %.anon10, align 8
%lt13 = icmp ult i64 %51, %52
br i1 %lt13, label %loop.body14, label %loop.exit29
loop.body14: ; preds = %loop.cond12
%53 = getelementptr inbounds %"variant[]", %"variant[]"* %x4, i32 0, i32 0
%54 = load %variant*, %variant** %53, align 8
%55 = load i64, i64* %.anon11, align 8
%ptroffset16 = getelementptr inbounds %variant, %variant* %54, i64 %55
%56 = bitcast %variant* %i15 to i8*
%57 = bitcast %variant* %ptroffset16 to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %56, i8* align 8 %57, i32 16, i1 false)
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.1, i32 0, i32 0), i64 2 }, %"char[]"* %taddr18, align 8
%58 = bitcast %"char[]"* %taddr18 to { i8*, i64 }*
%59 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %58, i32 0, i32 0
%lo19 = load i8*, i8** %59, align 8
%60 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %58, i32 0, i32 1
%hi20 = load i64, i64* %60, align 8
%61 = getelementptr inbounds %variant, %variant* %i15, i32 0, i32 0
%62 = bitcast i8** %61 to i32**
%63 = load i32*, i32** %62, align 8
%64 = bitcast i32* %63 to i8*
%65 = insertvalue %variant undef, i8* %64, 0
%66 = insertvalue %variant %65, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%67 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots22, i64 0, i64 0
store %variant %66, %variant* %67, align 16
%68 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg21, i32 0, i32 1
store i64 1, i64* %68, align 8
%69 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg21, i32 0, i32 0
%70 = bitcast [1 x %variant]* %varargslots22 to %variant*
store %variant* %70, %variant** %69, align 8
%71 = bitcast %"variant[]"* %vararg21 to { i8*, i64 }*
%72 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %71, i32 0, i32 0
%lo23 = load i8*, i8** %72, align 8
%73 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %71, i32 0, i32 1
%hi24 = load i64, i64* %73, align 8
%74 = call i64 @std_io_printfln(i64* %retparam17, i8* %lo19, i64 %hi20, i8* %lo23, i64 %hi24)
%not_err25 = icmp eq i64 %74, 0
br i1 %not_err25, label %after_check26, label %voiderr27
after_check26: ; preds = %loop.body14
br label %voiderr27
voiderr27: ; preds = %after_check26, %loop.body14
%75 = load i64, i64* %.anon11, align 8
%add28 = add i64 %75, 1
store i64 %add28, i64* %.anon11, align 8
br label %loop.cond12
loop.exit29: ; preds = %loop.cond12
ret void
}

View File

@@ -0,0 +1,25 @@
macro foo(...)
{
$vaarg_get_arg("hello"); // #error: Expected the argument index here
int x;
$vaarg_get_arg(x); // #error: Vararg functions need a constant argument
$vaarg_get_arg(-1); // #error: negative
$vaarg_get_arg(100); // #error: varargs exist
}
macro foo2(...)
{
$vaarg_get_const(0);
}
macro foo3(...)
{
$vaarg_get_type(0) a;
}
fn void main()
{
foo(1, -1, 3141, 999 + 1);
int x;
foo2(x); // #error: This argument needs to be a compile time constant
foo3(3); // #error: The argument was not a type.
}

View File

@@ -0,0 +1,463 @@
// #target: macos-x64
module test;
import std::io;
macro @foo(...)
{
int i = $vaarg_get_arg(1) + $vaarg_get_arg(1);
int j = $vaarg_get_expr(2) + $vaarg_get_expr(2);
$for (var $i = 0; $i < $vaarg_count(); $i++):
io::printfln("%d", $vaarg_get_arg($i));
$endfor;
}
macro foo2(...)
{
$for (var $i = 0; $i < $vaarg_count(); $i++):
{
$vaarg_get_type($i) x;
}
io::printfln("%s", $nameof($vaarg_get_type($i)));
$endfor;
}
macro foo3(...)
{
var $x = 0;
$for (var $i = 0; $i < $vaarg_count(); $i++):
$x += $vaarg_get_const($i);
$endfor;
return $x;
}
macro @foo4(...)
{
$typeof($vaarg_get_ref(0)) a = $vaarg_get_ref(0);
$vaarg_get_ref(0) = $vaarg_get_ref(1);
$vaarg_get_ref(1) = a;
}
fn int ping(int val)
{
io::printfln("Ping[%d]", val);
return val;
}
fn void main()
{
@foo(ping(1), ping(-1), ping(3141), ping(999 + 1));
foo2(int, double);
var $x = foo3(1, 4, 100);
io::printfln("%d", $x);
int x = 123;
int y = 33;
@foo4(x, y);
io::printfln("%d, %d", x, y);
@foo4(x, y);
io::printfln("%d, %d", x, y);
}
/* #expect: test.ll
define i32 @test_ping(i32 %0) #0 {
entry:
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%taddr1 = alloca i32, align 4
store %"char[]" { i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i64 8 }, %"char[]"* %taddr, align 8
%1 = bitcast %"char[]"* %taddr to { i8*, i64 }*
%2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 0
%lo = load i8*, i8** %2, align 8
%3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 1
%hi = load i64, i64* %3, align 8
store i32 %0, i32* %taddr1, align 4
%4 = bitcast i32* %taddr1 to i8*
%5 = insertvalue %variant undef, i8* %4, 0
%6 = insertvalue %variant %5, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%7 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0
store %variant %6, %variant* %7, align 16
%8 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1
store i64 1, i64* %8, align 8
%9 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0
%10 = bitcast [1 x %variant]* %varargslots to %variant*
store %variant* %10, %variant** %9, align 8
%11 = bitcast %"variant[]"* %vararg to { i8*, i64 }*
%12 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 0
%lo2 = load i8*, i8** %12, align 8
%13 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 1
%hi3 = load i64, i64* %13, align 8
%14 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo2, i64 %hi3)
%not_err = icmp eq i64 %14, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
ret i32 %0
}
define void @test_main() #0 {
entry:
%.anon = alloca i32, align 4
%.anon1 = alloca i32, align 4
%.anon2 = alloca i32, align 4
%.anon3 = alloca i32, align 4
%i = alloca i32, align 4
%j = alloca i32, align 4
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%retparam7 = alloca i64, align 8
%taddr8 = alloca %"char[]", align 8
%vararg11 = alloca %"variant[]", align 8
%varargslots12 = alloca [1 x %variant], align 16
%retparam18 = alloca i64, align 8
%taddr19 = alloca %"char[]", align 8
%vararg22 = alloca %"variant[]", align 8
%varargslots23 = alloca [1 x %variant], align 16
%retparam29 = alloca i64, align 8
%taddr30 = alloca %"char[]", align 8
%vararg33 = alloca %"variant[]", align 8
%varargslots34 = alloca [1 x %variant], align 16
%x = alloca i32, align 4
%retparam40 = alloca i64, align 8
%taddr41 = alloca %"char[]", align 8
%vararg44 = alloca %"variant[]", align 8
%varargslots45 = alloca [1 x %variant], align 16
%taddr46 = alloca [3 x i8]*, align 8
%x52 = alloca double, align 8
%retparam53 = alloca i64, align 8
%taddr54 = alloca %"char[]", align 8
%vararg57 = alloca %"variant[]", align 8
%varargslots58 = alloca [1 x %variant], align 16
%taddr59 = alloca [6 x i8]*, align 8
%retparam65 = alloca i64, align 8
%taddr66 = alloca %"char[]", align 8
%vararg69 = alloca %"variant[]", align 8
%varargslots70 = alloca [1 x %variant], align 16
%taddr71 = alloca i32, align 4
%x77 = alloca i32, align 4
%y = alloca i32, align 4
%a = alloca i32, align 4
%retparam78 = alloca i64, align 8
%taddr79 = alloca %"char[]", align 8
%vararg82 = alloca %"variant[]", align 8
%varargslots83 = alloca [2 x %variant], align 16
%a89 = alloca i32, align 4
%retparam90 = alloca i64, align 8
%taddr91 = alloca %"char[]", align 8
%vararg94 = alloca %"variant[]", align 8
%varargslots95 = alloca [2 x %variant], align 16
%0 = call i32 @test_ping(i32 -1)
store i32 %0, i32* %.anon, align 4
%1 = call i32 @test_ping(i32 1)
store i32 %1, i32* %.anon1, align 4
%2 = call i32 @test_ping(i32 3141)
store i32 %2, i32* %.anon2, align 4
%3 = call i32 @test_ping(i32 1000)
store i32 %3, i32* %.anon3, align 4
%4 = load i32, i32* %.anon, align 4
%5 = load i32, i32* %.anon, align 4
%add = add i32 %4, %5
store i32 %add, i32* %i, align 4
%6 = call i32 @test_ping(i32 3141)
%7 = call i32 @test_ping(i32 3141)
%add4 = add i32 %6, %7
store i32 %add4, i32* %j, align 4
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.1, i32 0, i32 0), i64 2 }, %"char[]"* %taddr, align 8
%8 = bitcast %"char[]"* %taddr to { i8*, i64 }*
%9 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %8, i32 0, i32 0
%lo = load i8*, i8** %9, align 8
%10 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %8, i32 0, i32 1
%hi = load i64, i64* %10, align 8
%11 = bitcast i32* %.anon1 to i8*
%12 = insertvalue %variant undef, i8* %11, 0
%13 = insertvalue %variant %12, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%14 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0
store %variant %13, %variant* %14, align 16
%15 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1
store i64 1, i64* %15, align 8
%16 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0
%17 = bitcast [1 x %variant]* %varargslots to %variant*
store %variant* %17, %variant** %16, align 8
%18 = bitcast %"variant[]"* %vararg to { i8*, i64 }*
%19 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %18, i32 0, i32 0
%lo5 = load i8*, i8** %19, align 8
%20 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %18, i32 0, i32 1
%hi6 = load i64, i64* %20, align 8
%21 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo5, i64 %hi6)
%not_err = icmp eq i64 %21, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2, i32 0, i32 0), i64 2 }, %"char[]"* %taddr8, align 8
%22 = bitcast %"char[]"* %taddr8 to { i8*, i64 }*
%23 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %22, i32 0, i32 0
%lo9 = load i8*, i8** %23, align 8
%24 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %22, i32 0, i32 1
%hi10 = load i64, i64* %24, align 8
%25 = bitcast i32* %.anon to i8*
%26 = insertvalue %variant undef, i8* %25, 0
%27 = insertvalue %variant %26, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%28 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots12, i64 0, i64 0
store %variant %27, %variant* %28, align 16
%29 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg11, i32 0, i32 1
store i64 1, i64* %29, align 8
%30 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg11, i32 0, i32 0
%31 = bitcast [1 x %variant]* %varargslots12 to %variant*
store %variant* %31, %variant** %30, align 8
%32 = bitcast %"variant[]"* %vararg11 to { i8*, i64 }*
%33 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %32, i32 0, i32 0
%lo13 = load i8*, i8** %33, align 8
%34 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %32, i32 0, i32 1
%hi14 = load i64, i64* %34, align 8
%35 = call i64 @std_io_printfln(i64* %retparam7, i8* %lo9, i64 %hi10, i8* %lo13, i64 %hi14)
%not_err15 = icmp eq i64 %35, 0
br i1 %not_err15, label %after_check16, label %voiderr17
after_check16: ; preds = %voiderr
br label %voiderr17
voiderr17: ; preds = %after_check16, %voiderr
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.3, i32 0, i32 0), i64 2 }, %"char[]"* %taddr19, align 8
%36 = bitcast %"char[]"* %taddr19 to { i8*, i64 }*
%37 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %36, i32 0, i32 0
%lo20 = load i8*, i8** %37, align 8
%38 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %36, i32 0, i32 1
%hi21 = load i64, i64* %38, align 8
%39 = bitcast i32* %.anon2 to i8*
%40 = insertvalue %variant undef, i8* %39, 0
%41 = insertvalue %variant %40, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%42 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots23, i64 0, i64 0
store %variant %41, %variant* %42, align 16
%43 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg22, i32 0, i32 1
store i64 1, i64* %43, align 8
%44 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg22, i32 0, i32 0
%45 = bitcast [1 x %variant]* %varargslots23 to %variant*
store %variant* %45, %variant** %44, align 8
%46 = bitcast %"variant[]"* %vararg22 to { i8*, i64 }*
%47 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %46, i32 0, i32 0
%lo24 = load i8*, i8** %47, align 8
%48 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %46, i32 0, i32 1
%hi25 = load i64, i64* %48, align 8
%49 = call i64 @std_io_printfln(i64* %retparam18, i8* %lo20, i64 %hi21, i8* %lo24, i64 %hi25)
%not_err26 = icmp eq i64 %49, 0
br i1 %not_err26, label %after_check27, label %voiderr28
after_check27: ; preds = %voiderr17
br label %voiderr28
voiderr28: ; preds = %after_check27, %voiderr17
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.4, i32 0, i32 0), i64 2 }, %"char[]"* %taddr30, align 8
%50 = bitcast %"char[]"* %taddr30 to { i8*, i64 }*
%51 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %50, i32 0, i32 0
%lo31 = load i8*, i8** %51, align 8
%52 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %50, i32 0, i32 1
%hi32 = load i64, i64* %52, align 8
%53 = bitcast i32* %.anon3 to i8*
%54 = insertvalue %variant undef, i8* %53, 0
%55 = insertvalue %variant %54, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%56 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots34, i64 0, i64 0
store %variant %55, %variant* %56, align 16
%57 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg33, i32 0, i32 1
store i64 1, i64* %57, align 8
%58 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg33, i32 0, i32 0
%59 = bitcast [1 x %variant]* %varargslots34 to %variant*
store %variant* %59, %variant** %58, align 8
%60 = bitcast %"variant[]"* %vararg33 to { i8*, i64 }*
%61 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %60, i32 0, i32 0
%lo35 = load i8*, i8** %61, align 8
%62 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %60, i32 0, i32 1
%hi36 = load i64, i64* %62, align 8
%63 = call i64 @std_io_printfln(i64* %retparam29, i8* %lo31, i64 %hi32, i8* %lo35, i64 %hi36)
%not_err37 = icmp eq i64 %63, 0
br i1 %not_err37, label %after_check38, label %voiderr39
after_check38: ; preds = %voiderr28
br label %voiderr39
voiderr39: ; preds = %after_check38, %voiderr28
store i32 0, i32* %x, align 4
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.5, i32 0, i32 0), i64 2 }, %"char[]"* %taddr41, align 8
%64 = bitcast %"char[]"* %taddr41 to { i8*, i64 }*
%65 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %64, i32 0, i32 0
%lo42 = load i8*, i8** %65, align 8
%66 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %64, i32 0, i32 1
%hi43 = load i64, i64* %66, align 8
store [3 x i8]* bitcast ([4 x i8]* @.str.6 to [3 x i8]*), [3 x i8]** %taddr46, align 8
%67 = bitcast [3 x i8]** %taddr46 to i8*
%68 = insertvalue %variant undef, i8* %67, 0
%69 = insertvalue %variant %68, i64 ptrtoint (%.introspect* @"ct$p$a3$char" to i64), 1
%70 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots45, i64 0, i64 0
store %variant %69, %variant* %70, align 16
%71 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg44, i32 0, i32 1
store i64 1, i64* %71, align 8
%72 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg44, i32 0, i32 0
%73 = bitcast [1 x %variant]* %varargslots45 to %variant*
store %variant* %73, %variant** %72, align 8
%74 = bitcast %"variant[]"* %vararg44 to { i8*, i64 }*
%75 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %74, i32 0, i32 0
%lo47 = load i8*, i8** %75, align 8
%76 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %74, i32 0, i32 1
%hi48 = load i64, i64* %76, align 8
%77 = call i64 @std_io_printfln(i64* %retparam40, i8* %lo42, i64 %hi43, i8* %lo47, i64 %hi48)
%not_err49 = icmp eq i64 %77, 0
br i1 %not_err49, label %after_check50, label %voiderr51
after_check50: ; preds = %voiderr39
br label %voiderr51
voiderr51: ; preds = %after_check50, %voiderr39
store double 0.000000e+00, double* %x52, align 8
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.7, i32 0, i32 0), i64 2 }, %"char[]"* %taddr54, align 8
%78 = bitcast %"char[]"* %taddr54 to { i8*, i64 }*
%79 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %78, i32 0, i32 0
%lo55 = load i8*, i8** %79, align 8
%80 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %78, i32 0, i32 1
%hi56 = load i64, i64* %80, align 8
store [6 x i8]* bitcast ([7 x i8]* @.str.8 to [6 x i8]*), [6 x i8]** %taddr59, align 8
%81 = bitcast [6 x i8]** %taddr59 to i8*
%82 = insertvalue %variant undef, i8* %81, 0
%83 = insertvalue %variant %82, i64 ptrtoint (%.introspect* @"ct$p$a6$char" to i64), 1
%84 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots58, i64 0, i64 0
store %variant %83, %variant* %84, align 16
%85 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg57, i32 0, i32 1
store i64 1, i64* %85, align 8
%86 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg57, i32 0, i32 0
%87 = bitcast [1 x %variant]* %varargslots58 to %variant*
store %variant* %87, %variant** %86, align 8
%88 = bitcast %"variant[]"* %vararg57 to { i8*, i64 }*
%89 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %88, i32 0, i32 0
%lo60 = load i8*, i8** %89, align 8
%90 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %88, i32 0, i32 1
%hi61 = load i64, i64* %90, align 8
%91 = call i64 @std_io_printfln(i64* %retparam53, i8* %lo55, i64 %hi56, i8* %lo60, i64 %hi61)
%not_err62 = icmp eq i64 %91, 0
br i1 %not_err62, label %after_check63, label %voiderr64
after_check63: ; preds = %voiderr51
br label %voiderr64
voiderr64: ; preds = %after_check63, %voiderr51
store %"char[]" { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.9, i32 0, i32 0), i64 2 }, %"char[]"* %taddr66, align 8
%92 = bitcast %"char[]"* %taddr66 to { i8*, i64 }*
%93 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %92, i32 0, i32 0
%lo67 = load i8*, i8** %93, align 8
%94 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %92, i32 0, i32 1
%hi68 = load i64, i64* %94, align 8
store i32 105, i32* %taddr71, align 4
%95 = bitcast i32* %taddr71 to i8*
%96 = insertvalue %variant undef, i8* %95, 0
%97 = insertvalue %variant %96, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%98 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots70, i64 0, i64 0
store %variant %97, %variant* %98, align 16
%99 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg69, i32 0, i32 1
store i64 1, i64* %99, align 8
%100 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg69, i32 0, i32 0
%101 = bitcast [1 x %variant]* %varargslots70 to %variant*
store %variant* %101, %variant** %100, align 8
%102 = bitcast %"variant[]"* %vararg69 to { i8*, i64 }*
%103 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %102, i32 0, i32 0
%lo72 = load i8*, i8** %103, align 8
%104 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %102, i32 0, i32 1
%hi73 = load i64, i64* %104, align 8
%105 = call i64 @std_io_printfln(i64* %retparam65, i8* %lo67, i64 %hi68, i8* %lo72, i64 %hi73)
%not_err74 = icmp eq i64 %105, 0
br i1 %not_err74, label %after_check75, label %voiderr76
after_check75: ; preds = %voiderr64
br label %voiderr76
voiderr76: ; preds = %after_check75, %voiderr64
store i32 123, i32* %x77, align 4
store i32 33, i32* %y, align 4
%106 = load i32, i32* %x77, align 4
store i32 %106, i32* %a, align 4
%107 = load i32, i32* %y, align 4
store i32 %107, i32* %x77, align 4
%108 = load i32, i32* %a, align 4
store i32 %108, i32* %y, align 4
store %"char[]" { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.10, i32 0, i32 0), i64 6 }, %"char[]"* %taddr79, align 8
%109 = bitcast %"char[]"* %taddr79 to { i8*, i64 }*
%110 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %109, i32 0, i32 0
%lo80 = load i8*, i8** %110, align 8
%111 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %109, i32 0, i32 1
%hi81 = load i64, i64* %111, align 8
%112 = bitcast i32* %x77 to i8*
%113 = insertvalue %variant undef, i8* %112, 0
%114 = insertvalue %variant %113, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%115 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots83, i64 0, i64 0
store %variant %114, %variant* %115, align 16
%116 = bitcast i32* %y to i8*
%117 = insertvalue %variant undef, i8* %116, 0
%118 = insertvalue %variant %117, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%119 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots83, i64 0, i64 1
store %variant %118, %variant* %119, align 16
%120 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg82, i32 0, i32 1
store i64 2, i64* %120, align 8
%121 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg82, i32 0, i32 0
%122 = bitcast [2 x %variant]* %varargslots83 to %variant*
store %variant* %122, %variant** %121, align 8
%123 = bitcast %"variant[]"* %vararg82 to { i8*, i64 }*
%124 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %123, i32 0, i32 0
%lo84 = load i8*, i8** %124, align 8
%125 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %123, i32 0, i32 1
%hi85 = load i64, i64* %125, align 8
%126 = call i64 @std_io_printfln(i64* %retparam78, i8* %lo80, i64 %hi81, i8* %lo84, i64 %hi85)
%not_err86 = icmp eq i64 %126, 0
br i1 %not_err86, label %after_check87, label %voiderr88
after_check87: ; preds = %voiderr76
br label %voiderr88
voiderr88: ; preds = %after_check87, %voiderr76
%127 = load i32, i32* %x77, align 4
store i32 %127, i32* %a89, align 4
%128 = load i32, i32* %y, align 4
store i32 %128, i32* %x77, align 4
%129 = load i32, i32* %a89, align 4
store i32 %129, i32* %y, align 4
store %"char[]" { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.11, i32 0, i32 0), i64 6 }, %"char[]"* %taddr91, align 8
%130 = bitcast %"char[]"* %taddr91 to { i8*, i64 }*
%131 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %130, i32 0, i32 0
%lo92 = load i8*, i8** %131, align 8
%132 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %130, i32 0, i32 1
%hi93 = load i64, i64* %132, align 8
%133 = bitcast i32* %x77 to i8*
%134 = insertvalue %variant undef, i8* %133, 0
%135 = insertvalue %variant %134, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%136 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots95, i64 0, i64 0
store %variant %135, %variant* %136, align 16
%137 = bitcast i32* %y to i8*
%138 = insertvalue %variant undef, i8* %137, 0
%139 = insertvalue %variant %138, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%140 = getelementptr inbounds [2 x %variant], [2 x %variant]* %varargslots95, i64 0, i64 1
store %variant %139, %variant* %140, align 16
%141 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg94, i32 0, i32 1
store i64 2, i64* %141, align 8
%142 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg94, i32 0, i32 0
%143 = bitcast [2 x %variant]* %varargslots95 to %variant*
store %variant* %143, %variant** %142, align 8
%144 = bitcast %"variant[]"* %vararg94 to { i8*, i64 }*
%145 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %144, i32 0, i32 0
%lo96 = load i8*, i8** %145, align 8
%146 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %144, i32 0, i32 1
%hi97 = load i64, i64* %146, align 8
%147 = call i64 @std_io_printfln(i64* %retparam90, i8* %lo92, i64 %hi93, i8* %lo96, i64 %hi97)
%not_err98 = icmp eq i64 %147, 0
br i1 %not_err98, label %after_check99, label %voiderr100
after_check99: ; preds = %voiderr88
br label %voiderr100
voiderr100: ; preds = %after_check99, %voiderr88
ret void
}

View File

@@ -11,7 +11,7 @@ fn void main()
printf("Foo %d\n", $i);
$endfor;
$for (var $i = 0, var $j = 100; $i < 4;):
$for ($i = 0, var $j = 100; $i < 4;):
printf("Foo %d %d\n", $i++, $j--);
$endfor;

View File

@@ -7,4 +7,6 @@ fn void foo3(bar) { } // #error: Only typed parameters are allowed for functions
fn void foo4($Type) { } // #error: Only regular parameters are allowed for functions.
fn void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions.
fn void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions.
fn void foo9(int x, int x) {} // #error: Duplicate parameter name 'x'.

View File

@@ -2,10 +2,10 @@ macro foo(a, $b, $Type) {}
macro @foo2(a, $b, $Type) {}
macro bar(&x) // #error: non-function
macro bar(&x) // #error: Ref and expression parameters
{}
macro baz(#y) {} // #error: non-function
macro baz(#y) {} // #error: Ref and expression parameters
macro baz2(a; @body()) {} // #error: non-function
macro baz2(a; @body()) {} // #error: Names of macros

View File

@@ -0,0 +1,191 @@
// #target: macos-x64
module test;
import std::io;
macro foo(int... x)
{
foreach (i : x)
{
io::printfln("%d", i);
}
}
macro foo2(x...)
{
foreach (i : x)
{
io::printfln("%d", *(int*)i);
}
}
fn void main()
{
foo(1, -1, 3141, 999 + 1);
foo2(1, -1, 3141, 999 + 1);
}
/* #expect: test.ll
define void @test_main() #0 {
entry:
%x = alloca %"int[]", align 8
%literal = alloca [4 x i32], align 16
%.anon = alloca i64, align 8
%.anon1 = alloca i64, align 8
%i = alloca i32, align 4
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%x4 = alloca %"variant[]", align 8
%literal5 = alloca [4 x %variant], align 16
%taddr6 = alloca i32, align 4
%taddr7 = alloca i32, align 4
%taddr8 = alloca i32, align 4
%taddr9 = alloca i32, align 4
%.anon10 = alloca i64, align 8
%.anon11 = alloca i64, align 8
%i15 = alloca %variant, align 8
%retparam17 = alloca i64, align 8
%taddr18 = alloca %"char[]", align 8
%vararg21 = alloca %"variant[]", align 8
%varargslots22 = alloca [1 x %variant], align 16
%0 = getelementptr inbounds [4 x i32], ptr %literal, i64 0, i64 0
store i32 1, ptr %0, align 4
%1 = getelementptr inbounds [4 x i32], ptr %literal, i64 0, i64 1
store i32 -1, ptr %1, align 4
%2 = getelementptr inbounds [4 x i32], ptr %literal, i64 0, i64 2
store i32 3141, ptr %2, align 4
%3 = getelementptr inbounds [4 x i32], ptr %literal, i64 0, i64 3
store i32 1000, ptr %3, align 4
%4 = insertvalue %"int[]" undef, ptr %literal, 0
%5 = insertvalue %"int[]" %4, i64 4, 1
store %"int[]" %5, ptr %x, align 8
%6 = getelementptr inbounds %"int[]", ptr %x, i32 0, i32 1
%7 = load i64, ptr %6, align 8
store i64 %7, ptr %.anon, align 8
store i64 0, ptr %.anon1, align 8
br label %loop.cond
loop.cond: ; preds = %voiderr, %entry
%8 = load i64, ptr %.anon1, align 8
%9 = load i64, ptr %.anon, align 8
%lt = icmp ult i64 %8, %9
br i1 %lt, label %loop.body, label %loop.exit
loop.body: ; preds = %loop.cond
%10 = getelementptr inbounds %"int[]", ptr %x, i32 0, i32 0
%11 = load ptr, ptr %10, align 8
%12 = load i64, ptr %.anon1, align 8
%ptroffset = getelementptr inbounds i32, ptr %11, i64 %12
%13 = load i32, ptr %ptroffset, align 4
store i32 %13, ptr %i, align 4
store %"char[]" { ptr @.str, i64 2 }, ptr %taddr, align 8
%14 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0
%lo = load ptr, ptr %14, align 8
%15 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1
%hi = load i64, ptr %15, align 8
%16 = insertvalue %variant undef, ptr %i, 0
%17 = insertvalue %variant %16, i64 ptrtoint (ptr @"ct$int" to i64), 1
%18 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0
store %variant %17, ptr %18, align 16
%19 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1
store i64 1, ptr %19, align 8
%20 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0
store ptr %varargslots, ptr %20, align 8
%21 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0
%lo2 = load ptr, ptr %21, align 8
%22 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1
%hi3 = load i64, ptr %22, align 8
%23 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo2, i64 %hi3)
%not_err = icmp eq i64 %23, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %loop.body
br label %voiderr
voiderr: ; preds = %after_check, %loop.body
%24 = load i64, ptr %.anon1, align 8
%add = add i64 %24, 1
store i64 %add, ptr %.anon1, align 8
br label %loop.cond
loop.exit: ; preds = %loop.cond
%25 = getelementptr inbounds [4 x %variant], ptr %literal5, i64 0, i64 0
store i32 1, ptr %taddr6, align 4
%26 = insertvalue %variant undef, ptr %taddr6, 0
%27 = insertvalue %variant %26, i64 ptrtoint (ptr @"ct$int" to i64), 1
store %variant %27, ptr %25, align 8
%28 = getelementptr inbounds [4 x %variant], ptr %literal5, i64 0, i64 1
store i32 -1, ptr %taddr7, align 4
%29 = insertvalue %variant undef, ptr %taddr7, 0
%30 = insertvalue %variant %29, i64 ptrtoint (ptr @"ct$int" to i64), 1
store %variant %30, ptr %28, align 8
%31 = getelementptr inbounds [4 x %variant], ptr %literal5, i64 0, i64 2
store i32 3141, ptr %taddr8, align 4
%32 = insertvalue %variant undef, ptr %taddr8, 0
%33 = insertvalue %variant %32, i64 ptrtoint (ptr @"ct$int" to i64), 1
store %variant %33, ptr %31, align 8
%34 = getelementptr inbounds [4 x %variant], ptr %literal5, i64 0, i64 3
store i32 1000, ptr %taddr9, align 4
%35 = insertvalue %variant undef, ptr %taddr9, 0
%36 = insertvalue %variant %35, i64 ptrtoint (ptr @"ct$int" to i64), 1
store %variant %36, ptr %34, align 8
%37 = insertvalue %"variant[]" undef, ptr %literal5, 0
%38 = insertvalue %"variant[]" %37, i64 4, 1
store %"variant[]" %38, ptr %x4, align 8
%39 = getelementptr inbounds %"variant[]", ptr %x4, i32 0, i32 1
%40 = load i64, ptr %39, align 8
store i64 %40, ptr %.anon10, align 8
store i64 0, ptr %.anon11, align 8
br label %loop.cond12
loop.cond12: ; preds = %voiderr27, %loop.exit
%41 = load i64, ptr %.anon11, align 8
%42 = load i64, ptr %.anon10, align 8
%lt13 = icmp ult i64 %41, %42
br i1 %lt13, label %loop.body14, label %loop.exit29
loop.body14: ; preds = %loop.cond12
%43 = getelementptr inbounds %"variant[]", ptr %x4, i32 0, i32 0
%44 = load ptr, ptr %43, align 8
%45 = load i64, ptr %.anon11, align 8
%ptroffset16 = getelementptr inbounds %variant, ptr %44, i64 %45
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %i15, ptr align 8 %ptroffset16, i32 16, i1 false)
store %"char[]" { ptr @.str.1, i64 2 }, ptr %taddr18, align 8
%46 = getelementptr inbounds { ptr, i64 }, ptr %taddr18, i32 0, i32 0
%lo19 = load ptr, ptr %46, align 8
%47 = getelementptr inbounds { ptr, i64 }, ptr %taddr18, i32 0, i32 1
%hi20 = load i64, ptr %47, align 8
%48 = getelementptr inbounds %variant, ptr %i15, i32 0, i32 0
%49 = load ptr, ptr %48, align 8
%50 = insertvalue %variant undef, ptr %49, 0
%51 = insertvalue %variant %50, i64 ptrtoint (ptr @"ct$int" to i64), 1
%52 = getelementptr inbounds [1 x %variant], ptr %varargslots22, i64 0, i64 0
store %variant %51, ptr %52, align 16
%53 = getelementptr inbounds %"variant[]", ptr %vararg21, i32 0, i32 1
store i64 1, ptr %53, align 8
%54 = getelementptr inbounds %"variant[]", ptr %vararg21, i32 0, i32 0
store ptr %varargslots22, ptr %54, align 8
%55 = getelementptr inbounds { ptr, i64 }, ptr %vararg21, i32 0, i32 0
%lo23 = load ptr, ptr %55, align 8
%56 = getelementptr inbounds { ptr, i64 }, ptr %vararg21, i32 0, i32 1
%hi24 = load i64, ptr %56, align 8
%57 = call i64 @std_io_printfln(ptr %retparam17, ptr %lo19, i64 %hi20, ptr %lo23, i64 %hi24)
%not_err25 = icmp eq i64 %57, 0
br i1 %not_err25, label %after_check26, label %voiderr27
after_check26: ; preds = %loop.body14
br label %voiderr27
voiderr27: ; preds = %after_check26, %loop.body14
%58 = load i64, ptr %.anon11, align 8
%add28 = add i64 %58, 1
store i64 %add28, ptr %.anon11, align 8
br label %loop.cond12
loop.exit29: ; preds = %loop.cond12
ret void
}

View File

@@ -0,0 +1,25 @@
macro foo(...)
{
$vaarg_get_arg("hello"); // #error: Expected the argument index here
int x;
$vaarg_get_arg(x); // #error: Vararg functions need a constant argument
$vaarg_get_arg(-1); // #error: negative
$vaarg_get_arg(100); // #error: varargs exist
}
macro foo2(...)
{
$vaarg_get_const(0);
}
macro foo3(...)
{
$vaarg_get_type(0) a;
}
fn void main()
{
foo(1, -1, 3141, 999 + 1);
int x;
foo2(x); // #error: This argument needs to be a compile time constant
foo3(3); // #error: The argument was not a type.
}

View File

@@ -0,0 +1,421 @@
// #target: macos-x64
module test;
import std::io;
macro @foo(...)
{
int i = $vaarg_get_arg(1) + $vaarg_get_arg(1);
int j = $vaarg_get_expr(2) + $vaarg_get_expr(2);
$for (var $i = 0; $i < $vaarg_count(); $i++):
io::printfln("%d", $vaarg_get_arg($i));
$endfor;
}
macro foo2(...)
{
$for (var $i = 0; $i < $vaarg_count(); $i++):
{
$vaarg_get_type($i) x;
}
io::printfln("%s", $nameof($vaarg_get_type($i)));
$endfor;
}
macro foo3(...)
{
var $x = 0;
$for (var $i = 0; $i < $vaarg_count(); $i++):
$x += $vaarg_get_const($i);
$endfor;
return $x;
}
macro @foo4(...)
{
$typeof($vaarg_get_ref(0)) a = $vaarg_get_ref(0);
$vaarg_get_ref(0) = $vaarg_get_ref(1);
$vaarg_get_ref(1) = a;
}
fn int ping(int val)
{
io::printfln("Ping[%d]", val);
return val;
}
fn void main()
{
@foo(ping(1), ping(-1), ping(3141), ping(999 + 1));
foo2(int, double);
var $x = foo3(1, 4, 100);
io::printfln("%d", $x);
int x = 123;
int y = 33;
@foo4(x, y);
io::printfln("%d, %d", x, y);
@foo4(x, y);
io::printfln("%d, %d", x, y);
}
/* #expect: test.ll
define i32 @test_ping(i32 %0) #0 {
entry:
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%taddr1 = alloca i32, align 4
store %"char[]" { ptr @.str, i64 8 }, ptr %taddr, align 8
%1 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0
%lo = load ptr, ptr %1, align 8
%2 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1
%hi = load i64, ptr %2, align 8
store i32 %0, ptr %taddr1, align 4
%3 = insertvalue %variant undef, ptr %taddr1, 0
%4 = insertvalue %variant %3, i64 ptrtoint (ptr @"ct$int" to i64), 1
%5 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0
store %variant %4, ptr %5, align 16
%6 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1
store i64 1, ptr %6, align 8
%7 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0
store ptr %varargslots, ptr %7, align 8
%8 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0
%lo2 = load ptr, ptr %8, align 8
%9 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1
%hi3 = load i64, ptr %9, align 8
%10 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo2, i64 %hi3)
%not_err = icmp eq i64 %10, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
ret i32 %0
}
define void @test_main() #0 {
entry:
%.anon = alloca i32, align 4
%.anon1 = alloca i32, align 4
%.anon2 = alloca i32, align 4
%.anon3 = alloca i32, align 4
%i = alloca i32, align 4
%j = alloca i32, align 4
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%retparam7 = alloca i64, align 8
%taddr8 = alloca %"char[]", align 8
%vararg11 = alloca %"variant[]", align 8
%varargslots12 = alloca [1 x %variant], align 16
%retparam18 = alloca i64, align 8
%taddr19 = alloca %"char[]", align 8
%vararg22 = alloca %"variant[]", align 8
%varargslots23 = alloca [1 x %variant], align 16
%retparam29 = alloca i64, align 8
%taddr30 = alloca %"char[]", align 8
%vararg33 = alloca %"variant[]", align 8
%varargslots34 = alloca [1 x %variant], align 16
%x = alloca i32, align 4
%retparam40 = alloca i64, align 8
%taddr41 = alloca %"char[]", align 8
%vararg44 = alloca %"variant[]", align 8
%varargslots45 = alloca [1 x %variant], align 16
%taddr46 = alloca ptr, align 8
%x52 = alloca double, align 8
%retparam53 = alloca i64, align 8
%taddr54 = alloca %"char[]", align 8
%vararg57 = alloca %"variant[]", align 8
%varargslots58 = alloca [1 x %variant], align 16
%taddr59 = alloca ptr, align 8
%retparam65 = alloca i64, align 8
%taddr66 = alloca %"char[]", align 8
%vararg69 = alloca %"variant[]", align 8
%varargslots70 = alloca [1 x %variant], align 16
%taddr71 = alloca i32, align 4
%x77 = alloca i32, align 4
%y = alloca i32, align 4
%a = alloca i32, align 4
%retparam78 = alloca i64, align 8
%taddr79 = alloca %"char[]", align 8
%vararg82 = alloca %"variant[]", align 8
%varargslots83 = alloca [2 x %variant], align 16
%a89 = alloca i32, align 4
%retparam90 = alloca i64, align 8
%taddr91 = alloca %"char[]", align 8
%vararg94 = alloca %"variant[]", align 8
%varargslots95 = alloca [2 x %variant], align 16
%0 = call i32 @test_ping(i32 -1)
store i32 %0, ptr %.anon, align 4
%1 = call i32 @test_ping(i32 1)
store i32 %1, ptr %.anon1, align 4
%2 = call i32 @test_ping(i32 3141)
store i32 %2, ptr %.anon2, align 4
%3 = call i32 @test_ping(i32 1000)
store i32 %3, ptr %.anon3, align 4
%4 = load i32, ptr %.anon, align 4
%5 = load i32, ptr %.anon, align 4
%add = add i32 %4, %5
store i32 %add, ptr %i, align 4
%6 = call i32 @test_ping(i32 3141)
%7 = call i32 @test_ping(i32 3141)
%add4 = add i32 %6, %7
store i32 %add4, ptr %j, align 4
store %"char[]" { ptr @.str.1, i64 2 }, ptr %taddr, align 8
%8 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0
%lo = load ptr, ptr %8, align 8
%9 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1
%hi = load i64, ptr %9, align 8
%10 = insertvalue %variant undef, ptr %.anon1, 0
%11 = insertvalue %variant %10, i64 ptrtoint (ptr @"ct$int" to i64), 1
%12 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0
store %variant %11, ptr %12, align 16
%13 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1
store i64 1, ptr %13, align 8
%14 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0
store ptr %varargslots, ptr %14, align 8
%15 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0
%lo5 = load ptr, ptr %15, align 8
%16 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1
%hi6 = load i64, ptr %16, align 8
%17 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo5, i64 %hi6)
%not_err = icmp eq i64 %17, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
store %"char[]" { ptr @.str.2, i64 2 }, ptr %taddr8, align 8
%18 = getelementptr inbounds { ptr, i64 }, ptr %taddr8, i32 0, i32 0
%lo9 = load ptr, ptr %18, align 8
%19 = getelementptr inbounds { ptr, i64 }, ptr %taddr8, i32 0, i32 1
%hi10 = load i64, ptr %19, align 8
%20 = insertvalue %variant undef, ptr %.anon, 0
%21 = insertvalue %variant %20, i64 ptrtoint (ptr @"ct$int" to i64), 1
%22 = getelementptr inbounds [1 x %variant], ptr %varargslots12, i64 0, i64 0
store %variant %21, ptr %22, align 16
%23 = getelementptr inbounds %"variant[]", ptr %vararg11, i32 0, i32 1
store i64 1, ptr %23, align 8
%24 = getelementptr inbounds %"variant[]", ptr %vararg11, i32 0, i32 0
store ptr %varargslots12, ptr %24, align 8
%25 = getelementptr inbounds { ptr, i64 }, ptr %vararg11, i32 0, i32 0
%lo13 = load ptr, ptr %25, align 8
%26 = getelementptr inbounds { ptr, i64 }, ptr %vararg11, i32 0, i32 1
%hi14 = load i64, ptr %26, align 8
%27 = call i64 @std_io_printfln(ptr %retparam7, ptr %lo9, i64 %hi10, ptr %lo13, i64 %hi14)
%not_err15 = icmp eq i64 %27, 0
br i1 %not_err15, label %after_check16, label %voiderr17
after_check16: ; preds = %voiderr
br label %voiderr17
voiderr17: ; preds = %after_check16, %voiderr
store %"char[]" { ptr @.str.3, i64 2 }, ptr %taddr19, align 8
%28 = getelementptr inbounds { ptr, i64 }, ptr %taddr19, i32 0, i32 0
%lo20 = load ptr, ptr %28, align 8
%29 = getelementptr inbounds { ptr, i64 }, ptr %taddr19, i32 0, i32 1
%hi21 = load i64, ptr %29, align 8
%30 = insertvalue %variant undef, ptr %.anon2, 0
%31 = insertvalue %variant %30, i64 ptrtoint (ptr @"ct$int" to i64), 1
%32 = getelementptr inbounds [1 x %variant], ptr %varargslots23, i64 0, i64 0
store %variant %31, ptr %32, align 16
%33 = getelementptr inbounds %"variant[]", ptr %vararg22, i32 0, i32 1
store i64 1, ptr %33, align 8
%34 = getelementptr inbounds %"variant[]", ptr %vararg22, i32 0, i32 0
store ptr %varargslots23, ptr %34, align 8
%35 = getelementptr inbounds { ptr, i64 }, ptr %vararg22, i32 0, i32 0
%lo24 = load ptr, ptr %35, align 8
%36 = getelementptr inbounds { ptr, i64 }, ptr %vararg22, i32 0, i32 1
%hi25 = load i64, ptr %36, align 8
%37 = call i64 @std_io_printfln(ptr %retparam18, ptr %lo20, i64 %hi21, ptr %lo24, i64 %hi25)
%not_err26 = icmp eq i64 %37, 0
br i1 %not_err26, label %after_check27, label %voiderr28
after_check27: ; preds = %voiderr17
br label %voiderr28
voiderr28: ; preds = %after_check27, %voiderr17
store %"char[]" { ptr @.str.4, i64 2 }, ptr %taddr30, align 8
%38 = getelementptr inbounds { ptr, i64 }, ptr %taddr30, i32 0, i32 0
%lo31 = load ptr, ptr %38, align 8
%39 = getelementptr inbounds { ptr, i64 }, ptr %taddr30, i32 0, i32 1
%hi32 = load i64, ptr %39, align 8
%40 = insertvalue %variant undef, ptr %.anon3, 0
%41 = insertvalue %variant %40, i64 ptrtoint (ptr @"ct$int" to i64), 1
%42 = getelementptr inbounds [1 x %variant], ptr %varargslots34, i64 0, i64 0
store %variant %41, ptr %42, align 16
%43 = getelementptr inbounds %"variant[]", ptr %vararg33, i32 0, i32 1
store i64 1, ptr %43, align 8
%44 = getelementptr inbounds %"variant[]", ptr %vararg33, i32 0, i32 0
store ptr %varargslots34, ptr %44, align 8
%45 = getelementptr inbounds { ptr, i64 }, ptr %vararg33, i32 0, i32 0
%lo35 = load ptr, ptr %45, align 8
%46 = getelementptr inbounds { ptr, i64 }, ptr %vararg33, i32 0, i32 1
%hi36 = load i64, ptr %46, align 8
%47 = call i64 @std_io_printfln(ptr %retparam29, ptr %lo31, i64 %hi32, ptr %lo35, i64 %hi36)
%not_err37 = icmp eq i64 %47, 0
br i1 %not_err37, label %after_check38, label %voiderr39
after_check38: ; preds = %voiderr28
br label %voiderr39
voiderr39: ; preds = %after_check38, %voiderr28
store i32 0, ptr %x, align 4
store %"char[]" { ptr @.str.5, i64 2 }, ptr %taddr41, align 8
%48 = getelementptr inbounds { ptr, i64 }, ptr %taddr41, i32 0, i32 0
%lo42 = load ptr, ptr %48, align 8
%49 = getelementptr inbounds { ptr, i64 }, ptr %taddr41, i32 0, i32 1
%hi43 = load i64, ptr %49, align 8
store ptr @.str.6, ptr %taddr46, align 8
%50 = insertvalue %variant undef, ptr %taddr46, 0
%51 = insertvalue %variant %50, i64 ptrtoint (ptr @"ct$p$a3$char" to i64), 1
%52 = getelementptr inbounds [1 x %variant], ptr %varargslots45, i64 0, i64 0
store %variant %51, ptr %52, align 16
%53 = getelementptr inbounds %"variant[]", ptr %vararg44, i32 0, i32 1
store i64 1, ptr %53, align 8
%54 = getelementptr inbounds %"variant[]", ptr %vararg44, i32 0, i32 0
store ptr %varargslots45, ptr %54, align 8
%55 = getelementptr inbounds { ptr, i64 }, ptr %vararg44, i32 0, i32 0
%lo47 = load ptr, ptr %55, align 8
%56 = getelementptr inbounds { ptr, i64 }, ptr %vararg44, i32 0, i32 1
%hi48 = load i64, ptr %56, align 8
%57 = call i64 @std_io_printfln(ptr %retparam40, ptr %lo42, i64 %hi43, ptr %lo47, i64 %hi48)
%not_err49 = icmp eq i64 %57, 0
br i1 %not_err49, label %after_check50, label %voiderr51
after_check50: ; preds = %voiderr39
br label %voiderr51
voiderr51: ; preds = %after_check50, %voiderr39
store double 0.000000e+00, ptr %x52, align 8
store %"char[]" { ptr @.str.7, i64 2 }, ptr %taddr54, align 8
%58 = getelementptr inbounds { ptr, i64 }, ptr %taddr54, i32 0, i32 0
%lo55 = load ptr, ptr %58, align 8
%59 = getelementptr inbounds { ptr, i64 }, ptr %taddr54, i32 0, i32 1
%hi56 = load i64, ptr %59, align 8
store ptr @.str.8, ptr %taddr59, align 8
%60 = insertvalue %variant undef, ptr %taddr59, 0
%61 = insertvalue %variant %60, i64 ptrtoint (ptr @"ct$p$a6$char" to i64), 1
%62 = getelementptr inbounds [1 x %variant], ptr %varargslots58, i64 0, i64 0
store %variant %61, ptr %62, align 16
%63 = getelementptr inbounds %"variant[]", ptr %vararg57, i32 0, i32 1
store i64 1, ptr %63, align 8
%64 = getelementptr inbounds %"variant[]", ptr %vararg57, i32 0, i32 0
store ptr %varargslots58, ptr %64, align 8
%65 = getelementptr inbounds { ptr, i64 }, ptr %vararg57, i32 0, i32 0
%lo60 = load ptr, ptr %65, align 8
%66 = getelementptr inbounds { ptr, i64 }, ptr %vararg57, i32 0, i32 1
%hi61 = load i64, ptr %66, align 8
%67 = call i64 @std_io_printfln(ptr %retparam53, ptr %lo55, i64 %hi56, ptr %lo60, i64 %hi61)
%not_err62 = icmp eq i64 %67, 0
br i1 %not_err62, label %after_check63, label %voiderr64
after_check63: ; preds = %voiderr51
br label %voiderr64
voiderr64: ; preds = %after_check63, %voiderr51
store %"char[]" { ptr @.str.9, i64 2 }, ptr %taddr66, align 8
%68 = getelementptr inbounds { ptr, i64 }, ptr %taddr66, i32 0, i32 0
%lo67 = load ptr, ptr %68, align 8
%69 = getelementptr inbounds { ptr, i64 }, ptr %taddr66, i32 0, i32 1
%hi68 = load i64, ptr %69, align 8
store i32 105, ptr %taddr71, align 4
%70 = insertvalue %variant undef, ptr %taddr71, 0
%71 = insertvalue %variant %70, i64 ptrtoint (ptr @"ct$int" to i64), 1
%72 = getelementptr inbounds [1 x %variant], ptr %varargslots70, i64 0, i64 0
store %variant %71, ptr %72, align 16
%73 = getelementptr inbounds %"variant[]", ptr %vararg69, i32 0, i32 1
store i64 1, ptr %73, align 8
%74 = getelementptr inbounds %"variant[]", ptr %vararg69, i32 0, i32 0
store ptr %varargslots70, ptr %74, align 8
%75 = getelementptr inbounds { ptr, i64 }, ptr %vararg69, i32 0, i32 0
%lo72 = load ptr, ptr %75, align 8
%76 = getelementptr inbounds { ptr, i64 }, ptr %vararg69, i32 0, i32 1
%hi73 = load i64, ptr %76, align 8
%77 = call i64 @std_io_printfln(ptr %retparam65, ptr %lo67, i64 %hi68, ptr %lo72, i64 %hi73)
%not_err74 = icmp eq i64 %77, 0
br i1 %not_err74, label %after_check75, label %voiderr76
after_check75: ; preds = %voiderr64
br label %voiderr76
voiderr76: ; preds = %after_check75, %voiderr64
store i32 123, ptr %x77, align 4
store i32 33, ptr %y, align 4
%78 = load i32, ptr %x77, align 4
store i32 %78, ptr %a, align 4
%79 = load i32, ptr %y, align 4
store i32 %79, ptr %x77, align 4
%80 = load i32, ptr %a, align 4
store i32 %80, ptr %y, align 4
store %"char[]" { ptr @.str.10, i64 6 }, ptr %taddr79, align 8
%81 = getelementptr inbounds { ptr, i64 }, ptr %taddr79, i32 0, i32 0
%lo80 = load ptr, ptr %81, align 8
%82 = getelementptr inbounds { ptr, i64 }, ptr %taddr79, i32 0, i32 1
%hi81 = load i64, ptr %82, align 8
%83 = insertvalue %variant undef, ptr %x77, 0
%84 = insertvalue %variant %83, i64 ptrtoint (ptr @"ct$int" to i64), 1
%85 = getelementptr inbounds [2 x %variant], ptr %varargslots83, i64 0, i64 0
store %variant %84, ptr %85, align 16
%86 = insertvalue %variant undef, ptr %y, 0
%87 = insertvalue %variant %86, i64 ptrtoint (ptr @"ct$int" to i64), 1
%88 = getelementptr inbounds [2 x %variant], ptr %varargslots83, i64 0, i64 1
store %variant %87, ptr %88, align 16
%89 = getelementptr inbounds %"variant[]", ptr %vararg82, i32 0, i32 1
store i64 2, ptr %89, align 8
%90 = getelementptr inbounds %"variant[]", ptr %vararg82, i32 0, i32 0
store ptr %varargslots83, ptr %90, align 8
%91 = getelementptr inbounds { ptr, i64 }, ptr %vararg82, i32 0, i32 0
%lo84 = load ptr, ptr %91, align 8
%92 = getelementptr inbounds { ptr, i64 }, ptr %vararg82, i32 0, i32 1
%hi85 = load i64, ptr %92, align 8
%93 = call i64 @std_io_printfln(ptr %retparam78, ptr %lo80, i64 %hi81, ptr %lo84, i64 %hi85)
%not_err86 = icmp eq i64 %93, 0
br i1 %not_err86, label %after_check87, label %voiderr88
after_check87: ; preds = %voiderr76
br label %voiderr88
voiderr88: ; preds = %after_check87, %voiderr76
%94 = load i32, ptr %x77, align 4
store i32 %94, ptr %a89, align 4
%95 = load i32, ptr %y, align 4
store i32 %95, ptr %x77, align 4
%96 = load i32, ptr %a89, align 4
store i32 %96, ptr %y, align 4
store %"char[]" { ptr @.str.11, i64 6 }, ptr %taddr91, align 8
%97 = getelementptr inbounds { ptr, i64 }, ptr %taddr91, i32 0, i32 0
%lo92 = load ptr, ptr %97, align 8
%98 = getelementptr inbounds { ptr, i64 }, ptr %taddr91, i32 0, i32 1
%hi93 = load i64, ptr %98, align 8
%99 = insertvalue %variant undef, ptr %x77, 0
%100 = insertvalue %variant %99, i64 ptrtoint (ptr @"ct$int" to i64), 1
%101 = getelementptr inbounds [2 x %variant], ptr %varargslots95, i64 0, i64 0
store %variant %100, ptr %101, align 16
%102 = insertvalue %variant undef, ptr %y, 0
%103 = insertvalue %variant %102, i64 ptrtoint (ptr @"ct$int" to i64), 1
%104 = getelementptr inbounds [2 x %variant], ptr %varargslots95, i64 0, i64 1
store %variant %103, ptr %104, align 16
%105 = getelementptr inbounds %"variant[]", ptr %vararg94, i32 0, i32 1
store i64 2, ptr %105, align 8
%106 = getelementptr inbounds %"variant[]", ptr %vararg94, i32 0, i32 0
store ptr %varargslots95, ptr %106, align 8
%107 = getelementptr inbounds { ptr, i64 }, ptr %vararg94, i32 0, i32 0
%lo96 = load ptr, ptr %107, align 8
%108 = getelementptr inbounds { ptr, i64 }, ptr %vararg94, i32 0, i32 1
%hi97 = load i64, ptr %108, align 8
%109 = call i64 @std_io_printfln(ptr %retparam90, ptr %lo92, i64 %hi93, ptr %lo96, i64 %hi97)
%not_err98 = icmp eq i64 %109, 0
br i1 %not_err98, label %after_check99, label %voiderr100
after_check99: ; preds = %voiderr88
br label %voiderr100
voiderr100: ; preds = %after_check99, %voiderr88
ret void
}