mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-03-22 02:31:28 +00:00
working on vararg piping: detecting several error cases, adding -Wunsafe-types and -funsafe-varargs
This commit is contained in:
parent
5012616cb0
commit
179da9241c
4 changed files with 126 additions and 24 deletions
97
ast.c
97
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;
|
||||
|
|
17
ast.h
17
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
|
||||
*
|
||||
|
|
2
opts.def
2
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
|
||||
|
|
34
parser.c
34
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))
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue