diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index e4c2749c6..d171b1cb1 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -440,6 +440,7 @@ typedef struct QCC_def_s pbool initialized:1; //true when a declaration included "= immediate". pbool isextern:1; //fteqw-specific lump entry pbool isparameter:1; //its an engine parameter (thus preinitialised). + const char *deprecated; //reason its deprecated (or empty for no reason given) int fromstatement; //statement that it is valid from. temp_t *temp; @@ -748,7 +749,7 @@ char *QCC_NameForWarning(int idx); enum { WARN_DEBUGGING, WARN_ERROR, - WARN_DEPRECATEDWARNING, //to silence warnings about old warnings. + WARN_REMOVEDWARNING, //to silence warnings about old warnings. WARN_WRITTENNOTREAD, WARN_READNOTWRITTEN, WARN_NOTREFERENCED, @@ -804,7 +805,8 @@ enum { WARN_DUPLICATEPRECOMPILER, WARN_IDENTICALPRECOMPILER, WARN_FORMATSTRING, //sprintf - WARN_DEPRECACTEDSYNTAX, //triggered when syntax is used that I'm trying to kill + WARN_DEPRECACTEDSYNTAX, //triggered when syntax is used that I'm trying to kill + WARN_DEPRECATEDVARIABLE, //triggered from usage of a symbol that someone tried to kill WARN_GMQCC_SPECIFIC, //extension created by gmqcc that conflicts or isn't properly implemented. WARN_FTE_SPECIFIC, //extension that only FTEQCC will have a clue about. WARN_EXTENSION_USED, //extension that frikqcc also understands diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index be60eff18..a00ed9134 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -5438,6 +5438,20 @@ static QCC_sref_t QCC_PR_Inline(QCC_sref_t fdef, QCC_ref_t **arglist, unsigned i return ctx.result; #endif } +pbool QCC_Intrinsic_strlen(QCC_sref_t *result, const QCC_eval_t *a) +{ + const char *str = &strings[a->string]; + size_t l = 0; + for (l = 0; str[l]; l++) + { //don't shortcut it when its an extended char. we don't know what the engine's strlen function would return. + if ((unsigned char)str[l] & 0x80) + return false; + } + if (l > 1u<<24) //wait, what? + return false; //too big for a float. + *result = QCC_MakeFloatConst(l); + return true; +} QCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func, QCC_ref_t **arglist, unsigned int argcount) //warning, the func could have no name set if it's a field call. { QCC_sref_t d, oself, self, retval; @@ -5478,6 +5492,9 @@ QCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func, return QCC_MakeFloatConst(sqrt(a->_float)); // if (!strcmp(funcname, "ftos")) // return QCC_MakeStringConst(ftos(a->_float)); //engines differ too much in their ftos implementation for this to be worthwhile + + if (!strcmp(funcname, "strlen") && QCC_Intrinsic_strlen(&d, a)) + return d; } } else if (opt_constantarithmatic && argcount == 2 && arglist[0]->type == REF_GLOBAL && arglist[1]->type == REF_GLOBAL) @@ -6068,36 +6085,63 @@ static QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the f { int sz; int oldstcount = numstatements; -#if 1 QCC_ref_t refbuf, *r; r = QCC_PR_RefExpression(&refbuf, TOP_PRIORITY, 0); -// r = QCC_PR_ParseRefValue(&refbuf, pr_classtype, false, false, false); - if (r->type == REF_ARRAYHEAD && !r->index.cast) - { - e = r->base; - sz = e.sym->arraysize; - } + if (r->type == REF_GLOBAL && r->base.sym->type == type_string && !strcmp(r->base.sym->name, "IMMEDIATE")) + sz = strlen(&strings[QCC_SRef_EvalConst(r->base)->string]) + 1; //sizeof("hello") includes the null, and is bytes not codepoints else - sz = 1; - sz *= r->cast->size; + { + sz = 4; //4 bytes per word. we don't support char/short (our string type is logically char*) + if (r->type == REF_ARRAYHEAD && !r->index.cast) + sz *= r->base.sym->arraysize; + sz *= r->cast->size; + } + QCC_FreeTemp(r->base); + if (r->index.cast) + QCC_FreeTemp(r->index); + //the term should not have side effects, or generate any actual statements. + numstatements = oldstcount; + QCC_PR_Expect(")"); + return QCC_MakeIntConst(sz); + } + } + if (!strcmp(funcname, "_length")) + { //for compat with gmqcc + QCC_type_t *t; + func.sym->unused = true; + func.sym->referenced = true; + QCC_FreeTemp(func); + t = QCC_PR_ParseType(false, true); + if (t) + { + QCC_PR_Expect(")"); + return QCC_PR_Statement(&pr_opcodes[OP_ADD_PIW], QCC_MakeIntConst(0), QCC_MakeIntConst(t->size), NULL); + } + else + { + int sz = 0; + int oldstcount = numstatements; + QCC_ref_t refbuf, *r; + r = QCC_PR_RefExpression(&refbuf, TOP_PRIORITY, 0); + if (r->type == REF_ARRAYHEAD) + sz = r->base.sym->arraysize; + else if (r->cast == type_string) + { + QCC_sref_t d = QCC_RefToDef(r, false); + const QCC_eval_t *c = QCC_SRef_EvalConst(d); + if (c) + sz = strlen(&strings[c->string]); //_length("hello") does NOT include the null (like strlen), but is bytes not codepoints + } + else if (r->cast == type_vector) + sz = 3; //might as well. considering that vectors can be indexed as an array. + else + QCC_PR_ParseError (ERR_TYPEMISMATCHPARM, "_length() unsupported argument type for intrinsic"); QCC_FreeTemp(r->base); if (r->index.cast) QCC_FreeTemp(r->index); -#else - e = QCC_PR_ParseValue(pr_classtype, false, true, false); - if (!e) - QCC_PR_ParseErrorPrintSRef (ERR_NOTAFUNCTION, func, "sizeof term not supported"); - if (!e->arraysize) - sz = 1; - else - sz = e->arraysize; - sz *= e->type->size; - QCC_FreeTemp(e); -#endif //the term should not have side effects, or generate any actual statements. numstatements = oldstcount; QCC_PR_Expect(")"); - sz *= 4; //4 bytes per word return QCC_MakeIntConst(sz); } } @@ -8047,7 +8091,8 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo if (!strcmp(name, "nil")) d = QCC_MakeIntConst(0); else if ( (!strcmp(name, "randomv")) || - (!strcmp(name, "sizeof")) || + (!strcmp(name, "sizeof")) || //FIXME: sizeof should be an operator, not a function (ie: 'sizeof foo' should work just like 'sizeof(foo)' does) + (!strcmp(name, "_length")) || (!strcmp(name, "alloca")) || (!strcmp(name, "entnum")) || (!strcmp(name, "autocvar")) || @@ -14314,6 +14359,13 @@ QCC_sref_t QCC_PR_GetSRef (QCC_type_t *type, const char *name, QCC_function_t *s sr.sym = def; sr.cast = def->type; sr.ofs = 0; + if (def->deprecated) + { + if (*def->deprecated) //we have a reason for it + QCC_PR_ParseWarning(WARN_DEPRECATEDVARIABLE, "Variable \"%s\" is deprecated: %s", def->name, def->deprecated); + else //we don't have any reason for it. + QCC_PR_ParseWarning(WARN_DEPRECATEDVARIABLE, "Variable \"%s\" is deprecated", def->name); + } return sr; } return nullsref; @@ -15171,6 +15223,7 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) pbool doweak = false; pbool forceused = false; pbool accumulate = false; + const char *deprecated = NULL; int arraysize; unsigned int gd_flags; const char *aliasof = NULL; @@ -15411,6 +15464,22 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) doweak = true; else if (QCC_PR_CheckKeyword(keyword_accumulate, "accumulate")) accumulate = true; + else if (QCC_PR_CheckKeyword(false, "deprecated")) + { + if (!QCC_PR_CheckToken("(")) + deprecated = ""; + else + { + if (pr_token_type == tt_immediate && pr_immediate_type == type_string) + { + deprecated = strcpy(qccHunkAlloc(strlen(pr_immediate_string)+1), pr_immediate_string); + QCC_PR_Lex(); + } + else + deprecated = ""; + QCC_PR_Expect(")"); + } + } else if (flag_attributes && !pr_scope && QCC_PR_CheckToken("[")) { QCC_PR_Expect("["); @@ -15440,6 +15509,22 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) } else if (QCC_PR_CheckName("eraseable")) noref = true; + else if (QCC_PR_CheckKeyword(false, "deprecated")) + { + if (!QCC_PR_CheckToken("(")) + deprecated = ""; + else + { + if (pr_token_type == tt_immediate && pr_immediate_type == type_string) + { + deprecated = strcpy(qccHunkAlloc(strlen(pr_immediate_string)+1), pr_immediate_string); + QCC_PR_Lex(); + } + else + QCC_PR_ParseError (ERR_EXPECTED, "expected string"); + QCC_PR_Expect(")"); + } + } else { QCC_PR_ParseWarning(WARN_GMQCC_SPECIFIC, "Unknown attribute \"%s\"", pr_token); @@ -15759,6 +15844,9 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal) def->isextern = true; } + if (deprecated) + def->deprecated = deprecated; + if (isstatic) { if (!strcmp(def->filen, s_filen)) diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index d7564f075..77b8a2ee3 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -223,6 +223,7 @@ struct { {" F323", WARN_UNREACHABLECODE}, {" F324", WARN_FORMATSTRING}, {" F325", WARN_NESTEDCOMMENT}, + {" F326", WARN_DEPRECATEDVARIABLE}, {" F207", WARN_NOTREFERENCEDFIELD}, {" F208", WARN_NOTREFERENCEDCONST}, @@ -244,7 +245,7 @@ struct { //Q618: Ran out of mem pointer space (malloc failure again) //we can put longer alternative names here... - {" field-redeclared", WARN_DEPRECATEDWARNING}, + {" field-redeclared", WARN_REMOVEDWARNING}, {NULL} }; @@ -383,7 +384,7 @@ compiler_flag_t compiler_flag[] = { {&pr_subscopedlocals, FLAG_MIDCOMPILE,"subscope", "Subscoped Locals", "Restrict the scope of locals to the block they are actually defined within, as in C."}, {&verbose, FLAG_MIDCOMPILE,"verbose", "Verbose", "Lots of extra compiler messages."}, {&flag_typeexplicit, FLAG_MIDCOMPILE,"typeexplicit", "Explicit types", "All type conversions must be explicit or directly supported by instruction set."}, - {&flag_boundchecks, defaultflag, "boundchecks","Disable Bound Checks", "Disable array index checks, speeding up array access but can result in your code misbehaving."}, + {&flag_boundchecks, defaultflag, "boundchecks", "Disable Bound Checks", "Disable array index checks, speeding up array access but can result in your code misbehaving."}, {&flag_attributes, hideflag, "attributes", "[[attributes]]", "WARNING: This syntax conflicts with vector constructors."}, {&flag_assumevar, hideflag, "assumevar", "explicit consts", "Initialised globals will be considered non-const by default."}, {&flag_dblstarexp, hideflag, "ssp", "** exponent", "Treat ** as an operator for exponents, instead of multiplying by a dereferenced pointer."},