]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
unary not now uses NOT_F with -ftrue-empty-strings
[xonotic/gmqcc.git] / parser.c
index 8e5f8b0c651f0fdaf7b889b0ca95c5ed1c13bdb8..84832b8cc2d4fa9238a37da9d6a36b0583e53758 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -98,11 +98,13 @@ typedef struct {
     bool noref;
 } parser_t;
 
+static const ast_expression *intrinsic_debug_typestring = (ast_expression*)0x10;
+
 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);
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn);
 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);
@@ -124,40 +126,22 @@ static void parseerror(parser_t *parser, const char *fmt, ...)
 /* returns true if it counts as an error */
 static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...)
 {
+    bool    r;
        va_list ap;
-       int lvl = LVL_WARNING;
-
-    if (!OPTS_WARN(warntype))
-        return false;
-
-    if (opts.werror) {
-           parser->errors++;
-           lvl = LVL_ERROR;
-       }
-
        va_start(ap, fmt);
-    con_vprintmsg(lvl, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, (opts.werror ? "error" : "warning"), fmt, ap);
+       r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap);
        va_end(ap);
-
-       return opts.werror;
+       return r;
 }
 
 static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
 {
+    bool    r;
        va_list ap;
-       int lvl = LVL_WARNING;
-
-    if (!OPTS_WARN(warntype))
-        return false;
-
-    if (opts.werror)
-           lvl = LVL_ERROR;
-
        va_start(ap, fmt);
-    con_vprintmsg(lvl, ctx.file, ctx.line, (opts.werror ? "error" : "warning"), fmt, ap);
+       r = vcompile_warning(ctx, warntype, fmt, ap);
        va_end(ap);
-
-       return opts.werror;
+       return r;
 }
 
 /**********************************************************************
@@ -664,10 +648,17 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]);
                     break;
                 case TYPE_STRING:
-                    if (CanConstFold1(exprs[0]))
-                        out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
-                    else
-                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
+                    if (CanConstFold1(exprs[0])) {
+                        if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                            out = (ast_expression*)parser_const_float(parser, !ConstS(0));
+                        else
+                            out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
+                    } else {
+                        if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                            out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]);
+                        else
+                            out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
+                    }
                     break;
                 /* we don't constant-fold NOT for these types */
                 case TYPE_ENTITY:
@@ -853,13 +844,40 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 return false;
             }
 #endif
-            if (opts.standard == COMPILER_GMQCC)
-                con_out("TODO: early out logic\n");
             if (CanConstFold(exprs[0], exprs[1]))
+            {
                 out = (ast_expression*)parser_const_float(parser,
                     (generated_op == INSTR_OR ? (ConstF(0) || ConstF(1)) : (ConstF(0) && ConstF(1))));
+            }
             else
+            {
+                if (OPTS_FLAG(PERL_LOGIC) && !ast_compare_type(exprs[0], exprs[1])) {
+                    ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                    ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                    parseerror(parser, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2);
+                    return false;
+                }
+                if (OPTS_FLAG(CORRECT_LOGIC)) {
+                    /* non-floats need to be NOTed */
+                    for (i = 0; i < 2; ++i) {
+                        if (exprs[i]->expression.vtype != TYPE_FLOAT) {
+                            if (type_not_instr[exprs[i]->expression.vtype] == AINSTR_END) {
+                                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                                ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                                parseerror(parser, "invalid types for logical operation with -fcorrect-logic: %s and %s", ty1, ty2);
+                                return false;
+                            }
+                            out = (ast_expression*)ast_unary_new(ctx, type_not_instr[exprs[i]->expression.vtype], exprs[i]);
+                            if (!out)
+                                break;
+                            exprs[i] = out; out = NULL;
+                        }
+                        if (OPTS_FLAG(PERL_LOGIC))
+                            break;
+                    }
+                }
                 out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+            }
             break;
 
         case opid2('?',':'):
@@ -1210,9 +1228,25 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
 
     fun = sy->out[fid].out;
 
+    if (fun == intrinsic_debug_typestring) {
+        char ty[1024];
+        if (fid+2 != vec_size(sy->out) ||
+            vec_last(sy->out).block)
+        {
+            parseerror(parser, "intrinsic __builtin_debug_typestring requires exactly 1 parameter");
+            return false;
+        }
+        ast_type_to_string(vec_last(sy->out).out, ty, sizeof(ty));
+        ast_unref(vec_last(sy->out).out);
+        sy->out[fid] = syexp(ast_ctx(vec_last(sy->out).out),
+                             (ast_expression*)parser_const_string(parser, ty, false));
+        vec_shrinkby(sy->out, 1);
+        return true;
+    }
+
     call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun);
     if (!call) {
-        parseerror(parser, "out of memory");
+        parseerror(parser, "internal error: failed to create ast_call node");
         return false;
     }
 
@@ -1253,7 +1287,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
         return false;
     } else {
         if (vec_size(fun->expression.params) != paramcount &&
-            !(fun->expression.variadic &&
+            !((fun->expression.flags & AST_FLAG_VARIADIC) &&
               vec_size(fun->expression.params) < paramcount))
         {
             ast_value *fval;
@@ -1269,9 +1303,9 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
                                fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
                 else
                     parseerror(parser, "too %s parameters for function call: expected %i, got %i\n"
-                               " -> `%s` has been declared here: %s:%i",
-                               fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
-                               fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
+                               " -> it has been declared here: %s:%i",
+                               fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+                               ast_ctx(fun).file, (int)ast_ctx(fun).line);
                 return false;
             }
             else
@@ -1285,9 +1319,9 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
                 else
                     return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
                                          "too %s parameters for function call: expected %i, got %i\n"
-                                         " -> `%s` has been declared here: %s:%i",
-                                         fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
-                                         fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
+                                         " -> it has been declared here: %s:%i",
+                                         fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+                                         ast_ctx(fun).file, (int)ast_ctx(fun).line);
             }
         }
     }
@@ -1450,16 +1484,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     var = parser_find_field(parser, parser_tokval(parser));
             }
             if (!var) {
-                parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
-                goto onerr;
-            }
-            if (ast_istype(var, ast_value)) {
-                ((ast_value*)var)->uses++;
+                /* intrinsics */
+                if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
+                    var = (ast_expression*)intrinsic_debug_typestring;
+
+                }
+                else
+                {
+                    parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
+                    goto onerr;
+                }
             }
-            else if (ast_istype(var, ast_member)) {
-                ast_member *mem = (ast_member*)var;
-                if (ast_istype(mem->owner, ast_value))
-                    ((ast_value*)(mem->owner))->uses++;
+            else
+            {
+                if (ast_istype(var, ast_value)) {
+                    ((ast_value*)var)->uses++;
+                }
+                else if (ast_istype(var, ast_member)) {
+                    ast_member *mem = (ast_member*)var;
+                    if (ast_istype(mem->owner, ast_value))
+                        ((ast_value*)(mem->owner))->uses++;
+                }
             }
             vec_push(sy.out, syexp(parser_ctx(parser), var));
             DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser)));
@@ -1681,7 +1726,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0));
                 wantop = false;
             } else if (op->id == opid2('?',':')) {
-                wantop = false;
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
                 vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_TERNARY, 0));
                 wantop = false;
@@ -2041,7 +2085,7 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
                              "current standard does not allow variable declarations in for-loop initializers"))
                 goto onerr;
         }
-        if (!parse_variable(parser, block, true, CV_VAR, typevar, false))
+        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false))
             goto onerr;
     }
     else if (parser->tok != ';')
@@ -2140,7 +2184,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
             parseerror(parser, "return with invalid expression");
         }
 
-        ret = ast_return_new(exp->expression.node.context, exp);
+        ret = ast_return_new(ctx, exp);
         if (!ret) {
             ast_delete(exp);
             return false;
@@ -2181,11 +2225,12 @@ 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)
+static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn)
 {
     bool had_const = false;
     bool had_var   = false;
     bool had_noref = false;
+    bool had_noreturn = false;
 
     for (;;) {
         if (!strcmp(parser_tokval(parser), "const"))
@@ -2196,7 +2241,9 @@ 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) {
+        else if (!strcmp(parser_tokval(parser), "noreturn"))
+            had_noreturn = true;
+        else if (!had_const && !had_var && !had_noref && !had_noreturn) {
             return false;
         }
         else
@@ -2210,7 +2257,8 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
         *cvq = CV_VAR;
     else
         *cvq = CV_NONE;
-    *noref = had_noref;
+    *noref    = had_noref;
+    *noreturn = had_noreturn;
     return true;
 onerr:
     parseerror(parser, "parse error after variable qualifier");
@@ -2227,7 +2275,7 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
     ast_switch_case swcase;
 
     int  cvq;
-    bool noref;
+    bool noref, noreturn;
 
     lex_ctx ctx = parser_ctx(parser);
 
@@ -2279,19 +2327,19 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
         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)) {
+            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false)) {
                 ast_delete(switchnode);
                 return false;
             }
             continue;
         }
-        if (parse_var_qualifiers(parser, true, &cvq, &noref))
+        if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn))
         {
             if (cvq == CV_WRONG) {
                 ast_delete(switchnode);
                 return false;
             }
-            if (!parse_variable(parser, block, false, cvq, NULL, noref)) {
+            if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn)) {
                 ast_delete(switchnode);
                 return false;
             }
@@ -2322,8 +2370,7 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
                 return false;
             }
             if (!OPTS_FLAG(RELAXED_SWITCH)) {
-                opval = (ast_value*)swcase.value;
-                if (!ast_istype(swcase.value, ast_value)) { /* || opval->cvq != CV_CONST) { */
+                if (!ast_istype(swcase.value, ast_value)) { /* || ((ast_value*)swcase.value)->cvq != CV_CONST) { */
                     parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch");
                     ast_unref(operand);
                     return false;
@@ -2507,7 +2554,7 @@ 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;
+    bool       noref, noreturn;
     int        cvq = CV_NONE;
     ast_value *typevar = NULL;
 
@@ -2527,15 +2574,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))
+        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false))
             return false;
         return true;
     }
-    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref))
+    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, block, true, cvq, NULL, noref);
+        return parse_variable(parser, block, true, cvq, NULL, noref, noreturn);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -2806,7 +2853,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         return false;
     }
 
-    if (var->expression.variadic) {
+    if (var->expression.flags & AST_FLAG_VARIADIC) {
         if (parsewarning(parser, WARN_VARIADIC_FUNCTION,
                          "variadic function with implementation will not be able to access additional parameters"))
         {
@@ -3525,7 +3572,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
     /* now turn 'var' into a function type */
     fval = ast_value_new(ctx, "<type()>", TYPE_FUNCTION);
     fval->expression.next     = (ast_expression*)var;
-    fval->expression.variadic = variadic;
+    if (variadic)
+        fval->expression.flags |= AST_FLAG_VARIADIC;
     var = fval;
 
     var->expression.params = params;
@@ -3790,7 +3838,7 @@ 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)
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn)
 {
     ast_value *var;
     ast_value *proto;
@@ -3857,6 +3905,8 @@ 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++;
+        if (noreturn)
+            var->expression.flags |= AST_FLAG_NORETURN;
 
         /* Part 1:
          * check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
@@ -4322,22 +4372,23 @@ cleanup:
 
 static bool parser_global_statement(parser_t *parser)
 {
-    int        cvq = CV_WRONG;
-    bool       noref = false;
-    ast_value *istype = NULL;
+    int        cvq      = CV_WRONG;
+    bool       noref    = false;
+    bool       noreturn = false;
+    ast_value *istype   = NULL;
 
     if (parser->tok == TOKEN_IDENT)
         istype = parser_find_typedef(parser, parser_tokval(parser), 0);
 
     if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
-        return parse_variable(parser, NULL, false, CV_NONE, istype, false);
+        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false);
     }
-    else if (parse_var_qualifiers(parser, false, &cvq, &noref))
+    else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, NULL, true, cvq, NULL, noref);
+        return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {