/*
* Copyright (C) 2012
* Wolfgang Bumiller
- *
+ * Dale Weiler
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
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, bool is_static);
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring);
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);
static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases);
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma);
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue);
static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
static void parseerror(parser_t *parser, const char *fmt, ...)
{
if (CanConstFold(exprs[0], exprs[1]))
out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1)));
+ else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[0])) {
+ vector vec = ConstV(0);
+ if (!vec.y && !vec.z) { /* 'n 0 0' * v */
+ ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+ out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL);
+ out->expression.node.keep = false;
+ ((ast_member*)out)->rvalue = true;
+ if (vec.x != 1)
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out);
+ }
+ else if (!vec.x && !vec.z) { /* '0 n 0' * v */
+ ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+ out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL);
+ out->expression.node.keep = false;
+ ((ast_member*)out)->rvalue = true;
+ if (vec.y != 1)
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out);
+ }
+ else if (!vec.x && !vec.y) { /* '0 n 0' * v */
+ ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+ out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL);
+ out->expression.node.keep = false;
+ ((ast_member*)out)->rvalue = true;
+ if (vec.z != 1)
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out);
+ }
+ else
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
+ }
+ else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[1])) {
+ vector vec = ConstV(1);
+ if (!vec.y && !vec.z) { /* v * 'n 0 0' */
+ ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+ out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
+ out->expression.node.keep = false;
+ ((ast_member*)out)->rvalue = true;
+ if (vec.x != 1)
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x));
+ }
+ else if (!vec.x && !vec.z) { /* v * '0 n 0' */
+ ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+ out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
+ out->expression.node.keep = false;
+ ((ast_member*)out)->rvalue = true;
+ if (vec.y != 1)
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y));
+ }
+ else if (!vec.x && !vec.y) { /* v * '0 n 0' */
+ ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
+ out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
+ out->expression.node.keep = false;
+ ((ast_member*)out)->rvalue = true;
+ if (vec.z != 1)
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z));
+ }
+ else
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
+ }
else
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
}
( (generated_op == INSTR_OR)
? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1]))
: (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) )
- ? 0 : 1);
+ ? 1 : 0);
}
else
{
parseerror(parser, "could not determine function return type");
return false;
} else {
+ ast_value *fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL);
+
+ if (fun->expression.flags & AST_FLAG_DEPRECATED) {
+ if (!fval) {
+ return !parsewarning(parser, WARN_DEPRECATED,
+ "call to function (which is marked deprecated)\n",
+ "-> it has been declared here: %s:%i",
+ ast_ctx(fun).file, ast_ctx(fun).line);
+ }
+ if (!fval->desc) {
+ return !parsewarning(parser, WARN_DEPRECATED,
+ "call to `%s` (which is marked deprecated)\n"
+ "-> `%s` declared here: %s:%i",
+ fval->name, fval->name, ast_ctx(fun).file, ast_ctx(fun).line);
+ }
+ return !parsewarning(parser, WARN_DEPRECATED,
+ "call to `%s` (deprecated: %s)\n"
+ "-> `%s` declared here: %s:%i",
+ fval->name, fval->desc, fval->name, ast_ctx(fun).file,
+ ast_ctx(fun).line);
+ }
+
if (vec_size(fun->expression.params) != paramcount &&
!((fun->expression.flags & AST_FLAG_VARIADIC) &&
vec_size(fun->expression.params) < paramcount))
{
- ast_value *fval;
const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many";
-
- fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL);
if (opts.standard == COMPILER_GMQCC)
{
if (fval)
}
}
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma)
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue)
{
ast_expression *expr = NULL;
shunt sy;
bool wantop = false;
bool gotmemberof = false;
+ /* only warn once about an assignment in a truth value because the current code
+ * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
+ */
+ bool warn_truthvalue = true;
/* count the parens because an if starts with one, so the
* end of a condition is an unmatched closing paren
if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
olast = &operators[vec_last(sy.ops).etype-1];
+#define IsAssignOp(x) (\
+ (x) == opid1('=') || \
+ (x) == opid2('+','=') || \
+ (x) == opid2('-','=') || \
+ (x) == opid2('*','=') || \
+ (x) == opid2('/','=') || \
+ (x) == opid2('%','=') || \
+ (x) == opid2('&','=') || \
+ (x) == opid2('|','=') || \
+ (x) == opid3('&','~','=') \
+ )
+ if (warn_truthvalue) {
+ if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
+ (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
+ (truthvalue && !vec_size(parser->pot) && IsAssignOp(op->id))
+ )
+ {
+ (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
+ warn_truthvalue = false;
+ }
+ }
+
while (olast && (
(op->prec < olast->prec) ||
(op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) )
static ast_expression* parse_expression(parser_t *parser, bool stopatcomma)
{
- ast_expression *e = parse_expression_leave(parser, stopatcomma);
+ ast_expression *e = parse_expression_leave(parser, stopatcomma, false);
if (!e)
return NULL;
if (!parser_next(parser)) {
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true);
if (!cond)
return false;
/* closing paren */
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true);
if (!cond)
return false;
/* closing paren */
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true);
if (!cond)
return false;
/* closing paren */
"current standard does not allow variable declarations in for-loop initializers"))
goto onerr;
}
- if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, false))
+ if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0, NULL))
goto onerr;
}
else if (parser->tok != ';')
{
- initexpr = parse_expression_leave(parser, false);
+ initexpr = parse_expression_leave(parser, false, false);
if (!initexpr)
goto onerr;
}
/* parse the condition */
if (parser->tok != ';') {
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true);
if (!cond)
goto onerr;
}
/* parse the incrementor */
if (parser->tok != ')') {
- increment = parse_expression_leave(parser, false);
+ increment = parse_expression_leave(parser, false, false);
if (!increment)
goto onerr;
if (!ast_side_effects(increment)) {
/* 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, bool *is_static)
+static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message)
{
bool had_const = false;
bool had_var = false;
bool had_noref = false;
- bool had_noreturn = false;
bool had_attrib = false;
bool had_static = false;
+ uint32_t flags = 0;
*cvq = CV_NONE;
for (;;) {
return false;
}
if (!strcmp(parser_tokval(parser), "noreturn")) {
- had_noreturn = true;
+ flags |= AST_FLAG_NORETURN;
if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`");
*cvq = CV_WRONG;
return false;
}
}
+ else if (!strcmp(parser_tokval(parser), "inline")) {
+ flags |= AST_FLAG_INLINE;
+ if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ }
+
+
+ else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) {
+ flags |= AST_FLAG_DEPRECATED;
+ *message = NULL;
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error in attribute");
+ goto argerr;
+ }
+
+ if (parser->tok == '(') {
+ if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) {
+ parseerror(parser, "`deprecated` attribute missing parameter");
+ goto argerr;
+ }
+
+ *message = util_strdup(parser_tokval(parser));
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error in attribute");
+ goto argerr;
+ }
+
+ if(parser->tok != ')') {
+ parseerror(parser, "`deprecated` attribute expected `)` after parameter");
+ goto argerr;
+ }
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error in attribute");
+ goto argerr;
+ }
+ }
+ /* no message */
+ if (parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`deprecated` attribute expected `]]`");
+
+ argerr: /* ugly */
+ if (*message) mem_d(*message);
+ *message = NULL;
+ *cvq = CV_WRONG;
+ return false;
+ }
+ }
else
{
/* Skip tokens until we hit a ]] */
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 && !had_static) {
+ else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) {
return false;
}
else
else
*cvq = CV_NONE;
*noref = had_noref;
- *noreturn = had_noreturn;
*is_static = had_static;
+ *_flags = flags;
return true;
onerr:
parseerror(parser, "parse error after variable qualifier");
ast_switch_case swcase;
int cvq;
- bool noref, noreturn, is_static;
+ bool noref, is_static;
+ uint32_t qflags = 0;
lex_ctx ctx = parser_ctx(parser);
return false;
}
/* parse the operand */
- operand = parse_expression_leave(parser, false);
+ operand = parse_expression_leave(parser, false, false);
if (!operand)
return false;
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, false)) {
+ if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL)) {
ast_delete(switchnode);
return false;
}
continue;
}
- if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn, &is_static))
+ if (parse_qualifiers(parser, true, &cvq, &noref, &is_static, &qflags, NULL))
{
if (cvq == CV_WRONG) {
ast_delete(switchnode);
return false;
}
- if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn, is_static)) {
+ if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, NULL)) {
ast_delete(switchnode);
return false;
}
parseerror(parser, "expected expression for case");
return false;
}
- swcase.value = parse_expression_leave(parser, false);
+ swcase.value = parse_expression_leave(parser, false, false);
if (!swcase.value) {
ast_delete(switchnode);
parseerror(parser, "expected expression for case");
static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
{
- bool noref, noreturn, is_static;
- int cvq = CV_NONE;
+ bool noref, is_static;
+ int cvq = CV_NONE;
+ uint32_t qflags = 0;
ast_value *typevar = NULL;
+ char *vstring = NULL;
*out = NULL;
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, false))
+ if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL))
return false;
return true;
}
- else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn, &is_static))
+ else if (parse_qualifiers(parser, !!block, &cvq, &noref, &is_static, &qflags, &vstring))
{
if (cvq == CV_WRONG)
return false;
- return parse_variable(parser, block, true, cvq, NULL, noref, noreturn, is_static);
+ return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, vstring);
}
else if (parser->tok == TOKEN_KEYWORD)
{
if (!parser_next(parser))
return false;
- framenum = parse_expression_leave(parser, true);
+ framenum = parse_expression_leave(parser, true, false);
if (!framenum) {
parseerror(parser, "expected a framenumber constant in[frame,think] notation");
return false;
nextthink = (ast_expression*)thinkfunc;
} else {
- nextthink = parse_expression_leave(parser, true);
+ nextthink = parse_expression_leave(parser, true, false);
if (!nextthink) {
ast_unref(framenum);
parseerror(parser, "expected a think-function in [frame,think] notation");
return NULL;
}
- cexp = parse_expression_leave(parser, true);
+ cexp = parse_expression_leave(parser, true, false);
if (!cexp || !ast_istype(cexp, ast_value)) {
if (cexp)
return true;
}
-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 const char *cvq_to_str(int cvq) {
+ switch (cvq) {
+ case CV_NONE: return "none";
+ case CV_VAR: return "`var`";
+ case CV_CONST: return "`const`";
+ default: return "<INVALID>";
+ }
+}
+
+static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, const ast_value *proto)
+{
+ bool av, ao;
+ if (proto->cvq != var->cvq) {
+ if (!(proto->cvq == CV_CONST && var->cvq == CV_NONE &&
+ !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+ parser->tok == '='))
+ {
+ return !parsewarning(parser, WARN_DIFFERENT_QUALIFIERS,
+ "`%s` declared with different qualifiers: %s\n"
+ " -> previous declaration here: %s:%i uses %s",
+ var->name, cvq_to_str(var->cvq),
+ ast_ctx(proto).file, ast_ctx(proto).line,
+ cvq_to_str(proto->cvq));
+ }
+ }
+ av = (var ->expression.flags & AST_FLAG_NORETURN);
+ ao = (proto->expression.flags & AST_FLAG_NORETURN);
+ if (!av != !ao) {
+ return !parsewarning(parser, WARN_DIFFERENT_ATTRIBUTES,
+ "`%s` declared with different attributes%s\n"
+ " -> previous declaration here: %s:%i",
+ var->name, (av ? ": noreturn" : ""),
+ ast_ctx(proto).file, ast_ctx(proto).line,
+ (ao ? ": noreturn" : ""));
+ }
+ return true;
+}
+
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring)
{
ast_value *var;
ast_value *proto;
}
var->cvq = qualifier;
- /* in a noref section we simply bump the usecount */
- if (noref || parser->noref)
- var->uses++;
- if (noreturn)
- var->expression.flags |= AST_FLAG_NORETURN;
+ var->expression.flags |= qflags;
+ if (var->expression.flags & AST_FLAG_DEPRECATED)
+ var->desc = vstring;
/* Part 1:
* check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
goto cleanup;
}
proto = (ast_value*)old;
+ proto->desc = var->desc;
if (!ast_compare_type((ast_expression*)proto, (ast_expression*)var)) {
parseerror(parser, "conflicting types for `%s`, previous declaration was here: %s:%i",
proto->name,
/* we need the new parameter-names */
for (i = 0; i < vec_size(proto->expression.params); ++i)
ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name);
+ if (!parser_check_qualifiers(parser, var, proto)) {
+ retval = false;
+ if (proto->desc)
+ mem_d(proto->desc);
+ proto = NULL;
+ goto cleanup;
+ }
+ proto->expression.flags |= var->expression.flags;
ast_delete(var);
var = proto;
}
proto = NULL;
goto cleanup;
}
+ if (!parser_check_qualifiers(parser, var, proto)) {
+ retval = false;
+ proto = NULL;
+ goto cleanup;
+ }
+ proto->expression.flags |= var->expression.flags;
ast_delete(var);
var = proto;
}
}
}
+ /* in a noref section we simply bump the usecount */
+ if (noref || parser->noref)
+ var->uses++;
+
/* Part 2:
* Create the global/local, and deal with vector types.
*/
ast_expression *cexp;
ast_value *cval;
- cexp = parse_expression_leave(parser, true);
+ cexp = parse_expression_leave(parser, true, false);
if (!cexp)
break;
if (!localblock) {
cval = (ast_value*)cexp;
- if (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
+ if (cval != parser->nil &&
+ (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
+ )
+ {
parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
+ }
else
{
if (opts.standard != COMPILER_GMQCC &&
{
var->cvq = CV_CONST;
}
- var->hasvalue = true;
- if (cval->expression.vtype == TYPE_STRING)
- var->constval.vstring = parser_strdup(cval->constval.vstring);
- else if (cval->expression.vtype == TYPE_FIELD)
- var->constval.vfield = cval;
+ if (cval == parser->nil)
+ var->expression.flags |= AST_FLAG_INITIALIZED;
else
- memcpy(&var->constval, &cval->constval, sizeof(var->constval));
- ast_unref(cval);
+ {
+ var->hasvalue = true;
+ if (cval->expression.vtype == TYPE_STRING)
+ var->constval.vstring = parser_strdup(cval->constval.vstring);
+ else if (cval->expression.vtype == TYPE_FIELD)
+ var->constval.vfield = cval;
+ else
+ memcpy(&var->constval, &cval->constval, sizeof(var->constval));
+ ast_unref(cval);
+ }
}
} else {
int cvq;
{
int cvq = CV_WRONG;
bool noref = false;
- bool noreturn = false;
bool is_static = false;
+ uint32_t qflags = 0;
ast_value *istype = NULL;
+ char *vstring = 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, false);
+ return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL);
}
- else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn, &is_static))
+ else if (parse_qualifiers(parser, false, &cvq, &noref, &is_static, &qflags, &vstring))
{
if (cvq == CV_WRONG)
return false;
- return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn, is_static);
+ return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring);
}
else if (parser->tok == TOKEN_KEYWORD)
{
empty_ctx.file = "<internal>";
empty_ctx.line = 0;
parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL);
+ parser->nil->cvq = CV_CONST;
if (OPTS_FLAG(UNTYPED_NIL))
util_htset(parser->htglobals, "nil", (void*)parser->nil);
return true;
vec_free(parser->breaks);
vec_free(parser->continues);
+ ast_value_delete(parser->nil);
+
mem_d(parser);
}
if (!ast_istype(parser->globals[i], ast_value))
continue;
asvalue = (ast_value*)(parser->globals[i]);
+ if (!(asvalue->expression.flags & AST_FLAG_INITIALIZED))
+ {
+ if (asvalue->cvq == CV_CONST && !asvalue->hasvalue)
+ (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_CONSTANT,
+ "uninitialized constant: `%s`",
+ asvalue->name);
+ else if ((asvalue->cvq == CV_NONE || asvalue->cvq == CV_CONST) && !asvalue->hasvalue)
+ (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_GLOBAL,
+ "uninitialized global: `%s`",
+ asvalue->name);
+ }
if (!ast_generate_accessors(asvalue, ir)) {
ir_builder_delete(ir);
return false;