]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
working on vararg piping: detecting several error cases, adding -Wunsafe-types and...
authorWolfgang Bumiller <wry.git@bumiller.com>
Wed, 12 Jun 2013 15:23:39 +0000 (17:23 +0200)
committerWolfgang Bumiller <wry.git@bumiller.com>
Sat, 15 Jun 2013 07:46:44 +0000 (09:46 +0200)
ast.c
ast.h
opts.def
parser.c

diff --git a/ast.c b/ast.c
index 94b254cc91f5a3ad3931fe81d9654e1d738f4047..332f708fbad59d4c9c951e728641df9494c57ba8 100644 (file)
--- 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 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*);
 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);
 }
 
     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);
 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);
 }
 
     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];
 {
     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) {
         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));
         {
             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) {
     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));
             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;
 }
 
     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;
 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 1c4574a57182143982355403ae03c96ab1dcb566..ebb29a779885ff4f5f0804db2cab26d881ff2470 100644 (file)
--- 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_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 */
 
 enum {
     TYPE_ast_node,        /*  0 */
@@ -73,7 +74,8 @@ enum {
     TYPE_ast_breakcont,   /* 17 */
     TYPE_ast_switch,      /* 18 */
     TYPE_ast_label,       /* 19 */
     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) )
 };
 
 #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);
 
 };
 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.
 /* 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);
 };
 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
  *
 
 /* Blocks
  *
index 77a4094115b3535e2090f5f62bf028a2288fe834..4e1aa18883075a73eaf631b58b942ebbce266c5a 100644 (file)
--- 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(LEGACY_VECTOR_MATHS)
     GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
     GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
+    GMQCC_DEFINE_FLAG(UNSAFE_VARARGS)
 #endif
 
 /* warning flags */
 #endif
 
 /* warning flags */
@@ -89,6 +90,7 @@
     GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES)
     GMQCC_DEFINE_FLAG(DEPRECATED)
     GMQCC_DEFINE_FLAG(PARENTHESIS)
     GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES)
     GMQCC_DEFINE_FLAG(DEPRECATED)
     GMQCC_DEFINE_FLAG(PARENTHESIS)
+    GMQCC_DEFINE_FLAG(UNSAFE_TYPES)
 #endif
 
 #ifdef GMQCC_TYPE_OPTIMIZATIONS
 #endif
 
 #ifdef GMQCC_TYPE_OPTIMIZATIONS
index d6490d4cd545be1a630cddf351b2107443d7295b..c9717b077eb5926f4fc8a743216f54782bb51b3c 100644 (file)
--- 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);
     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;
 
     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;
 
     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 != '(') {
     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 != ',') {
         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)) {
     }
 
     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;
     }
 
         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))
     {
     if (funtype->expression.varparam &&
         !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam))
     {