]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merge branch 'arithmetic_exceptions' into cooking
authorDale Weiler <weilercdale@gmail.com>
Sun, 25 May 2014 07:01:47 +0000 (03:01 -0400)
committerDale Weiler <weilercdale@gmail.com>
Sun, 25 May 2014 07:01:47 +0000 (03:01 -0400)
Conflicts:
doc/gmqcc.1
gmqcc.ini.example
opts.def
parser.c

1  2 
ast.c
ast.h
doc/gmqcc.1
exec.c
gmqcc.ini.example
opts.c
opts.def
parser.c
test.c

diff --combined ast.c
index 8b3f110f3fd18e2ff1a14222ca6bbbe749fef8fa,5c38fa96ea4819319bef814cf819005d734057b3..24d0479a71f0b6ae956cce8aa3e264ebdff5cc1c
--- 1/ast.c
--- 2/ast.c
+++ b/ast.c
@@@ -75,7 -75,6 +75,7 @@@ static void ast_binstore_delete(ast_bin
  static bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**);
  static void ast_binary_delete(ast_binary*);
  static bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
 +static bool ast_state_codegen(ast_state*, ast_function*, bool lvalue, ir_value**);
  
  /* It must not be possible to get here. */
  static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
@@@ -360,6 -359,7 +360,7 @@@ ast_value* ast_value_new(lex_ctx_t ctx
      self->cvq      = CV_NONE;
      self->hasvalue = false;
      self->isimm    = false;
+     self->inexact  = false;
      self->uses     = 0;
      memset(&self->constval, 0, sizeof(self->constval));
      self->initlist = NULL;
@@@ -973,26 -973,6 +974,26 @@@ void ast_goto_set_label(ast_goto *self
      self->target = label;
  }
  
 +ast_state* ast_state_new(lex_ctx_t ctx, ast_expression *frame, ast_expression *think)
 +{
 +    ast_instantiate(ast_state, ctx, ast_state_delete);
 +    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_state_codegen);
 +    self->framenum  = frame;
 +    self->nextthink = think;
 +    return self;
 +}
 +
 +void ast_state_delete(ast_state *self)
 +{
 +    if (self->framenum)
 +        ast_unref(self->framenum);
 +    if (self->nextthink)
 +        ast_unref(self->nextthink);
 +
 +    ast_expression_delete((ast_expression*)self);
 +    mem_d(self);
 +}
 +
  ast_call* ast_call_new(lex_ctx_t ctx,
                         ast_expression *funcexpr)
  {
@@@ -3368,44 -3348,6 +3369,44 @@@ bool ast_goto_codegen(ast_goto *self, a
      return true;
  }
  
 +#include <stdio.h>
 +bool ast_state_codegen(ast_state *self, ast_function *func, bool lvalue, ir_value **out)
 +{
 +    ast_expression_codegen *cgen;
 +
 +    ir_value *frameval, *thinkval;
 +
 +    if (lvalue) {
 +        compile_error(ast_ctx(self), "not an l-value (state operation)");
 +        return false;
 +    }
 +    if (self->expression.outr) {
 +        compile_error(ast_ctx(self), "internal error: ast_state cannot be reused!");
 +        return false;
 +    }
 +    *out = NULL;
 +
 +    cgen = self->framenum->codegen;
 +    if (!(*cgen)((ast_expression*)(self->framenum), func, false, &frameval))
 +        return false;
 +    if (!frameval)
 +        return false;
 +
 +    cgen = self->nextthink->codegen;
 +    if (!(*cgen)((ast_expression*)(self->nextthink), func, false, &thinkval))
 +        return false;
 +    if (!frameval)
 +        return false;
 +
 +    if (!ir_block_create_state_op(func->curblock, ast_ctx(self), frameval, thinkval)) {
 +        compile_error(ast_ctx(self), "failed to create STATE instruction");
 +        return false;
 +    }
 +
 +    self->expression.outr = (ir_value*)1;
 +    return true;
 +}
 +
  bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out)
  {
      ast_expression_codegen *cgen;
diff --combined ast.h
index dd7c5c1eadcfa12b3640a880de3a89f65245a30f,4720b402d4b3d4a9dbe28a209a604155e56db678..d1b250d8abb5cc4a5e2ffc1ebc945e8a13a7d09f
--- 1/ast.h
--- 2/ast.h
+++ b/ast.h
@@@ -54,7 -54,6 +54,7 @@@ typedef struct ast_switch_s      ast_sw
  typedef struct ast_label_s       ast_label;
  typedef struct ast_goto_s        ast_goto;
  typedef struct ast_argpipe_s     ast_argpipe;
 +typedef struct ast_state_s       ast_state;
  
  enum {
      AST_FLAG_VARIADIC       = 1 << 0,
@@@ -112,8 -111,7 +112,8 @@@ enum 
      TYPE_ast_switch,      /* 18 */
      TYPE_ast_label,       /* 19 */
      TYPE_ast_goto,        /* 20 */
 -    TYPE_ast_argpipe      /* 21 */
 +    TYPE_ast_argpipe,     /* 21 */
 +    TYPE_ast_state        /* 22 */
  };
  
  #define ast_istype(x, t) ( ((ast_node*)x)->nodetype == (TYPE_##t) )
@@@ -216,6 -214,7 +216,7 @@@ struct ast_value_
      bool isfield; /* this declares a field */
      bool isimm;   /* an immediate, not just const */
      bool hasvalue;
+     bool inexact; /* inexact coming from folded expression */
      basic_value_t constval;
      /* for TYPE_ARRAY we have an optional vector
       * of constants when an initializer list
@@@ -581,19 -580,6 +582,19 @@@ struct ast_goto_
  ast_goto* ast_goto_new(lex_ctx_t ctx, const char *name);
  void ast_goto_set_label(ast_goto*, ast_label*);
  
 +/* STATE node
 + *
 + * For frame/think state updates: void foo() [framenum, nextthink] {}
 + */
 +struct ast_state_s
 +{
 +    ast_expression  expression;
 +    ast_expression *framenum;
 +    ast_expression *nextthink;
 +};
 +ast_state* ast_state_new(lex_ctx_t ctx, ast_expression *frame, ast_expression *think);
 +void ast_state_delete(ast_state*);
 +
  /* CALL node
   *
   * Contains an ast_expression as target, rather than an ast_function/value.
diff --combined doc/gmqcc.1
index 66d2e4e4e14e3b50779e9f7f6bdb641c74ca1281,33a0eff3fbedef27673b700dc9511176e3be2029..50bce0ecf7cd8adae09f787055a909bf71c8137d
@@@ -168,11 -168,6 +168,11 @@@ DEBUG OPTION. Print the code's intermed
  optimization and finalization passes to stdout before generating the
  binary. The instructions will be enumerated, and values will contain a
  list of liferanges.
 +.It Fl force-crc= Ns Ar CRC
 +Force the produced progs file to use the specified CRC.
 +.It Fl state-fps= Ns Ar NUM
 +Activate \-femulate-state and set the emulated FPS to
 +.Ar NUM Ns .
  .El
  .Sh COMPILE WARNINGS
  .Bl -tag -width Ds
@@@ -349,6 -344,10 +349,10 @@@ will search its intrinsics table for so
  function name by appending "__builtin_" to it. This behaviour may
  be unexpected, so enabling this will produce a diagnostic when
  such a function is resolved to a builtin.
+ .It Fl W Ns Cm inexact-compares
+ When comparing an inexact value such as `1.0/3.0' the result is
+ pathologically wrong. Enabling this will trigger a compiler warning
+ on such expressions.
  .El
  .Sh COMPILE FLAGS
  .Bl -tag -width Ds
@@@ -582,11 -581,11 +586,16 @@@ breaks decompilers, but causes the outp
  In commutative instructions, always put the lower-numbered operand first.
  This shaves off 1 byte of entropy from all these instructions, reducing
  compressed size of the output file.
 +.It Fl f Ns Cm emulate-state
 +Emulate OP_STATE operations in code rather than using the instruction.
 +The desired fps can be set via -state-fps=NUM, defaults to 10.
 +Specifying \-state-fps implicitly sets this flag. Defaults to off in all
 +standards.
+ .It Fl f Ns Cm arithmetic-exceptions
+ Turn on arithmetic exception tests in the compiler. In constant expressions
+ which trigger exceptions like division by zero, overflow, underflow, etc,
+ the following flag will produce diagnostics for what triggered that
+ exception.
  .El
  .Sh OPTIMIZATIONS
  .Bl -tag -width Ds
diff --combined exec.c
index fde8196b2e4833ffebbd0f452b0c5517cf276916,3c36e11eedbebf44cb3a2d9ef208c5aac4bea75b..9b6677f38305469bf8a9866ae04b6cebc779765b
--- 1/exec.c
--- 2/exec.c
+++ b/exec.c
@@@ -55,16 -55,8 +55,16 @@@ qc_program_t* prog_load(const char *fil
  {
      prog_header_t   header;
      qc_program_t   *prog;
 +    size_t          i;
      fs_file_t      *file  = fs_file_open(filename, "rb");
  
 +    /* we need all those in order to support INSTR_STATE: */
 +    bool            has_self      = false,
 +                    has_time      = false,
 +                    has_think     = false,
 +                    has_nextthink = false,
 +                    has_frame     = false;
 +
      if (!file)
          return NULL;
  
      memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0]));
      prog->entities = 1;
  
 +    /* cache some globals and fields from names */
 +    for (i = 0; i < vec_size(prog->defs); ++i) {
 +        const char *name = prog_getstring(prog, prog->defs[i].name);
 +        if      (!strcmp(name, "self")) {
 +            prog->cached_globals.self = prog->defs[i].offset;
 +            has_self = true;
 +        }
 +        else if (!strcmp(name, "time")) {
 +            prog->cached_globals.time = prog->defs[i].offset;
 +            has_time = true;
 +        }
 +    }
 +    for (i = 0; i < vec_size(prog->fields); ++i) {
 +        const char *name = prog_getstring(prog, prog->fields[i].name);
 +        if      (!strcmp(name, "think")) {
 +            prog->cached_fields.think     = prog->fields[i].offset;
 +            has_think = true;
 +        }
 +        else if (!strcmp(name, "nextthink")) {
 +            prog->cached_fields.nextthink = prog->fields[i].offset;
 +            has_nextthink = true;
 +        }
 +        else if (!strcmp(name, "frame")) {
 +            prog->cached_fields.frame     = prog->fields[i].offset;
 +            has_frame = true;
 +        }
 +    }
 +    if (has_self && has_time && has_think && has_nextthink && has_frame)
 +        prog->supports_state = true;
 +
      return prog;
  
  error:
@@@ -950,7 -912,7 +950,7 @@@ static void prog_main_setparams(qc_prog
      }
  }
  
- void prog_disasm_function(qc_program_t *prog, size_t id);
static void prog_disasm_function(qc_program_t *prog, size_t id);
  
  int main(int argc, char **argv) {
      size_t      i;
      return 0;
  }
  
- void prog_disasm_function(qc_program_t *prog, size_t id) {
static void prog_disasm_function(qc_program_t *prog, size_t id) {
      prog_section_function_t *fdef = prog->functions + id;
      prog_section_statement_t *st;
  
@@@ -1612,24 -1574,8 +1612,24 @@@ while (prog->vmerror == 0) 
              break;
  
          case INSTR_STATE:
 -            qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
 +        {
 +            qcfloat_t *nextthink;
 +            qcfloat_t *time;
 +            qcfloat_t *frame;
 +            if (!prog->supports_state) {
 +                qcvmerror(prog, "`%s` tried to execute a STATE operation but misses its defs!", prog->filename);
 +                goto cleanup;
 +            }
 +            ed = prog_getedict(prog, prog->globals[prog->cached_globals.self]);
 +            ((qcint_t*)ed)[prog->cached_fields.think] = OPB->function;
 +
 +            frame     = (qcfloat_t*)&((qcint_t*)ed)[prog->cached_fields.frame];
 +            *frame    = OPA->_float;
 +            nextthink = (qcfloat_t*)&((qcint_t*)ed)[prog->cached_fields.nextthink];
 +            time      = (qcfloat_t*)(prog->globals + prog->cached_globals.time);
 +            *nextthink = *time + 0.1;
              break;
 +        }
  
          case INSTR_GOTO:
              st += st->o1.s1 - 1;    /* offset the s++ */
diff --combined gmqcc.ini.example
index 290e1bd5d3ea56bd8ba278dd9d30ea5ae688d737,f7f391396c9d61dc96f0f4e86a388a4294db9799..ceacf289c3fd606f43c9532171c9161fa8d464b4
      SORT_OPERANDS = false
  
  
 +    #Emulate OP_STATE operations in code rather than using the instruction.
 +    #The desired fps can be set via -state-fps=NUM, defaults to 10.
 +
 +    EMULATE_STATE = false
 +
 +
+     #Turn on arithmetic exception tests in the compiler. In constant expressions
+     #which trigger exceptions like division by zero, overflow, underflow, etc,
+     #the following flag will produce diagnostics for what triggered that
+     #exception.
+     ARITHMETIC_EXCEPTIONS = false
  [warnings]
      #Generate a warning about variables which are declared but never
      #used.  This can be avoided by adding the ‘noref’ keyword in front
      BUILTINS = true
  
  
+     #When comparing an inexact value such as `1.0/3.0' the result is
+     #pathologically wrong. Enabling this will trigger a compiler warning
+     #on such expressions.
+     INEXACT_COMPARES = true
  [optimizations]
      #Some general peephole optimizations. For instance the code `a = b
      #+ c` typically generates 2 instructions, an ADD and a STORE. This
diff --combined opts.c
index 34d76bbcc72fedfa1ee57544052730ca568e2c86,ddb48f7d00e6f6b27bad2341c4e13fb6594a3506..12429a8ac3e637d7b499ed1c358af9aab917b9c1
--- 1/opts.c
--- 2/opts.c
+++ b/opts.c
@@@ -93,6 -93,7 +93,7 @@@ static void opts_setdefault(void) 
      opts_set(opts.warn,  WARN_CONST_OVERWRITE,           true);
      opts_set(opts.warn,  WARN_DIRECTIVE_INMACRO,         true);
      opts_set(opts.warn,  WARN_BUILTINS,                  true);
+     opts_set(opts.warn,  WARN_INEXACT_COMPARES,          true);
  
      /* flags */
      opts_set(opts.flags, ADJUST_VECTOR_FIELDS,           true);
      opts_set(opts.flags, LEGACY_VECTOR_MATHS,            true);
      opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG,    true);
  
 +    /* options */
 +    OPTS_OPTION_U32(OPTION_STATE_FPS) = 10;
  }
  
  void opts_backup_non_Wall() {
diff --combined opts.def
index d311f87ac913905893efb064fe1a296269ea8e03,669e734801846565eab654e46f671b6fc19754b8..11571947dbbf96d41cefabbc3f41ed19b12a438d
+++ b/opts.def
@@@ -56,7 -56,7 +56,8 @@@
      GMQCC_DEFINE_FLAG(UNSAFE_VARARGS)
      GMQCC_DEFINE_FLAG(TYPELESS_STORES)
      GMQCC_DEFINE_FLAG(SORT_OPERANDS)
 +    GMQCC_DEFINE_FLAG(EMULATE_STATE)
+     GMQCC_DEFINE_FLAG(ARITHMETIC_EXCEPTIONS)
  #endif
  
  /* warning flags */
      GMQCC_DEFINE_FLAG(CONST_OVERWRITE)
      GMQCC_DEFINE_FLAG(DIRECTIVE_INMACRO)
      GMQCC_DEFINE_FLAG(BUILTINS)
+     GMQCC_DEFINE_FLAG(INEXACT_COMPARES)
  #endif
  
  #ifdef GMQCC_TYPE_OPTIMIZATIONS
      GMQCC_DEFINE_FLAG(STATISTICS)
      GMQCC_DEFINE_FLAG(PROGSRC)
      GMQCC_DEFINE_FLAG(COVERAGE)
 +    GMQCC_DEFINE_FLAG(STATE_FPS)
  #endif
  
  /* some cleanup so we don't have to */
diff --combined parser.c
index 06437436ef9c721dc4d8e4bfeafb2c5cd0e3a857,fc02cac8978ed942b95433c3564f28659956520b..b7d4bf56ae560296063261be7609ce112d43cc04
+++ b/parser.c
@@@ -1293,7 -1293,7 +1293,7 @@@ static bool parser_close_call(parser_t 
          if ((fun->flags & AST_FLAG_VARIADIC) &&
              !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
          {
-             call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount);
+             call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount, false);
          }
      }
  
@@@ -1548,14 -1548,14 +1548,14 @@@ static bool parse_sya_operand(parser_t 
          return true;
      }
      else if (parser->tok == TOKEN_FLOATCONST) {
-         ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f));
+         ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f), false);
          if (!val)
              return false;
          vec_push(sy->out, syexp(parser_ctx(parser), val));
          return true;
      }
      else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
-         ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i));
+         ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i), false);
          if (!val)
              return false;
          vec_push(sy->out, syexp(parser_ctx(parser), val));
@@@ -4015,83 -4015,69 +4015,83 @@@ static bool parse_function_body(parser_
      }
  
      if (has_frame_think) {
 -        lex_ctx_t ctx;
 -        ast_expression *self_frame;
 -        ast_expression *self_nextthink;
 -        ast_expression *self_think;
 -        ast_expression *time_plus_1;
 -        ast_store *store_frame;
 -        ast_store *store_nextthink;
 -        ast_store *store_think;
 -
 -        ctx = parser_ctx(parser);
 -        self_frame     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_frame);
 -        self_nextthink = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_nextthink);
 -        self_think     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think);
 -
 -        time_plus_1    = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F,
 -                         gbl_time, (ast_expression*)fold_constgen_float(parser->fold, 0.1f, false));
 -
 -        if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
 -            if (self_frame)     ast_delete(self_frame);
 -            if (self_nextthink) ast_delete(self_nextthink);
 -            if (self_think)     ast_delete(self_think);
 -            if (time_plus_1)    ast_delete(time_plus_1);
 -            retval = false;
 -        }
 -
 -        if (retval)
 -        {
 -            store_frame     = ast_store_new(ctx, INSTR_STOREP_F,   self_frame,     framenum);
 -            store_nextthink = ast_store_new(ctx, INSTR_STOREP_F,   self_nextthink, time_plus_1);
 -            store_think     = ast_store_new(ctx, INSTR_STOREP_FNC, self_think,     nextthink);
 -
 -            if (!store_frame) {
 -                ast_delete(self_frame);
 -                retval = false;
 -            }
 -            if (!store_nextthink) {
 -                ast_delete(self_nextthink);
 -                retval = false;
 -            }
 -            if (!store_think) {
 -                ast_delete(self_think);
 -                retval = false;
 +        if (!OPTS_FLAG(EMULATE_STATE)) {
 +            ast_state *state_op = ast_state_new(parser_ctx(parser), framenum, nextthink);
 +            if (!ast_block_add_expr(block, (ast_expression*)state_op)) {
 +                parseerror(parser, "failed to generate state op for [frame,think]");
 +                ast_unref(nextthink);
 +                ast_unref(framenum);
 +                ast_delete(block);
 +                return false;
              }
 -            if (!retval) {
 -                if (store_frame)     ast_delete(store_frame);
 -                if (store_nextthink) ast_delete(store_nextthink);
 -                if (store_think)     ast_delete(store_think);
 +        } else {
 +            /* emulate OP_STATE in code: */
 +            lex_ctx_t ctx;
 +            ast_expression *self_frame;
 +            ast_expression *self_nextthink;
 +            ast_expression *self_think;
 +            ast_expression *time_plus_1;
 +            ast_store *store_frame;
 +            ast_store *store_nextthink;
 +            ast_store *store_think;
 +
 +            float frame_delta = 1.0f / (float)OPTS_OPTION_U32(OPTION_STATE_FPS);
 +
 +            ctx = parser_ctx(parser);
 +            self_frame     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_frame);
 +            self_nextthink = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_nextthink);
 +            self_think     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think);
 +
 +            time_plus_1    = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F,
-                              gbl_time, (ast_expression*)fold_constgen_float(parser->fold, frame_delta));
++                             gbl_time, (ast_expression*)fold_constgen_float(parser->fold, frame_delta, false));
 +
 +            if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
 +                if (self_frame)     ast_delete(self_frame);
 +                if (self_nextthink) ast_delete(self_nextthink);
 +                if (self_think)     ast_delete(self_think);
 +                if (time_plus_1)    ast_delete(time_plus_1);
                  retval = false;
              }
 -            if (!ast_block_add_expr(block, (ast_expression*)store_frame) ||
 -                !ast_block_add_expr(block, (ast_expression*)store_nextthink) ||
 -                !ast_block_add_expr(block, (ast_expression*)store_think))
 +
 +            if (retval)
              {
 -                retval = false;
 +                store_frame     = ast_store_new(ctx, INSTR_STOREP_F,   self_frame,     framenum);
 +                store_nextthink = ast_store_new(ctx, INSTR_STOREP_F,   self_nextthink, time_plus_1);
 +                store_think     = ast_store_new(ctx, INSTR_STOREP_FNC, self_think,     nextthink);
 +
 +                if (!store_frame) {
 +                    ast_delete(self_frame);
 +                    retval = false;
 +                }
 +                if (!store_nextthink) {
 +                    ast_delete(self_nextthink);
 +                    retval = false;
 +                }
 +                if (!store_think) {
 +                    ast_delete(self_think);
 +                    retval = false;
 +                }
 +                if (!retval) {
 +                    if (store_frame)     ast_delete(store_frame);
 +                    if (store_nextthink) ast_delete(store_nextthink);
 +                    if (store_think)     ast_delete(store_think);
 +                    retval = false;
 +                }
 +                if (!ast_block_add_expr(block, (ast_expression*)store_frame) ||
 +                    !ast_block_add_expr(block, (ast_expression*)store_nextthink) ||
 +                    !ast_block_add_expr(block, (ast_expression*)store_think))
 +                {
 +                    retval = false;
 +                }
              }
 -        }
  
 -        if (!retval) {
 -            parseerror(parser, "failed to generate code for [frame,think]");
 -            ast_unref(nextthink);
 -            ast_unref(framenum);
 -            ast_delete(block);
 -            return false;
 +            if (!retval) {
 +                parseerror(parser, "failed to generate code for [frame,think]");
 +                ast_unref(nextthink);
 +                ast_unref(framenum);
 +                ast_delete(block);
 +                return false;
 +            }
          }
      }
  
              goto enderrfn;
          }
          func->varargs     = varargs;
-         func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params));
+         func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params), false);
      }
  
      parser->function = func;
@@@ -4227,7 -4213,7 +4227,7 @@@ static ast_expression *array_accessor_s
  
      cmp = ast_binary_new(ctx, INSTR_LT,
                           (ast_expression*)index,
-                          (ast_expression*)fold_constgen_float(parser->fold, middle));
+                          (ast_expression*)fold_constgen_float(parser->fold, middle, false));
      if (!cmp) {
          ast_delete(left);
          ast_delete(right);
@@@ -4260,7 -4246,7 +4260,7 @@@ static ast_expression *array_setter_nod
          if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
              assignop = INSTR_STORE_V;
  
-         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
+         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
          if (!subscript)
              return NULL;
  
@@@ -4326,7 -4312,7 +4326,7 @@@ static ast_expression *array_field_sett
          if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
              assignop = INSTR_STOREP_V;
  
-         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
+         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
          if (!subscript)
              return NULL;
  
@@@ -4389,7 -4375,7 +4389,7 @@@ static ast_expression *array_getter_nod
          ast_return      *ret;
          ast_array_index *subscript;
  
-         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
+         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
          if (!subscript)
              return NULL;
  
@@@ -5160,6 -5146,7 +5160,7 @@@ static bool parse_variable(parser_t *pa
      bool      wasarray  = false;
  
      ast_member *me[3] = { NULL, NULL, NULL };
+     ast_member *last_me[3] = { NULL, NULL, NULL };
  
      if (!localblock && is_static)
          parseerror(parser, "`static` qualifier is not supported in global scope");
                  }
              }
          }
+         memcpy(last_me, me, sizeof(me));
          me[0] = me[1] = me[2] = NULL;
          cleanvar = false;
          /* Part 2.2
@@@ -5830,15 -5818,44 +5832,44 @@@ skipvar
          } else {
              ast_expression *cexp;
              ast_value      *cval;
+             bool            folded_const = false;
  
              cexp = parse_expression_leave(parser, true, false, false);
              if (!cexp)
                  break;
+             cval = ast_istype(cexp, ast_value) ? (ast_value*)cexp : NULL;
  
-             if (!localblock || is_static) {
-                 cval = (ast_value*)cexp;
+             /* deal with foldable constants: */
+             if (localblock &&
+                 var->cvq == CV_CONST && cval && cval->hasvalue && cval->cvq == CV_CONST && !cval->isfield)
+             {
+                 /* remove it from the current locals */
+                 if (isvector) {
+                     for (i = 0; i < 3; ++i) {
+                         vec_pop(parser->_locals);
+                         vec_pop(localblock->collect);
+                     }
+                 }
+                 /* do sanity checking, this function really needs refactoring */
+                 if (vec_last(parser->_locals) != (ast_expression*)var)
+                     parseerror(parser, "internal error: unexpected change in local variable handling");
+                 else
+                     vec_pop(parser->_locals);
+                 if (vec_last(localblock->locals) != var)
+                     parseerror(parser, "internal error: unexpected change in local variable handling (2)");
+                 else
+                     vec_pop(localblock->locals);
+                 /* push it to the to-be-generated globals */
+                 vec_push(parser->globals, (ast_expression*)var);
+                 if (isvector)
+                     for (i = 0; i < 3; ++i)
+                         vec_push(parser->globals, (ast_expression*)last_me[i]);
+                 folded_const = true;
+             }
+             if (folded_const || !localblock || is_static) {
                  if (cval != parser->nil &&
-                     (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
+                     (!cval || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
                     )
                  {
                      parseerror(parser, "initializer is non constant");
                  vec_free(sy.argc);
                  var->cvq = cvq;
              }
+             /* a constant initialized to an inexact value should be marked inexact:
+              * const float x = <inexact>; should propagate the inexact flag
+              */
+             if (var->cvq == CV_CONST && var->expression.vtype == TYPE_FLOAT) {
+                 if (cval && cval->hasvalue && cval->cvq == CV_CONST)
+                     var->inexact = cval->inexact;
+             }
          }
  
  another:
diff --combined test.c
index f1f714fab34a6722f27674a100684fc458a5f37f,29f4b1100fb1ea0df4b0bbea4f2eaef206207460..23fbfb6b4ef8ed0d5f8c9e37446ddd9b1c9b775e
--- 1/test.c
--- 2/test.c
+++ b/test.c
@@@ -85,7 -85,7 +85,7 @@@ static fs_file_t **task_popen(const cha
              while (*line != '\0' && *line != ' ' &&
                     *line != '\t' && *line != '\n') line++;
          }
 -        vec_push(argv, '\0');
 +        vec_push(argv, NULL);
      }
  
  
@@@ -1100,6 -1100,7 +1100,7 @@@ static size_t task_schedualize(size_t *
      size_t i        = 0;
      size_t j        = 0;
      size_t failed   = 0;
+     int    status   = 0;
  
      util_snprintf(space[0], sizeof(space[0]), "%d", (int)vec_size(task_tasks));
  
              continue;
          }
  
-         if (task_pclose(task_tasks[i].runhandles) != EXIT_SUCCESS && strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) {
+         status = task_pclose(task_tasks[i].runhandles);
+         if ((!strcmp(task_tasks[i].tmpl->proceduretype, "-fail") && status == EXIT_SUCCESS)
+         ||  ( strcmp(task_tasks[i].tmpl->proceduretype, "-fail") && status == EXIT_FAILURE)) {
              con_out("failure:   `%s` %*s %*s\n",
                  task_tasks[i].tmpl->description,
                  (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),