]> 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

23 files changed:
ast.cpp
ast.h
doc/gmqcc.1
exec.cpp
fold.cpp
ftepp.cpp
gmqcc.h
gmqcc.ini.example
ir.cpp
ir.h
main.cpp
opts.cpp
opts.def
parser.cpp
parser.h
tests/defs.qh
tests/fieldfuncs.tmpl
tests/memberbinop.qc [new file with mode: 0644]
tests/memberbinop.tmpl [new file with mode: 0644]
tests/vecfields.qc [new file with mode: 0644]
tests/vecfields.tmpl [new file with mode: 0644]
tests/vecmath.qc [new file with mode: 0644]
tests/vecmath.tmpl [new file with mode: 0644]

diff --git a/ast.cpp b/ast.cpp
index cf8ffc70f445bb029b6bfa76396c8be4147e679a..cbc5db9657a9af3beaf6d12b5e64bb442c4f0066 100644 (file)
--- a/ast.cpp
+++ b/ast.cpp
@@ -1132,6 +1132,8 @@ bool ast_value::generateGlobal(ir_builder *ir, bool isfield)
         m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF;
     if (m_flags & AST_FLAG_ERASEABLE)
         m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+    if (m_flags & AST_FLAG_NOREF)
+        m_ir_v->m_flags |= IR_FLAG_NOREF;
 
     /* initialize */
     if (m_hasvalue) {
@@ -1236,6 +1238,8 @@ bool ast_value::generateGlobalField(ir_builder *ir)
             m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF;
         if (m_flags & AST_FLAG_ERASEABLE)
             m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+        if (m_flags & AST_FLAG_NOREF)
+            m_ir_v->m_flags |= IR_FLAG_NOREF;
 
         const size_t namelen = m_name.length();
         std::unique_ptr<char[]> name(new char[namelen+16]);
@@ -1254,7 +1258,9 @@ bool ast_value::generateGlobalField(ir_builder *ir)
             array->m_ir_values[ai]->m_unique_life = true;
             array->m_ir_values[ai]->m_locked      = true;
             if (m_flags & AST_FLAG_INCLUDE_DEF)
-                m_ir_values[ai]->m_flags |= IR_FLAG_INCLUDE_DEF;
+                array->m_ir_values[ai]->m_flags |= IR_FLAG_INCLUDE_DEF;
+            if (m_flags & AST_FLAG_NOREF)
+                array->m_ir_values[ai]->m_flags |= IR_FLAG_NOREF;
         }
     }
     else
@@ -1266,9 +1272,10 @@ bool ast_value::generateGlobalField(ir_builder *ir)
         m_ir_v = v;
         if (m_flags & AST_FLAG_INCLUDE_DEF)
             m_ir_v->m_flags |= IR_FLAG_INCLUDE_DEF;
-
         if (m_flags & AST_FLAG_ERASEABLE)
             m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+        if (m_flags & AST_FLAG_NOREF)
+            m_ir_v->m_flags |= IR_FLAG_NOREF;
     }
     return true;
 }
@@ -1299,7 +1306,9 @@ ir_value *ast_value::prepareGlobalArray(ir_builder *ir)
     if (m_flags & AST_FLAG_INCLUDE_DEF)
         v->m_flags |= IR_FLAG_INCLUDE_DEF;
     if (m_flags & AST_FLAG_ERASEABLE)
-        m_ir_v->m_flags |= IR_FLAG_ERASABLE;
+        v->m_flags |= IR_FLAG_ERASABLE;
+    if (m_flags & AST_FLAG_NOREF)
+        v->m_flags |= IR_FLAG_NOREF;
 
     const size_t namelen = m_name.length();
     std::unique_ptr<char[]> name(new char[namelen+16]);
@@ -1319,6 +1328,8 @@ ir_value *ast_value::prepareGlobalArray(ir_builder *ir)
         m_ir_values[ai]->m_locked      = true;
         if (m_flags & AST_FLAG_INCLUDE_DEF)
             m_ir_values[ai]->m_flags |= IR_FLAG_INCLUDE_DEF;
+        if (m_flags & AST_FLAG_NOREF)
+            m_ir_values[ai]->m_flags |= IR_FLAG_NOREF;
     }
 
     return v;
@@ -1365,6 +1376,9 @@ bool ast_value::generateLocal(ir_function *func, bool param)
         v->m_unique_life = true;
         v->m_locked      = true;
 
+        if (m_flags & AST_FLAG_NOREF)
+            v->m_flags |= IR_FLAG_NOREF;
+
         const size_t namelen = m_name.length();
         std::unique_ptr<char[]> name(new char[namelen+16]);
         util_strncpy(name.get(), m_name.c_str(), namelen);
@@ -1379,7 +1393,10 @@ bool ast_value::generateLocal(ir_function *func, bool param)
             }
             m_ir_values[ai]->m_context = m_context;
             m_ir_values[ai]->m_unique_life = true;
-            m_ir_values[ai]->m_locked      = true;
+            m_ir_values[ai]->m_locked = true;
+
+            if (m_flags & AST_FLAG_NOREF)
+                m_ir_values[ai]->m_flags |= IR_FLAG_NOREF;
         }
     }
     else
@@ -1418,6 +1435,9 @@ bool ast_value::generateLocal(ir_function *func, bool param)
     v->m_cvq = m_cvq;
     m_ir_v = v;
 
+    if (m_flags & AST_FLAG_NOREF)
+        m_ir_v->m_flags |= IR_FLAG_NOREF;
+
     if (!generateAccessors(func->m_owner))
         return false;
     return true;
@@ -1490,9 +1510,9 @@ bool ast_function::generateFunction(ir_builder *ir)
     /* fill the parameter list */
     for (auto &it : m_function_type->m_type_params) {
         if (it->m_vtype == TYPE_FIELD)
-            vec_push(irf->m_params, it->m_next->m_vtype);
+            irf->m_params.push_back(it->m_next->m_vtype);
         else
-            vec_push(irf->m_params, it->m_vtype);
+            irf->m_params.push_back(it->m_vtype);
         if (!m_builtin) {
             if (!it->generateLocal(m_ir_func, true))
                 return false;
@@ -1562,7 +1582,7 @@ bool ast_function::generateFunction(ir_builder *ir)
         {
             return ir_block_create_return(m_curblock, m_context, nullptr);
         }
-        else if (vec_size(m_curblock->m_entries) || m_curblock == irf->m_first)
+        else if (m_curblock->m_entries.size() || m_curblock == irf->m_first)
         {
             if (m_return_value) {
                 if (!m_return_value->codegen(this, false, &dummy))
@@ -1784,7 +1804,7 @@ bool ast_binary::codegen(ast_function *func, bool lvalue, ir_value **out)
                 return false;
         }
         /* use the likely flag */
-        vec_last(func->m_curblock->m_instr)->m_likely = true;
+        func->m_curblock->m_instr.back()->m_likely = true;
 
         /* enter the right-expression's block */
         func->m_curblock = other;
@@ -2098,13 +2118,45 @@ bool ast_member::codegen(ast_function *func, bool lvalue, ir_value **out)
         compile_error(m_context, "not an l-value (member access)");
         return false;
     }
-    if (m_outl) {
+    if (lvalue && m_outl) {
         *out = m_outl;
         return true;
     }
+    if (!lvalue && m_outr) {
+        *out = m_outr;
+        return true;
+    }
 
-    if (!m_owner->codegen(func, false, &vec))
-        return false;
+    if (ast_istype(m_owner, ast_entfield)) {
+        ir_value *ent, *field;
+        auto entfield = reinterpret_cast<ast_entfield*>(m_owner);
+        if (!entfield->m_entity->codegen(func, false, &ent))
+            return false;
+        if (!entfield->m_field->codegen(func, false, &vec))
+            return false;
+        field = vec->vectorMember(m_field);
+        if (lvalue) {
+            *out = ir_block_create_fieldaddress(func->m_curblock, m_context, func->makeLabel("mefa"),
+                                                ent, field);
+        } else {
+            *out = ir_block_create_load_from_ent(func->m_curblock, m_context, func->makeLabel("mefv"),
+                                                 ent, field, m_vtype);
+        }
+        if (!*out) {
+            compile_error(m_context, "failed to create %s instruction (output type %s)",
+                     (lvalue ? "ADDRESS" : "FIELD"),
+                     type_name[m_vtype]);
+            return false;
+        }
+        if (lvalue)
+            m_outl = *out;
+        else
+            m_outr = *out;
+        return (*out != nullptr);
+    } else {
+        if (!m_owner->codegen(func, false, &vec))
+            return false;
+    }
 
     if (vec->m_vtype != TYPE_VECTOR &&
         !(vec->m_vtype == TYPE_FIELD && m_owner->m_next->m_vtype == TYPE_VECTOR))
diff --git a/ast.h b/ast.h
index b305dac393f82001e018cc2a0d53d7c64394ffc4..04fa4d80b5e5b248c3a2b3d73c8a7f77c04f3b28 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -62,6 +62,12 @@ enum {
     AST_FLAG_COVERAGE       = 1 << 12,
     AST_FLAG_BLOCK_COVERAGE = 1 << 13,
 
+    /*
+     * Propagates norefness to the IR so the unused (read/write) check can be
+     * more intelligently done.
+     */
+    AST_FLAG_NOREF          = 1 << 14,
+
     AST_FLAG_LAST,
     AST_FLAG_TYPE_MASK      = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN),
     AST_FLAG_COVERAGE_MASK  = (AST_FLAG_BLOCK_COVERAGE)
@@ -223,9 +229,6 @@ struct ast_value : ast_expression
      */
     std::vector<basic_value_t> m_initlist;
 
-    /* usecount for the parser */
-    size_t m_uses = 0;
-
     ir_value *m_ir_v = nullptr;
     std::vector<ir_value*> m_ir_values;
     size_t m_ir_value_count = 0;
index 78d767e26f384ae38e8e7394a0a0d607c9f90a50..510e754d31dbe276eb4eab87e365ebe4291a4bba 100644 (file)
@@ -186,6 +186,9 @@ variables can be opened using
 .Ql #pragma noref 1
 and closed via
 .Ql #pragma noref 0 Ns .
+.It Fl W Ns Cm unused-component
+Generate a warning about vector variables which are declared but not all their
+components are used.
 .It Fl W Ns Cm used-uninitialized
 Generate a warning if it is possible that a variable can be used
 without prior initialization. Note that this warning is not
index e76004ec313a4b5c7e6e29796e43741a618666b8..387923f27b3c9d86f7cf772085ba2740a689087a 100644 (file)
--- a/exec.cpp
+++ b/exec.cpp
@@ -28,6 +28,12 @@ static void qcvmerror(qc_program_t *prog, const char *fmt, ...)
     putchar('\n');
 }
 
+qc_program::qc_program(const char *name, uint16_t crc, size_t entfields)
+    : filename(name)
+    , crc16(crc)
+    , entityfields(entfields)
+{}
+
 qc_program_t* prog_load(const char *filename, bool skipversion)
 {
     prog_header_t header;
@@ -58,22 +64,7 @@ qc_program_t* prog_load(const char *filename, bool skipversion)
         return nullptr;
     }
 
-    prog = (qc_program_t*)mem_a(sizeof(qc_program_t));
-    if (!prog) {
-        fclose(file);
-        fprintf(stderr, "failed to allocate program data\n");
-        return nullptr;
-    }
-    memset(prog, 0, sizeof(*prog));
-
-    prog->entityfields = header.entfield;
-    prog->crc16 = header.crc16;
-
-    prog->filename = util_strdup(filename);
-    if (!prog->filename) {
-        loaderror("failed to store program name");
-        goto error;
-    }
+    prog = new qc_program(filename, header.crc16, header.entfield);
 
 #define read_data(hdrvar, progvar, reserved)                           \
     if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {            \
@@ -110,7 +101,8 @@ qc_program_t* prog_load(const char *filename, bool skipversion)
     fclose(file);
 
     /* profile counters */
-    memset(vec_add(prog->profile, prog->code.size()), 0, sizeof(prog->profile[0]) * prog->code.size());
+    prog->profile.resize(prog->code.size());
+    memset(&prog->profile[0], 0, sizeof(prog->profile[0]) * prog->profile.size());
 
     /* Add tempstring area */
     prog->tempstring_start = prog->strings.size();
@@ -119,8 +111,10 @@ qc_program_t* prog_load(const char *filename, bool skipversion)
     prog->strings.resize(prog->strings.size() + 16*1024, '\0');
 
     /* spawn the world entity */
-    vec_push(prog->entitypool, true);
-    memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0]));
+    prog->entitypool.emplace_back(true);
+    prog->entitydata.resize(prog->entityfields);
+    if (prog->entitydata.size())
+        memset(prog->entitydata.data(), 0, sizeof(prog->entitydata[0]) * prog->entityfields);
     prog->entities = 1;
 
     /* cache some globals and fields from names */
@@ -156,11 +150,7 @@ qc_program_t* prog_load(const char *filename, bool skipversion)
     return prog;
 
 error:
-    if (prog->filename)
-        mem_d(prog->filename);
-    vec_free(prog->entitydata);
-    vec_free(prog->entitypool);
-    mem_d(prog);
+    delete prog;
 
     fclose(file);
     return nullptr;
@@ -168,13 +158,7 @@ error:
 
 void prog_delete(qc_program_t *prog)
 {
-    if (prog->filename) mem_d(prog->filename);
-    vec_free(prog->entitydata);
-    vec_free(prog->entitypool);
-    vec_free(prog->localstack);
-    vec_free(prog->stack);
-    vec_free(prog->profile);
-    mem_d(prog);
+    delete prog;
 }
 
 /***********************************************************************
@@ -205,28 +189,31 @@ prog_section_def_t* prog_getdef(qc_program_t *prog, qcint_t off)
 }
 
 qcany_t* prog_getedict(qc_program_t *prog, qcint_t e) {
-    if (e >= (qcint_t)vec_size(prog->entitypool)) {
+    if (e >= (qcint_t)prog->entitypool.size()) {
         prog->vmerror++;
         fprintf(stderr, "Accessing out of bounds edict %i\n", (int)e);
         e = 0;
     }
-    return (qcany_t*)(prog->entitydata + (prog->entityfields * e));
+    return (qcany_t*)&prog->entitydata[prog->entityfields * e];
 }
 
 static qcint_t prog_spawn_entity(qc_program_t *prog) {
     char  *data;
     qcint_t  e;
-    for (e = 0; e < (qcint_t)vec_size(prog->entitypool); ++e) {
+    for (e = 0; e < (qcint_t)prog->entitypool.size(); ++e) {
         if (!prog->entitypool[e]) {
-            data = (char*)(prog->entitydata + (prog->entityfields * e));
+            data = (char*)&prog->entitydata[prog->entityfields * e];
             memset(data, 0, prog->entityfields * sizeof(qcint_t));
             return e;
         }
     }
-    vec_push(prog->entitypool, true);
+    prog->entitypool.emplace_back(true);
     prog->entities++;
-    data = (char*)vec_add(prog->entitydata, prog->entityfields);
-    memset(data, 0, prog->entityfields * sizeof(qcint_t));
+    size_t sz = prog->entitydata.size();
+    prog->entitydata.resize(sz + prog->entityfields);
+    data = (char*)&prog->entitydata[sz];
+    memset(data, 0, sz * sizeof(qcint_t));
+
     return e;
 }
 
@@ -236,7 +223,7 @@ static void prog_free_entity(qc_program_t *prog, qcint_t e) {
         fprintf(stderr, "Trying to free world entity\n");
         return;
     }
-    if (e >= (qcint_t)vec_size(prog->entitypool)) {
+    if (e >= (qcint_t)prog->entitypool.size()) {
         prog->vmerror++;
         fprintf(stderr, "Trying to free out of bounds entity\n");
         return;
@@ -369,11 +356,11 @@ static void prog_print_statement(qc_program_t *prog, prog_section_statement_t *s
         printf("<illegal instruction %d>\n", st->opcode);
         return;
     }
-    if ((prog->xflags & VMXF_TRACE) && vec_size(prog->function_stack)) {
+    if ((prog->xflags & VMXF_TRACE) && !prog->function_stack.empty()) {
         size_t i;
-        for (i = 0; i < vec_size(prog->function_stack); ++i)
+        for (i = 0; i < prog->function_stack.size(); ++i)
             printf("->");
-        printf("%s:", vec_last(prog->function_stack));
+        printf("%s:", prog->function_stack.back());
     }
     printf(" <> %-12s", util_instr_str[st->opcode]);
     if (st->opcode >= INSTR_IF &&
@@ -467,30 +454,30 @@ static qcint_t prog_enterfunction(qc_program_t *prog, prog_section_function_t *f
     int32_t p;
 
     /* back up locals */
-    st.localsp  = vec_size(prog->localstack);
+    st.localsp  = prog->localstack.size();
     st.stmt     = prog->statement;
     st.function = func;
 
     if (prog->xflags & VMXF_TRACE) {
         const char *str = prog_getstring(prog, func->name);
-        vec_push(prog->function_stack, str);
+        prog->function_stack.emplace_back(str);
     }
 
 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
-    if (vec_size(prog->stack))
+    if (prog->stack.size())
     {
         prog_section_function_t *cur;
-        cur = prog->stack[vec_size(prog->stack)-1].function;
+        cur = prog->stack.back().function;
         if (cur)
         {
             qcint_t *globals = &prog->globals[0] + cur->firstlocal;
-            vec_append(prog->localstack, cur->locals, globals);
+            prog->localstack.insert(prog->localstack.end(), globals, globals + cur->locals);
         }
     }
 #else
     {
         qcint_t *globals = &prog->globals[0] + func->firstlocal;
-        vec_append(prog->localstack, func->locals, globals);
+        prog->localstack.insert(prog->localstack.end(), globals, globals + func->locals);
     }
 #endif
 
@@ -505,7 +492,7 @@ static qcint_t prog_enterfunction(qc_program_t *prog, prog_section_function_t *f
         }
     }
 
-    vec_push(prog->stack, st);
+    prog->stack.emplace_back(st);
 
     return func->entry;
 }
@@ -514,30 +501,31 @@ static qcint_t prog_leavefunction(qc_program_t *prog) {
     prog_section_function_t *prev = nullptr;
     size_t oldsp;
 
-    qc_exec_stack_t st = vec_last(prog->stack);
+    qc_exec_stack_t st = prog->stack.back();
 
     if (prog->xflags & VMXF_TRACE) {
-        if (vec_size(prog->function_stack))
-            vec_pop(prog->function_stack);
+        if (!prog->function_stack.empty())
+            prog->function_stack.pop_back();
     }
 
 #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
-    if (vec_size(prog->stack) > 1) {
-        prev  = prog->stack[vec_size(prog->stack)-2].function;
-        oldsp = prog->stack[vec_size(prog->stack)-2].localsp;
+    if (prog->stack.size() > 1) {
+        prev  = prog->stack[prog->stack.size()-2].function;
+        oldsp = prog->stack[prog->stack.size()-2].localsp;
     }
 #else
-    prev  = prog->stack[vec_size(prog->stack)-1].function;
-    oldsp = prog->stack[vec_size(prog->stack)-1].localsp;
+    prev  = prog->stack[prog->stack.size()-1].function;
+    oldsp = prog->stack[prog->stack.size()-1].localsp;
 #endif
     if (prev) {
-        qcint_t *globals = &prog->globals[0] + prev->firstlocal;
-        memcpy(globals, prog->localstack + oldsp, prev->locals * sizeof(prog->localstack[0]));
-        /* vec_remove(prog->localstack, oldsp, vec_size(prog->localstack)-oldsp); */
-        vec_shrinkto(prog->localstack, oldsp);
+        if (prev->locals) {
+            qcint_t *globals = &prog->globals[0] + prev->firstlocal;
+            memcpy(globals, &prog->localstack[oldsp], prev->locals * sizeof(prog->localstack[0]));
+        }
+        prog->localstack.resize(oldsp);
     }
 
-    vec_pop(prog->stack);
+    prog->stack.pop_back();
 
     return st.stmt - 1; /* offset the ++st */
 }
@@ -584,8 +572,8 @@ bool prog_exec(qc_program_t *prog, prog_section_function_t *func, size_t flags,
 
 cleanup:
     prog->xflags = oldxflags;
-    vec_free(prog->localstack);
-    vec_free(prog->stack);
+    prog->localstack.clear();
+    prog->stack.clear();
     if (prog->vmerror)
         return false;
     return true;
@@ -623,7 +611,7 @@ struct qcvm_parameter {
     const char *value;
 };
 
-static qcvm_parameter *main_params = nullptr;
+static std::vector<qcvm_parameter> main_params;
 
 #define CheckArgs(num) do {                                                    \
     if (prog->argc != (num)) {                                                 \
@@ -683,6 +671,19 @@ static int qc_stof(qc_program_t *prog) {
     return 0;
 }
 
+static int qc_stov(qc_program_t *prog) {
+    qcany_t *str;
+    qcany_t num;
+    CheckArgs(1);
+    str = GetArg(0);
+    (void)util_sscanf(prog_getstring(prog, str->string), " ' %f %f %f ' ",
+                      &num.vector[0],
+                      &num.vector[1],
+                      &num.vector[2]);
+    Return(num);
+    return 0;
+}
+
 static int qc_vtos(qc_program_t *prog) {
     char buffer[512];
     qcany_t *num;
@@ -848,7 +849,8 @@ static prog_builtin_t qc_builtins[] = {
     &qc_normalize,   /*   12  */
     &qc_sqrt,        /*   13  */
     &qc_floor,       /*   14  */
-    &qc_pow          /*   15  */
+    &qc_pow,         /*   15  */
+    &qc_stov         /*   16  */
 };
 
 static const char *arg0 = nullptr;
@@ -887,7 +889,7 @@ static void prog_main_setparams(qc_program_t *prog) {
     size_t i;
     qcany_t *arg;
 
-    for (i = 0; i < vec_size(main_params); ++i) {
+    for (i = 0; i < main_params.size(); ++i) {
         arg = GetGlobal(OFS_PARM0 + 3*i);
         arg->vector[0] = 0;
         arg->vector[1] = 0;
@@ -926,8 +928,8 @@ int main(int argc, char **argv) {
     bool        opts_info        = false;
     bool        noexec           = false;
     const char *progsfile        = nullptr;
-    const char **dis_list        = nullptr;
     int         opts_v           = 0;
+    std::vector<const char*> dis_list;
 
     arg0 = argv[0];
 
@@ -997,7 +999,7 @@ int main(int argc, char **argv) {
                 usage();
                 exit(EXIT_FAILURE);
             }
-            vec_push(dis_list, argv[1]);
+            dis_list.emplace_back(argv[1]);
             --argc;
             ++argv;
             noexec = true;
@@ -1042,7 +1044,7 @@ int main(int argc, char **argv) {
             }
             p.value = argv[1];
 
-            vec_push(main_params, p);
+            main_params.emplace_back(p);
             --argc;
             ++argv;
         }
@@ -1111,7 +1113,7 @@ int main(int argc, char **argv) {
         prog_delete(prog);
         return 0;
     }
-    for (i = 0; i < vec_size(dis_list); ++i) {
+    for (i = 0; i < dis_list.size(); ++i) {
         size_t k;
         printf("Looking for `%s`\n", dis_list[i]);
         for (k = 1; k < prog->functions.size(); ++k) {
@@ -1282,7 +1284,7 @@ while (prog->vmerror == 0) {
     switch (st->opcode)
     {
         default:
-            qcvmerror(prog, "Illegal instruction in %s\n", prog->filename);
+            qcvmerror(prog, "Illegal instruction in %s\n", prog->filename.c_str());
             goto cleanup;
 
         case INSTR_DONE:
@@ -1293,7 +1295,7 @@ while (prog->vmerror == 0) {
             GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
 
             st = &prog->code[0] + prog_leavefunction(prog);
-            if (!vec_size(prog->stack))
+            if (prog->stack.empty())
                 goto cleanup;
 
             break;
@@ -1402,12 +1404,12 @@ while (prog->vmerror == 0) {
         case INSTR_LOAD_ENT:
         case INSTR_LOAD_FNC:
             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
-                qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
+                qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename.c_str());
                 goto cleanup;
             }
             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
-                          prog->filename,
+                          prog->filename.c_str(),
                           OPB->_int);
                 goto cleanup;
             }
@@ -1416,13 +1418,13 @@ while (prog->vmerror == 0) {
             break;
         case INSTR_LOAD_V:
             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
-                qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
+                qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename.c_str());
                 goto cleanup;
             }
             if (OPB->_int < 0 || OPB->_int + 3 > (qcint_t)prog->entityfields)
             {
                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
-                          prog->filename,
+                          prog->filename.c_str(),
                           OPB->_int + 2);
                 goto cleanup;
             }
@@ -1435,19 +1437,19 @@ while (prog->vmerror == 0) {
 
         case INSTR_ADDRESS:
             if (OPA->edict < 0 || OPA->edict >= prog->entities) {
-                qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
+                qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename.c_str(), OPA->edict);
                 goto cleanup;
             }
             if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
             {
                 qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
-                          prog->filename,
+                          prog->filename.c_str(),
                           OPB->_int);
                 goto cleanup;
             }
 
             ed = prog_getedict(prog, OPA->edict);
-            OPC->_int = ((qcint_t*)ed) - prog->entitydata + OPB->_int;
+            OPC->_int = ((qcint_t*)ed) - prog->entitydata.data() + OPB->_int;
             break;
 
         case INSTR_STORE_F:
@@ -1468,29 +1470,29 @@ while (prog->vmerror == 0) {
         case INSTR_STOREP_ENT:
         case INSTR_STOREP_FLD:
         case INSTR_STOREP_FNC:
-            if (OPB->_int < 0 || OPB->_int >= (qcint_t)vec_size(prog->entitydata)) {
-                qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
+            if (OPB->_int < 0 || OPB->_int >= (qcint_t)prog->entitydata.size()) {
+                qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename.c_str(), OPB->_int);
                 goto cleanup;
             }
             if (OPB->_int < (qcint_t)prog->entityfields && !prog->allowworldwrites)
                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
-                          prog->filename,
+                          prog->filename.c_str(),
                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
                           OPB->_int);
-            ptr = (qcany_t*)(prog->entitydata + OPB->_int);
+            ptr = (qcany_t*)&prog->entitydata[OPB->_int];
             ptr->_int = OPA->_int;
             break;
         case INSTR_STOREP_V:
-            if (OPB->_int < 0 || OPB->_int + 2 >= (qcint_t)vec_size(prog->entitydata)) {
-                qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
+            if (OPB->_int < 0 || OPB->_int + 2 >= (qcint_t)prog->entitydata.size()) {
+                qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename.c_str(), OPB->_int);
                 goto cleanup;
             }
             if (OPB->_int < (qcint_t)prog->entityfields && !prog->allowworldwrites)
                 qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
-                          prog->filename,
+                          prog->filename.c_str(),
                           prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
                           OPB->_int);
-            ptr = (qcany_t*)(prog->entitydata + OPB->_int);
+            ptr = (qcany_t*)&prog->entitydata[OPB->_int];
             ptr->ivector[0] = OPA->ivector[0];
             ptr->ivector[1] = OPA->ivector[1];
             ptr->ivector[2] = OPA->ivector[2];
@@ -1521,7 +1523,7 @@ while (prog->vmerror == 0) {
             {
                 st += st->o2.s1 - 1;    /* offset the s++ */
                 if (++jumpcount >= maxjumps)
-                    qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
+                    qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
             }
             break;
         case INSTR_IFNOT:
@@ -1529,7 +1531,7 @@ while (prog->vmerror == 0) {
             {
                 st += st->o2.s1 - 1;    /* offset the s++ */
                 if (++jumpcount >= maxjumps)
-                    qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
+                    qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
             }
             break;
 
@@ -1544,11 +1546,11 @@ while (prog->vmerror == 0) {
         case INSTR_CALL8:
             prog->argc = st->opcode - INSTR_CALL0;
             if (!OPA->function)
-                qcvmerror(prog, "nullptr function in `%s`", prog->filename);
+                qcvmerror(prog, "nullptr function in `%s`", prog->filename.c_str());
 
             if(!OPA->function || OPA->function >= (qcint_t)prog->functions.size())
             {
-                qcvmerror(prog, "CALL outside the program in `%s`", prog->filename);
+                qcvmerror(prog, "CALL outside the program in `%s`", prog->filename.c_str());
                 goto cleanup;
             }
 
@@ -1565,7 +1567,7 @@ while (prog->vmerror == 0) {
                     prog->builtins[builtinnumber](prog);
                 else
                     qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
-                              builtinnumber, prog->filename);
+                              builtinnumber, prog->filename.c_str());
             }
             else
                 st = &prog->code[0] + prog_enterfunction(prog, newf) - 1; /* offset st++ */
@@ -1579,7 +1581,7 @@ while (prog->vmerror == 0) {
             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);
+                qcvmerror(prog, "`%s` tried to execute a STATE operation but misses its defs!", prog->filename.c_str());
                 goto cleanup;
             }
             ed = prog_getedict(prog, prog->globals[prog->cached_globals.self]);
@@ -1596,7 +1598,7 @@ while (prog->vmerror == 0) {
         case INSTR_GOTO:
             st += st->o1.s1 - 1;    /* offset the s++ */
             if (++jumpcount == 10000000)
-                qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
+                qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename.c_str(), jumpcount);
             break;
 
         case INSTR_AND:
index 7a0df0ed9af6d9b3e0950bd0ed6bb9200285ab58..7b13db60ed0c80f306d09e080f305363f7212d2e 100644 (file)
--- a/fold.cpp
+++ b/fold.cpp
@@ -1392,8 +1392,8 @@ ast_expression *fold::op(const oper_info *info, ast_expression **opexprs) {
         return nullptr;
 
     switch(info->operands) {
-        case 3: if(!c) return nullptr;
-        case 2: if(!b) return nullptr;
+        case 3: if(!c) return nullptr; [[fallthrough]];
+        case 2: if(!b) return nullptr; [[fallthrough]];
         case 1:
         if(!a) {
             compile_error(ctx(), "internal error: fold_op no operands to fold\n");
@@ -1560,6 +1560,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i
         case INSTR_DIV_F:
             if (swapped)
                 return nullptr;
+            [[fallthrough]];
         case INSTR_MUL_F:
             if (immvalue_float(load) == 1.0f) {
                 ++opts_optimizationcount[OPTIM_PEEPHOLE];
@@ -1572,6 +1573,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i
         case INSTR_SUB_F:
             if (swapped)
                 return nullptr;
+            [[fallthrough]];
         case INSTR_ADD_F:
             if (immvalue_float(load) == 0.0f) {
                 ++opts_optimizationcount[OPTIM_PEEPHOLE];
@@ -1591,6 +1593,7 @@ ast_expression *fold::superfluous(ast_expression *left, ast_expression *right, i
         case INSTR_SUB_V:
             if (swapped)
                 return nullptr;
+            [[fallthrough]];
         case INSTR_ADD_V:
             if (vec3_cmp(immvalue_vector(load), vec3_create(0, 0, 0))) {
                 ++opts_optimizationcount[OPTIM_PEEPHOLE];
index f1d5f36f3efc96ee3355a3fecc7d399976fcd14f..190978fe60045c4df46d0e20c94a9d870367881e 100644 (file)
--- a/ftepp.cpp
+++ b/ftepp.cpp
@@ -920,7 +920,7 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
     if ( vec_size(params) < vec_size(macro->params) ||
         (vec_size(params) > vec_size(macro->params) && !macro->variadic) )
     {
-        ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name,
+        ftepp_error(ftepp, "macro %s expects%s %u parameters, %u provided", macro->name,
                     (macro->variadic ? " at least" : ""),
                     (unsigned int)vec_size(macro->params),
                     (unsigned int)vec_size(params));
diff --git a/gmqcc.h b/gmqcc.h
index 4e3fb399eba42b7604ac7b76b413d6cd74ec8e92..10b74f5ab2741e92ff9421fc7a3d8bc2ac888244 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -660,7 +660,7 @@ enum {
 #define VMXF_TRACE   0x0001     /* trace: print statements before executing */
 #define VMXF_PROFILE 0x0002     /* profile: increment the profile counters */
 
-struct qc_program_t;
+typedef struct qc_program qc_program_t;
 typedef int (*prog_builtin_t)(qc_program_t *prog);
 
 struct qc_exec_stack_t {
@@ -669,18 +669,21 @@ struct qc_exec_stack_t {
     prog_section_function_t *function;
 };
 
-struct qc_program_t {
-    char *filename;
+struct qc_program {
+    qc_program() = delete;
+    qc_program(const char *name, uint16_t crc, size_t entfields);
+
+    std::string filename;
     std::vector<prog_section_statement_t> code;
     std::vector<prog_section_def_t> defs;
     std::vector<prog_section_def_t> fields;
     std::vector<prog_section_function_t> functions;
     std::vector<char> strings;
     std::vector<qcint_t> globals;
-    qcint_t *entitydata;
-    bool *entitypool;
+    std::vector<qcint_t> entitydata;
+    std::vector<bool> entitypool;
 
-    const char* *function_stack;
+    std::vector<const char*> function_stack;
 
     uint16_t crc16;
 
@@ -689,7 +692,7 @@ struct qc_program_t {
 
     qcint_t  vmerror;
 
-    size_t *profile;
+    std::vector<size_t> profile;
 
     prog_builtin_t *builtins;
     size_t          builtins_count;
@@ -699,8 +702,8 @@ struct qc_program_t {
     size_t entityfields;
     bool   allowworldwrites;
 
-    qcint_t         *localstack;
-    qc_exec_stack_t *stack;
+    std::vector<qcint_t> localstack;
+    std::vector<qc_exec_stack_t> stack;
     size_t statement;
 
     size_t xflags;
@@ -738,7 +741,6 @@ parser_t *parser_create(void);
 bool parser_compile_file(parser_t *parser, const char *);
 bool parser_compile_string(parser_t *parser, const char *, const char *, size_t);
 bool parser_finish(parser_t *parser, const char *);
-void parser_cleanup(parser_t *parser);
 
 /* ftepp.c */
 struct ftepp_t;
index 59a27f9a2091476bd7882b41e9bb0984774d7d4f..5c1ad3777a8c999ad7815492da0fec9f0ba3d927 100644 (file)
 
 [warnings]
     #Generate a warning about variables which are declared but never
-    #used.  This can be avoided by adding the ‘noref’ keyword in front
+    #used. This can be avoided by adding the ‘noref’ keyword in front
     #of the variable declaration. Additionally a complete section of
     #unreferenced variables can be opened using ‘#pragma noref 1’ and
     #closed via ‘#pragma noref 0’.
     UNUSED_VARIABLE = false
 
 
+    #Generate a warning about vector variables which are declared but
+    #components of it are never used.
+
+    UNUSED_COMPONENT = false
+
     #Generate a warning if it is possible that a variable can be used
     #without prior initialization. Note that this warning is not nec‐
     #essarily reliable if the initialization happens only under cer‐
diff --git a/ir.cpp b/ir.cpp
index 3a166c7af26bf848ec4475370f64052aa0d0846f..facbc33fccaeaaf9829a4fcf72fd18a2e409f6d7 100644 (file)
--- a/ir.cpp
+++ b/ir.cpp
@@ -235,15 +235,14 @@ static bool GMQCC_WARN vec_ir_value_find(std::vector<ir_value *> &vec, const ir_
     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;
 }
@@ -433,7 +432,7 @@ static bool ir_function_pass_peephole(ir_function *self)
 {
     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];
 
@@ -481,7 +480,7 @@ static bool ir_function_pass_peephole(ir_function *self)
                 ++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)
@@ -515,15 +514,15 @@ static bool ir_function_pass_peephole(ir_function *self)
                     (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];
@@ -548,14 +547,14 @@ static bool ir_function_pass_tailrecursion(ir_function *self)
         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
@@ -563,11 +562,11 @@ static bool ir_function_pass_tailrecursion(ir_function *self)
              * 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)
@@ -581,7 +580,7 @@ static bool ir_function_pass_tailrecursion(ir_function *self)
             {
                 ++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
@@ -602,7 +601,7 @@ static bool ir_function_pass_tailrecursion(ir_function *self)
             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 */
 
@@ -631,6 +630,36 @@ bool ir_function_finalize(ir_function *self)
     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());
@@ -713,19 +742,15 @@ ir_block::ir_block(ir_function* owner, const std::string& name)
 
 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;
 }
 
@@ -1236,7 +1261,7 @@ bool ir_block_create_store_op(ir_block *self, lex_ctx_t ctx, int op, ir_value *t
         delete in;
         return false;
     }
-    vec_push(self->m_instr, in);
+    self->m_instr.push_back(in);
     return true;
 }
 
@@ -1256,7 +1281,7 @@ bool ir_block_create_state_op(ir_block *self, lex_ctx_t ctx, ir_value *frame, ir
         delete in;
         return false;
     }
-    vec_push(self->m_instr, in);
+    self->m_instr.push_back(in);
     return true;
 }
 
@@ -1325,7 +1350,7 @@ bool ir_block_create_return(ir_block *self, lex_ctx_t ctx, ir_value *v)
         return false;
     }
 
-    vec_push(self->m_instr, in);
+    self->m_instr.push_back(in);
     return true;
 }
 
@@ -1349,12 +1374,12 @@ bool ir_block_create_if(ir_block *self, lex_ctx_t ctx, ir_value *v,
     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;
 }
 
@@ -1369,10 +1394,10 @@ bool ir_block_create_jump(ir_block *self, lex_ctx_t ctx, ir_block *to)
         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;
 }
 
@@ -1400,7 +1425,7 @@ ir_instr* ir_block_create_phi(ir_block *self, lex_ctx_t ctx, const char *label,
         delete in;
         return nullptr;
     }
-    vec_push(self->m_instr, in);
+    self->m_instr.push_back(in);
     return in;
 }
 
@@ -1452,7 +1477,7 @@ ir_instr* ir_block_create_call(ir_block *self, lex_ctx_t ctx, const char *label,
         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)) {
@@ -1619,7 +1644,7 @@ ir_value* ir_block_create_unary(ir_block *self, lex_ctx_t ctx,
         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;
@@ -1656,7 +1681,7 @@ static ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx_t ctx, co
         goto on_error;
     }
 
-    vec_push(self->m_instr, instr);
+    self->m_instr.push_back(instr);
 
     return out;
 on_error:
@@ -1730,13 +1755,13 @@ static bool ir_block_naive_phi(ir_block *self)
      * 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) {
@@ -1748,14 +1773,14 @@ static bool ir_block_naive_phi(ir_block *self)
                     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;
             }
         }
@@ -1777,12 +1802,9 @@ static bool ir_block_naive_phi(ir_block *self)
  */
 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;
 }
 
@@ -1815,10 +1837,10 @@ void ir_function_enumerate(ir_function *self)
  * 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)
@@ -1826,7 +1848,7 @@ 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)
@@ -1835,9 +1857,9 @@ static bool function_allocator_alloc(function_allocator *alloc, ir_value *var)
     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;
 
@@ -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;
@@ -1884,7 +1905,7 @@ static bool ir_function_allocator_assign(ir_function *self, function_allocator *
         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;
     }
@@ -1893,7 +1914,6 @@ static bool ir_function_allocator_assign(ir_function *self, function_allocator *
 
 bool ir_function_allocate_locals(ir_function *self)
 {
-    bool   retval = true;
     size_t pos;
     bool   opt_gt = OPTS_OPTIMIZATION(OPTIM_GLOBAL_TEMPS);
 
@@ -1902,15 +1922,6 @@ bool ir_function_allocate_locals(ir_function *self)
     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)
     {
@@ -1919,12 +1930,12 @@ bool ir_function_allocate_locals(ir_function *self)
             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)
     {
@@ -1932,7 +1943,7 @@ bool ir_function_allocate_locals(ir_function *self)
         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 */
@@ -1958,7 +1969,7 @@ bool ir_function_allocate_locals(ir_function *self)
                 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;
@@ -1992,33 +2003,33 @@ bool ir_function_allocate_locals(ir_function *self)
         }
 
         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 */
@@ -2036,24 +2047,7 @@ bool ir_function_allocate_locals(ir_function *self)
             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
@@ -2118,21 +2112,19 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
 {
     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];
@@ -2323,7 +2315,7 @@ static bool ir_block_life_propagate(ir_block *self, bool *changed)
 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");
 
@@ -2490,7 +2482,7 @@ static bool gen_blocks_recursive(code_t *code, ir_function *func, ir_block *bloc
 
     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];
 
@@ -3011,7 +3003,7 @@ bool ir_builder::generateGlobalFunction(ir_value *global)
     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;
 
@@ -3076,7 +3068,7 @@ static bool gen_function_extparam_copy(code_t *code, ir_function *self)
 {
     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;
 
@@ -3112,7 +3104,7 @@ static bool gen_function_varargs_copy(code_t *code, ir_function *self)
     ir_value   *ep;
     prog_section_statement_t stmt;
 
-    numparams = vec_size(self->m_params);
+    numparams = self->m_params.size();
     if (!numparams)
         return true;
 
@@ -3248,7 +3240,7 @@ bool ir_builder::generateGlobalFunctionCode(ir_value *global)
     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;
@@ -3256,7 +3248,7 @@ static void gen_vector_defs(code_t *code, prog_section_def_t def, const char *na
     if (!name || name[0] == '#' || OPTS_FLAG(SINGLE_VECTOR_DEFS))
         return;
 
-    def.type = TYPE_FLOAT;
+    def.type = type;
 
     len = strlen(name);
 
@@ -3356,9 +3348,9 @@ bool ir_builder::generateGlobal(ir_value *global, bool islocal)
             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;
         }
     }
@@ -3400,7 +3392,7 @@ bool ir_builder::generateGlobal(ir_value *global, bool islocal)
         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:
@@ -3466,7 +3458,7 @@ bool ir_builder::generateGlobal(ir_value *global, bool islocal)
         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;
     }
@@ -3569,7 +3561,7 @@ static bool ir_builder_gen_field(ir_builder *self, ir_value *field)
     }
 
     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());
     }
 
@@ -3953,14 +3945,13 @@ void ir_function_dump(ir_function *f, char *ind,
 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 --git a/ir.h b/ir.h
index 9fa8ab5ab62d2ee49323af529e7ed2a1e6508410..42f6ce578ea958bd5e4cf409da8561eef2fc91b5 100644 (file)
--- a/ir.h
+++ b/ir.h
@@ -27,8 +27,8 @@ enum {
     IR_FLAG_INCLUDE_DEF             = 1 << 3,
     IR_FLAG_ERASABLE                = 1 << 4,
     IR_FLAG_BLOCK_COVERAGE          = 1 << 5,
-
-    IR_FLAG_SPLIT_VECTOR            = 1 << 6,
+    IR_FLAG_NOREF                   = 1 << 6,
+    IR_FLAG_SPLIT_VECTOR            = 1 << 7,
 
     IR_FLAG_LAST,
     IR_FLAG_MASK_NO_OVERLAP      = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED),
@@ -148,9 +148,9 @@ struct ir_block {
     lex_ctx_t m_context;
     bool m_final = false; /* once a jump is added we're done */
 
-    ir_instr **m_instr = nullptr;
-    ir_block **m_entries = nullptr;
-    ir_block **m_exits = nullptr;
+    std::vector<ir_instr *> m_instr;
+    std::vector<ir_block *> m_entries;
+    std::vector<ir_block *> m_exits;
     std::vector<ir_value *> m_living;
 
     /* For the temp-allocation */
@@ -201,11 +201,11 @@ struct ir_function {
 
     ir_builder *m_owner;
 
-    std::string m_name;
-    qc_type     m_outtype;
-    int        *m_params  = nullptr;
-    ir_flag_t   m_flags   = 0;
-    int         m_builtin = 0;
+    std::string      m_name;
+    qc_type          m_outtype;
+    std::vector<int> m_params;
+    ir_flag_t        m_flags   = 0;
+    int              m_builtin = 0;
 
     std::vector<std::unique_ptr<ir_block>> m_blocks;
 
index 449067dec909a1225a1b8659b70f95d8fe78cea7..76c1d31a709394360dbc8fb7fdbf865aeb76a62a 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -3,6 +3,7 @@
 
 #include "gmqcc.h"
 #include "lexer.h"
+#include "parser.h"
 
 /* TODO: cleanup this whole file .. it's a fuckign mess */
 
@@ -736,7 +737,7 @@ cleanup:
     vec_free(ppems);
 
     if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
-        if(parser) parser_cleanup(parser);
+        delete parser;
 
     /* free allocated option strings */
     for (itr = 0; itr < OPTION_COUNT; itr++)
index b2517a05aa3f0f06fe6cd06d8f35a139f61ece9a..c35fd73e2fb23a7752b091993dc021dfce174043 100644 (file)
--- a/opts.cpp
+++ b/opts.cpp
@@ -149,9 +149,9 @@ void opts_set(uint32_t *flags, size_t idx, bool on) {
     LONGBIT_SET(lb, idx);
 
     if (on)
-        flags[lb.idx] |= (1<<(lb.bit));
+        flags[lb.idx] |= (1u<<(lb.bit));
     else
-        flags[lb.idx] &= ~(1<<(lb.bit));
+        flags[lb.idx] &= ~(1u<<(lb.bit));
 }
 
 void opts_setoptimlevel(unsigned int level) {
index f5ce0af60c58c0fb00a6866935ebabe3a482b0a5..2dc4fb77cd9d8a38d3fa15422ce74b38bde3e7e2 100644 (file)
--- a/opts.def
+++ b/opts.def
@@ -44,6 +44,7 @@
     GMQCC_DEFINE_FLAG(UNINITIALIZED_GLOBAL)
     GMQCC_DEFINE_FLAG(DEBUG)
     GMQCC_DEFINE_FLAG(UNUSED_VARIABLE)
+    GMQCC_DEFINE_FLAG(UNUSED_COMPONENT)
     GMQCC_DEFINE_FLAG(USED_UNINITIALIZED)
     GMQCC_DEFINE_FLAG(UNKNOWN_CONTROL_SEQUENCE)
     GMQCC_DEFINE_FLAG(EXTENSIONS)
index 7bb2a10f8f2401f783feebed27da3fb12fac980f..ddde65469ec77e7cb596e58451dc88096c5ad8f1 100644 (file)
@@ -139,7 +139,7 @@ static ast_expression* parser_find_local(parser_t *parser, const char *name, siz
     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 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t
     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,6 +293,23 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     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)) {
@@ -728,6 +745,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         case opid2('|','|'):
             generated_op += 1; /* INSTR_OR */
+            [[fallthrough]];
         case opid2('&','&'):
             generated_op += INSTR_AND;
             if (!(out = parser->m_fold.op(op, exprs))) {
@@ -857,10 +875,13 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         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)) {
@@ -896,14 +917,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         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));
@@ -921,15 +935,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             }
             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));
@@ -1024,10 +1030,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 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,
@@ -1059,10 +1062,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 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,
@@ -1103,10 +1103,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 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),
@@ -1128,10 +1125,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               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
@@ -1263,14 +1257,14 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
      * 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;
         }
 
@@ -1280,7 +1274,6 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
          */
         sy->out[fid] = syexp(foldval->m_context, foldval);
         sy->out.erase(sy->out.end() - paramcount, sy->out.end());
-        vec_free(exprs);
 
         return true;
     }
@@ -1668,13 +1661,16 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
         }
         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));
@@ -1993,11 +1989,11 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool
 
 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)
@@ -2005,48 +2001,37 @@ 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);
@@ -2634,6 +2619,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
             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)) {
@@ -3120,6 +3106,52 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
                 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");
@@ -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;
 
@@ -3625,7 +3657,7 @@ static bool parse_enum(parser_t *parser)
                 break;
             }
             parseerror(parser, "expected identifier or `}`");
-            goto onerror;
+            return false;
         }
 
         old = parser_find_field(parser, parser_tokval(parser));
@@ -3634,11 +3666,11 @@ static bool parse_enum(parser_t *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;
 
@@ -3648,7 +3680,7 @@ static bool parse_enum(parser_t *parser)
 
         if (!parser_next(parser)) {
             parseerror(parser, "expected `=`, `}` or comma after identifier");
-            goto onerror;
+            return false;
         }
 
         if (parser->tok == ',')
@@ -3657,12 +3689,12 @@ static bool parse_enum(parser_t *parser)
             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! */
@@ -3670,7 +3702,7 @@ static bool parse_enum(parser_t *parser)
         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;
 
@@ -3678,38 +3710,33 @@ static bool parse_enum(parser_t *parser)
             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)
@@ -4128,7 +4155,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     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;
     }
@@ -4947,15 +4974,15 @@ static bool parse_typedef(parser_t *parser)
         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");
@@ -5187,12 +5214,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             /* 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;
             }
@@ -5322,7 +5349,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         }
         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);
@@ -5352,9 +5379,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->m_uses++;
+            var->m_flags |= AST_FLAG_NOREF;
 
         /* Part 2:
          * Create the global/local, and deal with vector types.
@@ -5450,7 +5476,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     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);
@@ -5480,8 +5506,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     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]);
                         }
                     }
@@ -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
@@ -5755,7 +5781,10 @@ skipvar:
                         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;
@@ -5977,21 +6006,72 @@ static void generate_checksum(parser_t *parser, ir_builder *ir)
     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;
@@ -6000,43 +6080,10 @@ parser_t *parser_create()
     }
     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;
 }
 
@@ -6092,54 +6139,42 @@ bool parser_compile_string(parser_t *parser, const char *name, const char *str,
     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) {
@@ -6227,7 +6262,7 @@ bool parser_finish(parser_t *parser, const char *output)
         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);
         }
@@ -6323,13 +6358,17 @@ bool parser_finish(parser_t *parser, const char *output)
             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))
@@ -6340,6 +6379,9 @@ bool parser_finish(parser_t *parser, const char *output)
             delete ir;
             return false;
         }
+
+        // ir->generate can generate compiler warnings
+        fnCheckWErrors();
     }
     delete ir;
     return retval;
index a09238df68d768dbd1d0f2fbee2bc5165c0d5686..e5361a8aae020ba1c8ac8d2e2d74bcc78a55ecc9 100644 (file)
--- a/parser.h
+++ b/parser.h
@@ -2,7 +2,7 @@
 #define GMQCC_PARSER_HDR
 #include "gmqcc.h"
 #include "lexer.h"
-//#include "ast.h"
+#include "ast.h"
 
 #include "intrin.h"
 #include "fold.h"
@@ -12,7 +12,10 @@ struct parser_t;
 #define parser_ctx(p) ((p)->lex->tok.ctx)
 
 struct parser_t {
-    parser_t() { }
+    parser_t();
+    ~parser_t();
+
+    void remove_ast();
 
     lex_file *lex;
     int tok;
@@ -45,17 +48,17 @@ struct parser_t {
     std::vector<const char *> continues;
 
     /* A list of hashtables for each scope */
-    ht *variables;
+    std::vector<ht> variables;
     ht htfields;
     ht htglobals;
-    ht *typedefs;
+    std::vector<ht> typedefs;
 
     /* not to be used directly, we use the hash table */
-    ast_expression **_locals;
-    size_t *_blocklocals;
-    ast_value **_typedefs;
-    size_t *_blocktypedefs;
-    lex_ctx_t *_block_ctx;
+    std::vector<ast_expression*> _locals;
+    std::vector<size_t> _blocklocals;
+    std::vector<std::unique_ptr<ast_value>> _typedefs;
+    std::vector<size_t> _blocktypedefs;
+    std::vector<lex_ctx_t> _block_ctx;
 
     /* we store the '=' operator info */
     const oper_info *assign_op;
index f6ea408ad9ed7f26ff6b812f07a7de719dd82095..cbe8c98300ec04f2d0cb5a9df039e01c0f507a43 100644 (file)
@@ -18,3 +18,4 @@ vector (vector vec)               normalize = #12;
 float  (float val)                sqrt      = #13;
 float  (float val)                floor     = #14;
 float  (float val1, float val2)   pow       = #15;
+vector (string str)               stov      = #16;
index 1199b9b53f9db1d7c9c67bee046bb43c31935f84..04e0b8dda1863512bf300d188a67e06cdb153235 100644 (file)
@@ -1,6 +1,6 @@
 I: fieldfuncs.qc
 D: test fields with functions
-T: -compile
+T: -execute
 C: -std=fte
 M: 42
 M: 42
diff --git a/tests/memberbinop.qc b/tests/memberbinop.qc
new file mode 100644 (file)
index 0000000..55f5aea
--- /dev/null
@@ -0,0 +1,15 @@
+.vector k;
+
+void t(entity e)
+{
+    e.k = '0 0 0';
+    e.k.x += 2;
+    e.k.y += 4;
+    e.k.z += 6;
+}
+
+void main() {
+    entity e = spawn();
+    t(e);
+    print(vtos(e.k));
+}
diff --git a/tests/memberbinop.tmpl b/tests/memberbinop.tmpl
new file mode 100644 (file)
index 0000000..21abd20
--- /dev/null
@@ -0,0 +1,6 @@
+I: memberbinop.qc
+D: test member bin ops
+T: -execute
+C: -std=gmqcc
+E: $null
+M: '2 4 6'
diff --git a/tests/vecfields.qc b/tests/vecfields.qc
new file mode 100644 (file)
index 0000000..6cc053a
--- /dev/null
@@ -0,0 +1,13 @@
+.vector v1;
+
+float set(entity e, float v) {
+       e.v1.y = v;
+       return e.v1.y;
+}
+
+void main() {
+       entity e = spawn();
+       e.v1 = '1 2 3';
+       print(ftos(set(e, 42)), " => ");
+       print(vtos(e.v1), "\n");
+}
diff --git a/tests/vecfields.tmpl b/tests/vecfields.tmpl
new file mode 100644 (file)
index 0000000..d54ba70
--- /dev/null
@@ -0,0 +1,5 @@
+I: vecfields.qc
+D: vector field member accesses
+T: -execute
+C: -std=gmqcc -fftepp
+M: 42 => '1 42 3'
diff --git a/tests/vecmath.qc b/tests/vecmath.qc
new file mode 100644 (file)
index 0000000..5a656bf
--- /dev/null
@@ -0,0 +1,5 @@
+void main(vector vin) {
+       stov("'15 43 0'"); // set OFS_RETURN
+       vector v2 = -vin;
+       print(vtos(v2), "\n");
+}
diff --git a/tests/vecmath.tmpl b/tests/vecmath.tmpl
new file mode 100644 (file)
index 0000000..8bcea3a
--- /dev/null
@@ -0,0 +1,6 @@
+I: vecmath.qc
+D: previously problematic vector math
+T: -execute
+C: -std=gmqcc -fftepp
+E: -vector '5 5 5'
+M: '-5 -5 -5'