]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Only optimize (a - (-b)) into (a + b) when the unary operand is a negation. This...
[xonotic/gmqcc.git] / parser.c
index f3bfdfb6ff23e72b1fd674aaaab8e4ef96030472..f53116180c5679591dd2bc7ed52d7484367e9852 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -687,12 +687,44 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         case opid2('<','<'):
         case opid2('>','>'):
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform shift between types %s and %s",
+                    type_name[exprs[0]->vtype],
+                    type_name[exprs[1]->vtype]);
+                return false;
+            }
+
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                ast_expression *shift = intrin_func(parser->intrin, (op->id == opid2('<','<')) ? "__builtin_lshift" : "__builtin_rshift");
+                ast_call       *call  = ast_call_new(parser_ctx(parser), shift);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+                out = (ast_expression*)call;
+            }
+            break;
+
         case opid3('<','<','='):
         case opid3('>','>','='):
-            if(!(out = fold_op(parser->fold, op, exprs))) {
-                compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform shift operation between types %s and %s",
+                    type_name[exprs[0]->vtype],
+                    type_name[exprs[1]->vtype]);
                 return false;
             }
+
+            if(!(out = fold_op(parser->fold, op, exprs))) {
+                ast_expression *shift = intrin_func(parser->intrin, (op->id == opid3('<','<','=')) ? "__builtin_lshift" : "__builtin_rshift");
+                ast_call       *call  = ast_call_new(parser_ctx(parser), shift);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+                out = (ast_expression*)ast_store_new(
+                    parser_ctx(parser),
+                    INSTR_STORE_F,
+                    exprs[0],
+                    (ast_expression*)call
+                );
+            }
+
             break;
 
         case opid2('|','|'):
@@ -2830,6 +2862,14 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "final")) {
+                flags |= AST_FLAG_FINAL_DECL;
+                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`final` attribute has no parameters, expected `]]`");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+            }
             else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
                 flags   |= AST_FLAG_ALIAS;
                 *message = NULL;
@@ -2934,6 +2974,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
             had_var = true;
         else if (!strcmp(parser_tokval(parser), "noref"))
             had_noref = true;
+        else if (!strcmp(parser_tokval(parser), "final"))
+            flags |= AST_FLAG_FINAL_DECL;
         else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) {
             return false;
         }
@@ -5257,6 +5299,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             retval = false;
                             goto cleanup;
                         }
+                        if (old->flags & AST_FLAG_FINAL_DECL) {
+                            parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i",
+                                       var->name, ast_ctx(old).file, ast_ctx(old).line);
+                            retval = false;
+                            goto cleanup;
+                        }
                         proto = (ast_value*)old;
                         if (!ast_istype(old, ast_value)) {
                             parseerror(parser, "internal error: not an ast_value");
@@ -5270,6 +5318,11 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             goto cleanup;
                         }
                         proto->expression.flags |= var->expression.flags;
+                        /* copy the context for finals,
+                         * so the error can show where it was actually made 'final'
+                         */
+                        if (proto->expression.flags & AST_FLAG_FINAL_DECL)
+                            ast_ctx(old) = ast_ctx(var);
                         ast_delete(var);
                         var = proto;
                     }