]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
-fbail-on-werror option
[xonotic/gmqcc.git] / parser.c
index 8f3638d6ea5d35920e494cc48dc5c34fab762f8c..422930d0d8d1ca7612caee58af4e41ac92f2665a 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -76,8 +76,6 @@ typedef struct {
     size_t          *_blocktypedefs;
     lex_ctx         *_block_ctx;
 
-    size_t errors;
-
     /* we store the '=' operator info */
     const oper_info *assign_op;
 
@@ -103,7 +101,7 @@ static void parser_enterblock(parser_t *parser);
 static bool parser_leaveblock(parser_t *parser);
 static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
 static bool parse_typedef(parser_t *parser);
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn);
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static);
 static ast_block* parse_block(parser_t *parser);
 static bool parse_block_into(parser_t *parser, ast_block *block);
 static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
@@ -113,34 +111,31 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
-  va_list ap;
-
-  parser->errors++;
-
-  va_start(ap, fmt);
-    con_vprintmsg(LVL_ERROR, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "parse error", fmt, ap);
-  va_end(ap);
+    va_list ap;
+    va_start(ap, fmt);
+    vcompile_error(parser->lex->tok.ctx, fmt, ap);
+    va_end(ap);
 }
 
 /* returns true if it counts as an error */
 static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...)
 {
     bool    r;
-  va_list ap;
-  va_start(ap, fmt);
-  r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap);
-  va_end(ap);
-  return r;
+    va_list ap;
+    va_start(ap, fmt);
+    r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap);
+    va_end(ap);
+    return r;
 }
 
 static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
 {
     bool    r;
-  va_list ap;
-  va_start(ap, fmt);
-  r = vcompile_warning(ctx, warntype, fmt, ap);
-  va_end(ap);
-  return r;
+    va_list ap;
+    va_start(ap, fmt);
+    r = vcompile_warning(ctx, warntype, fmt, ap);
+    va_end(ap);
+    return r;
 }
 
 /**********************************************************************
@@ -992,11 +987,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         field->expression.next->expression.vtype == TYPE_FUNCTION &&
                         exprs[1]->expression.vtype == TYPE_FUNCTION)
                     {
-                        if (parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES,
-                                         "invalid types in assignment: cannot assign %s to %s", ty2, ty1))
-                        {
-                            parser->errors++;
-                        }
+                        (void)!parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES,
+                                            "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                     }
                     else
                         parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
@@ -1026,11 +1018,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         exprs[0]->expression.vtype == TYPE_FUNCTION &&
                         exprs[1]->expression.vtype == TYPE_FUNCTION)
                     {
-                        if (parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES,
-                                         "invalid types in assignment: cannot assign %s to %s", ty2, ty1))
-                        {
-                            parser->errors++;
-                        }
+                        (void)!parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES,
+                                            "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                     }
                     else
                         parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
@@ -1306,8 +1295,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
             params->exprs = NULL;
             ast_delete(params);
         }
-        if (!ast_call_check_types(call))
-            parser->errors++;
+        (void)!ast_call_check_types(call);
     } else {
         parseerror(parser, "invalid function call");
         return false;
@@ -1873,10 +1861,8 @@ static bool parser_leaveblock(parser_t *parser)
         ast_value      *v = (ast_value*)e;
         vec_pop(parser->_locals);
         if (ast_istype(e, ast_value) && !v->uses) {
-            if (compile_warning(ast_ctx(v), WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->name)) {
-                parser->errors++;
+            if (compile_warning(ast_ctx(v), WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->name))
                 rv = false;
-            }
         }
     }
 
@@ -2199,7 +2185,7 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
                              "current standard does not allow variable declarations in for-loop initializers"))
                 goto onerr;
         }
-        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false))
+        if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, false))
             goto onerr;
     }
     else if (parser->tok != ';')
@@ -2344,13 +2330,14 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
 /* returns true when it was a variable qualifier, false otherwise!
  * on error, cvq is set to CV_WRONG
  */
-static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn)
+static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn, bool *is_static)
 {
-    bool had_const = false;
-    bool had_var   = false;
-    bool had_noref = false;
+    bool had_const    = false;
+    bool had_var      = false;
+    bool had_noref    = false;
     bool had_noreturn = false;
-    bool had_attrib = false;
+    bool had_attrib   = false;
+    bool had_static   = false;
 
     *cvq = CV_NONE;
     for (;;) {
@@ -2391,6 +2378,8 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
                 }
             }
         }
+        else if (!strcmp(parser_tokval(parser), "static"))
+            had_static = true;
         else if (!strcmp(parser_tokval(parser), "const"))
             had_const = true;
         else if (!strcmp(parser_tokval(parser), "var"))
@@ -2399,7 +2388,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
             had_var = true;
         else if (!strcmp(parser_tokval(parser), "noref"))
             had_noref = true;
-        else if (!had_const && !had_var && !had_noref && !had_noreturn && !had_attrib) {
+        else if (!had_const && !had_var && !had_noref && !had_noreturn && !had_attrib && !had_static) {
             return false;
         }
         else
@@ -2413,8 +2402,9 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
         *cvq = CV_VAR;
     else
         *cvq = CV_NONE;
-    *noref    = had_noref;
-    *noreturn = had_noreturn;
+    *noref     = had_noref;
+    *noreturn  = had_noreturn;
+    *is_static = had_static;
     return true;
 onerr:
     parseerror(parser, "parse error after variable qualifier");
@@ -2431,7 +2421,7 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
     ast_switch_case swcase;
 
     int  cvq;
-    bool noref, noreturn;
+    bool noref, noreturn, is_static;
 
     lex_ctx ctx = parser_ctx(parser);
 
@@ -2483,19 +2473,19 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
         if (parser->tok == TOKEN_IDENT)
             typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
         if (typevar || parser->tok == TOKEN_TYPENAME) {
-            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false)) {
+            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false)) {
                 ast_delete(switchnode);
                 return false;
             }
             continue;
         }
-        if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn))
+        if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn, &is_static))
         {
             if (cvq == CV_WRONG) {
                 ast_delete(switchnode);
                 return false;
             }
-            if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn)) {
+            if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn, is_static)) {
                 ast_delete(switchnode);
                 return false;
             }
@@ -2508,11 +2498,6 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
     while (parser->tok != '}') {
         ast_block *caseblock;
 
-        if (parser->tok != TOKEN_KEYWORD) {
-            ast_delete(switchnode);
-            parseerror(parser, "expected 'case' or 'default'");
-            return false;
-        }
         if (!strcmp(parser_tokval(parser), "case")) {
             if (!parser_next(parser)) {
                 ast_delete(switchnode);
@@ -2541,6 +2526,11 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
                 return false;
             }
         }
+        else {
+            ast_delete(switchnode);
+            parseerror(parser, "expected 'case' or 'default'");
+            return false;
+        }
 
         /* Now the colon and body */
         if (parser->tok != ':') {
@@ -2710,7 +2700,7 @@ static bool parse_pragma(parser_t *parser)
 
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
 {
-    bool       noref, noreturn;
+    bool       noref, noreturn, is_static;
     int        cvq = CV_NONE;
     ast_value *typevar = NULL;
 
@@ -2730,15 +2720,15 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
                 return false;
         }
-        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false))
+        if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false))
             return false;
         return true;
     }
-    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn))
+    else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn, &is_static))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, block, true, cvq, NULL, noref, noreturn);
+        return parse_variable(parser, block, true, cvq, NULL, noref, noreturn, is_static);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -3994,7 +3984,7 @@ static bool parse_typedef(parser_t *parser)
     return true;
 }
 
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn)
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static)
 {
     ast_value *var;
     ast_value *proto;
@@ -4011,6 +4001,9 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
     ast_member *me[3];
 
+    if (!localblock && is_static)
+        parseerror(parser, "`static` qualifier is not supported in global scope");
+
     /* get the first complete variable */
     var = parse_typename(parser, &basetype, cached_typedef);
     if (!var) {
@@ -4257,16 +4250,55 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     }
                 }
             } else {
-                vec_push(localblock->locals, var);
-                parser_addlocal(parser, var->name, (ast_expression*)var);
-                if (isvector) {
-                    for (i = 0; i < 3; ++i) {
-                        parser_addlocal(parser, me[i]->name, (ast_expression*)me[i]);
-                        ast_block_collect(localblock, (ast_expression*)me[i]);
+                if (is_static) {
+                    /* a static adds itself to be generated like any other global
+                     * but is added to the local namespace instead
+                     */
+                    char   *defname = NULL;
+                    size_t  prefix_len, ln;
+
+                    ln = strlen(parser->function->name);
+                    vec_append(defname, ln, parser->function->name);
+
+                    vec_append(defname, 2, "::");
+                    /* remember the length up to here */
+                    prefix_len = vec_size(defname);
+
+                    /* Add it to the local scope */
+                    util_htset(vec_last(parser->variables), var->name, (void*)var);
+                    /* now rename the global */
+                    ln = strlen(var->name);
+                    vec_append(defname, ln, var->name);
+                    ast_value_set_name(var, defname);
+
+                    /* push it to the to-be-generated globals */
+                    vec_push(parser->globals, (ast_expression*)var);
+
+                    /* same game for the vector members */
+                    if (isvector) {
+                        for (i = 0; i < 3; ++i) {
+                            util_htset(vec_last(parser->variables), me[i]->name, (void*)(me[i]));
+
+                            vec_shrinkto(defname, prefix_len);
+                            ln = strlen(me[i]->name);
+                            vec_append(defname, ln, me[i]->name);
+                            ast_member_set_name(me[i], defname);
+
+                            vec_push(parser->globals, (ast_expression*)me[i]);
+                        }
+                    }
+                    vec_free(defname);
+                } else {
+                    vec_push(localblock->locals, var);
+                    parser_addlocal(parser, var->name, (ast_expression*)var);
+                    if (isvector) {
+                        for (i = 0; i < 3; ++i) {
+                            parser_addlocal(parser, me[i]->name, (ast_expression*)me[i]);
+                            ast_block_collect(localblock, (ast_expression*)me[i]);
+                        }
                     }
                 }
             }
-
         }
         me[0] = me[1] = me[2] = NULL;
         cleanvar = false;
@@ -4527,23 +4559,24 @@ cleanup:
 
 static bool parser_global_statement(parser_t *parser)
 {
-    int        cvq      = CV_WRONG;
-    bool       noref    = false;
-    bool       noreturn = false;
-    ast_value *istype   = NULL;
+    int        cvq       = CV_WRONG;
+    bool       noref     = false;
+    bool       noreturn  = false;
+    bool       is_static = false;
+    ast_value *istype    = NULL;
 
     if (parser->tok == TOKEN_IDENT)
         istype = parser_find_typedef(parser, parser_tokval(parser), 0);
 
     if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
-        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false);
+        return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, false);
     }
-    else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn))
+    else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn, &is_static))
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn);
+        return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn, is_static);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -4600,56 +4633,56 @@ static void generate_checksum(parser_t *parser)
     size_t     i;
     ast_value *value;
 
-  crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{");
-  crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n");
-  /*
-  progdefs_crc_file("\tint\tpad;\n");
-  progdefs_crc_file("\tint\tofs_return[3];\n");
-  progdefs_crc_file("\tint\tofs_parm0[3];\n");
-  progdefs_crc_file("\tint\tofs_parm1[3];\n");
-  progdefs_crc_file("\tint\tofs_parm2[3];\n");
-  progdefs_crc_file("\tint\tofs_parm3[3];\n");
-  progdefs_crc_file("\tint\tofs_parm4[3];\n");
-  progdefs_crc_file("\tint\tofs_parm5[3];\n");
-  progdefs_crc_file("\tint\tofs_parm6[3];\n");
-  progdefs_crc_file("\tint\tofs_parm7[3];\n");
-  */
-  for (i = 0; i < parser->crc_globals; ++i) {
-      if (!ast_istype(parser->globals[i], ast_value))
-          continue;
-      value = (ast_value*)(parser->globals[i]);
-      switch (value->expression.vtype) {
-          case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
-          case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
-          case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
-          case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
-          default:
-              crc = progdefs_crc_both(crc, "\tint\t");
-              break;
-      }
-      crc = progdefs_crc_both(crc, value->name);
-      crc = progdefs_crc_both(crc, ";\n");
-  }
-  crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n");
-  for (i = 0; i < parser->crc_fields; ++i) {
-      if (!ast_istype(parser->fields[i], ast_value))
-          continue;
-      value = (ast_value*)(parser->fields[i]);
-      switch (value->expression.next->expression.vtype) {
-          case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
-          case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
-          case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
-          case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
-          default:
-              crc = progdefs_crc_both(crc, "\tint\t");
-              break;
-      }
-      crc = progdefs_crc_both(crc, value->name);
-      crc = progdefs_crc_both(crc, ";\n");
-  }
-  crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
-
-  code_crc = crc;
+    crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{");
+    crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n");
+    /*
+    progdefs_crc_file("\tint\tpad;\n");
+    progdefs_crc_file("\tint\tofs_return[3];\n");
+    progdefs_crc_file("\tint\tofs_parm0[3];\n");
+    progdefs_crc_file("\tint\tofs_parm1[3];\n");
+    progdefs_crc_file("\tint\tofs_parm2[3];\n");
+    progdefs_crc_file("\tint\tofs_parm3[3];\n");
+    progdefs_crc_file("\tint\tofs_parm4[3];\n");
+    progdefs_crc_file("\tint\tofs_parm5[3];\n");
+    progdefs_crc_file("\tint\tofs_parm6[3];\n");
+    progdefs_crc_file("\tint\tofs_parm7[3];\n");
+    */
+    for (i = 0; i < parser->crc_globals; ++i) {
+        if (!ast_istype(parser->globals[i], ast_value))
+            continue;
+        value = (ast_value*)(parser->globals[i]);
+        switch (value->expression.vtype) {
+            case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
+            case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
+            case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
+            case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
+            default:
+                crc = progdefs_crc_both(crc, "\tint\t");
+                break;
+        }
+        crc = progdefs_crc_both(crc, value->name);
+        crc = progdefs_crc_both(crc, ";\n");
+    }
+    crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n");
+    for (i = 0; i < parser->crc_fields; ++i) {
+        if (!ast_istype(parser->fields[i], ast_value))
+            continue;
+        value = (ast_value*)(parser->fields[i]);
+        switch (value->expression.next->expression.vtype) {
+            case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
+            case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
+            case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
+            case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
+            default:
+                crc = progdefs_crc_both(crc, "\tint\t");
+                break;
+        }
+        crc = progdefs_crc_both(crc, value->name);
+        crc = progdefs_crc_both(crc, ";\n");
+    }
+    crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
+
+    code_crc = crc;
 }
 
 static parser_t *parser;
@@ -4695,7 +4728,7 @@ bool parser_compile()
             if (!parser_global_statement(parser)) {
                 if (parser->tok == TOKEN_EOF)
                     parseerror(parser, "unexpected eof");
-                else if (!parser->errors)
+                else if (compile_errors)
                     parseerror(parser, "there have been errors, bailing out");
                 lex_close(parser->lex);
                 parser->lex = NULL;
@@ -4712,7 +4745,7 @@ bool parser_compile()
     lex_close(parser->lex);
     parser->lex = NULL;
 
-    return !parser->errors;
+    return !compile_errors;
 }
 
 bool parser_compile_file(const char *filename)
@@ -4797,133 +4830,138 @@ bool parser_finish(const char *output)
     ir_builder *ir;
     bool retval = true;
 
-    if (!parser->errors)
-    {
-        ir = ir_builder_new("gmqcc_out");
-        if (!ir) {
-            con_out("failed to allocate builder\n");
+    if (compile_errors) {
+        con_out("*** there were compile errors\n");
+        return false;
+    }
+
+    ir = ir_builder_new("gmqcc_out");
+    if (!ir) {
+        con_out("failed to allocate builder\n");
+        return false;
+    }
+
+    for (i = 0; i < vec_size(parser->fields); ++i) {
+        ast_value *field;
+        bool hasvalue;
+        if (!ast_istype(parser->fields[i], ast_value))
+            continue;
+        field = (ast_value*)parser->fields[i];
+        hasvalue = field->hasvalue;
+        field->hasvalue = false;
+        if (!ast_global_codegen((ast_value*)field, ir, true)) {
+            con_out("failed to generate field %s\n", field->name);
+            ir_builder_delete(ir);
             return false;
         }
-
-        for (i = 0; i < vec_size(parser->fields); ++i) {
-            ast_value *field;
-            bool hasvalue;
-            if (!ast_istype(parser->fields[i], ast_value))
-                continue;
-            field = (ast_value*)parser->fields[i];
-            hasvalue = field->hasvalue;
-            field->hasvalue = false;
-            if (!ast_global_codegen((ast_value*)field, ir, true)) {
-                con_out("failed to generate field %s\n", field->name);
-                ir_builder_delete(ir);
-                return false;
-            }
-            if (hasvalue) {
-                ir_value *ifld;
-                ast_expression *subtype;
-                field->hasvalue = true;
-                subtype = field->expression.next;
-                ifld = ir_builder_create_field(ir, field->name, subtype->expression.vtype);
-                if (subtype->expression.vtype == TYPE_FIELD)
-                    ifld->fieldtype = subtype->expression.next->expression.vtype;
-                else if (subtype->expression.vtype == TYPE_FUNCTION)
-                    ifld->outtype = subtype->expression.next->expression.vtype;
-                (void)!ir_value_set_field(field->ir_v, ifld);
-            }
-        }
-        for (i = 0; i < vec_size(parser->globals); ++i) {
-            ast_value *asvalue;
-            if (!ast_istype(parser->globals[i], ast_value))
-                continue;
-            asvalue = (ast_value*)(parser->globals[i]);
-            if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) {
-                retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
-                                               "unused global: `%s`", asvalue->name);
-            }
-            if (!ast_global_codegen(asvalue, ir, false)) {
-                con_out("failed to generate global %s\n", asvalue->name);
-                ir_builder_delete(ir);
-                return false;
-            }
+        if (hasvalue) {
+            ir_value *ifld;
+            ast_expression *subtype;
+            field->hasvalue = true;
+            subtype = field->expression.next;
+            ifld = ir_builder_create_field(ir, field->name, subtype->expression.vtype);
+            if (subtype->expression.vtype == TYPE_FIELD)
+                ifld->fieldtype = subtype->expression.next->expression.vtype;
+            else if (subtype->expression.vtype == TYPE_FUNCTION)
+                ifld->outtype = subtype->expression.next->expression.vtype;
+            (void)!ir_value_set_field(field->ir_v, ifld);
         }
-        for (i = 0; i < vec_size(parser->imm_float); ++i) {
-            if (!ast_global_codegen(parser->imm_float[i], ir, false)) {
-                con_out("failed to generate global %s\n", parser->imm_float[i]->name);
-                ir_builder_delete(ir);
-                return false;
-            }
+    }
+    for (i = 0; i < vec_size(parser->globals); ++i) {
+        ast_value *asvalue;
+        if (!ast_istype(parser->globals[i], ast_value))
+            continue;
+        asvalue = (ast_value*)(parser->globals[i]);
+        if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) {
+            retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
+                                           "unused global: `%s`", asvalue->name);
+        }
+        if (!ast_global_codegen(asvalue, ir, false)) {
+            con_out("failed to generate global %s\n", asvalue->name);
+            ir_builder_delete(ir);
+            return false;
         }
-        for (i = 0; i < vec_size(parser->imm_string); ++i) {
-            if (!ast_global_codegen(parser->imm_string[i], ir, false)) {
-                con_out("failed to generate global %s\n", parser->imm_string[i]->name);
-                ir_builder_delete(ir);
-                return false;
-            }
+    }
+    for (i = 0; i < vec_size(parser->imm_float); ++i) {
+        if (!ast_global_codegen(parser->imm_float[i], ir, false)) {
+            con_out("failed to generate global %s\n", parser->imm_float[i]->name);
+            ir_builder_delete(ir);
+            return false;
         }
-        for (i = 0; i < vec_size(parser->imm_vector); ++i) {
-            if (!ast_global_codegen(parser->imm_vector[i], ir, false)) {
-                con_out("failed to generate global %s\n", parser->imm_vector[i]->name);
-                ir_builder_delete(ir);
-                return false;
-            }
+    }
+    for (i = 0; i < vec_size(parser->imm_string); ++i) {
+        if (!ast_global_codegen(parser->imm_string[i], ir, false)) {
+            con_out("failed to generate global %s\n", parser->imm_string[i]->name);
+            ir_builder_delete(ir);
+            return false;
         }
-        for (i = 0; i < vec_size(parser->globals); ++i) {
-            ast_value *asvalue;
-            if (!ast_istype(parser->globals[i], ast_value))
-                continue;
-            asvalue = (ast_value*)(parser->globals[i]);
-            if (!ast_generate_accessors(asvalue, ir)) {
-                ir_builder_delete(ir);
-                return false;
-            }
+    }
+    for (i = 0; i < vec_size(parser->imm_vector); ++i) {
+        if (!ast_global_codegen(parser->imm_vector[i], ir, false)) {
+            con_out("failed to generate global %s\n", parser->imm_vector[i]->name);
+            ir_builder_delete(ir);
+            return false;
+        }
+    }
+    for (i = 0; i < vec_size(parser->globals); ++i) {
+        ast_value *asvalue;
+        if (!ast_istype(parser->globals[i], ast_value))
+            continue;
+        asvalue = (ast_value*)(parser->globals[i]);
+        if (!ast_generate_accessors(asvalue, ir)) {
+            ir_builder_delete(ir);
+            return false;
         }
-        for (i = 0; i < vec_size(parser->fields); ++i) {
-            ast_value *asvalue;
-            asvalue = (ast_value*)(parser->fields[i]->expression.next);
+    }
+    for (i = 0; i < vec_size(parser->fields); ++i) {
+        ast_value *asvalue;
+        asvalue = (ast_value*)(parser->fields[i]->expression.next);
 
-            if (!ast_istype((ast_expression*)asvalue, ast_value))
-                continue;
-            if (asvalue->expression.vtype != TYPE_ARRAY)
-                continue;
-            if (!ast_generate_accessors(asvalue, ir)) {
-                ir_builder_delete(ir);
-                return false;
-            }
+        if (!ast_istype((ast_expression*)asvalue, ast_value))
+            continue;
+        if (asvalue->expression.vtype != TYPE_ARRAY)
+            continue;
+        if (!ast_generate_accessors(asvalue, ir)) {
+            ir_builder_delete(ir);
+            return false;
         }
-        for (i = 0; i < vec_size(parser->functions); ++i) {
-            if (!ast_function_codegen(parser->functions[i], ir)) {
-                con_out("failed to generate function %s\n", parser->functions[i]->name);
-                ir_builder_delete(ir);
-                return false;
-            }
+    }
+    for (i = 0; i < vec_size(parser->functions); ++i) {
+        if (!ast_function_codegen(parser->functions[i], ir)) {
+            con_out("failed to generate function %s\n", parser->functions[i]->name);
+            ir_builder_delete(ir);
+            return false;
         }
-        if (opts.dump)
-            ir_builder_dump(ir, con_out);
-        for (i = 0; i < vec_size(parser->functions); ++i) {
-            if (!ir_function_finalize(parser->functions[i]->ir_func)) {
-                con_out("failed to finalize function %s\n", parser->functions[i]->name);
-                ir_builder_delete(ir);
-                return false;
-            }
+    }
+    if (opts.dump)
+        ir_builder_dump(ir, con_out);
+    for (i = 0; i < vec_size(parser->functions); ++i) {
+        if (!ir_function_finalize(parser->functions[i]->ir_func)) {
+            con_out("failed to finalize function %s\n", parser->functions[i]->name);
+            ir_builder_delete(ir);
+            return false;
         }
+    }
+
+    if (compile_Werrors) {
+        con_out("*** there were warnings treated as errors\n");
+        compile_show_werrors();
+        retval = false;
+    }
 
-        if (retval) {
-            if (opts.dumpfin)
-                ir_builder_dump(ir, con_out);
+    if (retval) {
+        if (opts.dumpfin)
+            ir_builder_dump(ir, con_out);
 
-            generate_checksum(parser);
+        generate_checksum(parser);
 
-            if (!ir_builder_generate(ir, output)) {
-                con_out("*** failed to generate output file\n");
-                ir_builder_delete(ir);
-                return false;
-            }
+        if (!ir_builder_generate(ir, output)) {
+            con_out("*** failed to generate output file\n");
+            ir_builder_delete(ir);
+            return false;
         }
-
-        ir_builder_delete(ir);
-        return retval;
     }
 
-    con_out("*** there were compile errors\n");
-    return false;
+    ir_builder_delete(ir);
+    return retval;
 }