]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merge pull request #180 from xonotic/mem_leak_fix_on_failure_paths
authorDale Weiler <graphitemaster@users.noreply.github.com>
Sun, 26 Nov 2017 22:30:30 +0000 (17:30 -0500)
committerGitHub <noreply@github.com>
Sun, 26 Nov 2017 22:30:30 +0000 (17:30 -0500)
two small memory leak fixes on failure paths

1  2 
ir.cpp
parser.cpp

diff --combined ir.cpp
index d94558e156a4a4f91a3332bdca215b52017a6db0,3a166c7af26bf848ec4475370f64052aa0d0846f..facbc33fccaeaaf9829a4fcf72fd18a2e409f6d7
--- 1/ir.cpp
--- 2/ir.cpp
+++ b/ir.cpp
@@@ -235,14 -235,15 +235,14 @@@ static bool GMQCC_WARN vec_ir_value_fin
      return false;
  }
  
 -static bool GMQCC_WARN vec_ir_block_find(ir_block **vec, ir_block *what, size_t *idx)
 +static bool GMQCC_WARN vec_ir_block_find(std::vector<ir_block *> &vec, ir_block *what, size_t *idx)
  {
 -    size_t i;
 -    size_t len = vec_size(vec);
 -    for (i = 0; i < len; ++i) {
 -        if (vec[i] == what) {
 -            if (idx) *idx = i;
 -            return true;
 -        }
 +    for (auto &it : vec) {
 +        if (it != what)
 +            continue;
 +        if (idx)
 +            *idx = &it - &vec[0];
 +        return true;
      }
      return false;
  }
@@@ -432,7 -433,7 +432,7 @@@ static bool ir_function_pass_peephole(i
  {
      for (auto& bp : self->m_blocks) {
          ir_block *block = bp.get();
 -        for (size_t i = 0; i < vec_size(block->m_instr); ++i) {
 +        for (size_t i = 0; i < block->m_instr.size(); ++i) {
              ir_instr *inst;
              inst = block->m_instr[i];
  
                  ++opts_optimizationcount[OPTIM_PEEPHOLE];
                  (void)!ir_instr_op(oper, 0, store->_m_ops[0], true);
  
 -                vec_remove(block->m_instr, i, 1);
 +                block->m_instr.erase(block->m_instr.begin() + i);
                  delete store;
              }
              else if (inst->m_opcode == VINSTR_COND)
                      (void)!ir_instr_op(inst, 0, inot->_m_ops[1], false);
                      /* remove NOT */
                      tmp = inot->m_owner;
 -                    for (inotid = 0; inotid < vec_size(tmp->m_instr); ++inotid) {
 +                    for (inotid = 0; inotid < tmp->m_instr.size(); ++inotid) {
                          if (tmp->m_instr[inotid] == inot)
                              break;
                      }
 -                    if (inotid >= vec_size(tmp->m_instr)) {
 +                    if (inotid >= tmp->m_instr.size()) {
                          compile_error(inst->m_context, "sanity-check failed: failed to find instruction to optimize out");
                          return false;
                      }
 -                    vec_remove(tmp->m_instr, inotid, 1);
 +                    tmp->m_instr.erase(tmp->m_instr.begin() + inotid);
                      delete inot;
                      /* swap ontrue/onfalse */
                      tmp = inst->m_bops[0];
@@@ -547,14 -548,14 +547,14 @@@ static bool ir_function_pass_tailrecurs
          ir_value *funcval;
          ir_instr *ret, *call, *store = nullptr;
  
 -        if (!block->m_final || vec_size(block->m_instr) < 2)
 +        if (!block->m_final || block->m_instr.size() < 2)
              continue;
  
 -        ret = block->m_instr[vec_size(block->m_instr)-1];
 +        ret = block->m_instr.back();
          if (ret->m_opcode != INSTR_DONE && ret->m_opcode != INSTR_RETURN)
              continue;
  
 -        call = block->m_instr[vec_size(block->m_instr)-2];
 +        call = block->m_instr[block->m_instr.size()-2];
          if (call->m_opcode >= INSTR_STORE_F && call->m_opcode <= INSTR_STORE_FNC) {
              /* account for the unoptimized
               * CALL
               * RETURN %tmp
               * version
               */
 -            if (vec_size(block->m_instr) < 3)
 +            if (block->m_instr.size() < 3)
                  continue;
  
              store = call;
 -            call = block->m_instr[vec_size(block->m_instr)-3];
 +            call = block->m_instr[block->m_instr.size()-3];
          }
  
          if (call->m_opcode < INSTR_CALL0 || call->m_opcode > INSTR_CALL8)
              {
                  ++opts_optimizationcount[OPTIM_PEEPHOLE];
                  call->_m_ops[0] = store->_m_ops[0];
 -                vec_remove(block->m_instr, vec_size(block->m_instr) - 2, 1);
 +                block->m_instr.erase(block->m_instr.end()-2);
                  delete store;
              }
              else
              continue;
  
          ++opts_optimizationcount[OPTIM_TAIL_RECURSION];
 -        vec_shrinkby(block->m_instr, 2);
 +        block->m_instr.erase(block->m_instr.end()-2, block->m_instr.end());
  
          block->m_final = false; /* open it back up */
  
@@@ -630,36 -631,6 +630,36 @@@ bool ir_function_finalize(ir_function *
      if (self->m_builtin)
          return true;
  
 +    for (auto& lp : self->m_locals) {
 +        ir_value *v = lp.get();
 +        if (v->m_reads.empty() && v->m_writes.size() && !(v->m_flags & IR_FLAG_NOREF)) {
 +            // if it's a vector check to ensure all it's members are unused before
 +            // claiming it's unused, otherwise skip the vector entierly
 +            if (v->m_vtype == TYPE_VECTOR)
 +            {
 +                size_t mask = (1 << 3) - 1, bits = 0;
 +                for (size_t i = 0; i < 3; i++)
 +                    if (!v->m_members[i] || (v->m_members[i]->m_reads.empty()
 +                        && v->m_members[i]->m_writes.size()))
 +                        bits |= (1 << i);
 +                // all components are unused so just report the vector
 +                if (bits == mask && irwarning(v->m_context, WARN_UNUSED_VARIABLE,
 +                    "unused variable: `%s`", v->m_name.c_str()))
 +                    return false;
 +                else if (bits != mask)
 +                    // individual components are unused so mention them
 +                    for (size_t i = 0; i < 3; i++)
 +                        if ((bits & (1 << i))
 +                            && irwarning(v->m_context, WARN_UNUSED_COMPONENT,
 +                                "unused vector component: `%s.%c`", v->m_name.c_str(), "xyz"[i]))
 +                            return false;
 +            }
 +            // just a standard variable
 +            else if (irwarning(v->m_context, WARN_UNUSED_VARIABLE,
 +                    "unused variable: `%s`", v->m_name.c_str())) return false;
 +        }
 +    }
 +
      if (OPTS_OPTIMIZATION(OPTIM_PEEPHOLE)) {
          if (!ir_function_pass_peephole(self)) {
              irerror(self->m_context, "generic optimization pass broke something in `%s`", self->m_name.c_str());
@@@ -742,15 -713,19 +742,15 @@@ ir_block::ir_block(ir_function* owner, 
  
  ir_block::~ir_block()
  {
 -    for (size_t i = 0; i != vec_size(m_instr); ++i)
 -        delete m_instr[i];
 -    vec_free(m_instr);
 -    vec_free(m_entries);
 -    vec_free(m_exits);
 +    for (auto &i : m_instr)
 +        delete i;
  }
  
  static void ir_block_delete_quick(ir_block* self)
  {
 -    size_t i;
 -    for (i = 0; i != vec_size(self->m_instr); ++i)
 -        ir_instr_delete_quick(self->m_instr[i]);
 -    vec_free(self->m_instr);
 +    for (auto &i : self->m_instr)
 +        ir_instr_delete_quick(i);
 +    self->m_instr.clear();
      delete self;
  }
  
@@@ -1261,7 -1236,7 +1261,7 @@@ bool ir_block_create_store_op(ir_block 
          delete in;
          return false;
      }
 -    vec_push(self->m_instr, in);
 +    self->m_instr.push_back(in);
      return true;
  }
  
@@@ -1281,7 -1256,7 +1281,7 @@@ bool ir_block_create_state_op(ir_block 
          delete in;
          return false;
      }
 -    vec_push(self->m_instr, in);
 +    self->m_instr.push_back(in);
      return true;
  }
  
@@@ -1350,7 -1325,7 +1350,7 @@@ bool ir_block_create_return(ir_block *s
          return false;
      }
  
 -    vec_push(self->m_instr, in);
 +    self->m_instr.push_back(in);
      return true;
  }
  
@@@ -1374,12 -1349,12 +1374,12 @@@ bool ir_block_create_if(ir_block *self
      in->m_bops[0] = ontrue;
      in->m_bops[1] = onfalse;
  
 -    vec_push(self->m_instr, in);
 +    self->m_instr.push_back(in);
  
 -    vec_push(self->m_exits, ontrue);
 -    vec_push(self->m_exits, onfalse);
 -    vec_push(ontrue->m_entries,  self);
 -    vec_push(onfalse->m_entries, self);
 +    self->m_exits.push_back(ontrue);
 +    self->m_exits.push_back(onfalse);
 +    ontrue->m_entries.push_back(self);
 +    onfalse->m_entries.push_back(self);
      return true;
  }
  
@@@ -1394,10 -1369,10 +1394,10 @@@ bool ir_block_create_jump(ir_block *sel
          return false;
  
      in->m_bops[0] = to;
 -    vec_push(self->m_instr, in);
 +    self->m_instr.push_back(in);
  
 -    vec_push(self->m_exits, to);
 -    vec_push(to->m_entries, self);
 +    self->m_exits.push_back(to);
 +    to->m_entries.push_back(self);
      return true;
  }
  
@@@ -1425,7 -1400,7 +1425,7 @@@ ir_instr* ir_block_create_phi(ir_block 
          delete in;
          return nullptr;
      }
 -    vec_push(self->m_instr, in);
 +    self->m_instr.push_back(in);
      return in;
  }
  
@@@ -1474,9 -1449,10 +1474,10 @@@ ir_instr* ir_block_create_call(ir_bloc
          !ir_instr_op(in, 1, func, false))
      {
          delete in;
+         delete out;
          return nullptr;
      }
 -    vec_push(self->m_instr, in);
 +    self->m_instr.push_back(in);
      /*
      if (noreturn) {
          if (!ir_block_create_return(self, ctx, nullptr)) {
@@@ -1643,7 -1619,7 +1644,7 @@@ ir_value* ir_block_create_unary(ir_bloc
          case VINSTR_NEG_F:
              return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_F, nullptr, operand, ot);
          case VINSTR_NEG_V:
 -            return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, nullptr, operand, TYPE_VECTOR);
 +            return ir_block_create_general_instr(self, ctx, label, INSTR_SUB_V, self->m_owner->m_owner->m_nil, operand, TYPE_VECTOR);
  
          default:
              ot = operand->m_vtype;
@@@ -1680,7 -1656,7 +1681,7 @@@ static ir_value* ir_block_create_genera
          goto on_error;
      }
  
 -    vec_push(self->m_instr, instr);
 +    self->m_instr.push_back(instr);
  
      return out;
  on_error:
@@@ -1754,13 -1730,13 +1755,13 @@@ static bool ir_block_naive_phi(ir_bloc
       * to a list so we don't need to loop through blocks
       * - anyway: "don't optimize YET"
       */
 -    for (i = 0; i < vec_size(self->m_instr); ++i)
 +    for (i = 0; i < self->m_instr.size(); ++i)
      {
          ir_instr *instr = self->m_instr[i];
          if (instr->m_opcode != VINSTR_PHI)
              continue;
  
 -        vec_remove(self->m_instr, i, 1);
 +        self->m_instr.erase(self->m_instr.begin()+i);
          --i; /* NOTE: i+1 below */
  
          for (auto &it : instr->m_phi) {
                      return false;
              } else {
                  /* force a move instruction */
 -                ir_instr *prevjump = vec_last(b->m_instr);
 -                vec_pop(b->m_instr);
 +                ir_instr *prevjump = b->m_instr.back();
 +                b->m_instr.pop_back();
                  b->m_final = false;
                  instr->_m_ops[0]->m_store = store_global;
                  if (!ir_block_create_store(b, instr->m_context, instr->_m_ops[0], v))
                      return false;
                  instr->_m_ops[0]->m_store = store_value;
 -                vec_push(b->m_instr, prevjump);
 +                b->m_instr.push_back(prevjump);
                  b->m_final = true;
              }
          }
   */
  static void ir_block_enumerate(ir_block *self, size_t *_eid)
  {
 -    size_t i;
      size_t eid = *_eid;
 -    for (i = 0; i < vec_size(self->m_instr); ++i)
 -    {
 -        self->m_instr[i]->m_eid = eid++;
 -    }
 +    for (auto &i : self->m_instr)
 +        i->m_eid = eid++;
      *_eid = eid;
  }
  
@@@ -1836,10 -1815,10 +1837,10 @@@ void ir_function_enumerate(ir_function 
   * This is the counterpart to register-allocation in register machines.
   */
  struct function_allocator {
 -    ir_value **locals;
 -    size_t *sizes;
 -    size_t *positions;
 -    bool *unique;
 +    std::vector<std::unique_ptr<ir_value>> locals;
 +    std::vector<size_t> sizes;
 +    std::vector<size_t> positions;
 +    std::vector<bool> unique;
  };
  
  static bool function_allocator_alloc(function_allocator *alloc, ir_value *var)
      ir_value *slot;
      size_t vsize = var->size();
  
 -    var->m_code.local = vec_size(alloc->locals);
 +    var->m_code.local = alloc->locals.size();
  
      slot = new ir_value("reg", store_global, var->m_vtype);
      if (!slot)
      if (!slot->mergeLife(var))
          goto localerror;
  
 -    vec_push(alloc->locals, slot);
 -    vec_push(alloc->sizes, vsize);
 -    vec_push(alloc->unique, var->m_unique_life);
 +    alloc->locals.emplace_back(slot);
 +    alloc->sizes.push_back(vsize);
 +    alloc->unique.push_back(var->m_unique_life);
  
      return true;
  
@@@ -1870,22 -1849,23 +1871,22 @@@ localerror
  static bool ir_function_allocator_assign(ir_function *self, function_allocator *alloc, ir_value *v)
  {
      size_t a;
 -    ir_value *slot;
  
      if (v->m_unique_life)
          return function_allocator_alloc(alloc, v);
  
 -    for (a = 0; a < vec_size(alloc->locals); ++a)
 +    for (a = 0; a < alloc->locals.size(); ++a)
      {
          /* if it's reserved for a unique liferange: skip */
          if (alloc->unique[a])
              continue;
  
 -        slot = alloc->locals[a];
 +        ir_value *slot = alloc->locals[a].get();
  
          /* never resize parameters
           * will be required later when overlapping temps + locals
           */
 -        if (a < vec_size(self->m_params) &&
 +        if (a < self->m_params.size() &&
              alloc->sizes[a] < v->size())
          {
              continue;
          v->m_code.local = a;
          return true;
      }
 -    if (a >= vec_size(alloc->locals)) {
 +    if (a >= alloc->locals.size()) {
          if (!function_allocator_alloc(alloc, v))
              return false;
      }
  
  bool ir_function_allocate_locals(ir_function *self)
  {
 -    bool   retval = true;
      size_t pos;
      bool   opt_gt = OPTS_OPTIMIZATION(OPTIM_GLOBAL_TEMPS);
  
      if (self->m_locals.empty() && self->m_values.empty())
          return true;
  
 -    globalloc.locals    = nullptr;
 -    globalloc.sizes     = nullptr;
 -    globalloc.positions = nullptr;
 -    globalloc.unique    = nullptr;
 -    lockalloc.locals    = nullptr;
 -    lockalloc.sizes     = nullptr;
 -    lockalloc.positions = nullptr;
 -    lockalloc.unique    = nullptr;
 -
      size_t i;
      for (i = 0; i < self->m_locals.size(); ++i)
      {
              v->m_locked      = true;
              v->m_unique_life = true;
          }
 -        else if (i >= vec_size(self->m_params))
 +        else if (i >= self->m_params.size())
              break;
          else
              v->m_locked = true; /* lock parameters locals */
          if (!function_allocator_alloc((v->m_locked || !opt_gt ? &lockalloc : &globalloc), v))
 -            goto error;
 +            return false;
      }
      for (; i < self->m_locals.size(); ++i)
      {
          if (v->m_life.empty())
              continue;
          if (!ir_function_allocator_assign(self, (v->m_locked || !opt_gt ? &lockalloc : &globalloc), v))
 -            goto error;
 +            return false;
      }
  
      /* Allocate a slot for any value that still exists */
                  ir_instr *call = v->m_reads[0];
                  if (!vec_ir_value_find(call->m_params, v, &param)) {
                      irerror(call->m_context, "internal error: unlocked parameter %s not found", v->m_name.c_str());
 -                    goto error;
 +                    return false;
                  }
                  ++opts_optimizationcount[OPTIM_CALL_STORES];
                  v->m_callparam = true;
          }
  
          if (!ir_function_allocator_assign(self, (v->m_locked || !opt_gt ? &lockalloc : &globalloc), v))
 -            goto error;
 +            return false;
      }
  
 -    if (!lockalloc.sizes && !globalloc.sizes) {
 -        goto cleanup;
 -    }
 -    vec_push(lockalloc.positions, 0);
 -    vec_push(globalloc.positions, 0);
 +    if (lockalloc.sizes.empty() && globalloc.sizes.empty())
 +        return true;
 +
 +    lockalloc.positions.push_back(0);
 +    globalloc.positions.push_back(0);
  
      /* Adjust slot positions based on sizes */
 -    if (lockalloc.sizes) {
 -        pos = (vec_size(lockalloc.sizes) ? lockalloc.positions[0] : 0);
 -        for (i = 1; i < vec_size(lockalloc.sizes); ++i)
 +    if (!lockalloc.sizes.empty()) {
 +        pos = (lockalloc.sizes.size() ? lockalloc.positions[0] : 0);
 +        for (i = 1; i < lockalloc.sizes.size(); ++i)
          {
              pos = lockalloc.positions[i-1] + lockalloc.sizes[i-1];
 -            vec_push(lockalloc.positions, pos);
 +            lockalloc.positions.push_back(pos);
          }
 -        self->m_allocated_locals = pos + vec_last(lockalloc.sizes);
 +        self->m_allocated_locals = pos + lockalloc.sizes.back();
      }
 -    if (globalloc.sizes) {
 -        pos = (vec_size(globalloc.sizes) ? globalloc.positions[0] : 0);
 -        for (i = 1; i < vec_size(globalloc.sizes); ++i)
 +    if (!globalloc.sizes.empty()) {
 +        pos = (globalloc.sizes.size() ? globalloc.positions[0] : 0);
 +        for (i = 1; i < globalloc.sizes.size(); ++i)
          {
              pos = globalloc.positions[i-1] + globalloc.sizes[i-1];
 -            vec_push(globalloc.positions, pos);
 +            globalloc.positions.push_back(pos);
          }
 -        self->m_globaltemps = pos + vec_last(globalloc.sizes);
 +        self->m_globaltemps = pos + globalloc.sizes.back();
      }
  
      /* Locals need to know their new position */
              value->m_code.local = globalloc.positions[value->m_code.local];
      }
  
 -    goto cleanup;
 -
 -error:
 -    retval = false;
 -cleanup:
 -    for (i = 0; i < vec_size(lockalloc.locals); ++i)
 -        delete lockalloc.locals[i];
 -    for (i = 0; i < vec_size(globalloc.locals); ++i)
 -        delete globalloc.locals[i];
 -    vec_free(globalloc.unique);
 -    vec_free(globalloc.locals);
 -    vec_free(globalloc.sizes);
 -    vec_free(globalloc.positions);
 -    vec_free(lockalloc.unique);
 -    vec_free(lockalloc.locals);
 -    vec_free(lockalloc.sizes);
 -    vec_free(lockalloc.positions);
 -    return retval;
 +    return true;
  }
  
  /* Get information about which operand
@@@ -2111,19 -2118,21 +2112,19 @@@ static bool ir_block_life_propagate(ir_
  {
      ir_instr *instr;
      ir_value *value;
 -    size_t i, o, p, mem;
 +    size_t i, o, mem;
      // bitmasks which operands are read from or written to
      size_t read, write;
  
      self->m_living.clear();
  
 -    p = vec_size(self->m_exits);
 -    for (i = 0; i < p; ++i) {
 -        ir_block *prev = self->m_exits[i];
 +    for (auto &prev : self->m_exits) {
          for (auto &it : prev->m_living)
              if (!vec_ir_value_find(self->m_living, it, nullptr))
                  self->m_living.push_back(it);
      }
  
 -    i = vec_size(self->m_instr);
 +    i = self->m_instr.size();
      while (i)
      { --i;
          instr = self->m_instr[i];
  bool ir_function_calculate_liferanges(ir_function *self)
  {
      /* parameters live at 0 */
 -    for (size_t i = 0; i < vec_size(self->m_params); ++i)
 +    for (size_t i = 0; i < self->m_params.size(); ++i)
          if (!self->m_locals[i].get()->setAlive(0))
              compile_error(self->m_context, "internal error: failed value-life merging");
  
@@@ -2481,7 -2490,7 +2482,7 @@@ static bool gen_blocks_recursive(code_
  
      block->m_generated = true;
      block->m_code_start = code->statements.size();
 -    for (i = 0; i < vec_size(block->m_instr); ++i)
 +    for (i = 0; i < block->m_instr.size(); ++i)
      {
          instr = block->m_instr[i];
  
@@@ -3002,7 -3011,7 +3003,7 @@@ bool ir_builder::generateGlobalFunction
      fun.name = global->m_code.name;
      fun.file = filestring(global->m_context.file);
      fun.profile = 0; /* always 0 */
 -    fun.nargs = vec_size(irfun->m_params);
 +    fun.nargs = irfun->m_params.size();
      if (fun.nargs > 8)
          fun.nargs = 8;
  
@@@ -3067,7 -3076,7 +3068,7 @@@ static bool gen_function_extparam_copy(
  {
      ir_builder *ir = self->m_owner;
  
 -    size_t numparams = vec_size(self->m_params);
 +    size_t numparams = self->m_params.size();
      if (!numparams)
          return true;
  
@@@ -3103,7 -3112,7 +3104,7 @@@ static bool gen_function_varargs_copy(c
      ir_value   *ep;
      prog_section_statement_t stmt;
  
 -    numparams = vec_size(self->m_params);
 +    numparams = self->m_params.size();
      if (!numparams)
          return true;
  
@@@ -3239,7 -3248,7 +3240,7 @@@ bool ir_builder::generateGlobalFunction
      return true;
  }
  
 -static void gen_vector_defs(code_t *code, prog_section_def_t def, const char *name)
 +static void gen_vector_defs(code_t *code, prog_section_def_t def, const char *name, int type)
  {
      char  *component;
      size_t len, i;
      if (!name || name[0] == '#' || OPTS_FLAG(SINGLE_VECTOR_DEFS))
          return;
  
 -    def.type = TYPE_FLOAT;
 +    def.type = type;
  
      len = strlen(name);
  
@@@ -3347,9 -3356,9 +3348,9 @@@ bool ir_builder::generateGlobal(ir_valu
              def.offset = global->codeAddress();
              m_code->defs.push_back(def);
              if (global->m_vtype == TYPE_VECTOR)
 -                gen_vector_defs(m_code.get(), def, global->m_name.c_str());
 +                gen_vector_defs(m_code.get(), def, global->m_name.c_str(), TYPE_FLOAT);
              else if (global->m_vtype == TYPE_FIELD && global->m_fieldtype == TYPE_VECTOR)
 -                gen_vector_defs(m_code.get(), def, global->m_name.c_str());
 +                gen_vector_defs(m_code.get(), def, global->m_name.c_str(), TYPE_FIELD);
              return true;
          }
      }
          if (pushdef) {
              m_code->defs.push_back(def);
              if (global->m_fieldtype == TYPE_VECTOR)
 -                gen_vector_defs(m_code.get(), def, global->m_name.c_str());
 +                gen_vector_defs(m_code.get(), def, global->m_name.c_str(), TYPE_FIELD);
          }
          return gen_global_field(m_code.get(), global);
      case TYPE_ENTITY:
          if (pushdef) {
              m_code->defs.push_back(def);
              def.type &= ~DEF_SAVEGLOBAL;
 -            gen_vector_defs(m_code.get(), def, global->m_name.c_str());
 +            gen_vector_defs(m_code.get(), def, global->m_name.c_str(), TYPE_FLOAT);
          }
          return global->m_code.globaladdr >= 0;
      }
@@@ -3560,7 -3569,7 +3561,7 @@@ static bool ir_builder_gen_field(ir_bui
      }
  
      if (field->m_fieldtype == TYPE_VECTOR) {
 -        gen_vector_defs  (self->m_code.get(), def, field->m_name.c_str());
 +        gen_vector_defs  (self->m_code.get(), def, field->m_name.c_str(), TYPE_FIELD);
          gen_vector_fields(self->m_code.get(), fld, field->m_name.c_str());
      }
  
@@@ -3944,13 -3953,14 +3945,13 @@@ void ir_function_dump(ir_function *f, c
  void ir_block_dump(ir_block* b, char *ind,
                     int (*oprintf)(const char*, ...))
  {
 -    size_t i;
      oprintf("%s:%s\n", ind, b->m_label.c_str());
      util_strncat(ind, "\t", IND_BUFSZ-1);
  
 -    if (b->m_instr && b->m_instr[0])
 +    if (!b->m_instr.empty() && b->m_instr[0])
          oprintf("%s (%i) [entry]\n", ind, (int)(b->m_instr[0]->m_eid-1));
 -    for (i = 0; i < vec_size(b->m_instr); ++i)
 -        ir_instr_dump(b->m_instr[i], ind, oprintf);
 +    for (auto &i : b->m_instr)
 +        ir_instr_dump(i, ind, oprintf);
      ind[strlen(ind)-1] = 0;
  }
  
diff --combined parser.cpp
index 0560849e35719956c4f8f6c86bf05fdf319461a9,7bb2a10f8f2401f783feebed27da3fb12fac980f..ddde65469ec77e7cb596e58451dc88096c5ad8f1
@@@ -139,7 -139,7 +139,7 @@@ static ast_expression* parser_find_loca
      hash = util_hthash(parser->htglobals, name);
  
      *isparam = false;
 -    for (i = vec_size(parser->variables); i > upto;) {
 +    for (i = parser->variables.size(); i > upto;) {
          --i;
          if ( (e = (ast_expression*)util_htgeth(parser->variables[i], name, hash)) )
              return e;
@@@ -171,7 -171,7 +171,7 @@@ static ast_value* parser_find_typedef(p
      ast_value *e;
      hash = util_hthash(parser->typedefs[0], name);
  
 -    for (i = vec_size(parser->typedefs); i > upto;) {
 +    for (i = parser->typedefs.size(); i > upto;) {
          --i;
          if ( (e = (ast_value*)util_htgeth(parser->typedefs[i], name, hash)) )
              return e;
@@@ -293,23 -293,6 +293,23 @@@ static bool rotate_entfield_array_index
      return true;
  }
  
 +static int store_op_for(ast_expression* expr)
 +{
 +    if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) && expr->m_vtype == TYPE_FIELD && expr->m_next->m_vtype == TYPE_VECTOR) {
 +        return type_storep_instr[TYPE_VECTOR];
 +    }
 +
 +    if (ast_istype(expr, ast_member) && ast_istype(((ast_member*)expr)->m_owner, ast_entfield)) {
 +        return type_storep_instr[expr->m_vtype];
 +    }
 +
 +    if (ast_istype(expr, ast_entfield)) {
 +        return type_storep_instr[expr->m_vtype];
 +    }
 +
 +    return type_store_instr[expr->m_vtype];
 +}
 +
  static bool check_write_to(lex_ctx_t ctx, ast_expression *expr)
  {
      if (ast_istype(expr, ast_value)) {
@@@ -745,7 -728,6 +745,7 @@@ static bool parser_sy_apply_operator(pa
  
          case opid2('|','|'):
              generated_op += 1; /* INSTR_OR */
 +            [[fallthrough]];
          case opid2('&','&'):
              generated_op += INSTR_AND;
              if (!(out = parser->m_fold.op(op, exprs))) {
  
          case opid1('>'):
              generated_op += 1; /* INSTR_GT */
 +            [[fallthrough]];
          case opid1('<'):
              generated_op += 1; /* INSTR_LT */
 +            [[fallthrough]];
          case opid2('>', '='):
              generated_op += 1; /* INSTR_GE */
 +            [[fallthrough]];
          case opid2('<', '='):
              generated_op += INSTR_LE;
              if (NotSameType(TYPE_FLOAT)) {
          case opid1('='):
              if (ast_istype(exprs[0], ast_entfield)) {
                  ast_expression *field = ((ast_entfield*)exprs[0])->m_field;
 -                if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
 -                    exprs[0]->m_vtype == TYPE_FIELD &&
 -                    exprs[0]->m_next->m_vtype == TYPE_VECTOR)
 -                {
 -                    assignop = type_storep_instr[TYPE_VECTOR];
 -                }
 -                else
 -                    assignop = type_storep_instr[exprs[0]->m_vtype];
 +                assignop = store_op_for(exprs[0]);
                  if (assignop == VINSTR_END || !field->m_next->compareType(*exprs[1]))
                  {
                      ast_type_to_string(field->m_next, ty1, sizeof(ty1));
              }
              else
              {
 -                if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
 -                    exprs[0]->m_vtype == TYPE_FIELD &&
 -                    exprs[0]->m_next->m_vtype == TYPE_VECTOR)
 -                {
 -                    assignop = type_store_instr[TYPE_VECTOR];
 -                }
 -                else {
 -                    assignop = type_store_instr[exprs[0]->m_vtype];
 -                }
 +                assignop = store_op_for(exprs[0]);
  
                  if (assignop == VINSTR_END) {
                      ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                  return false;
              }
              (void)check_write_to(ctx, exprs[0]);
 -            if (ast_istype(exprs[0], ast_entfield))
 -                assignop = type_storep_instr[exprs[0]->m_vtype];
 -            else
 -                assignop = type_store_instr[exprs[0]->m_vtype];
 +            assignop = store_op_for(exprs[0]);
              switch (exprs[0]->m_vtype) {
                  case TYPE_FLOAT:
                      out = new ast_binstore(ctx, assignop,
                  return false;
              }
              (void)check_write_to(ctx, exprs[0]);
 -            if (ast_istype(exprs[0], ast_entfield))
 -                assignop = type_storep_instr[exprs[0]->m_vtype];
 -            else
 -                assignop = type_store_instr[exprs[0]->m_vtype];
 +            assignop = store_op_for(exprs[0]);
              switch (exprs[0]->m_vtype) {
                  case TYPE_FLOAT:
                      out = new ast_binstore(ctx, assignop,
                  return false;
              }
              (void)check_write_to(ctx, exprs[0]);
 -            if (ast_istype(exprs[0], ast_entfield))
 -                assignop = type_storep_instr[exprs[0]->m_vtype];
 -            else
 -                assignop = type_store_instr[exprs[0]->m_vtype];
 +            assignop = store_op_for(exprs[0]);
              if (exprs[0]->m_vtype == TYPE_FLOAT)
                  out = new ast_binstore(ctx, assignop,
                                         (op->id == opid2('^','=') ? VINSTR_BITXOR : op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
                                ty1, ty2);
                  return false;
              }
 -            if (ast_istype(exprs[0], ast_entfield))
 -                assignop = type_storep_instr[exprs[0]->m_vtype];
 -            else
 -                assignop = type_store_instr[exprs[0]->m_vtype];
 +            assignop = store_op_for(exprs[0]);
              if (exprs[0]->m_vtype == TYPE_FLOAT)
                  out = fold::binary(ctx, INSTR_BITAND, exprs[0], exprs[1]);
              else
@@@ -1257,14 -1263,14 +1257,14 @@@ static bool parser_close_call(parser_t 
       * intrinsic call and just evaluate it i.e constant fold it.
       */
      if (fold && ast_istype(fun, ast_value) && ((ast_value*)fun)->m_intrinsic) {
 -        ast_expression **exprs  = nullptr;
 +        std::vector<ast_expression*> exprs;
          ast_expression *foldval = nullptr;
  
 +        exprs.reserve(paramcount);
          for (i = 0; i < paramcount; i++)
 -            vec_push(exprs, sy->out[fid+1 + i].out);
 +            exprs.push_back(sy->out[fid+1 + i].out);
  
 -        if (!(foldval = parser->m_intrin.do_fold((ast_value*)fun, exprs))) {
 -            vec_free(exprs);
 +        if (!(foldval = parser->m_intrin.do_fold((ast_value*)fun, exprs.data()))) {
              goto fold_leave;
          }
  
           */
          sy->out[fid] = syexp(foldval->m_context, foldval);
          sy->out.erase(sy->out.end() - paramcount, sy->out.end());
 -        vec_free(exprs);
  
          return true;
      }
@@@ -1661,16 -1668,13 +1661,16 @@@ static bool parse_sya_operand(parser_t 
          }
          else
          {
 -            if (ast_istype(var, ast_value)) {
 -                ((ast_value*)var)->m_uses++;
 +            // promote these to norefs
 +            if (ast_istype(var, ast_value))
 +            {
 +                ((ast_value *)var)->m_flags |= AST_FLAG_NOREF;
              }
 -            else if (ast_istype(var, ast_member)) {
 -                ast_member *mem = (ast_member*)var;
 +            else if (ast_istype(var, ast_member))
 +            {
 +                ast_member *mem = (ast_member *)var;
                  if (ast_istype(mem->m_owner, ast_value))
 -                    ((ast_value*)(mem->m_owner))->m_uses++;
 +                    ((ast_value *)mem->m_owner)->m_flags |= AST_FLAG_NOREF;
              }
          }
          sy->out.push_back(syexp(parser_ctx(parser), var));
@@@ -1989,11 -1993,11 +1989,11 @@@ static ast_expression* parse_expression
  
  static void parser_enterblock(parser_t *parser)
  {
 -    vec_push(parser->variables, util_htnew(PARSER_HT_SIZE));
 -    vec_push(parser->_blocklocals, vec_size(parser->_locals));
 -    vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
 -    vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs));
 -    vec_push(parser->_block_ctx, parser_ctx(parser));
 +    parser->variables.push_back(util_htnew(PARSER_HT_SIZE));
 +    parser->_blocklocals.push_back(parser->_locals.size());
 +    parser->typedefs.push_back(util_htnew(TYPEDEF_HT_SIZE));
 +    parser->_blocktypedefs.push_back(parser->_typedefs.size());
 +    parser->_block_ctx.push_back(parser_ctx(parser));
  }
  
  static bool parser_leaveblock(parser_t *parser)
      bool   rv = true;
      size_t locals, typedefs;
  
 -    if (vec_size(parser->variables) <= PARSER_HT_LOCALS) {
 +    if (parser->variables.size() <= PARSER_HT_LOCALS) {
          parseerror(parser, "internal error: parser_leaveblock with no block");
          return false;
      }
  
 -    util_htdel(vec_last(parser->variables));
 +    util_htdel(parser->variables.back());
  
 -    vec_pop(parser->variables);
 -    if (!vec_size(parser->_blocklocals)) {
 +    parser->variables.pop_back();
 +    if (!parser->_blocklocals.size()) {
          parseerror(parser, "internal error: parser_leaveblock with no block (2)");
          return false;
      }
  
 -    locals = vec_last(parser->_blocklocals);
 -    vec_pop(parser->_blocklocals);
 -    while (vec_size(parser->_locals) != locals) {
 -        ast_expression *e = vec_last(parser->_locals);
 -        ast_value      *v = (ast_value*)e;
 -        vec_pop(parser->_locals);
 -        if (ast_istype(e, ast_value) && !v->m_uses) {
 -            if (compile_warning(v->m_context, WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->m_name))
 -                rv = false;
 -        }
 -    }
 +    locals = parser->_blocklocals.back();
 +    parser->_blocklocals.pop_back();
 +    parser->_locals.resize(locals);
  
 -    typedefs = vec_last(parser->_blocktypedefs);
 -    while (vec_size(parser->_typedefs) != typedefs) {
 -        delete vec_last(parser->_typedefs);
 -        vec_pop(parser->_typedefs);
 -    }
 -    util_htdel(vec_last(parser->typedefs));
 -    vec_pop(parser->typedefs);
 +    typedefs = parser->_blocktypedefs.back();
 +    parser->_typedefs.resize(typedefs);
 +    util_htdel(parser->typedefs.back());
 +    parser->typedefs.pop_back();
  
 -    vec_pop(parser->_block_ctx);
 +    parser->_block_ctx.pop_back();
  
      return rv;
  }
  
  static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e)
  {
 -    vec_push(parser->_locals, e);
 -    util_htset(vec_last(parser->variables), name, (void*)e);
 +    parser->_locals.push_back(e);
 +    util_htset(parser->variables.back(), name, (void*)e);
  }
  static void parser_addlocal(parser_t *parser, const std::string &name, ast_expression *e) {
      return parser_addlocal(parser, name.c_str(), e);
@@@ -2619,7 -2634,6 +2619,7 @@@ static bool parse_return(parser_t *pars
              retval = new ast_value(ctx, "#LOCAL_RETURN", TYPE_VOID);
              retval->adoptType(*expected->m_next);
              parser->function->m_return_value = retval;
 +            parser->function->m_return_value->m_flags |= AST_FLAG_NOREF;
          }
  
          if (!exp->compareType(*retval)) {
@@@ -3106,52 -3120,6 +3106,52 @@@ static bool parse_switch_go(parser_t *p
                  return false;
              }
              swcase.m_value = parse_expression_leave(parser, false, false, false);
 +
 +            if (!operand->compareType(*swcase.m_value)) {
 +                char ty1[1024];
 +                char ty2[1024];
 +
 +                ast_type_to_string(swcase.m_value, ty1, sizeof ty1);
 +                ast_type_to_string(operand, ty2, sizeof ty2);
 +
 +                auto fnLiteral = [](ast_expression *expression) -> char* {
 +                    if (!ast_istype(expression, ast_value))
 +                        return nullptr;
 +                    ast_value *value = (ast_value *)expression;
 +                    if (!value->m_hasvalue)
 +                        return nullptr;
 +                    char *string = nullptr;
 +                    basic_value_t *constval = &value->m_constval;
 +                    switch (value->m_vtype)
 +                    {
 +                    case TYPE_FLOAT:
 +                        util_asprintf(&string, "%.2f", constval->vfloat);
 +                        return string;
 +                    case TYPE_VECTOR:
 +                        util_asprintf(&string, "'%.2f %.2f %.2f'",
 +                            constval->vvec.x,
 +                            constval->vvec.y,
 +                            constval->vvec.z);
 +                        return string;
 +                    case TYPE_STRING:
 +                        util_asprintf(&string, "\"%s\"", constval->vstring);
 +                        return string;
 +                    default:
 +                        break;
 +                    }
 +                    return nullptr;
 +                };
 +
 +                char *literal = fnLiteral(swcase.m_value);
 +                if (literal)
 +                    compile_error(parser_ctx(parser), "incompatible type `%s` for switch case `%s` expected `%s`", ty1, literal, ty2);
 +                else
 +                    compile_error(parser_ctx(parser), "incompatible type `%s` for switch case expected `%s`", ty1, ty2);
 +                mem_d(literal);
 +                delete switchnode;
 +                return false;
 +            }
 +
              if (!swcase.m_value) {
                  delete switchnode;
                  parseerror(parser, "expected expression for case");
              }
              if (!OPTS_FLAG(RELAXED_SWITCH)) {
                  if (!ast_istype(swcase.m_value, ast_value)) { /* || ((ast_value*)swcase.m_value)->m_cvq != CV_CONST) { */
+                     delete switchnode;
                      parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch");
                      ast_unref(operand);
                      return false;
@@@ -3612,9 -3581,9 +3613,9 @@@ static bool parse_enum(parser_t *parser
      bool        flag = false;
      bool        reverse = false;
      qcfloat_t     num = 0;
 -    ast_value **values = nullptr;
      ast_value  *var = nullptr;
      ast_value  *asvalue;
 +    std::vector<ast_value*> values;
  
      ast_expression *old;
  
                  break;
              }
              parseerror(parser, "expected identifier or `}`");
 -            goto onerror;
 +            return false;
          }
  
          old = parser_find_field(parser, parser_tokval(parser));
          if (old) {
              parseerror(parser, "value `%s` has already been declared here: %s:%i",
                         parser_tokval(parser), old->m_context.file, old->m_context.line);
 -            goto onerror;
 +            return false;
          }
  
          var = new ast_value(parser_ctx(parser), parser_tokval(parser), TYPE_FLOAT);
 -        vec_push(values, var);
 +        values.push_back(var);
          var->m_cvq             = CV_CONST;
          var->m_hasvalue        = true;
  
  
          if (!parser_next(parser)) {
              parseerror(parser, "expected `=`, `}` or comma after identifier");
 -            goto onerror;
 +            return false;
          }
  
          if (parser->tok == ',')
              break;
          if (parser->tok != '=') {
              parseerror(parser, "expected `=`, `}` or comma after identifier");
 -            goto onerror;
 +            return false;
          }
  
          if (!parser_next(parser)) {
              parseerror(parser, "expected expression after `=`");
 -            goto onerror;
 +            return false;
          }
  
          /* We got a value! */
          asvalue = (ast_value*)old;
          if (!ast_istype(old, ast_value) || asvalue->m_cvq != CV_CONST || !asvalue->m_hasvalue) {
              compile_error(var->m_context, "constant value or expression expected");
 -            goto onerror;
 +            return false;
          }
          num = (var->m_constval.vfloat = asvalue->m_constval.vfloat) + 1;
  
              break;
          if (parser->tok != ',') {
              parseerror(parser, "expected `}` or comma after expression");
 -            goto onerror;
 +            return false;
          }
      }
  
      /* patch them all (for reversed attribute) */
      if (reverse) {
          size_t i;
 -        for (i = 0; i < vec_size(values); i++)
 -            values[i]->m_constval.vfloat = vec_size(values) - i - 1;
 +        for (i = 0; i < values.size(); i++)
 +            values[i]->m_constval.vfloat = values.size() - i - 1;
      }
  
      if (parser->tok != '}') {
          parseerror(parser, "internal error: breaking without `}`");
 -        goto onerror;
 +        return false;
      }
  
      if (!parser_next(parser) || parser->tok != ';') {
          parseerror(parser, "expected semicolon after enumeration");
 -        goto onerror;
 +        return false;
      }
  
      if (!parser_next(parser)) {
          parseerror(parser, "parse error after enumeration");
 -        goto onerror;
 +        return false;
      }
  
 -    vec_free(values);
      return true;
 -
 -onerror:
 -    vec_free(values);
 -    return false;
  }
  
  static bool parse_block_into(parser_t *parser, ast_block *block)
@@@ -4154,7 -4128,7 +4155,7 @@@ static bool parse_function_body(parser_
      parser->function = old;
      if (!parser_leaveblock(parser))
          retval = false;
 -    if (vec_size(parser->variables) != PARSER_HT_LOCALS) {
 +    if (parser->variables.size() != PARSER_HT_LOCALS) {
          parseerror(parser, "internal error: local scopes left");
          retval = false;
      }
@@@ -4973,15 -4947,15 +4974,15 @@@ static bool parse_typedef(parser_t *par
          return false;
      }
  
 -    if ( (oldtype = parser_find_typedef(parser, typevar->m_name, vec_last(parser->_blocktypedefs))) ) {
 +    if ( (oldtype = parser_find_typedef(parser, typevar->m_name, parser->_blocktypedefs.back())) ) {
          parseerror(parser, "type `%s` has already been declared here: %s:%i",
                     typevar->m_name, oldtype->m_context.file, oldtype->m_context.line);
          delete typevar;
          return false;
      }
  
 -    vec_push(parser->_typedefs, typevar);
 -    util_htset(vec_last(parser->typedefs), typevar->m_name.c_str(), typevar);
 +    parser->_typedefs.emplace_back(typevar);
 +    util_htset(parser->typedefs.back(), typevar->m_name.c_str(), typevar);
  
      if (parser->tok != ';') {
          parseerror(parser, "expected semicolon after typedef");
@@@ -5213,12 -5187,12 +5214,12 @@@ static bool parse_variable(parser_t *pa
              /* Deal with end_sys_ vars */
              was_end = false;
              if (var->m_name == "end_sys_globals") {
 -                var->m_uses++;
 +                var->m_flags |= AST_FLAG_NOREF;
                  parser->crc_globals = parser->globals.size();
                  was_end = true;
              }
              else if (var->m_name == "end_sys_fields") {
 -                var->m_uses++;
 +                var->m_flags |= AST_FLAG_NOREF;
                  parser->crc_fields = parser->fields.size();
                  was_end = true;
              }
          }
          else /* it's not a global */
          {
 -            old = parser_find_local(parser, var->m_name, vec_size(parser->variables)-1, &isparam);
 +            old = parser_find_local(parser, var->m_name, parser->variables.size()-1, &isparam);
              if (old && !isparam) {
                  parseerror(parser, "local `%s` already declared here: %s:%i",
                             var->m_name, old->m_context.file, (int)old->m_context.line);
              }
          }
  
 -        /* in a noref section we simply bump the usecount */
          if (noref || parser->noref)
 -            var->m_uses++;
 +            var->m_flags |= AST_FLAG_NOREF;
  
          /* Part 2:
           * Create the global/local, and deal with vector types.
                      prefix_len = defname.length();
  
                      // Add it to the local scope
 -                    util_htset(vec_last(parser->variables), var->m_name.c_str(), (void*)var);
 +                    util_htset(parser->variables.back(), var->m_name.c_str(), (void*)var);
  
                      // now rename the global
                      defname.append(var->m_name);
                      if (isvector) {
                          defname.erase(prefix_len);
                          for (i = 0; i < 3; ++i) {
 -                            util_htset(vec_last(parser->variables), me[i]->m_name.c_str(), (void*)(me[i]));
 -                            me[i]->m_name = move(defname + me[i]->m_name);
 +                            util_htset(parser->variables.back(), me[i]->m_name.c_str(), (void*)(me[i]));
 +                            me[i]->m_name = defname + me[i]->m_name;
                              parser->globals.push_back(me[i]);
                          }
                      }
@@@ -5743,15 -5718,15 +5744,15 @@@ skipvar
                  /* remove it from the current locals */
                  if (isvector) {
                      for (i = 0; i < 3; ++i) {
 -                        vec_pop(parser->_locals);
 +                        parser->_locals.pop_back();
                          localblock->m_collect.pop_back();
                      }
                  }
                  /* do sanity checking, this function really needs refactoring */
 -                if (vec_last(parser->_locals) != var)
 +                if (parser->_locals.back() != var)
                      parseerror(parser, "internal error: unexpected change in local variable handling");
                  else
 -                    vec_pop(parser->_locals);
 +                    parser->_locals.pop_back();
                  if (localblock->m_locals.back() != var)
                      parseerror(parser, "internal error: unexpected change in local variable handling (2)");
                  else
                          var->m_cvq = CV_CONST;
                      }
                      if (cval == parser->nil)
 +                    {
                          var->m_flags |= AST_FLAG_INITIALIZED;
 +                        var->m_flags |= AST_FLAG_NOREF;
 +                    }
                      else
                      {
                          var->m_hasvalue = true;
@@@ -6005,72 -5977,21 +6006,72 @@@ static void generate_checksum(parser_t 
      ir->m_code->crc = crc;
  }
  
 +parser_t::parser_t()
 +    : lex(nullptr)
 +    , tok(0)
 +    , ast_cleaned(false)
 +    , translated(0)
 +    , crc_globals(0)
 +    , crc_fields(0)
 +    , function(nullptr)
 +    , aliases(util_htnew(PARSER_HT_SIZE))
 +    , htfields(util_htnew(PARSER_HT_SIZE))
 +    , htglobals(util_htnew(PARSER_HT_SIZE))
 +    , assign_op(nullptr)
 +    , noref(false)
 +    , max_param_count(1)
 +    // finish initializing the rest of the parser before initializing
 +    // m_fold and m_intrin with the parser passed along
 +    , m_fold()
 +    , m_intrin()
 +{
 +    variables.push_back(htfields);
 +    variables.push_back(htglobals);
 +    typedefs.push_back(util_htnew(TYPEDEF_HT_SIZE));
 +    _blocktypedefs.push_back(0);
 +
 +    lex_ctx_t empty_ctx;
 +    empty_ctx.file   = "<internal>";
 +    empty_ctx.line   = 0;
 +    empty_ctx.column = 0;
 +    nil = new ast_value(empty_ctx, "nil", TYPE_NIL);
 +    nil->m_cvq = CV_CONST;
 +    if (OPTS_FLAG(UNTYPED_NIL))
 +        util_htset(htglobals, "nil", (void*)nil);
 +
 +    const_vec[0] = new ast_value(empty_ctx, "<vector.x>", TYPE_NOEXPR);
 +    const_vec[1] = new ast_value(empty_ctx, "<vector.y>", TYPE_NOEXPR);
 +    const_vec[2] = new ast_value(empty_ctx, "<vector.z>", TYPE_NOEXPR);
 +
 +    if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) {
 +        reserved_version = new ast_value(empty_ctx, "reserved:version", TYPE_STRING);
 +        reserved_version->m_cvq = CV_CONST;
 +        reserved_version->m_hasvalue = true;
 +        reserved_version->m_flags |= AST_FLAG_INCLUDE_DEF;
 +        reserved_version->m_flags |= AST_FLAG_NOREF;
 +        reserved_version->m_constval.vstring = util_strdup(GMQCC_FULL_VERSION_STRING);
 +    } else {
 +        reserved_version = nullptr;
 +    }
 +
 +    m_fold = fold(this);
 +    m_intrin = intrin(this);
 +}
 +
 +parser_t::~parser_t()
 +{
 +    remove_ast();
 +}
 +
  parser_t *parser_create()
  {
      parser_t *parser;
 -    lex_ctx_t empty_ctx;
      size_t i;
  
 -    parser = (parser_t*)mem_a(sizeof(parser_t));
 +    parser = new parser_t;
      if (!parser)
          return nullptr;
  
 -    memset(parser, 0, sizeof(*parser));
 -
 -    // TODO: remove
 -    new (parser) parser_t();
 -
      for (i = 0; i < operator_count; ++i) {
          if (operators[i].id == opid1('=')) {
              parser->assign_op = operators+i;
      }
      if (!parser->assign_op) {
          con_err("internal error: initializing parser: failed to find assign operator\n");
 -        mem_d(parser);
 +        delete parser;
          return nullptr;
      }
  
 -    vec_push(parser->variables, parser->htfields  = util_htnew(PARSER_HT_SIZE));
 -    vec_push(parser->variables, parser->htglobals = util_htnew(PARSER_HT_SIZE));
 -    vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
 -    vec_push(parser->_blocktypedefs, 0);
 -
 -    parser->aliases = util_htnew(PARSER_HT_SIZE);
 -
 -    empty_ctx.file   = "<internal>";
 -    empty_ctx.line   = 0;
 -    empty_ctx.column = 0;
 -    parser->nil = new ast_value(empty_ctx, "nil", TYPE_NIL);
 -    parser->nil->m_cvq = CV_CONST;
 -    if (OPTS_FLAG(UNTYPED_NIL))
 -        util_htset(parser->htglobals, "nil", (void*)parser->nil);
 -
 -    parser->max_param_count = 1;
 -
 -    parser->const_vec[0] = new ast_value(empty_ctx, "<vector.x>", TYPE_NOEXPR);
 -    parser->const_vec[1] = new ast_value(empty_ctx, "<vector.y>", TYPE_NOEXPR);
 -    parser->const_vec[2] = new ast_value(empty_ctx, "<vector.z>", TYPE_NOEXPR);
 -
 -    if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) {
 -        parser->reserved_version = new ast_value(empty_ctx, "reserved:version", TYPE_STRING);
 -        parser->reserved_version->m_cvq = CV_CONST;
 -        parser->reserved_version->m_hasvalue = true;
 -        parser->reserved_version->m_flags |= AST_FLAG_INCLUDE_DEF;
 -        parser->reserved_version->m_constval.vstring = util_strdup(GMQCC_FULL_VERSION_STRING);
 -    } else {
 -        parser->reserved_version = nullptr;
 -    }
 -
 -    parser->m_fold = fold(parser);
 -    parser->m_intrin = intrin(parser);
      return parser;
  }
  
@@@ -6138,42 -6092,54 +6139,42 @@@ bool parser_compile_string(parser_t *pa
      return parser_compile(parser);
  }
  
 -static void parser_remove_ast(parser_t *parser)
 +void parser_t::remove_ast()
  {
 -    size_t i;
 -    if (parser->ast_cleaned)
 +    if (ast_cleaned)
          return;
 -    parser->ast_cleaned = true;
 -    for (auto &it : parser->accessors) {
 +    ast_cleaned = true;
 +    for (auto &it : accessors) {
          delete it->m_constval.vfunc;
          it->m_constval.vfunc = nullptr;
          delete it;
      }
 -    for (auto &it : parser->functions) delete it;
 -    for (auto &it : parser->globals) delete it;
 -    for (auto &it : parser->fields) delete it;
 -
 -    for (i = 0; i < vec_size(parser->variables); ++i)
 -        util_htdel(parser->variables[i]);
 -    vec_free(parser->variables);
 -    vec_free(parser->_blocklocals);
 -    vec_free(parser->_locals);
 +    for (auto &it : functions) delete it;
 +    for (auto &it : globals) delete it;
 +    for (auto &it : fields) delete it;
  
 -    for (i = 0; i < vec_size(parser->_typedefs); ++i)
 -        delete parser->_typedefs[i];
 -    vec_free(parser->_typedefs);
 -    for (i = 0; i < vec_size(parser->typedefs); ++i)
 -        util_htdel(parser->typedefs[i]);
 -    vec_free(parser->typedefs);
 -    vec_free(parser->_blocktypedefs);
 +    for (auto &it : variables) util_htdel(it);
 +    variables.clear();
 +    _blocklocals.clear();
 +    _locals.clear();
  
 -    vec_free(parser->_block_ctx);
 +    _typedefs.clear();
 +    for (auto &it : typedefs) util_htdel(it);
 +    typedefs.clear();
 +    _blocktypedefs.clear();
  
 -    delete parser->nil;
 +    _block_ctx.clear();
  
 -    delete parser->const_vec[0];
 -    delete parser->const_vec[1];
 -    delete parser->const_vec[2];
 +    delete nil;
  
 -    if (parser->reserved_version)
 -        delete parser->reserved_version;
 +    delete const_vec[0];
 +    delete const_vec[1];
 +    delete const_vec[2];
  
 -    util_htdel(parser->aliases);
 -}
 +    if (reserved_version)
 +        delete reserved_version;
  
 -void parser_cleanup(parser_t *parser)
 -{
 -    parser_remove_ast(parser);
 -    parser->~parser_t();
 -    mem_d(parser);
 +    util_htdel(aliases);
  }
  
  static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) {
@@@ -6261,7 -6227,7 +6262,7 @@@ bool parser_finish(parser_t *parser, co
          if (!ast_istype(it, ast_value))
              continue;
          asvalue = (ast_value*)it;
 -        if (!asvalue->m_uses && !asvalue->m_hasvalue && asvalue->m_vtype != TYPE_FUNCTION) {
 +        if (!(asvalue->m_flags & AST_FLAG_NOREF) && asvalue->m_cvq != CV_CONST && asvalue->m_vtype != TYPE_FUNCTION) {
              retval = retval && !compile_warning(asvalue->m_context, WARN_UNUSED_VARIABLE,
                                                  "unused global: `%s`", asvalue->m_name);
          }
              return false;
          }
      }
 -    parser_remove_ast(parser);
 +    parser->remove_ast();
  
 -    if (compile_Werrors) {
 -        con_out("*** there were warnings treated as errors\n");
 -        compile_show_werrors();
 -        retval = false;
 -    }
 +    auto fnCheckWErrors = [&retval]() {
 +        if (compile_Werrors) {
 +            con_out("*** there were warnings treated as errors\n");
 +            compile_show_werrors();
 +            retval = false;
 +        }
 +    };
 +
 +    fnCheckWErrors();
  
      if (retval) {
          if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
              delete ir;
              return false;
          }
 +
 +        // ir->generate can generate compiler warnings
 +        fnCheckWErrors();
      }
      delete ir;
      return retval;