working on vararg piping: detecting several error cases, adding -Wunsafe-types and -funsafe-varargs

This commit is contained in:
Wolfgang Bumiller 2013-06-12 17:23:39 +02:00
parent 5012616cb0
commit 179da9241c
4 changed files with 126 additions and 24 deletions

97
ast.c
View file

@ -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
View file

@ -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
*

View file

@ -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

View file

@ -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))
{