]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
-Wdifferent-qualifiers, -Wdifferent-attributes
[xonotic/gmqcc.git] / parser.c
index 90dc44cb91613e2db84439d10bb568ea9cf1b336..8b7cc7b80f29534d3cfcab4115e8156607af60d7 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -104,7 +104,7 @@ static void parser_enterblock(parser_t *parser);
 static bool parser_leaveblock(parser_t *parser);
 static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
 static bool parse_typedef(parser_t *parser);
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static);
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags);
 static ast_block* parse_block(parser_t *parser);
 static bool parse_block_into(parser_t *parser, ast_block *block);
 static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
@@ -800,6 +800,64 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     {
                         if (CanConstFold(exprs[0], exprs[1]))
                             out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1)));
+                        else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[0])) {
+                            vector vec = ConstV(0);
+                            if (!vec.y && !vec.z) { /* 'n 0 0' * v */
+                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+                                out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL);
+                                out->expression.node.keep = false;
+                                ((ast_member*)out)->rvalue = true;
+                                if (vec.x != 1)
+                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out);
+                            }
+                            else if (!vec.x && !vec.z) { /* '0 n 0' * v */
+                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+                                out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL);
+                                out->expression.node.keep = false;
+                                ((ast_member*)out)->rvalue = true;
+                                if (vec.y != 1)
+                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out);
+                            }
+                            else if (!vec.x && !vec.y) { /* '0 n 0' * v */
+                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+                                out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL);
+                                out->expression.node.keep = false;
+                                ((ast_member*)out)->rvalue = true;
+                                if (vec.z != 1)
+                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out);
+                            }
+                            else
+                                out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
+                        }
+                        else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[1])) {
+                            vector vec = ConstV(1);
+                            if (!vec.y && !vec.z) { /* v * 'n 0 0' */
+                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+                                out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
+                                out->expression.node.keep = false;
+                                ((ast_member*)out)->rvalue = true;
+                                if (vec.x != 1)
+                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x));
+                            }
+                            else if (!vec.x && !vec.z) { /* v * '0 n 0' */
+                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+                                out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
+                                out->expression.node.keep = false;
+                                ((ast_member*)out)->rvalue = true;
+                                if (vec.y != 1)
+                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y));
+                            }
+                            else if (!vec.x && !vec.y) { /* v * '0 n 0' */
+                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+                                out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
+                                out->expression.node.keep = false;
+                                ((ast_member*)out)->rvalue = true;
+                                if (vec.z != 1)
+                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z));
+                            }
+                            else
+                                out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
+                        }
                         else
                             out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
                     }
@@ -2332,7 +2390,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
                              "current standard does not allow variable declarations in for-loop initializers"))
                 goto onerr;
         }
-        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, false))
+        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0))
             goto onerr;
     }
     else if (parser->tok != ';')
@@ -2505,14 +2563,14 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
 /* returns true when it was a variable qualifier, false otherwise!
  * on error, cvq is set to CV_WRONG
  */
-static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn, bool *is_static)
+static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags)
 {
     bool had_const    = false;
     bool had_var      = false;
     bool had_noref    = false;
-    bool had_noreturn = false;
     bool had_attrib   = false;
     bool had_static   = false;
+    uint32_t flags    = 0;
 
     *cvq = CV_NONE;
     for (;;) {
@@ -2525,7 +2583,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
                 return false;
             }
             if (!strcmp(parser_tokval(parser), "noreturn")) {
-                had_noreturn = true;
+                flags |= AST_FLAG_NORETURN;
                 if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
                     parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`");
                     *cvq = CV_WRONG;
@@ -2540,6 +2598,14 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "inline")) {
+                flags |= AST_FLAG_INLINE;
+                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+            }
             else
             {
                 /* Skip tokens until we hit a ]] */
@@ -2563,7 +2629,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
             had_var = true;
         else if (!strcmp(parser_tokval(parser), "noref"))
             had_noref = true;
-        else if (!had_const && !had_var && !had_noref && !had_noreturn && !had_attrib && !had_static) {
+        else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) {
             return false;
         }
         else
@@ -2578,8 +2644,8 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
     else
         *cvq = CV_NONE;
     *noref     = had_noref;
-    *noreturn  = had_noreturn;
     *is_static = had_static;
+    *_flags    = flags;
     return true;
 onerr:
     parseerror(parser, "parse error after variable qualifier");
@@ -2648,7 +2714,8 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
     ast_switch_case swcase;
 
     int  cvq;
-    bool noref, noreturn, is_static;
+    bool noref, is_static;
+    uint32_t qflags = 0;
 
     lex_ctx ctx = parser_ctx(parser);
 
@@ -2694,19 +2761,19 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
         if (parser->tok == TOKEN_IDENT)
             typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
         if (typevar || parser->tok == TOKEN_TYPENAME) {
-            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false)) {
+            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0)) {
                 ast_delete(switchnode);
                 return false;
             }
             continue;
         }
-        if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn, &is_static))
+        if (parse_qualifiers(parser, true, &cvq, &noref, &is_static, &qflags))
         {
             if (cvq == CV_WRONG) {
                 ast_delete(switchnode);
                 return false;
             }
-            if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn, is_static)) {
+            if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags)) {
                 ast_delete(switchnode);
                 return false;
             }
@@ -2921,8 +2988,9 @@ static bool parse_pragma(parser_t *parser)
 
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
 {
-    bool       noref, noreturn, is_static;
+    bool       noref, is_static;
     int        cvq = CV_NONE;
+    uint32_t   qflags = 0;
     ast_value *typevar = NULL;
 
     *out = NULL;
@@ -2941,15 +3009,15 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
                 return false;
         }
-        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false))
+        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0))
             return false;
         return true;
     }
-    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn, &is_static))
+    else if (parse_qualifiers(parser, !!block, &cvq, &noref, &is_static, &qflags))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, block, true, cvq, NULL, noref, noreturn, is_static);
+        return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -4205,7 +4273,45 @@ static bool parse_typedef(parser_t *parser)
     return true;
 }
 
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static)
+static const char *cvq_to_str(int cvq) {
+    switch (cvq) {
+        case CV_NONE:  return "none";
+        case CV_VAR:   return "`var`";
+        case CV_CONST: return "`const`";
+        default:       return "<INVALID>";
+    }
+}
+
+static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, const ast_value *proto)
+{
+    bool av, ao;
+    if (proto->cvq != var->cvq) {
+        if (!(proto->cvq == CV_CONST && var->cvq == CV_NONE &&
+              !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+              parser->tok == '='))
+        {
+            return !parsewarning(parser, WARN_DIFFERENT_QUALIFIERS,
+                                 "`%s` declared with different qualifiers: %s\n"
+                                 " -> previous declaration here: %s:%i uses %s",
+                                 var->name, cvq_to_str(var->cvq),
+                                 ast_ctx(proto).file, ast_ctx(proto).line,
+                                 cvq_to_str(proto->cvq));
+        }
+    }
+    av = (var  ->expression.flags & AST_FLAG_NORETURN);
+    ao = (proto->expression.flags & AST_FLAG_NORETURN);
+    if (av != ao) {
+        return !parsewarning(parser, WARN_DIFFERENT_ATTRIBUTES,
+                             "`%s` declared with different attributes%s\n"
+                             " -> previous declaration here: %s:%i",
+                             var->name, (av ? ": noreturn" : ""),
+                             ast_ctx(proto).file, ast_ctx(proto).line,
+                             (ao ? ": noreturn" : ""));
+    }
+    return true;
+}
+
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags)
 {
     ast_value *var;
     ast_value *proto;
@@ -4272,11 +4378,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         }
 
         var->cvq = qualifier;
-        /* in a noref section we simply bump the usecount */
-        if (noref || parser->noref)
-            var->uses++;
-        if (noreturn)
-            var->expression.flags |= AST_FLAG_NORETURN;
+        var->expression.flags |= qflags;
 
         /* Part 1:
          * check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
@@ -4367,6 +4469,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     /* we need the new parameter-names */
                     for (i = 0; i < vec_size(proto->expression.params); ++i)
                         ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name);
+                    if (!parser_check_qualifiers(parser, var, proto)) {
+                        retval = false;
+                        proto = NULL;
+                        goto cleanup;
+                    }
+                    proto->expression.flags |= var->expression.flags;
                     ast_delete(var);
                     var = proto;
                 }
@@ -4394,6 +4502,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                                 proto = NULL;
                                 goto cleanup;
                             }
+                            if (!parser_check_qualifiers(parser, var, proto)) {
+                                retval = false;
+                                proto = NULL;
+                                goto cleanup;
+                            }
+                            proto->expression.flags |= var->expression.flags;
                             ast_delete(var);
                             var = proto;
                         }
@@ -4437,6 +4551,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             }
         }
 
+        /* in a noref section we simply bump the usecount */
+        if (noref || parser->noref)
+            var->uses++;
+
         /* Part 2:
          * Create the global/local, and deal with vector types.
          */
@@ -4789,8 +4907,8 @@ static bool parser_global_statement(parser_t *parser)
 {
     int        cvq       = CV_WRONG;
     bool       noref     = false;
-    bool       noreturn  = false;
     bool       is_static = false;
+    uint32_t   qflags    = 0;
     ast_value *istype    = NULL;
 
     if (parser->tok == TOKEN_IDENT)
@@ -4798,13 +4916,13 @@ static bool parser_global_statement(parser_t *parser)
 
     if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
-        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, false);
+        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0);
     }
-    else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn, &is_static))
+    else if (parse_qualifiers(parser, false, &cvq, &noref, &is_static, &qflags))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn, is_static);
+        return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {