From: Wolfgang Bumiller Date: Wed, 12 Jun 2013 15:23:39 +0000 (+0200) Subject: working on vararg piping: detecting several error cases, adding -Wunsafe-types and... X-Git-Tag: v0.3.0~133 X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=commitdiff_plain;h=179da9241c50a79f76ac9ec9c097702ec21ad2b3 working on vararg piping: detecting several error cases, adding -Wunsafe-types and -funsafe-varargs --- diff --git a/ast.c b/ast.c index 94b254c..332f708 100644 --- a/ast.c +++ b/ast.c @@ -42,6 +42,8 @@ static bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**); static void ast_array_index_delete(ast_array_index*); static bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_value**); +static void ast_argpipe_delete(ast_argpipe*); +static bool ast_argpipe_codegen(ast_argpipe*, ast_function*, bool lvalue, ir_value**); static void ast_store_delete(ast_store*); static bool ast_store_codegen(ast_store*, ast_function*, bool lvalue, ir_value**); static void ast_ifthen_delete(ast_ifthen*); @@ -694,6 +696,23 @@ void ast_array_index_delete(ast_array_index *self) mem_d(self); } +ast_argpipe* ast_argpipe_new(lex_ctx ctx, ast_expression *index) +{ + ast_instantiate(ast_argpipe, ctx, ast_argpipe_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_argpipe_codegen); + self->index = index; + self->expression.vtype = TYPE_NOEXPR; + return self; +} + +void ast_argpipe_delete(ast_argpipe *self) +{ + if (self->index) + ast_unref(self->index); + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse) { ast_instantiate(ast_ifthen, ctx, ast_ifthen_delete); @@ -950,7 +969,50 @@ void ast_call_delete(ast_call *self) mem_d(self); } -bool ast_call_check_types(ast_call *self) +static bool ast_call_check_vararg(ast_call *self, ast_expression *va_type, ast_expression *exp_type) +{ + char texp[1024]; + char tgot[1024]; + if (!exp_type) + return true; + if (!va_type || !ast_compare_type(va_type, exp_type)) + { + if (va_type && exp_type) + { + ast_type_to_string(va_type, tgot, sizeof(tgot)); + ast_type_to_string(exp_type, texp, sizeof(texp)); + if (OPTS_FLAG(UNSAFE_VARARGS)) { + if (compile_warning(ast_ctx(self), WARN_UNSAFE_TYPES, + "piped variadic argument differs in type: constrained to type %s, expected type %s", + tgot, texp)) + return false; + } else { + compile_error(ast_ctx(self), + "piped variadic argument differs in type: constrained to type %s, expected type %s", + tgot, texp); + return false; + } + } + else + { + ast_type_to_string(exp_type, texp, sizeof(texp)); + if (OPTS_FLAG(UNSAFE_VARARGS)) { + if (compile_warning(ast_ctx(self), WARN_UNSAFE_TYPES, + "piped variadic argument may differ in type: expected type %s", + texp)) + return false; + } else { + compile_error(ast_ctx(self), + "piped variadic argument may differ in type: expected type %s", + texp); + return false; + } + } + } + return true; +} + +bool ast_call_check_types(ast_call *self, ast_expression *va_type) { char texp[1024]; char tgot[1024]; @@ -962,7 +1024,16 @@ bool ast_call_check_types(ast_call *self) count = vec_size(func->params); for (i = 0; i < count; ++i) { - if (!ast_compare_type(self->params[i], (ast_expression*)(func->params[i]))) + if (ast_istype(self->params[i], ast_argpipe)) { + // warn about type safety instead + if (i+1 != count) { + compile_error(ast_ctx(self), "argpipe must be the last parameter to a function call"); + return false; + } + if (!ast_call_check_vararg(self, va_type, (ast_expression*)func->params[i])) + retval = false; + } + else if (!ast_compare_type(self->params[i], (ast_expression*)(func->params[i]))) { ast_type_to_string(self->params[i], tgot, sizeof(tgot)); ast_type_to_string((ast_expression*)func->params[i], texp, sizeof(texp)); @@ -975,6 +1046,15 @@ bool ast_call_check_types(ast_call *self) count = vec_size(self->params); if (count > vec_size(func->params) && func->varparam) { for (; i < count; ++i) { + if (ast_istype(self->params[i], ast_argpipe)) { + // warn about type safety instead + if (i+1 != count) { + compile_error(ast_ctx(self), "argpipe must be the last parameter to a function call"); + return false; + } + if (!ast_call_check_vararg(self, va_type, func->varparam)) + retval = false; + } if (!ast_compare_type(self->params[i], func->varparam)) { ast_type_to_string(self->params[i], tgot, sizeof(tgot)); @@ -2404,6 +2484,19 @@ bool ast_array_index_codegen(ast_array_index *self, ast_function *func, bool lva return true; } +bool ast_argpipe_codegen(ast_argpipe *self, ast_function *func, bool lvalue, ir_value **out) +{ + *out = NULL; + if (lvalue) { + compile_error(ast_ctx(self), "argpipe node: not an lvalue"); + return false; + } + (void)func; + (void)out; + compile_error(ast_ctx(self), "TODO: argpipe codegen not implemented"); + return false; +} + bool ast_ifthen_codegen(ast_ifthen *self, ast_function *func, bool lvalue, ir_value **out) { ast_expression_codegen *cgen; diff --git a/ast.h b/ast.h index 1c4574a..ebb29a7 100644 --- a/ast.h +++ b/ast.h @@ -51,6 +51,7 @@ typedef struct ast_breakcont_s ast_breakcont; typedef struct ast_switch_s ast_switch; typedef struct ast_label_s ast_label; typedef struct ast_goto_s ast_goto; +typedef struct ast_argpipe_s ast_argpipe; enum { TYPE_ast_node, /* 0 */ @@ -73,7 +74,8 @@ enum { TYPE_ast_breakcont, /* 17 */ TYPE_ast_switch, /* 18 */ TYPE_ast_label, /* 19 */ - TYPE_ast_goto /* 20 */ + TYPE_ast_goto, /* 20 */ + TYPE_ast_argpipe /* 21 */ }; #define ast_istype(x, t) ( ((ast_node*)x)->nodetype == (TYPE_##t) ) @@ -365,6 +367,17 @@ struct ast_array_index_s }; ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_expression *index); +/* Vararg pipe node: + * + * copy all varargs starting from a specific index + */ +struct ast_argpipe_s +{ + ast_expression expression; + ast_expression *index; +}; +ast_argpipe* ast_argpipe_new(lex_ctx ctx, ast_expression *index); + /* Store * * Stores left<-right and returns left. @@ -555,7 +568,7 @@ struct ast_call_s }; ast_call* ast_call_new(lex_ctx ctx, ast_expression *funcexpr); -bool ast_call_check_types(ast_call*); +bool ast_call_check_types(ast_call*, ast_expression *this_func_va_type); /* Blocks * diff --git a/opts.def b/opts.def index 77a4094..4e1aa18 100644 --- a/opts.def +++ b/opts.def @@ -52,6 +52,7 @@ GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS) GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS) GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS) + GMQCC_DEFINE_FLAG(UNSAFE_VARARGS) #endif /* warning flags */ @@ -89,6 +90,7 @@ GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES) GMQCC_DEFINE_FLAG(DEPRECATED) GMQCC_DEFINE_FLAG(PARENTHESIS) + GMQCC_DEFINE_FLAG(UNSAFE_TYPES) #endif #ifdef GMQCC_TYPE_OPTIMIZATIONS diff --git a/parser.c b/parser.c index d6490d4..c9717b0 100644 --- a/parser.c +++ b/parser.c @@ -1568,7 +1568,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) for (i = 0; i < paramcount; ++i) vec_push(call->params, sy->out[fid+1 + i].out); vec_shrinkby(sy->out, paramcount); - (void)!ast_call_check_types(call); + (void)!ast_call_check_types(call, parser->function->vtype->expression.varparam); if (parser->max_param_count < paramcount) parser->max_param_count = paramcount; @@ -1704,6 +1704,11 @@ static ast_expression* parse_vararg_do(parser_t *parser) ast_value *typevar; ast_value *funtype = parser->function->vtype; + if (!parser->function->varargs) { + parseerror(parser, "function has no variable argument list"); + return NULL; + } + lex_ctx ctx = parser_ctx(parser); if (!parser_next(parser) || parser->tok != '(') { @@ -1720,9 +1725,14 @@ static ast_expression* parse_vararg_do(parser_t *parser) return NULL; if (parser->tok != ',') { - ast_unref(idx); - parseerror(parser, "expected comma after parameter index"); - return NULL; + if (parser->tok != ')') { + ast_unref(idx); + parseerror(parser, "expected comma after parameter index"); + return NULL; + } + /* vararg piping: ...(start) */ + out = (ast_expression*)ast_argpipe_new(ctx, idx); + return out; } if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) { @@ -1744,22 +1754,6 @@ static ast_expression* parse_vararg_do(parser_t *parser) return NULL; } -#if 0 - if (!parser_next(parser)) { - ast_unref(idx); - ast_delete(typevar); - parseerror(parser, "parse error after vararg"); - return NULL; - } -#endif - - if (!parser->function->varargs) { - ast_unref(idx); - ast_delete(typevar); - parseerror(parser, "function has no variable argument list"); - return NULL; - } - if (funtype->expression.varparam && !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam)) {