/* ** Splint - annotation-assisted static program checker ** Copyright (C) 1994-2003 University of Virginia, ** Massachusetts Institute of Technology ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2 of the License, or (at your ** option) any later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** General Public License for more details. ** ** The GNU General Public License is available from http://www.gnu.org/ or ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, ** MA 02111-1307, USA. ** ** For information on splint: info@splint.org ** To report a bug: splint-bug@splint.org ** For more information: http://www.splint.org */ /* ** exprNode.c */ # include /* for isdigit */ # include "splintMacros.nf" # include "basic.h" # include "cgrammar.h" # include "cscanner.h" # include "cscannerHelp.h" # include "cgrammar_tokens.h" # include "exprChecks.h" # include "transferChecks.h" # include "exprNodeSList.h" static bool exprNode_sameStorage (exprNode p_e1, exprNode p_e2) /*@*/ ; static bool exprNode_isEmptyStatement (exprNode p_e); static /*@exposed@*/ exprNode exprNode_firstStatement (/*@returned@*/ exprNode p_e); static bool exprNode_isFalseConstant (exprNode p_e) /*@*/ ; static bool exprNode_isStatement (exprNode p_e); static void checkGlobUse (uentry p_glob, bool p_isCall, /*@notnull@*/ exprNode p_e); static void exprNode_addUse (exprNode p_e, /*@exposed@*/ sRef p_s); static bool exprNode_matchArgType (ctype p_ct, exprNode p_e); static exprNode exprNode_fakeCopy (exprNode p_e) /*@*/ ; static exprNode exprNode_statementError (/*@only@*/ exprNode p_e, /*@only@*/ lltok p_t); static bool exprNode_matchTypes (exprNode p_e1, exprNode p_e2); static void checkUniqueParams (exprNode p_fcn, /*@notnull@*/ exprNode p_current, exprNodeList p_args, int p_paramno, uentry p_ucurrent); static void updateAliases (/*@notnull@*/ exprNode p_e1, /*@notnull@*/ exprNode p_e2); static bool abstractOpError (ctype p_tr1, ctype p_tr2, lltok p_op, /*@notnull@*/ exprNode p_e1, /*@notnull@*/ exprNode p_e2, fileloc p_loc1, fileloc p_loc2) /*@modifies g_warningstream@*/ ; static ctype checkNumerics (ctype p_tr1, ctype p_tr2, ctype p_te1, ctype p_te2, /*@notnull@*/ exprNode p_e1, /*@notnull@*/ exprNode p_e2, lltok p_op); static void doAssign (/*@notnull@*/ exprNode p_e1, /*@notnull@*/ exprNode p_e2, bool p_isInit); static void checkSafeUse (exprNode p_e, /*@exposed@*/ sRef p_s); static void reflectNullTest (/*@notnull@*/ exprNode p_e, bool p_isnull); static void checkMacroParen (exprNode p_e); static exprNodeSList exprNode_flatten (/*@dependent@*/ exprNode p_e); static void exprNode_checkSetAny (exprNode p_e, /*@dependent@*/ cstring p_name); static void exprNode_checkUse (exprNode p_e, /*@exposed@*/ sRef p_s, fileloc p_loc); static void exprNode_mergeUSs (exprNode p_res, exprNode p_other); static void exprNode_mergeCondUSs (exprNode p_res, exprNode p_other1, exprNode p_other2); static /*@only@*/ /*@notnull@*/ exprNode exprNode_fromIdentifierAux (/*@observer@*/ uentry p_c); static void checkAnyCall (/*@notnull@*/ /*@dependent@*/ exprNode p_fcn, /*@dependent@*/ cstring p_fname, uentryList p_pn, exprNodeList p_args, bool p_hasMods, sRefSet p_mods, bool p_isSpec, int p_specialArgs); static void checkOneArg (uentry p_ucurrent, /*@notnull@*/ exprNode p_current, /*@dependent@*/ exprNode p_fcn, bool p_isSpec, int p_argno, int p_totargs); static void checkUnspecCall (/*@notnull@*/ /*@dependent@*/ exprNode p_fcn, uentryList p_params, exprNodeList p_args); static /*@only@*/ exprNode exprNode_effect (exprNode p_e) /*@globals internalState@*/ ; static /*@only@*/ cstring exprNode_doUnparse (exprNode p_e); static /*@observer@*/ cstring exprNode_rootVarName (exprNode p_e); static /*@exposed@*/ exprNode exprNode_lastStatement (/*@returned@*/ exprNode p_e); static /*@only@*/ exprNode s_mustExitNode = exprNode_undefined; static int checkArgsReal (uentry p_fcn, /*@dependent@*/ exprNode p_f, uentryList p_cl, exprNodeList p_args, bool p_isIter, exprNode p_ret); static bool inEffect = FALSE; static int nowalloc = 0; static int totalloc = 0; static int maxalloc = 0; static /*@only@*/ uentry regArg; static /*@only@*/ uentry outArg; static /*@only@*/ uentry outStringArg; static /*@exposed@*/ sRef stdinRef; static /*@exposed@*/ sRef stdoutRef; static /*@only@*/ uentry csArg; static /*@only@*/ uentry csOnlyArg; static ctype cstringType; static ctype ctypeType; static ctype filelocType; static bool initMod = FALSE; /*@function void exprNode_swap (sef exprNode, sef exprNode)@*/ /*@-macroassign@*/ # define exprNode_swap(e1,e2) do { exprNode m_tmp = (e1); (e1) = (e2); (e2) = m_tmp; } while (FALSE) /*@=macroassign@*/ static void exprNode_defineConstraints(/*@sef@*/ /*@special@*/ /*@notnull@*/ exprNode e) /*@defines e->requiresConstraints, e->ensuresConstraints, e->trueEnsuresConstraints, e->falseEnsuresConstraints @*/ { e->requiresConstraints = constraintList_makeNew (); e->ensuresConstraints = constraintList_makeNew (); e->trueEnsuresConstraints = constraintList_makeNew (); e->falseEnsuresConstraints = constraintList_makeNew (); } /* ** must occur after library has been read */ void exprNode_initMod (void) /*@globals undef regArg, undef outArg, undef outStringArg, undef csOnlyArg, undef csArg; @*/ { uentry ue; idDecl tmp; initMod = TRUE; cstringType = ctype_unknown; ctypeType = ctype_unknown; filelocType = ctype_unknown; if (usymtab_existsType (cstring_makeLiteralTemp ("cstring"))) { cstringType = usymtab_lookupAbstractType (cstring_makeLiteralTemp ("cstring")); } if (usymtab_existsType (cstring_makeLiteralTemp ("ctype"))) { ctypeType = usymtab_lookupAbstractType (cstring_makeLiteralTemp ("ctype")); } if (usymtab_existsType (cstring_makeLiteralTemp ("fileloc"))) { filelocType = usymtab_lookupAbstractType (cstring_makeLiteralTemp ("fileloc")); } if (usymtab_existsGlob (cstring_makeLiteralTemp ("stdin"))) { ue = usymtab_lookupGlob (cstring_makeLiteralTemp ("stdin")); } else /* define stdin */ { ue = uentry_makeVariable (cstring_makeLiteralTemp ("stdin"), ctype_unknown, fileloc_getBuiltin (), FALSE); uentry_setHasNameError (ue); ue = usymtab_supGlobalEntryReturn (ue); } stdinRef = sRef_makePointer (uentry_getSref (ue)); if (usymtab_existsGlob (cstring_makeLiteralTemp ("stdout"))) { ue = usymtab_lookupGlob (cstring_makeLiteralTemp ("stdout")); } else { ue = uentry_makeVariable (cstring_makeLiteralTemp ("stdout"), ctype_unknown, fileloc_getBuiltin (), FALSE); uentry_setHasNameError (ue); ue = usymtab_supGlobalEntryReturn (ue); } stdoutRef = sRef_makePointer (uentry_getSref (ue)); tmp = idDecl_create (cstring_undefined, qtype_create (ctype_unknown)); regArg = uentry_makeParam (tmp, PARAMUNKNOWN); idDecl_setTyp (tmp, qtype_addQual (qtype_create (ctype_makePointer (ctype_unknown)), qual_createOut ())); outArg = uentry_makeParam (tmp, PARAMUNKNOWN); idDecl_setTyp (tmp, qtype_addQual (qtype_create (ctype_string), qual_createOut ())); outStringArg = uentry_makeParam (tmp, PARAMUNKNOWN); idDecl_setTyp (tmp, qtype_addQual (qtype_addQual (qtype_create (cstringType), qual_createOnly ()), qual_createNull ())); csOnlyArg = uentry_makeParam (tmp, PARAMUNKNOWN); idDecl_setTyp (tmp, qtype_addQual (qtype_create (cstringType), qual_createNull ())); csArg = uentry_makeParam (tmp, PARAMUNKNOWN); idDecl_free (tmp); } void exprNode_destroyMod (void) /*@globals killed regArg, killed outArg, killed outStringArg, killed s_mustExitNode, initMod @*/ { if (initMod) { /* evans 2002-07-12: changed uentry_free to uentry_freeComplete */ uentry_freeComplete (regArg); uentry_freeComplete (outArg); uentry_freeComplete (outStringArg); exprNode_free (s_mustExitNode); initMod = FALSE; /*@-branchstate@*/ } /*@=branchstate@*/ } static void exprNode_resetSref (/*@notnull@*/ exprNode e) { e->sref = sRef_undefined; } exprNode exprNode_fakeCopy (exprNode e) { /*@-temptrans@*/ /*@-retalias@*/ return e; /*@=temptrans@*/ /*@=retalias@*/ } static bool isFlagKey (char key) { return (key == '-' || key == '+' || key == ' ' || key == '#'); } static void exprNode_combineControl (/*@notnull@*/ exprNode ret, /*@notnull@*/ exprNode ifclause, /*@notnull@*/ exprNode elseclause) { ret->canBreak = ifclause->canBreak || elseclause->canBreak; ret->mustBreak = (ifclause->mustBreak || exprNode_mustEscape (ifclause)) && (elseclause->mustBreak || exprNode_mustEscape (elseclause)); ret->exitCode = exitkind_combine (ifclause->exitCode, elseclause->exitCode); } /* ** For exprNode's returned by exprNode_effect. */ static bool shallowKind (exprKind kind) { return (kind == XPR_STRINGLITERAL || kind == XPR_NUMLIT || kind == XPR_EMPTY || kind == XPR_BODY || kind == XPR_NODE); } static void exprNode_freeIniter (/*@only@*/ exprNode e) { if (!exprNode_isError (e)) { switch (e->kind) { case XPR_FACCESS: /* ** Its a fake copy, don't free the field->rec and field->field ** fields. */ /*@-compdestroy@*/ sfree (e->edata->field); /*@=compdestroy@*/ sfree (e->edata); break; case XPR_FETCH: exprNode_free (e->edata->op->b); /*@-compdestroy@*/ sfree (e->edata->op); /*@=compdestroy@*/ sfree (e->edata); break; default: llbug (message ("other: %s", exprNode_unparse (e))); } multiVal_free (e->val); cstring_free (e->etext); fileloc_free (e->loc); sRefSet_free (e->uses); sRefSet_free (e->sets); sRefSet_free (e->msets); guardSet_free (e->guards); constraintList_free(e->requiresConstraints); constraintList_free(e->ensuresConstraints); constraintList_free(e->trueEnsuresConstraints); constraintList_free(e->falseEnsuresConstraints); e->requiresConstraints = NULL; e->ensuresConstraints = NULL; e->trueEnsuresConstraints = NULL; e->falseEnsuresConstraints = NULL; sfree (e); } } void exprNode_freeShallow (/*@only@*/ exprNode e) { if (!exprNode_isError (e)) { if (shallowKind (e->kind)) { } else { if (!inEffect) { if (e->kind == XPR_EMPTY || e->kind == XPR_BODY || e->kind == XPR_STRINGLITERAL || e->kind == XPR_NUMLIT || e->kind == XPR_NODE || e->kind == XPR_OFFSETOF || e->kind == XPR_ALIGNOFT || e->kind == XPR_ALIGNOF || e->kind == XPR_SIZEOFT || e->kind == XPR_SIZEOF) { /* don't free anything */ } else { /* multiVal_free (e->val); */ cstring_free (e->etext); fileloc_free (e->loc); sRefSet_free (e->uses); sRefSet_free (e->sets); sRefSet_free (e->msets); guardSet_free (e->guards); exprData_freeShallow (e->edata, e->kind); nowalloc--; /*@-compdestroy@*/ sfree (e); /*@=compdestroy@*/ /*@-branchstate@*/ } } } /*@=branchstate@*/ } } void exprNode_free (exprNode e) { if (!exprNode_isError (e)) { if (!inEffect) { multiVal_free (e->val); cstring_free (e->etext); fileloc_free (e->loc); sRefSet_free (e->uses); sRefSet_free (e->sets); sRefSet_free (e->msets); guardSet_free (e->guards); exprData_free (e->edata, e->kind); constraintList_free(e->requiresConstraints); constraintList_free(e->ensuresConstraints); constraintList_free(e->trueEnsuresConstraints); constraintList_free(e->falseEnsuresConstraints); e->requiresConstraints = NULL; e->ensuresConstraints = NULL; e->trueEnsuresConstraints = NULL; e->falseEnsuresConstraints = NULL; nowalloc--; sfree (e); /*@-branchstate@*/ } /*@=branchstate@*/ } } exprNode exprNode_makeError () { return exprNode_undefined; } static /*@out@*/ /*@only@*/ /*@notnull@*/ exprNode exprNode_new (void) { exprNode ret = (exprNode) dmalloc (sizeof (*ret)); /* static int lastexpnodes = 0; */ nowalloc++; totalloc++; if (nowalloc > maxalloc) { maxalloc = nowalloc; } return ret; } static /*@notnull@*/ /*@special@*/ exprNode exprNode_createPlain (ctype c) /*@defines result@*/ /*@ensures isnull result->edata, result->loc, result->val, result->guards, result->uses, result->sets, result->msets, result->etext @*/ /*@*/ { exprNode e = exprNode_new (); e->typ = c; e->kind = XPR_EMPTY; e->val = multiVal_undefined; e->sref = sRef_undefined; e->etext = cstring_undefined; e->loc = fileloc_undefined; e->guards = guardSet_undefined; e->uses = sRefSet_undefined; e->sets = sRefSet_undefined; e->msets = sRefSet_undefined; e->edata = exprData_undefined; e->exitCode = XK_NEVERESCAPE; e->canBreak = FALSE; e->mustBreak = FALSE; e->isJumpPoint = FALSE; exprNode_defineConstraints(e); return (e); } /*@observer@*/ exprNode exprNode_makeMustExit (void) { if (exprNode_isUndefined (s_mustExitNode)) { s_mustExitNode = exprNode_createPlain (ctype_unknown); s_mustExitNode->exitCode = XK_MUSTEXIT; } return s_mustExitNode; } static /*@notnull@*/ /*@special@*/ exprNode exprNode_create (ctype c) /*@defines result@*/ /*@post:isnull result->edata, result->guards, result->val, result->uses, result->sets, result->msets@*/ /*@*/ { exprNode e = exprNode_createPlain (c); e->loc = fileloc_copy (g_currentloc); return (e); } static /*@notnull@*/ /*@special@*/ exprNode exprNode_createUnknown (void) /*@defines result@*/ /*@post:isnull result->edata, result->guards, result->uses, result->sets, result->msets@*/ /*@*/ { return (exprNode_create (ctype_unknown)); } static /*@notnull@*/ /*@special@*/ exprNode exprNode_createLoc (ctype c, /*@keep@*/ fileloc loc) /*@defines result@*/ /*@post:isnull result->edata, result->guards, result->val, result->uses, result->sets, result->msets@*/ /*@*/ { exprNode e = exprNode_createPlain (c); e->loc = loc; return (e); } static void exprNode_copySets (/*@special@*/ /*@notnull@*/ exprNode ret, exprNode e) /*@defines ret->guards, ret->uses, ret->sets, ret->msets@*/ { if (exprNode_isDefined (e)) { ret->guards = guardSet_copy (e->guards); ret->uses = sRefSet_newCopy (e->uses); ret->sets = sRefSet_newCopy (e->sets); ret->msets = sRefSet_newCopy (e->msets); } else { ret->guards = guardSet_undefined; ret->uses = sRefSet_undefined; ret->sets = sRefSet_undefined; ret->msets = sRefSet_undefined; } } static /*@notnull@*/ /*@special@*/ exprNode exprNode_createPartialLocCopy (exprNode e, /*@only@*/ fileloc loc) /*@defines result@*/ /*@post:isnull result->edata, result->etext@*/ /*@*/ { exprNode ret = exprNode_new (); if (exprNode_isError (e)) { ret->typ = ctype_unknown; ret->val = multiVal_undefined; ret->loc = loc; ret->guards = guardSet_undefined; ret->uses = sRefSet_undefined; ret->sets = sRefSet_undefined; ret->msets = sRefSet_undefined; } else { ret->typ = e->typ; ret->val = multiVal_copy (e->val); ret->loc = loc; ret->guards = guardSet_copy (e->guards); ret->uses = sRefSet_newCopy (e->uses); ret->sets = sRefSet_newCopy (e->sets); ret->msets = sRefSet_newCopy (e->msets); } ret->kind = XPR_EMPTY; ret->sref = sRef_undefined; ret->etext = cstring_undefined; ret->exitCode = XK_NEVERESCAPE; ret->canBreak = FALSE; ret->mustBreak = FALSE; ret->isJumpPoint = FALSE; ret->edata = exprData_undefined; exprNode_defineConstraints(ret); return (ret); } static /*@notnull@*/ /*@special@*/ exprNode exprNode_createPartialCopy (exprNode e) /*@defines result@*/ /*@post:isnull result->edata, result->etext@*/ /*@*/ { return (exprNode_createPartialLocCopy (e, fileloc_copy (exprNode_loc (e)))); } static /*@notnull@*/ /*@special@*/ exprNode exprNode_createPartialNVCopy (exprNode e) /*@defines result@*/ /*@post:isnull result->edata, result->etext, result->val @*/ /*@*/ { exprNode ret = exprNode_new (); if (exprNode_isError (e)) { ret->typ = ctype_unknown; ret->loc = fileloc_undefined; ret->guards = guardSet_undefined; ret->uses = sRefSet_undefined; ret->sets = sRefSet_undefined; ret->msets = sRefSet_undefined; } else { ret->typ = e->typ; ret->loc = fileloc_copy (e->loc); ret->guards = guardSet_copy (e->guards); ret->uses = sRefSet_newCopy (e->uses); ret->sets = sRefSet_newCopy (e->sets); ret->msets = sRefSet_newCopy (e->msets); } ret->val = multiVal_undefined; ret->kind = XPR_EMPTY; ret->sref = sRef_undefined; ret->etext = cstring_undefined; ret->exitCode = XK_NEVERESCAPE; ret->canBreak = FALSE; ret->mustBreak = FALSE; ret->isJumpPoint = FALSE; ret->edata = exprData_undefined; exprNode_defineConstraints(ret); return (ret); } static /*@notnull@*/ /*@special@*/ exprNode exprNode_createSemiCopy (exprNode e) /*@defines result@*/ /*@post:isnull result->edata, result->etext, result->sets, result->msets, result->uses, result->guards@*/ /*@*/ { if (exprNode_isError (e)) { return exprNode_createPlain (ctype_unknown); } else { exprNode ret = exprNode_new (); ret->typ = e->typ; ret->val = multiVal_copy (e->val); ret->loc = fileloc_copy (e->loc); ret->guards = guardSet_undefined; ret->uses = sRefSet_undefined; ret->sets = sRefSet_undefined; ret->msets = sRefSet_undefined; ret->kind = XPR_EMPTY; ret->sref = sRef_undefined; ret->etext = cstring_undefined; ret->exitCode = XK_NEVERESCAPE; ret->canBreak = FALSE; ret->mustBreak = FALSE; ret->isJumpPoint = FALSE; ret->edata = exprData_undefined; exprNode_defineConstraints(ret); return (ret); } } bool exprNode_isNullValue (exprNode e) { if (exprNode_isDefined (e)) { multiVal m = exprNode_getValue (e); if (multiVal_isInt (m)) { return (multiVal_forceInt (m) == 0); } } return FALSE; } static bool exprNode_isUnknownConstant (/*@notnull@*/ exprNode e) { while (e->kind == XPR_PARENS) { e = exprData_getUopNode (e->edata); if (!exprNode_isDefined (e)) { return FALSE; } /* evans 2002-02-05: was llassert (exprNode_isDefined (e)); but this can fail */ } if (e->kind == XPR_CONST) { multiVal m = exprNode_getValue (e); if (multiVal_isUnknown (m)) { return TRUE; } } return FALSE; } /*@only@*/ exprNode exprNode_numLiteral (ctype c, /*@temp@*/ cstring t, /*@only@*/ fileloc loc, long val) { exprNode e = exprNode_createLoc (c, loc); e->kind = XPR_NUMLIT; llassert (multiVal_isUndefined (e->val)); e->val = multiVal_makeInt (val); e->edata = exprData_makeLiteral (cstring_copy (t)); if (val == 0) { e->sref = sRef_makeUnknown (); sRef_setDefNull (e->sref, e->loc); } DPRINTF (("Num lit: %s / %s", exprNode_unparse (e), ctype_unparse (exprNode_getType (e)))); return (e); } /*@only@*/ exprNode exprNode_charLiteral (char c, cstring text, /*@only@*/ fileloc loc) { exprNode e = exprNode_createLoc (ctype_char, loc); if (context_getFlag (FLG_CHARINTLITERAL)) { e->typ = ctype_makeConj (ctype_char, ctype_int); } e->kind = XPR_NUMLIT; e->val = multiVal_makeChar (c); e->edata = exprData_makeLiteral (cstring_copy (text)); return (e); } /*@only@*/ exprNode exprNode_floatLiteral (double d, ctype ct, cstring text, /*@only@*/ fileloc loc) { exprNode e = exprNode_createLoc (ct, loc); e->kind = XPR_NUMLIT; e->val = multiVal_makeDouble (d); e->edata = exprData_makeLiteral (cstring_copy (text)); return (e); } multiVal exprNode_getValue (exprNode e) { while (exprNode_isInParens (e)) { if (e->edata != NULL) { e = exprData_getUopNode (e->edata); } else { break; } } if (exprNode_isDefined (e)) { return e->val; } else { return multiVal_undefined; } } /*@only@*/ exprNode exprNode_combineLiterals (exprNode e, exprNode rest) { cstring ns; /* Both must be string literals. */ if (exprNode_isUndefined (rest) || exprNode_isUndefined (e)) { exprNode_free (rest); return e; } if (!exprNode_isStringLiteral (e)) { voptgenerror (FLG_SYNTAX, message ("Constant concatentation is ungrammatical: %s %s", exprNode_unparse (e), exprNode_unparse (rest)), e->loc); exprNode_free (rest); return e; } if (!exprNode_isStringLiteral (rest)) { voptgenerror (FLG_SYNTAX, message ("Constant concatentation is ungrammatical: %s %s", exprNode_unparse (e), exprNode_unparse (rest)), rest->loc); exprNode_free (rest); return e; } ns = cstring_concat (multiVal_forceString (exprNode_getValue (e)), multiVal_forceString (exprNode_getValue (rest))); multiVal_free (e->val); exprData_free (e->edata, e->kind); e->edata = exprData_makeLiteral (cstring_copy (ns)); e->val = multiVal_makeString (ns); exprNode_free (rest); return e; } /*@only@*/ exprNode exprNode_rawStringLiteral (/*@only@*/ cstring t, /*@only@*/ fileloc loc) { exprNode e = exprNode_createLoc (ctype_string, loc); size_t len = cstring_length (t); if (context_getFlag (FLG_STRINGLITERALLEN)) { if (len > size_fromInt (context_getValue (FLG_STRINGLITERALLEN))) { voptgenerror (FLG_STRINGLITERALLEN, message ("String literal length (%d) exceeds maximum " "length (%d): \"%s\"", size_toInt (len), context_getValue (FLG_STRINGLITERALLEN), t), e->loc); } } e->kind = XPR_STRINGLITERAL; e->val = multiVal_makeString (cstring_copy (t)); e->edata = exprData_makeLiteral (t); e->sref = sRef_makeConst (ctype_string); if (context_getFlag (FLG_READONLYSTRINGS)) { sRef_setAliasKind (e->sref, AK_STATIC, fileloc_undefined); sRef_setExKind (e->sref, XO_OBSERVER, loc); } else { sRef_setAliasKind (e->sref, AK_ERROR, fileloc_undefined); } return (e); /* s released */ } /*@only@*/ exprNode exprNode_wideStringLiteral (/*@only@*/ cstring t, /*@only@*/ fileloc loc) { exprNode res = exprNode_stringLiteral (t, loc); res->typ = ctype_makeWideString (); return res; } /*@only@*/ exprNode exprNode_stringLiteral (/*@only@*/ cstring t, /*@only@*/ fileloc loc) { size_t len = size_fromInt (size_toInt (cstring_length (t)) - 2); char *ts = cstring_toCharsSafe (t); char *s = cstring_toCharsSafe (cstring_create (len + 1)); llassert (*ts == '\"' && *(ts + len + 1) == '\"'); strncpy (s, ts+1, len); *(s + len) = '\0'; cstring_free (t); return exprNode_rawStringLiteral (cstring_fromCharsO (s), loc); } exprNode exprNode_fromUIO (cstring c) { fileloc loc = context_getSaveLocation (); exprNode e = exprNode_createPlain (ctype_unknown); e->kind = XPR_VAR; if (fileloc_isUndefined (loc)) { loc = fileloc_copy (g_currentloc); } e->loc = loc; /* save loc was mangled */ e->sref = sRef_undefined; if (usymtab_exists (c)) { uentry ue = usymtab_lookupEither (c); if (uentry_isDatatype (ue) && uentry_isSpecified (ue)) { llfatalerror (message ("%q: Specified datatype %s used in code, but not defined. " "(Cannot continue reasonably from this error.)", fileloc_unparse (e->loc), c)); } else { BADBRANCH; } } llassertprint (!usymtab_exists (c), ("Entry exists: %s", c)); /* ** was supercedeGlobalEntry...is this better? */ if (!context_inIterEnd ()) { if (context_inMacro ()) { if (context_getFlag (FLG_UNRECOG)) { voptgenerror (FLG_MACROUNDEF, message ("Unrecognized identifier in macro definition: %s", c), e->loc); } else { flagcode_recordSuppressed (FLG_UNRECOG); } } else { voptgenerror (FLG_UNRECOG, message ("Unrecognized identifier: %s", c), e->loc); } } e->edata = exprData_makeId (uentry_makeUnrecognized (c, fileloc_copy (loc))); /* No alias errors for unrecognized identifiers */ sRef_setAliasKind (e->sref, AK_ERROR, loc); return (e); } exprNode exprNode_makeConstantString (cstring c, /*@only@*/ fileloc loc) { exprNode e = exprNode_createPlain (ctype_unknown); e->kind = XPR_VAR; e->loc = loc; e->sref = sRef_makeConst (ctype_string); e->edata = exprData_makeId (uentry_makeUnrecognized (c, fileloc_copy (loc))); e->typ = ctype_string; /* No alias errors for unrecognized identifiers */ sRef_setAliasKind (e->sref, AK_STATIC, loc); sRef_setExKind (e->sref, XO_OBSERVER, loc); return (e); } exprNode exprNode_createId (/*@observer@*/ uentry c) { if (uentry_isValid (c)) { exprNode e = exprNode_new (); DPRINTF (("create id: %s", uentry_unparse (c))); e->typ = uentry_getType (c); if (uentry_isFunction (c) && !sRef_isLocalVar (uentry_getSref (c))) { e->sref = sRef_undefined; } else { e->sref = uentry_getSref (c); } if (sRef_isStateUnknown (e->sref) && uentry_isNonLocal (c)) { sRef_setDefined (e->sref, fileloc_undefined); } /* ** yoikes! leaving this out was a heinous bug...that would have been ** caught if i had splint working first. gag! */ e->etext = cstring_undefined; if (uentry_isEitherConstant (c)) { e->kind = XPR_CONST; e->val = multiVal_copy (uentry_getConstantValue (c)); } else { e->kind = XPR_VAR; e->val = multiVal_unknown (); } e->edata = exprData_makeId (c); e->loc = context_getSaveLocation (); if (fileloc_isUndefined (e->loc)) { fileloc_free (e->loc); e->loc = fileloc_copy (g_currentloc); } e->guards = guardSet_new (); e->sets = sRefSet_new (); e->msets = sRefSet_new (); e->uses = sRefSet_new (); /*> missing fields, detected by splint <*/ e->exitCode = XK_NEVERESCAPE; e->isJumpPoint = FALSE; e->canBreak = FALSE; e->mustBreak = FALSE; exprNode_defineConstraints (e); return e; } else { return exprNode_createUnknown (); } } /*@notnull@*/ exprNode exprNode_fromIdentifier (/*@observer@*/ uentry c) { exprNode ret; if (context_justPopped ()) /* watch out! c could be dead */ { uentry ce = usymtab_lookupSafe (cscannerHelp_observeLastIdentifier ()); if (uentry_isValid (ce)) { c = ce; } else { llbuglit ("Looks like Aunt Millie forgot to walk to dog again."); } } ret = exprNode_fromIdentifierAux (c); return ret; } static void exprNode_checkStringLiteralLength (ctype t1, exprNode e2) { multiVal mval = exprNode_getValue (e2); cstring slit; size_t len; if (ctype_isFixedArray (t1)) { size_t nelements = ctype_getArraySize (t1); llassert (multiVal_isString (mval)); slit = multiVal_forceString (mval); len = cstring_lengthExpandEscapes (slit); llassert (exprNode_isDefined (e2)); if (len == nelements) { mstring temp; temp = cstring_expandEscapes (slit); if (temp[len-1] == '\0') { voptgenerror (FLG_STRINGLITNOROOMFINALNULL, message ("String literal with %d character%& " "is assigned to %s (no room for final null terminator): %s", size_toInt (len + 1), ctype_unparse (t1), exprNode_unparse (e2)), e2->loc); } else { voptgenerror (FLG_STRINGLITNOROOM, message ("String literal with %d character%& " "is assigned to %s (no room for null terminator): %s", size_toInt (len + 1), ctype_unparse (t1), exprNode_unparse (e2)), e2->loc); } } else if (len > nelements) { voptgenerror (FLG_STRINGLITTOOLONG, message ("String literal with %d character%& (counting null terminator) " "is assigned to %s (insufficient storage available): %s", size_toInt (len + 1), ctype_unparse (t1), exprNode_unparse (e2)), e2->loc); } else if (len < nelements - 1) { voptgenerror (FLG_STRINGLITSMALLER, message ("String literal with %d character%& is assigned to %s (possible waste of storage): %s", size_toInt (len + 1), ctype_unparse (t1), exprNode_unparse (e2)), e2->loc); } else { ; /* okay */ } } } static /*@only@*/ /*@notnull@*/ exprNode exprNode_fromIdentifierAux (/*@observer@*/ uentry c) { exprNode e = exprNode_createId (c); sRef sr = e->sref; uentry_setUsed (c, e->loc); if (uentry_isVar (c) && sRef_isFileOrGlobalScope (sr)) { checkGlobUse (c, FALSE, e); } return (e); } static bool exprNode_isZero (exprNode e) { if (exprNode_isDefined (e)) { multiVal m = exprNode_getValue (e); if (multiVal_isInt (m)) { return (multiVal_forceInt (m) == 0); } } return FALSE; } static bool exprNode_isNonNegative (exprNode e) { if (exprNode_isDefined (e)) { multiVal m = exprNode_getValue (e); if (multiVal_isInt (m)) { return (multiVal_forceInt (m) >= 0); } /* ** This is not always true if programmer defines enum ** values, but then the constant should be known. */ if (ctype_isEnum (ctype_realType (e->typ))) { return TRUE; } } return FALSE; } /* ** a[x] - uses a but NOT a[] ** result sref = a[] (set/use in assignment) ** ** The syntax x[a] is also legal in C, and has the same ** semantics. If ind is an array, and arr is an int, flip ** the arguments. */ /*@only@*/ exprNode exprNode_arrayFetch (/*@only@*/ exprNode e1, /*@only@*/ exprNode e2) { /* ** error in arr, error propagates (no new messages) ** error in ind, assume valid and continue */ DPRINTF (("Array fetch: %s / %s", exprNode_unparse (e1), exprNode_unparse (e2))); if (exprNode_isError (e1)) { exprNode_free (e2); return (exprNode_makeError ()); } else { exprNode arr; exprNode ind; ctype carr = exprNode_getType (e1); ctype crarr = ctype_realType (carr); /* ** this sets up funny aliasing, that leads to spurious ** splint errors. Hence, the i2 comments. */ /* evans 2001-09-09 added ctype_isKnown so there is no swap when e1 type is unknown */ if (ctype_isKnown (crarr) && !ctype_isRealArray (crarr) && ctype_isRealNumeric (crarr) && !exprNode_isError (e2) && ctype_isRealAP (exprNode_getType (e2))) /* fetch like 3[a] */ { arr = e2; ind = e1; carr = exprNode_getType (arr); crarr = ctype_realType (carr); } else { arr = e1; ind = e2; } DPRINTF (("arr: %s", exprNode_unparse (arr))); if (sRef_possiblyNull (arr->sref)) { if (!usymtab_isGuarded (arr->sref)) { if (!context_inSizeof() ) { if (optgenerror (FLG_NULLDEREF, message ("Index of %s pointer %q: %s", sRef_nullMessage (arr->sref), sRef_unparse (arr->sref), exprNode_unparse (arr)), arr->loc)) { DPRINTF (("ref: %s", sRef_unparseFull (arr->sref))); sRef_showNullInfo (arr->sref); /* suppress future messages */ sRef_setNullError (arr->sref); } } } } if (exprNode_isError (ind)) { if ((ctype_isArrayPtr (crarr) && !ctype_isFunction (crarr)) || ctype_isUnknown (carr)) { exprNode ret = exprNode_createPartialCopy (arr); if (ctype_isKnown (carr)) { ret->typ = ctype_baseArrayPtr (crarr); } else { ret->typ = ctype_unknown; } ret->sref = sRef_makeArrayFetch (arr->sref); ret->kind = XPR_FETCH; /* ** Because of funny aliasing (when arr and ind are ** flipped) spurious errors would be reported here. */ /*@i2@*/ ret->edata = exprData_makePair (arr, ind); checkSafeUse (ret, arr->sref); return (ret); } else { voptgenerror (FLG_TYPE, message ("Array fetch from non-array (%t): %s[%s]", carr, exprNode_unparse (e1), exprNode_unparse (e2)), arr->loc); exprNode_free (arr); return (exprNode_makeError ()); } } else { if (!ctype_isForceRealInt (&(ind->typ))) { ctype rt = ctype_realType (ind->typ); if (ctype_isChar (rt)) { vnoptgenerror (FLG_CHARINDEX, message ("Array fetch using non-integer, %t: %s[%s]", ind->typ, exprNode_unparse (e1), exprNode_unparse (e2)), arr->loc); } else if (ctype_isEnum (rt)) { vnoptgenerror (FLG_ENUMINDEX, message ("Array fetch using non-integer, %t: %s[%s]", ind->typ, exprNode_unparse (e1), exprNode_unparse (e2)), arr->loc); } else if (ctype_isNumAbstract (rt)) { vnoptgenerror (FLG_NUMABSTRACTINDEX, message ("Array fetch using numabstract type, %t: %s[%s]", ind->typ, exprNode_unparse (e1), exprNode_unparse (e2)), arr->loc); } else { voptgenerror (FLG_TYPE, message ("Array fetch using non-integer, %t: %s[%s]", ind->typ, exprNode_unparse (e1), exprNode_unparse (e2)), arr->loc); } multiVal_free (ind->val); ind->val = multiVal_unknown (); } if (ctype_isArrayPtr (crarr) && !ctype_isFunction (crarr)) { exprNode ret = exprNode_createSemiCopy (arr); multiVal m = exprNode_getValue (ind); ret->typ = ctype_baseArrayPtr (crarr); ret->kind = XPR_FETCH; if (multiVal_isInt (m)) { int i = (int) multiVal_forceInt (m); if (sRef_isValid (arr->sref)) { ret->sref = sRef_makeArrayFetchKnown (arr->sref, i); } else { ret->sref = sRef_undefined; } } else { ret->sref = sRef_makeArrayFetch (arr->sref); } ret->sets = sRefSet_realNewUnion (arr->sets, ind->sets); ret->msets = sRefSet_realNewUnion (arr->msets, ind->msets); ret->uses = sRefSet_realNewUnion (arr->uses, ind->uses); /* (see comment on spurious errors above) */ /*@i2@*/ ret->edata = exprData_makePair (arr, ind); exprNode_checkUse (ret, ind->sref, ind->loc); exprNode_checkUse (ret, arr->sref, arr->loc); return (ret); } else { if (ctype_isUnknown (carr)) { exprNode ret = exprNode_createPartialCopy (arr); ret->kind = XPR_FETCH; ret->typ = ctype_unknown; ret->sets = sRefSet_union (ret->sets, ind->sets); ret->msets = sRefSet_union (ret->msets, ind->msets); ret->uses = sRefSet_union (ret->uses, ind->uses); /* (see comment on spurious errors above) */ /*@i2@*/ ret->edata = exprData_makePair (arr, ind); exprNode_checkUse (ret, ind->sref, ind->loc); exprNode_checkUse (ret, arr->sref, arr->loc); return (ret); } else { voptgenerror (FLG_TYPE, message ("Array fetch from non-array (%t): %s[%s]", carr, exprNode_unparse (e1), exprNode_unparse (e2)), arr->loc); exprNode_free (arr); exprNode_free (ind); return (exprNode_makeError ()); } } } } BADEXIT; } static int checkArgs (uentry fcn, /*@dependent@*/ exprNode f, ctype t, exprNodeList args, exprNode ret) { return (checkArgsReal (fcn, f, ctype_argsFunction (t), args, FALSE, ret)); } /* ** checkPrintfArgs --- checks arguments for printf-like functions ** Arguments before ... have already been checked. ** The argument before the ... is a char *. ** argno is the format string argument. */ static void checkPrintfArgs (/*@notnull@*/ /*@dependent@*/ exprNode f, uentry fcn, exprNodeList args, exprNode ret, int argno) { /* ** the last argument before the elips is the format string */ int i = argno; fileloc formatloc; int nargs = exprNodeList_size (args); uentryList params = uentry_getParams (fcn); exprNode a; /* ** These should be ensured by checkSpecialFunction */ llassert (uentryList_size (params) == argno + 1); llassert (uentry_isElipsisMarker (uentryList_getN (params, argno))); a = exprNodeList_getN (args, argno - 1); formatloc = fileloc_copy (exprNode_loc (a)); if (exprNode_isDefined (a) && exprNode_isStringLiteral (a) && exprNode_knownStringValue (a)) { char *format = cstring_toCharsSafe (multiVal_forceString (exprNode_getValue (a))); char *code = format; char *ocode = code; nargs = exprNodeList_size (args); while ((code = strchr (code, '%')) != NULL) { char *origcode = code; cstring codetext = cstring_newEmpty (); char key = *(++code); ctype modtype = ctype_int; bool modified = FALSE; fileloc_addColumn (formatloc, code - ocode); codetext = cstring_appendChar (codetext, key); /* ignore flags */ while (isFlagKey (key)) { key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } if (key == 'm') /* skipped in syslog */ { continue; } /* ignore field width */ while (isdigit ((int) key) != 0) { key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } /* ignore precision */ if (key == '.') { key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); /* ** In printf, '*' means: read the next arg as an int for the ** field width. This seems to be missing from my copy of the ** standard x3.159-1989. Setion 4.9.6.1 refers to * (described ** later) but never does. */ if (key == '*') { ; /* don't do anything --- handle later */ } else { while (isdigit ((int) key) != 0) { key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } } } if (key == 'h') { modtype = ctype_sint; /* short */ key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } else if (key == 'l' || key == 'L') { modtype = ctype_lint; /* long */ key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); if (key == 'l' || key == 'L') { modtype = ctype_llint; /* long long */ key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } } else { ; /* no modifier */ } /* now, key = type of conversion to apply */ ++code; fileloc_incColumn (formatloc); if (key != '%') { if (i >= nargs) { if (optgenerror (FLG_TYPE, message ("No argument corresponding to %q format " "code %d (%%%s): \"%s\"", uentry_getName (fcn), i, codetext, cstring_fromChars (format)), f->loc)) { if (fileloc_isDefined (formatloc) && context_getFlag (FLG_SHOWCOL)) { llgenindentmsg (cstring_makeLiteral ("Corresponding format code"), formatloc); } } i++; } else { a = exprNodeList_getN (args, i); i++; if (!exprNode_isError (a)) { ctype expecttype; switch (key) { case '*': /* int argument for fieldwidth */ expecttype = ctype_int; *(--code) = '%'; /* convert it for next code */ fileloc_subColumn (formatloc, 1); /*@switchbreak@*/ break; case 'u': case 'o': expecttype = ctype_combine (ctype_uint, modtype); /*@switchbreak@*/ break; case 'i': /* int argument */ case 'd': expecttype = ctype_combine (ctype_int, modtype); /*@switchbreak@*/ break; case 'x': /* unsigned int */ case 'X': expecttype = ctype_combine (ctype_uint, modtype); /*@switchbreak@*/ break; case 'e': case 'E': case 'g': case 'G': case 'f': /* double */ expecttype = ctype_combine (ctype_double, modtype); /*@switchbreak@*/ break; case 'c': /* int converted to char (check its a char?) */ expecttype = ctype_makeConj (ctype_int, ctype_makeConj (ctype_char, ctype_uchar)); /* evans 2001-10-05 - changed to reflect correct ISO spec: int converted to char */ /*@switchbreak@*/ break; case 's': /* string */ expecttype = ctype_string; /*@switchbreak@*/ break; case '[': /* skip to ']' */ while (((key = *(++code)) != ']') && (key != '\0')) { codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } if (key == '\0') { llfatalerrorLoc (message ("Bad character set format: %s", cstring_fromChars (origcode))); } expecttype = ctype_string; /*@switchbreak@*/ break; case 'p': /* pointer */ expecttype = ctype_makePointer (ctype_void); /* need not be defined */ uentry_setDefState (regArg, SS_RELDEF); sRef_setPosNull (uentry_getSref (regArg), fileloc_undefined); /* could be null */ /*@switchbreak@*/ break; case 'n': /* pointer to int, modified by call! */ expecttype = ctype_combine (ctype_makePointer (ctype_int), modtype); modified = TRUE; uentry_setDefState (regArg, SS_ALLOCATED); /* corresponds to out */ /*@switchbreak@*/ break; case 'm': /* in a syslog, it doesn't consume an argument */ /* should check we're really doing syslog */ /*@switchbreak@*/ break; default: expecttype = ctype_unknown; voptgenerror (FLG_FORMATCODE, message ("Unrecognized format code: %s", cstring_fromChars (origcode)), fileloc_isDefined (formatloc) ? formatloc : g_currentloc); /*@switchbreak@*/ break; } if (!(exprNode_matchArgType (expecttype, a))) { if (ctype_isVoidPointer (expecttype) && ctype_isRealAbstract (a->typ) && (context_getFlag (FLG_ABSTVOIDP))) { ; } else { if (llgenformattypeerror (expecttype, exprNode_undefined, a->typ, a, message ("Format argument %d to %q (%%%s) expects " "%t gets %t: %s", i - argno, uentry_getName (fcn), codetext, expecttype, a->typ, exprNode_unparse (a)), a->loc)) { if (fileloc_isDefined (formatloc) && context_getFlag (FLG_SHOWCOL)) { llgenindentmsg (cstring_makeLiteral ("Corresponding format code"), formatloc); } } } } uentry_setType (regArg, expecttype); checkOneArg (regArg, a, f, FALSE, i+1, nargs); if (ctype_equal (expecttype, ctype_string)) { exprNode_checkUse (a, sRef_makePointer (a->sref), a->loc); } uentry_setType (regArg, ctype_unknown); uentry_fixupSref (regArg); if (modified) { exprNode_checkCallModifyVal (a->sref, args, f, ret); } } else { ; } } } ocode = code; cstring_free (codetext); } if (i < nargs) { voptgenerror (FLG_TYPE, message ("Format string for %q has %d arg%&, given %d", uentry_getName (fcn), i - argno, nargs - argno), f->loc); } } else { /* no checking possible for compile-time unknown format strings */ if (exprNode_isDefined (a)) { voptgenerror (FLG_FORMATCONST, message ("Format string parameter to %s is not a compile-time constant: %s", exprNode_unparse (f), exprNode_unparse (a)), f->loc); } } fileloc_free (formatloc); } static void checkScanfArgs (/*@notnull@*/ /*@dependent@*/ exprNode f, uentry fcn, exprNodeList args, exprNode ret, int argno) { int i = argno; fileloc formatloc; int nargs = exprNodeList_size (args); uentryList params = uentry_getParams (fcn); exprNode a; /* ** These should be ensured by checkSpecialFunction */ llassert (uentryList_size (params) == argno + 1); llassert (uentry_isElipsisMarker (uentryList_getN (params, argno))); a = exprNodeList_getN (args, argno - 1); formatloc = fileloc_copy (exprNode_loc (a)); if (exprNode_isDefined (a) && exprNode_isStringLiteral (a) && exprNode_knownStringValue (a)) { char *format = cstring_toCharsSafe (multiVal_forceString (exprNode_getValue (a))); char *code = format; char *ocode = code; nargs = exprNodeList_size (args); while ((code = strchr (code, '%')) != NULL) { char *origcode = code; char key = *(++code); cstring codetext = cstring_newEmpty (); ctype modtype = ctype_int; char modifier = '\0'; bool modified = TRUE; bool ignore = FALSE; codetext = cstring_appendChar (codetext, key); fileloc_addColumn (formatloc, code - ocode); /* ** this is based on ANSI standard library description of fscanf ** (from ANSI standard X3.159-1989, 4.9.6.1) */ /* '*' suppresses assignment (does not need match argument) */ if (key == '*') { key = *(++code); codetext = cstring_appendChar (codetext, key); modified = FALSE; ignore = TRUE; fileloc_incColumn (formatloc); } /* ignore field width */ while (isdigit ((int) key) != 0) { key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } if (key == 'h') { modtype = ctype_sint; /* short */ key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } else if (key == 'l' || key == 'L') { modtype = ctype_lint; /* long */ modifier = key; key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); if (key == 'l' || key == 'L') { modtype = ctype_llint; /* long long */ key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } } else { ; /* no modifier */ } /* now, key = type of conversion to apply */ ++code; fileloc_incColumn (formatloc); if (key != '%') { if (ignore) { ; } else { if (i >= nargs) { if (optgenerror (FLG_TYPE, message ("No argument corresponding to %q format " "code %d (%%%s): \"%s\"", uentry_getName (fcn), i, codetext, cstring_fromChars (format)), f->loc)) { if (fileloc_isDefined (formatloc) && context_getFlag (FLG_SHOWCOL)) { llgenindentmsg (cstring_makeLiteral ("Corresponding format code"), formatloc); } } i++; } else { a = exprNodeList_getN (args, i); i++; if (!exprNode_isError (a)) { ctype expecttype; switch (key) { case '*': /* int argument for fieldwidth */ expecttype = ctype_makePointer (ctype_int); *(--code) = '%'; /* convert it for next code */ fileloc_subColumn (formatloc, 1); /*@switchbreak@*/ break; case 'u': case 'o': expecttype = ctype_makePointer (ctype_combine (ctype_uint, modtype)); /*@switchbreak@*/ break; case 'i': case 'd': expecttype = ctype_makePointer (ctype_combine (ctype_int, modtype)); /*@switchbreak@*/ break; case 'x': case 'X': /* unsigned int */ expecttype = ctype_makePointer (ctype_combine (ctype_uint, modtype)); /*@switchbreak@*/ break; case 'e': case 'E': case 'g': case 'G': case 'f': /* printf is double, scanf is float! */ if (modifier == 'l') { expecttype = ctype_makePointer (ctype_double); } else if (modifier == 'L') { expecttype = ctype_makePointer (ctype_ldouble); } else { llassert (modifier == '\0'); expecttype = ctype_makePointer (ctype_float); } /*@switchbreak@*/ break; case 'c': /* int converted to char (check its a char?) */ expecttype = ctype_makePointer (ctype_makeConj (ctype_char, ctype_uchar)); /*@switchbreak@*/ break; case 's': /* string */ expecttype = ctype_string; /*@switchbreak@*/ break; case '[': /* skip to ']' */ while (((key = *(++code)) != ']') && (key != '\0')) { codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } if (key == '\0') { llfatalerrorLoc (message ("Bad character set format: %s", cstring_fromChars (origcode))); } expecttype = ctype_string; /*@switchbreak@*/ break; case 'p': /* pointer */ voptgenerror (FLG_FORMATCODE, message ("Format code should not be used in scanf: %s", cstring_fromChars (origcode)), fileloc_isDefined (formatloc) ? formatloc : g_currentloc); expecttype = ctype_unknown; /*@switchbreak@*/ break; case 'n': /* pointer to int, modified by call! */ expecttype = ctype_makePointer (ctype_int); /*@switchbreak@*/ break; default: expecttype = ctype_unknown; voptgenerror (FLG_FORMATCODE, message ("Unrecognized format code: %s", cstring_fromChars (origcode)), fileloc_isDefined (formatloc) ? formatloc : g_currentloc); /*@switchbreak@*/ break; } if (!(exprNode_matchArgType (expecttype, a))) { if (ctype_isVoidPointer (expecttype) && ctype_isRealAbstract (a->typ) && (context_getFlag (FLG_ABSTVOIDP))) { ; } else { if (llgenformattypeerror (expecttype, exprNode_undefined, a->typ, a, message ("Format argument %d to %q (%%%s) expects " "%t gets %t: %s", i - argno, uentry_getName (fcn), codetext, expecttype, a->typ, exprNode_unparse (a)), a->loc)) { if (fileloc_isDefined (formatloc) && context_getFlag (FLG_SHOWCOL)) { llgenindentmsg (cstring_makeLiteral ("Corresponding format code"), formatloc); } } } } uentry_setType (outArg, expecttype); checkOneArg (outArg, a, f, FALSE, i+1, nargs); uentry_setType (outArg, ctype_unknown); uentry_fixupSref (outArg); if (modified) { exprNode_checkCallModifyVal (a->sref, args, f, ret); } } else { /* a->sref = sRef_undefined; */ } } } } ocode = code; cstring_free (codetext); } if (i < nargs) { voptgenerror (FLG_TYPE, message ("Format string for %q has %d arg%&, given %d", uentry_getName (fcn), i - argno, nargs - argno), f->loc); } } else { /* no checking possible for compile-time unknown format strings */ } fileloc_free (formatloc); } static void checkMessageArgs (/*@notnull@*/ /*@dependent@*/ exprNode f, uentry fcn, exprNodeList args, /*@unused@*/ int argno) { /* ** the last argument before the elips is the format string */ int nargs = exprNodeList_size (args); int i = argno; fileloc formatloc; exprNode a; a = exprNodeList_getN (args, argno - 1); formatloc = fileloc_copy (exprNode_loc (a)); if (ctype_isUnknown (cstringType)) { if (usymtab_existsType (cstring_makeLiteralTemp ("cstring"))) { cstringType = usymtab_lookupAbstractType (cstring_makeLiteralTemp ("cstring")); } } if (ctype_isUnknown (ctypeType)) { if (usymtab_existsType (cstring_makeLiteralTemp ("ctype"))) { ctypeType = usymtab_lookupAbstractType (cstring_makeLiteralTemp ("ctype")); } } if (ctype_isUnknown (filelocType)) { if (usymtab_existsType (cstring_makeLiteralTemp ("fileloc"))) { filelocType = usymtab_lookupAbstractType (cstring_makeLiteralTemp ("fileloc")); } } if (exprNode_isDefined (a) && exprNode_isStringLiteral (a) && exprNode_knownStringValue (a)) { cstring format = multiVal_forceString (exprNode_getValue (a)); char *code = cstring_toCharsSafe (format); char *ocode = code; nargs = exprNodeList_size (args); while ((code = strchr (code, '%')) != NULL) { char *origcode = code; char key = *(++code); cstring codetext = cstring_newEmpty (); bool isOnly = FALSE; codetext = cstring_appendChar (codetext, key); fileloc_addColumn (formatloc, code - ocode); while (key >= '0' && key <= '9') { key = *(++code); codetext = cstring_appendChar (codetext, key); fileloc_incColumn (formatloc); } ++code; fileloc_incColumn (formatloc); if (key != '%') { if (key == '&') /* plural marker */ { goto nextKey; } if (i >= nargs) { voptgenerror (FLG_TYPE, message ("Message missing format arg %d (%%%s): \"%s\"", i + 1, codetext, format), f->loc); i++; } else { a = exprNodeList_getN (args, i); i++; nextKey: if (!exprNode_isError (a)) { ctype expecttype; /*@-loopswitchbreak@*/ switch (key) { case 'c': case 'h': expecttype = ctype_char; break; case 's': expecttype = cstringType; break; case 'q': expecttype = cstringType; isOnly = TRUE; break; case 'x': expecttype = cstringType; isOnly = TRUE; break; case 'd': expecttype = ctype_int; break; case 'u': expecttype = ctype_uint; break; case 'w': expecttype = ctype_ulint; break; case 'f': expecttype = ctype_float; break; case 'b': expecttype = ctype_bool; break; case 't': expecttype = ctypeType; break; case 'p': expecttype = ctype_makePointer (ctype_void); /* need not be defined */ uentry_setDefState (regArg, SS_RELDEF); sRef_setPosNull (uentry_getSref (regArg), fileloc_undefined); /* could be null */ /*@switchbreak@*/ break; case 'l': expecttype = filelocType; break; case '&': /* a wee bit of a hack methinks */ expecttype = ctype_int; break; case 'r': expecttype = ctype_bool; break; default: expecttype = ctype_unknown; voptgenerror (FLG_FORMATCODE, message ("Unrecognized format code: %s", cstring_fromChars (origcode)), fileloc_isDefined (formatloc) ? formatloc : g_currentloc); break; } /*@=loopswitchbreak@*/ if (!(exprNode_matchArgType (expecttype, a))) { if (ctype_isVoidPointer (expecttype) && ctype_isRealAbstract (a->typ) && (context_getFlag (FLG_ABSTVOIDP))) { ; } else { if (llgenformattypeerror (expecttype, exprNode_undefined, a->typ, a, message ("Format argument %d to %q (%%%s) expects " "%t gets %t: %s", i - argno, uentry_getName (fcn), codetext, expecttype, a->typ, exprNode_unparse (a)), a->loc)) { if (fileloc_isDefined (formatloc) && context_getFlag (FLG_SHOWCOL)) { llgenindentmsg (cstring_makeLiteral ("Corresponding format code"), formatloc); } } } } if (ctype_equal (expecttype, cstringType)) { if (isOnly) { checkOneArg (csOnlyArg, a, f, FALSE, i+1, nargs); uentry_fixupSref (csOnlyArg); } else { checkOneArg (csArg, a, f, FALSE, i+1, nargs); uentry_fixupSref (csArg); } } else { checkOneArg (regArg, a, f, FALSE, i+1, nargs); uentry_fixupSref (regArg); } } } } cstring_free (codetext); } if (i < nargs) { voptgenerror (FLG_TYPE, message ("Format string for %q has %d arg%&, given %d", uentry_getName (fcn), i - argno, nargs -argno), f->loc); } } else { /* no checking possible for compile-time unknown format strings */ } fileloc_free (formatloc); } static void checkExpressionDefinedAux (/*@notnull@*/ exprNode e1, /*@notnull@*/ exprNode e2, sRefSet sets1, sRefSet sets2, lltok op, flagcode flag) { bool hadUncon = FALSE; if (sRef_isFileOrGlobalScope (sRef_getRootBase (e1->sref)) && sRefSet_hasUnconstrained (sets2)) { voptgenerror (FLG_EVALORDERUNCON, message ("Expression may have undefined behavior (%q used in right operand " "may set global variable %q used in left operand): %s %s %s", sRefSet_unparseUnconstrained (sets2), sRef_unparse (sRef_getRootBase (e1->sref)), exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } if (sRef_isFileOrGlobalScope (sRef_getRootBase (e2->sref)) && sRefSet_hasUnconstrained (sets1)) { voptgenerror (FLG_EVALORDERUNCON, message ("Expression has undefined behavior (%q used in left operand " "may set global variable %q used in right operand): %s %s %s", sRefSet_unparseUnconstrained (sets1), sRef_unparse (e2->sref), exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } sRefSet_realElements (e1->uses, sr) { if (sRef_isMeaningful (sr) && sRefSet_member (sets2, sr)) { voptgenerror (FLG_EVALORDER, message ("Expression has undefined behavior (left operand uses %q, " "modified by right operand): %s %s %s", sRef_unparse (sr), exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } } end_sRefSet_realElements; sRefSet_realElements (sets1, sr) { if (sRef_isMeaningful (sr)) { if (sRef_same (sr, e2->sref)) { voptgenerror (flag, message ("Expression has undefined behavior (value of right operand " "modified by left operand): %s %s %s", exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } else if (sRefSet_member (e2->uses, sr)) { voptgenerror (flag, message ("Expression has undefined behavior (left operand modifies %q, " "used by right operand): %s %s %s", sRef_unparse (sr), exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } else { if (sRefSet_member (sets2, sr)) { if (sRef_isUnconstrained (sr)) { if (hadUncon) { ; } else { hadUncon = optgenerror (FLG_EVALORDERUNCON, message ("Expression may have undefined behavior. Left operand " "calls %q; right operand calls %q. The unconstrained " "functions may modify global state used by " "the other operand): %s %s %s", sRefSet_unparseUnconstrained (sets1), sRefSet_unparseUnconstrained (sets2), exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } } else { voptgenerror (flag, message ("Expression has undefined behavior (both " "operands modify %q): %s %s %s", sRef_unparse (sr), exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } } } } } end_sRefSet_realElements; } static void checkExpressionDefined (exprNode e1, exprNode e2, lltok op) { bool hasError = FALSE; if (exprNode_isError (e1) || exprNode_isError (e2)) { return; } if (sRefSet_member (e2->sets, e1->sref)) { if (e2->kind == XPR_CALL) { ; } else { hasError = optgenerror (FLG_EVALORDER, message ("Expression has undefined behavior " "(value of left operand %s is modified " "by right operand %s): %s %s %s", exprNode_unparse (e1), exprNode_unparse (e2), exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } } if (context_getFlag (FLG_EVALORDERUNCON)) { if (sRefSet_member (e2->msets, e1->sref)) { if (e2->kind == XPR_CALL) { ; } else { hasError = optgenerror (FLG_EVALORDER, message ("Expression has undefined behavior (value of left " "operand may be modified by right operand): %s %s %s", exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } } } if (!hasError) { checkExpressionDefinedAux (e1, e2, e1->sets, e2->sets, op, FLG_EVALORDER); if (context_maybeSet (FLG_EVALORDERUNCON)) { checkExpressionDefinedAux (e1, e2, e1->msets, e2->msets, op, FLG_EVALORDERUNCON); } } } static void checkSequencing (exprNode p_f, exprNodeList p_args); static int checkArgsReal (uentry fcn, /*@dependent@*/ exprNode f, uentryList cl, exprNodeList args, bool isIter, exprNode ret) { int special = 0; if (!exprNode_isError (f)) { if (!uentryList_isMissingParams (cl)) { int nargs = exprNodeList_size (args); int expectargs = uentryList_size (cl); ctype last; int i = 0; if (expectargs == 0) { if (nargs != 0) { if (isIter) { voptgenerror (FLG_TYPE, message ("Iter %q invoked with %d args, " "declared void", uentry_getName (fcn), nargs), f->loc); } else { voptgenerror (FLG_TYPE, message ("Function %s called with %d args, " "declared void", exprNode_unparse (f), nargs), f->loc); } } return special; } last = uentry_getType (uentryList_getN (cl, expectargs - 1)); exprNodeList_reset (args); uentryList_elements (cl, current) { ctype ct = uentry_getType (current); exprNode a; if (ctype_isElips (ct)) { /* ** do special checking for printf/scanf library functions ** ** this is kludgey code, just for handling the special case ** */ if (uentry_isPrintfLike (fcn)) { checkPrintfArgs (f, fcn, args, ret, i); special = i; } else if (uentry_isScanfLike (fcn)) { checkScanfArgs (f, fcn, args, ret, i); special = i; } else if (uentry_isMessageLike (fcn)) { checkMessageArgs (f, fcn, args, i); special = i; } else { llassert (!uentry_isSpecialFunction (fcn)); } nargs = expectargs; /* avoid errors */ break; } else { if (i >= nargs) break; a = exprNodeList_current (args); exprNodeList_advance (args); i++; if (exprNode_isError (a)) { ; } else { /* probably necessary? I'm not sure about this one checkMacroParen (a); */ f->guards = guardSet_union (f->guards, a->guards); DPRINTF (("match arg: %s / %s", ctype_unparse (ct), ctype_unparse (a->typ))); if (!(exprNode_matchArgType (ct, a))) { DPRINTF (("Args mismatch!")); if (ctype_isVoidPointer (ct) && (ctype_isPointer (a->typ) && (ctype_isRealAbstract (ctype_baseArrayPtr (a->typ))))) { vnoptgenerror (FLG_ABSTVOIDP, message ("Pointer to abstract type (%t) used " "as void pointer " "(arg %d to %q): %s", a->typ, i, uentry_getName (fcn), exprNode_unparse (a)), a->loc); } else { if (isIter) { (void) gentypeerror (ct, exprNode_undefined, a->typ, a, message ("Iter %q expects arg %d to " "be %t gets %t: %s", uentry_getName (fcn), i, ct, a->typ, exprNode_unparse (a)), a->loc); } else { if (gentypeerror (ct, exprNode_undefined, a->typ, a, message ("Function %q expects arg %d to be %t gets %t: %s", uentry_getName (fcn), i, ct, a->typ, exprNode_unparse (a)), a->loc)) { DPRINTF (("Types: %s / %s", ctype_unparse (ct), ctype_unparse (a->typ))); } /* ** Clear null marker for abstract types. ** (It is not revealed, so suppress future messages.) */ if (ctype_isAbstract (a->typ)) { sRef_setNullUnknown (exprNode_getSref (a), a->loc); } } } } } } } end_uentryList_elements ; if (expectargs != nargs) /* note: not != since we may have ... */ { if (ctype_isElips (last)) { voptgenerror (FLG_TYPE, message ("Function %s called with %d args, expects at least %d", exprNode_unparse (f), nargs, expectargs - 1), f->loc); } else { if (isIter) { voptgenerror (FLG_TYPE, message ("Iter %q invoked with %d args, expects %d", uentry_getName (fcn), nargs, expectargs), f->loc); } else { voptgenerror (FLG_TYPE, message ("Function %s called with %d args, expects %d", exprNode_unparse (f), nargs, expectargs), f->loc); } } } } } return special; } /* ** Check for undefined code sequences in function arguments: ** ** one parameter sets something used by another parameter ** one parameter sets something set by another parameter */ static void checkSequencingOne (exprNode f, exprNodeList args, /*@notnull@*/ exprNode el, int argno) { /* ** Do second loop, iff +undefunspec */ int checkloop; int numloops = context_maybeSet (FLG_EVALORDERUNCON) ? 2 : 1; for (checkloop = 0; checkloop < numloops; checkloop++) { sRefSet thissets; if (checkloop == 0) { thissets = el->sets; } else { llassert (checkloop == 1); thissets = el->msets; } sRefSet_realElements (thissets, thisset) { int j; /*@access exprNodeList@*/ for (j = 0; j < args->nelements; j++) { exprNode jl = args->elements[j]; int thisargno = j + 1; if (thisargno != argno && exprNode_isDefined (jl)) { sRefSet otheruses = jl->uses; if (sRef_isFileOrGlobalScope (sRef_getRootBase (jl->sref)) && sRefSet_hasUnconstrained (thissets)) { voptgenerror (FLG_EVALORDERUNCON, /*@-sefparams@*/ message ("%q used in argument %d may set " "global variable %q used by argument %d: %s(%q)", cstring_capitalizeFree (sRefSet_unparseUnconstrained (thissets)), /*@=sefparams@*/ argno, sRef_unparse (sRef_getRootBase (jl->sref)), thisargno, exprNode_unparse (f), exprNodeList_unparse (args)), el->loc); } if (sRefSet_member (otheruses, thisset)) { if (sRef_isUnconstrained (thisset)) { voptgenerror (FLG_EVALORDERUNCON, message ("Unconstrained functions used in arguments %d (%q) " "and %d (%s) may modify " "or use global state in undefined way: %s(%q)", argno, sRefSet_unparseUnconstrainedPlain (otheruses), thisargno, sRef_unconstrainedName (thisset), exprNode_unparse (f), exprNodeList_unparse (args)), el->loc); } else { voptgenerror (FLG_EVALORDER, message ("Argument %d modifies %q, used by argument %d " "(order of evaluation of actual parameters is " "undefined): %s(%q)", argno, sRef_unparse (thisset), thisargno, exprNode_unparse (f), exprNodeList_unparse (args)), el->loc); } } else { sRefSet othersets = jl->sets; if (sRefSet_member (othersets, thisset)) { if (sRef_isUnconstrained (thisset)) { voptgenerror (FLG_EVALORDERUNCON, message ("Unconstrained functions used in " "arguments %d (%q) and %d (%s) may modify " "or use global state in undefined way: %s(%q)", argno, sRefSet_unparseUnconstrainedPlain (othersets), thisargno, sRef_unconstrainedName (thisset), exprNode_unparse (f), exprNodeList_unparse (args)), el->loc); } else { voptgenerror (FLG_EVALORDER, message ("Argument %d modifies %q, set by argument %d (order of" " evaluation of actual parameters is undefined): %s(%q)", argno, sRef_unparse (thisset), thisargno, exprNode_unparse (f), exprNodeList_unparse (args)), el->loc); } } } } } /*@noaccess exprNodeList@*/ } end_sRefSet_realElements; } } static void checkSequencing (exprNode f, exprNodeList args) { if (exprNodeList_size (args) > 1) { int i; exprNode el; /*@access exprNodeList*/ for (i = 0; i < args->nelements; i++) { el = args->elements[i]; if (!exprNode_isError (el)) { checkSequencingOne (f, args, el, i + 1); } } /*@noaccess exprNodeList*/ } } /* ** requires le = exprNode_getUentry (f) */ static void checkGlobMods (/*@notnull@*/ /*@dependent@*/ exprNode f, uentry le, exprNodeList args, /*@notnull@*/ exprNode ret, int specialArgs) { bool isSpec = FALSE; bool hasMods = FALSE; cstring fname; globSet usesGlobs = globSet_undefined; sRefSet mods = sRefSet_undefined; bool freshMods = FALSE; uentryList params = uentryList_undefined; DPRINTF (("Check glob mods: %s", exprNode_unparse (ret))); /* ** check globals and modifies */ setCodePoint (); if (!uentry_isValid (le)) { ctype fr = ctype_realType (f->typ); if (ctype_isFunction (fr)) { params = ctype_argsFunction (fr); } else { params = uentryList_missingParams; } if (!context_getFlag (FLG_MODNOMODS) && !context_getFlag (FLG_GLOBUNSPEC)) { checkUnspecCall (f, params, args); } return; } fname = uentry_rawName (le); setCodePoint (); if (uentry_isFunction (le)) { params = uentry_getParams (le); mods = uentry_getMods (le); hasMods = uentry_hasMods (le); usesGlobs = uentry_getGlobs (le); isSpec = uentry_isSpecified (le); } else /* not a function */ { ctype ct = ctype_realType (uentry_getType (le)); llassertprint (uentry_isVar (le) && ctype_isFunction (ct), ("checkModGlobs: uentry not a function: %s", uentry_unparse (le))); params = ctype_argsFunction (ct); return; /* No checking for non-function */ } /* ** check globals */ setCodePoint (); globSet_allElements (usesGlobs, el) { if (sRef_isValid (el)) { if (sRef_isInternalState (el) || sRef_isSystemState (el)) { context_usedGlobal (el); exprNode_checkUse (f, el, f->loc); if (context_checkInternalUse ()) { if (!context_globAccess (el)) { if (sRef_isSystemState (el) && !context_getFlag (FLG_MODFILESYSTEM)) { ; } else { voptgenerror (FLG_INTERNALGLOBS, message ("Called procedure %s may access %q, but " "globals list does not include globals %s", exprNode_unparse (f), sRef_unparse (el), cstring_makeLiteralTemp (sRef_isInternalState (el) ? "internalState" : "fileSystem")), f->loc); } } } } else if (sRef_isNothing (el) || sRef_isSpecState (el)) { ; } else { uentry gle = sRef_getUentry (el); sRef sr = sRef_updateSref (el); if (sRef_isUndefGlob (el)) { sRef_setDefined (sr, f->loc); exprNode_checkSet (f, sr); } else { /* ** check definition */ if (sRef_isAllocated (el)) { exprNode_checkSet (f, sr); } else { if (sRef_isStateUndefined (sr)) { voptgenerror (FLG_GLOBSTATE, message ("%s %q used by function undefined before call: %s", sRef_getScopeName (sr), sRef_unparse (sr), exprNode_unparse (f)), f->loc); sRef_setDefined (sr, f->loc); } exprNode_checkUse (f, sr, f->loc); } checkGlobUse (gle, TRUE, f); } if (sRef_isKilledGlob (el)) { sRef_kill (sr, f->loc); context_usedGlobal (sr); } } } } end_globSet_allElements; /* ** check modifies */ if (context_hasMods () || context_getFlag (FLG_MODNOMODS)) { sRefSet smods = sRefSet_undefined; /* ** NEED to check for modifies anything */ /* ** check each sRef that called function modifies (ml), is ** modifiable by tl */ setCodePoint (); sRefSet_allElements (mods, s) /* s is something which may be modified */ { DPRINTF (("Check modify: %s", sRef_unparse (s))); if (sRef_isKindSpecial (s)) { if (sRef_isSpecInternalState (s)) { if (context_getFlag (FLG_MODINTERNALSTRICT)) { exprNode_checkCallModifyVal (s, args, f, ret); } else { sRefSet mmods = context_modList (); sRefSet_allElements (mmods, el) { if (sRef_isInternalState (el)) { sRef_setModified (el); } } end_sRefSet_allElements ; } } else { exprNode_checkCallModifyVal (s, args, f, ret); } } else { sRef rb = sRef_getRootBase (s); if (sRef_isFileOrGlobalScope (rb)) { context_usedGlobal (rb); } if (sRef_isFileStatic (s) && !fileId_equal (fileloc_fileId (f->loc), fileloc_fileId (uentry_whereDefined (le)))) { smods = sRefSet_insert (smods, s); } else { exprNode_checkCallModifyVal (s, args, f, ret); } } } end_sRefSet_allElements; setCodePoint (); /* ** Static elements in modifies set can have nasty consequences. ** (I think...have not been able to reproduce a possible bug.) */ if (!sRefSet_isDefined (smods)) { mods = sRefSet_newCopy (mods); freshMods = TRUE; sRefSet_allElements (smods, el) { bool res = sRefSet_delete (mods, el); llassert (res); } end_sRefSet_allElements; sRefSet_free (smods); /*@-branchstate@*/ } /*@=branchstate@*/ } else if (sRefSet_isDefined (mods)) { /* just check observers */ setCodePoint (); sRefSet_allElements (mods, s) /* s is something which may be modified */ { sRef rb = sRef_getRootBase (s); setCodePoint (); if (sRef_isParam (rb)) { sRef b = sRef_fixBaseParam (s, args); if (sRef_isObserver (b)) { exprNode e = exprNodeList_nth (args, sRef_getParam (rb)); if (optgenerror (FLG_MODOBSERVER, message ("Function call may modify observer%q: %s", sRef_unparsePreOpt (b), exprNode_unparse (e)), exprNode_loc (e))) { sRef_showExpInfo (b); } } } } end_sRefSet_allElements; } else { if (!hasMods) /* no specified modifications */ { if (context_getFlag (FLG_MODOBSERVERUNCON)) { exprNodeList_elements (args, e) { if (exprNode_isDefined (e)) { sRef s = exprNode_getSref (e); if (sRef_isObserver (s) && ctype_isMutable (sRef_getType (s))) { if (optgenerror (FLG_MODOBSERVERUNCON, message ("Call to unconstrained function %s may modify observer%q: %s", exprNode_unparse (f), sRef_unparsePreOpt (s), exprNode_unparse (e)), exprNode_loc (e))) { sRef_showExpInfo (s); } } } } end_exprNodeList_elements; } } } checkAnyCall (f, fname, params, args, hasMods, mods, isSpec, specialArgs); ret->uses = sRefSet_union (ret->uses, f->uses); ret->sets = sRefSet_union (ret->sets, f->sets); ret->msets = sRefSet_union (ret->msets, f->msets); if (freshMods) { /* ** Spurious errors reported, because splint can't tell ** mods must be fresh if freshMods is true. */ /*@i@*/ sRefSet_free (mods); } setCodePoint (); } void checkGlobUse (uentry glob, bool isCall, /*@notnull@*/ exprNode e) { if (uentry_isVar (glob)) { if (context_inFunctionLike ()) { sRef sr = uentry_getSref (glob); context_usedGlobal (sr); if (context_checkGlobUse (glob)) { if (!context_globAccess (sr)) { if (isCall) { voptgenerror (FLG_GLOBALS, message ("Called procedure %s may access %s %q", exprNode_unparse (e), sRef_unparseScope (sr), uentry_getName (glob)), e->loc); } else { voptgenerror (FLG_GLOBALS, message ("Undocumented use of %s %s", sRef_unparseScope (sr), exprNode_unparse (e)), e->loc); } } } } } else { llbug (message ("Global not variable: %q", uentry_unparse (glob))); } } static void reflectEnsuresClause (exprNode ret, uentry le, exprNode f, exprNodeList args) { DPRINTF (("Reflect ensures clause: %s(%s) / %s / %s", exprNode_unparse (f), exprNodeList_unparse (args), uentry_unparseFull (le), stateClauseList_unparse (uentry_getStateClauseList (le)))); if (uentry_isValid (le) && uentry_isFunction (le)) { stateClauseList sclauses = uentry_getStateClauseList (le); if (stateClauseList_isDefined (sclauses)) { DPRINTF (("Reflect ensures: %s / %s / %s", uentry_unparse (le), exprNode_unparse (f), exprNodeList_unparse (args))); stateClauseList_elements (sclauses, cl) { if (stateClause_hasEnsures (cl)) { /* Same in usymtab.c:1904 */ if (stateClause_setsMetaState (cl)) { qual q = stateClause_getMetaQual (cl); annotationInfo ainfo = qual_getAnnotationInfo (q); metaStateInfo minfo = annotationInfo_getState (ainfo); cstring key = metaStateInfo_getName (minfo); int mvalue = annotationInfo_getValue (ainfo); sRefSet osrs = sRefSet_undefined; sRefSet srs; if (stateClause_isGlobal (cl)) { srs = sRefSet_single (usymtab_lookupGlobalMarker ()); osrs = srs; } else { srs = stateClause_getRefs (cl); } DPRINTF (("Reflect ensures clause: %s", stateClause_unparse (cl))); DPRINTF (("Sets meta state! %s", stateClause_unparse (cl))); sRefSet_elements (srs, sel) { sRef s; if (sRef_isResult (sRef_getRootBase (sel))) { s = exprNode_getSref (ret); } else { s = sRef_fixBaseParam (sel, args); } DPRINTF (("Reflecting state clause on: %s / %s", sRef_unparse (sel), sRef_unparse (s))); sRef_setMetaStateValueComplete (s, key, mvalue, exprNode_loc (f)); } end_sRefSet_elements; sRefSet_free (osrs); } else { sRefSet srs = stateClause_getRefs (cl); sRefModVal modf = stateClause_getEnsuresFunction (cl); int eparam = stateClause_getStateParameter (cl); llassert (modf != NULL); DPRINTF (("Reflect after clause: %s / %s", stateClause_unparse (cl), sRefSet_unparse (srs))); sRefSet_elements (srs, sel) { sRef s; DPRINTF (("elements: %s", sRef_unparse (sel))); DPRINTF (("elements: %s", sRef_unparseFull (sel))); if (sRef_isResult (sRef_getRootBase (sel))) { DPRINTF (("Fix base: %s / %s", sRef_unparse (sel), sRef_unparse (exprNode_getSref (ret)))); s = sRef_fixBase (sel, exprNode_getSref (ret)); DPRINTF (("==> %s", sRef_unparseFull (s))); } else { s = sRef_fixBaseParam (sel, args); } DPRINTF (("elements: %s", sRef_unparse (s))); DPRINTF (("elements: %s", sRef_unparseFull (s))); DPRINTF (("Reflecting state clause on: %s / %s", sRef_unparseFull (sel), sRef_unparseFull (s))); /* evans 2001-08-24 - added aliasSetCompleteParam */ sRef_aliasSetCompleteParam (modf, s, eparam, exprNode_loc (f)); DPRINTF (("After reflecting state clause on: %s / %s", sRef_unparseFull (sel), sRef_unparseFull (s))); } end_sRefSet_elements; } } } end_stateClauseList_elements ; } DPRINTF (("Here: %s / %s", uentry_unparseFull (le), bool_unparse (uentry_hasMetaStateEnsures (le)))); if (uentry_hasMetaStateEnsures (le)) { fileloc loc = exprNode_loc (f); metaStateConstraintList mscl = uentry_getMetaStateEnsures (le); metaStateConstraintList_elements (mscl, msc) { metaStateSpecifier msspec = metaStateConstraint_getSpecifier (msc); metaStateInfo msinfo = metaStateSpecifier_getMetaStateInfo (msspec); metaStateExpression msexpr = metaStateConstraint_getExpression (msc); cstring key = metaStateInfo_getName (msinfo); sRef mlsr = metaStateSpecifier_getSref (msspec); sRef s; sRef lastref = sRef_undefined; stateValue sval = stateValue_undefined; DPRINTF (("Meta state constraint for %s: %s", uentry_unparse (le), metaStateConstraint_unparse (msc))); DPRINTF (("Matches left: %s", sRef_unparseDebug (mlsr))); if (sRef_isResult (sRef_getRootBase (mlsr))) { s = exprNode_getSref (ret); } else { s = sRef_fixBaseParam (mlsr, args); } DPRINTF (("Setting state: %s", sRef_unparseFull (s))); while (metaStateExpression_isDefined (msexpr)) { metaStateSpecifier ms = metaStateExpression_getSpecifier (msexpr); metaStateInfo msi = metaStateSpecifier_getMetaStateInfo (ms); sRef msr, fs; DPRINTF (("Check expression: %s", metaStateExpression_unparse (msexpr))); if (metaStateExpression_isMerge (msexpr)) { msexpr = metaStateExpression_getRest (msexpr); } else { msexpr = metaStateExpression_undefined; } if (metaStateInfo_isDefined (msi)) { /* Must match lhs state */ llassert (metaStateInfo_equal (msinfo, msi)); } if (metaStateSpecifier_isElipsis (ms)) { /* ** For elipsis, we need to merge all the relevant elipsis parameters ** */ uentryList params = uentry_getParams (le); int paramno = uentryList_size (params) - 1; if (!uentry_isElipsisMarker (uentryList_getN (params, paramno))) { voptgenerror (FLG_TYPE, message ("Ensures clauses uses ... for function without ... in parameter list: %q", uentry_getName (le)), uentry_whereLast (le)); /*@innerbreak@*/ break; } while (paramno < exprNodeList_size (args)) { exprNode arg = exprNodeList_getN (args, paramno); fs = exprNode_getSref (arg); DPRINTF (("Merge arg: %s", exprNode_unparse (arg))); /* cut and pasted... gack*/ if (stateValue_isDefined (sval)) { /* Use combination table to merge old state value with new one: */ stateValue tval = sRef_getMetaStateValue (fs, key); if (stateValue_isDefined (tval)) { stateCombinationTable sctable = metaStateInfo_getMergeTable (msinfo); cstring msg = cstring_undefined; int nval = stateCombinationTable_lookup (sctable, stateValue_getValue (sval), stateValue_getValue (tval), &msg); DPRINTF (("Combining: %s + %s -> %d", stateValue_unparseValue (sval, msinfo), stateValue_unparseValue (tval, msinfo), nval)); if (nval == stateValue_error) { if (optgenerror (FLG_STATEMERGE, message ("Attributes merged in ensures clause in states that " "cannot be combined (%q is %q, %q is %q)%q", sRef_unparse (lastref), stateValue_unparseValue (sval, msinfo), sRef_unparse (fs), stateValue_unparseValue (tval, msinfo), cstring_isDefined (msg) ? message (": %s", msg) : cstring_undefined), exprNode_loc (f))) { sRef_showMetaStateInfo (fs, key); } } stateValue_updateValueLoc (sval, nval, fileloc_undefined); loc = exprNode_loc (arg); } else { DPRINTF (("No value for: %s:%s", sRef_unparse (fs), key)); } } else { sval = sRef_getMetaStateValue (fs, key); } lastref = fs; if (stateValue_isError (sval)) { /*@innerbreak@*/ break; /* Don't merge any more values if here was an error */ } paramno++; } } else { msr = metaStateSpecifier_getSref (ms); llassert (sRef_isParam (sRef_getRootBase (msr))); fs = sRef_fixBaseParam (msr, args); if (stateValue_isDefined (sval)) { /* Use combination table to merge old state value with new one: */ stateValue tval = sRef_getMetaStateValue (fs, key); if (stateValue_isDefined (tval)) { stateCombinationTable sctable = metaStateInfo_getMergeTable (msinfo); cstring msg = cstring_undefined; int nval = stateCombinationTable_lookup (sctable, stateValue_getValue (sval), stateValue_getValue (tval), &msg); DPRINTF (("Combining: %s + %s -> %d", stateValue_unparseValue (sval, msinfo), stateValue_unparseValue (tval, msinfo), nval)); if (nval == stateValue_error) { if (optgenerror (FLG_STATEMERGE, message ("Attributes merged in ensures clause in states that " "cannot be combined (%q is %q, %q is %q)%q", sRef_unparse (lastref), stateValue_unparseValue (sval, msinfo), sRef_unparse (fs), stateValue_unparseValue (tval, msinfo), cstring_isDefined (msg) ? message (": %s", msg) : cstring_undefined), exprNode_loc (f))) { sRef_showMetaStateInfo (fs, key); } } stateValue_updateValueLoc (sval, nval, fileloc_undefined); } else { DPRINTF (("No value for: %s:%s", sRef_unparse (fs), key)); } } else { sval = sRef_getMetaStateValue (fs, key); } lastref = fs; if (stateValue_isError (sval)) { /*@innerbreak@*/ break; /* Don't merge any more values if here was an error */ } } } DPRINTF (("Setting: %s:%s <- %s", sRef_unparse (s), key, stateValue_unparse (sval))); if (stateValue_isDefined (sval)) { sRef_setMetaStateValueComplete (s, key, stateValue_getValue (sval), loc); } else { DPRINTF (("Undefined state: %s", cstring_toCharsSafe (sRef_unparse (s)))); } } end_metaStateConstraintList_elements ; metaStateConstraintList_free (mscl); } } } static void checkRequiresClause (uentry le, exprNode f, exprNodeList args) { DPRINTF (("Check requires clause: %s(%s) / %s / %s", exprNode_unparse (f), exprNodeList_unparse (args), uentry_unparseFull (le), stateClauseList_unparse (uentry_getStateClauseList (le)))); if (uentry_isValid (le) && uentry_isFunction (le)) { stateClauseList sclauses = uentry_getStateClauseList (le); if (stateClauseList_isDefined (sclauses)) { DPRINTF (("Check requires: %s / %s / %s", uentry_unparse (le), exprNode_unparse (f), exprNodeList_unparse (args))); stateClauseList_elements (sclauses, cl) { DPRINTF (("Check clause: %s / %s", stateClause_unparse (cl), bool_unparse (stateClause_hasRequires (cl)))); if (stateClause_hasRequires (cl)) { sRefSet osrs = sRefSet_undefined; sRefSet srs; if (stateClause_isGlobal (cl)) { srs = sRefSet_single (usymtab_lookupGlobalMarker ()); osrs = srs; } else { srs = stateClause_getRefs (cl); } DPRINTF (("Refs: %s", sRefSet_unparse (srs))); if (stateClause_setsMetaState (cl)) { qual q = stateClause_getMetaQual (cl); annotationInfo ainfo = qual_getAnnotationInfo (q); metaStateInfo minfo = annotationInfo_getState (ainfo); cstring key = metaStateInfo_getName (minfo); int mvalue = annotationInfo_getValue (ainfo); DPRINTF (("Requires meta state! %s = %d", key, mvalue)); sRefSet_elements (srs, sel) { sRef s = sRef_fixBaseParam (sel, args); if (sRef_isResult (sRef_getRootBase (sel))) { BADBRANCH; } else { DPRINTF (("Checking state clause on: %s / %s / %s = %d", sRef_unparseFull (sel), sRef_unparseFull (s), key, mvalue)); if (!sRef_checkMetaStateValue (s, key, mvalue)) { DPRINTF (("HERE: %s", sRef_unparse (s))); if (optgenerror (FLG_STATETRANSFER, message ("Requires clause of called function %q not satisfied%q (state is %q): %q", uentry_getName (le), sRef_isGlobalMarker (s) ? message ("") : message (" by %q", sRef_unparse (s)), stateValue_unparseValue (sRef_getMetaStateValue (s, key), minfo), stateClause_unparse (cl)), exprNode_loc (f))) { sRef_showAliasInfo (s); } else { DPRINTF (("Error supressed!")); DPRINTF (("Loc: %s", fileloc_unparse (exprNode_loc (f)))); DPRINTF (("Context supress: %s", bool_unparse (context_suppressFlagMsg (FLG_STATETRANSFER, exprNode_loc (f))))); } } } } end_sRefSet_elements; } else { sRefModVal modf = stateClause_getRequiresBodyFunction (cl); int eparam = stateClause_getStateParameter (cl); DPRINTF (("Reflect after clause: %s / %s", stateClause_unparse (cl), sRefSet_unparse (srs))); llassert (modf != NULL); sRefSet_elements (srs, sel) { sRef s; DPRINTF (("elements: %s", sRef_unparse (sel))); DPRINTF (("elements: %s", sRef_unparseFull (sel))); s = sRef_fixBaseParam (sel, args); DPRINTF (("elements: %s", sRef_unparse (s))); DPRINTF (("elements: %s", sRef_unparseFull (s))); if (sRef_isResult (sRef_getRootBase (sel))) { ; /* what do we do about results? */ } else { DPRINTF (("Reflecting state clause on: %s / %s", sRef_unparse (sel), sRef_unparse (s))); modf (s, eparam, exprNode_loc (f)); } } end_sRefSet_elements; } sRefSet_free (osrs); } } end_stateClauseList_elements ; } } } static /*@only@*/ exprNode functionCallSafe (/*@only@*/ /*@notnull@*/ exprNode f, ctype t, /*@keep@*/ exprNodeList args) { /* requires f is a non-error exprNode, with type function */ cstring fname = exprNode_unparse (f); uentry le = exprNode_getUentry (f); exprNode ret = exprNode_createPartialCopy (f); int special; setCodePoint (); DPRINTF (("Call: %s %s",exprNode_unparse (f), exprNodeList_unparse (args))); ret->typ = ctype_getReturnType (t); ret->kind = XPR_CALL; ret->edata = exprData_makeCall (f, args); /* ** Order of these steps is very important! ** ** Must check for argument dependencies before messing up uses and sets. */ if (context_getFlag (FLG_EVALORDER)) { exprNodeList_elements (args, current) { if (exprNode_isDefined (current)) { exprNode_addUse (current, current->sref); } } end_exprNodeList_elements; if (context_maybeSet (FLG_EVALORDER) || context_maybeSet (FLG_EVALORDERUNCON)) { checkSequencing (f, args); } exprNodeList_elements (args, current) { if (exprNode_isDefined (current) && sRef_isMeaningful (current->sref)) { exprNode_addUse (ret, sRef_makeDerived (current->sref)); } } end_exprNodeList_elements ; } special = checkArgs (le, f, t, args, ret); checkGlobMods (f, le, args, ret, special); checkRequiresClause (le, f, args); setCodePoint (); if (uentry_isValid (le) && (uentry_isFunction (le) || (uentry_isVariable (le) && ctype_isFunction (uentry_getType (le))))) { exitkind exk = uentry_getExitCode (le); /* f->typ is already set to the return type */ DPRINTF (("Function: %s", uentry_unparseFull (le))); ret->sref = uentry_returnedRef (le, args, exprNode_loc (f)); DPRINTF (("Returned: %s / %s", uentry_unparseFull (le), sRef_unparseFull (ret->sref))); if (uentry_isFunction (le) && exprNodeList_size (args) >= 1) { qual nullPred = uentry_nullPred (le); if (qual_isTrueNull (nullPred)) { exprNode arg = exprNodeList_head (args); if (exprNode_isDefined (arg)) { ret->guards = guardSet_addFalseGuard (ret->guards, arg->sref); } } else if (qual_isFalseNull (nullPred)) { exprNode arg = exprNodeList_head (args); if (exprNode_isDefined (arg)) { ret->guards = guardSet_addTrueGuard (ret->guards, arg->sref); } } else { llassert (qual_isUnknown (nullPred)); } } if (exitkind_isConditionalExit (exk)) { /* ** True exit is: ** if (arg0) then { exit! } else { ; } ** False exit is: ** if (arg0) then { ; } else { exit! } */ exprNode firstArg; llassert (!exprNodeList_isEmpty (args)); firstArg = exprNodeList_head (args); if (exprNode_isDefined (firstArg) && !guardSet_isEmpty (firstArg->guards)) { usymtab_trueBranch (guardSet_undefined); usymtab_altBranch (guardSet_undefined); if (exitkind_isTrueExit (exk)) { usymtab_popBranches (firstArg, exprNode_makeMustExit (), exprNode_undefined, TRUE, TRUEEXITCLAUSE); } else { usymtab_popBranches (firstArg, exprNode_undefined, exprNode_makeMustExit (), TRUE, FALSEEXITCLAUSE); } } ret->exitCode = XK_MAYEXIT; } else if (exitkind_mustExit (exk)) { ret->exitCode = XK_MUSTEXIT; } else if (exitkind_couldExit (exk)) { ret->exitCode = XK_MAYEXIT; } else { ; } if (cstring_equalLit (fname, "exit")) { if (exprNodeList_size (args) == 1) { exprNode arg = exprNodeList_head (args); if (exprNode_isDefined (arg) && exprNode_knownIntValue (arg)) { long int val = multiVal_forceInt (exprNode_getValue (arg)); if (val != 0) { voptgenerror (FLG_EXITARG, message ("Argument to exit has implementation defined behavior: %s", exprNode_unparse (arg)), exprNode_loc (arg)); } } } } } else { ret->sref = sRef_undefined; exprNode_checkSetAny (ret, uentry_rawName (le)); } DPRINTF (("Before reflect: %s", sRef_unparseFull (ret->sref))); DPRINTF (("Reflect: %s", uentry_unparseFull (le))); reflectEnsuresClause (ret, le, f, args); setCodePoint (); DPRINTF (("Here: %s", sRef_unparseFull (ret->sref))); return (ret); } /* ** this is yucky! should keep the uentry as part of exprNode! */ uentry exprNode_getUentry (exprNode e) { if (exprNode_isError (e)) { return uentry_undefined; } else { cstring s = exprNode_rootVarName (e); uentry ue = usymtab_lookupSafe (s); return ue; } } /* ** Returns true iff e1 and e2 are both exactly the same storage ** (conservative). */ static bool exprNode_sameStorage (exprNode e1, exprNode e2) { sRef s1 = exprNode_getSref (e1); sRef s2 = exprNode_getSref (e2); return (sRef_realSame (s1, s2)); } exprNode exprNode_makeInitBlock (lltok brace, /*@only@*/ exprNodeList inits) { exprNode ret = exprNode_createPlain (ctype_unknown); ret->kind = XPR_INITBLOCK; ret->edata = exprData_makeCall (exprNode_undefined, inits); ret->loc = fileloc_update (ret->loc, lltok_getLoc (brace)); return (ret); } exprNode exprNode_functionCall (/*@only@*/ exprNode f, /*@only@*/ exprNodeList args) { ctype t; # ifdef DEBUGSPLINT usymtab_checkAllValid (); # endif if (exprNode_isUndefined (f)) { exprNode_free (f); exprNodeList_free (args); return exprNode_undefined; } t = exprNode_getType (f); if (sRef_isLocalVar (f->sref)) { exprNode_checkUse (f, f->sref, f->loc); if (sRef_possiblyNull (f->sref)) { if (!usymtab_isGuarded (f->sref)) { if (optgenerror (FLG_NULLDEREF, message ("Function call using %s pointer %q", sRef_nullMessage (f->sref), sRef_unparse (f->sref)), f->loc)) { sRef_showNullInfo (f->sref); sRef_setNullError (f->sref); } } } } setCodePoint (); if (ctype_isRealFunction (t)) { exprNode ret = functionCallSafe (f, t, args); setCodePoint (); return ret; } else if (ctype_isUnknown (t)) { exprNode ret = exprNode_createPartialCopy (f); cstring tstring; setCodePoint (); ret->typ = t; exprNodeList_elements (args, current) { if (exprNode_isDefined (current)) { exprNode_checkUse (ret, current->sref, ret->loc); /* ** also, anything derivable from current->sref may be used */ exprNode_addUse (ret, sRef_makeDerived (current->sref)); exprNode_mergeUSs (ret, current); } } end_exprNodeList_elements; ret->edata = exprData_makeCall (f, args); ret->kind = XPR_CALL; tstring = cstring_copy (exprNode_unparse (f)); cstring_markOwned (tstring); exprNode_checkSetAny (ret, tstring); return (ret); } else { voptgenerror (FLG_TYPE, message ("Call to non-function (type %t): %s", t, exprNode_unparse (f)), f->loc); exprNode_free (f); exprNodeList_free (args); return (exprNode_makeError ()); } } static exprNode exprNode_fieldAccessAux (/*@only@*/ exprNode s, /*@observer@*/ fileloc loc, /*@only@*/ cstring f) { exprNode ret = exprNode_createPartialCopy (s); ret->kind = XPR_FACCESS; if (exprNode_isError (s)) { ret->edata = exprData_makeField (s, f); return ret; } else { ctype t = exprNode_getType (s); ctype tr = ctype_realType (t); checkMacroParen (s); ret->edata = exprData_makeField (s, f); if (ctype_isStructorUnion (tr)) { uentry tf = uentryList_lookupField (ctype_getFields (tr), f); if (uentry_isUndefined (tf)) { voptgenerror (FLG_TYPE, message ("Access non-existent field %s of %t: %s", f, t, exprNode_unparse (ret)), loc); /*! cstring_free (f); */ /* evans 2001-03-25 self-detect */ return (ret); } else { uentry_setUsed (tf, exprNode_loc (ret)); ret->typ = uentry_getType (tf); checkSafeUse (ret, s->sref); ret->sref = sRef_makeField (s->sref, uentry_rawName (tf)); /*!? exprNode_free (s); */ /* evans 2001-03-25 self-detect */ return (ret); } } else /* isStructorUnion */ { if (ctype_isRealAbstract (tr)) { voptgenerror (FLG_ABSTRACT, message ("Access field of abstract type (%t): %s.%s", t, exprNode_unparse (s), f), loc); ret->typ = ctype_unknown; } else { if (ctype_isKnown (tr)) { voptgenerror (FLG_TYPE, message ("Access field of non-struct or union (%t): %s.%s", t, exprNode_unparse (s), f), loc); ret->typ = ctype_unknown; } else { cstring sn = cstring_copy (f); checkSafeUse (ret, s->sref); cstring_markOwned (sn); ret->sref = sRef_makeField (s->sref, sn); return (ret); } } return (ret); } } BADEXIT; } exprNode exprNode_fieldAccess (/*@only@*/ exprNode s, /*@only@*/ lltok dot, /*@only@*/ cstring f) { exprNode res = exprNode_fieldAccessAux (s, lltok_getLoc (dot), f); lltok_free (dot); return res; } exprNode exprNode_addParens (/*@only@*/ lltok lpar, /*@only@*/ exprNode e) { exprNode ret = exprNode_createPartialCopy (e); ret->loc = fileloc_update (ret->loc, lltok_getLoc (lpar)); ret->kind = XPR_PARENS; ret->edata = exprData_makeUop (e, lpar); if (!exprNode_isError (e)) { ret->exitCode = e->exitCode; ret->canBreak = e->canBreak; ret->mustBreak = e->mustBreak; ret->isJumpPoint = e->isJumpPoint; ret->sref = e->sref; } return ret; } static exprNode exprNode_arrowAccessAux (/*@only@*/ exprNode s, /*@observer@*/ fileloc loc, /*@only@*/ cstring f) { exprNode ret = exprNode_createPartialCopy (s); ret->edata = exprData_makeField (s, f); ret->kind = XPR_ARROW; if (exprNode_isError (s)) { return (ret); } else { ctype t = exprNode_getType (s); ctype tr = ctype_realType (t); checkMacroParen (s); (void) ctype_fixArrayPtr (tr); /* REWRITE THIS */ if (ctype_isRealPointer (tr)) { ctype b = ctype_realType (ctype_baseArrayPtr (tr)); if (ctype_isStructorUnion (b)) { uentry fentry = uentryList_lookupField (ctype_getFields (b), f); if (sRef_isKnown (s->sref) && sRef_possiblyNull (s->sref)) { if (!usymtab_isGuarded (s->sref) && !context_inProtectVars ()) { if (optgenerror (FLG_NULLDEREF, message ("Arrow access from %s pointer%q: %s", sRef_nullMessage (s->sref), sRef_unparsePreOpt (s->sref), exprNode_unparse (ret)), loc)) { sRef_showNullInfo (s->sref); sRef_setNullError (s->sref); } } } if (uentry_isUndefined (fentry)) { voptgenerror (FLG_TYPE, message ("Access non-existent field %s of %t: %s", f, t, exprNode_unparse (ret)), loc); ret->typ = ctype_unknown; return (ret); } else { /* ** was safeUse: shouldn't be safe! ** ** to do rec->field ** rec must be defined, ** *rec must be allocated ** rec->field need only be defined it if is an rvalue */ uentry_setUsed (fentry, exprNode_loc (ret)); ret->typ = uentry_getType (fentry); exprNode_checkUse (ret, s->sref, s->loc); /* exprNode_checkUse (ret, sRef_makePointer (s->sref), s->loc); */ ret->sref = sRef_makeArrow (s->sref, uentry_rawName (fentry)); return (ret); } } else /* Pointer to something that is not a struct or union*/ { if (ctype_isRealAbstract (tr)) { ctype xrt = ctype_forceRealType (tr); voptgenerror (FLG_ABSTRACT, message ("Arrow access field of abstract type (%t): %s->%s", t, exprNode_unparse (s), f), loc); /* ** Set the state correctly, as if the abstraction is broken. */ if (ctype_isRealPointer (xrt) && (b = ctype_realType (ctype_baseArrayPtr (xrt)), ctype_isStructorUnion (b))) { uentry fentry = uentryList_lookupField (ctype_getFields (b), f); ret->typ = uentry_getType (fentry); ret->sref = sRef_makeArrow (s->sref, uentry_rawName (fentry)); } else { ret->typ = ctype_unknown; ret->sref = sRef_undefined; } } else /* not a struct, union or abstract */ { if (ctype_isUnknown (tr)) { cstring sn = cstring_copy (f); DPRINTF (("Here: %s", exprNode_unparse (s))); exprNode_checkUse (ret, s->sref, s->loc); exprNode_checkUse (ret, sRef_makePointer (s->sref), s->loc); cstring_markOwned (sn); ret->sref = sRef_makeArrow (s->sref, sn); ret->kind = XPR_ARROW; return (ret); } else { voptgenerror (FLG_TYPE, message ("Arrow access field of non-struct or union " "pointer (%t): %s->%s", t, exprNode_unparse (s), f), loc); ret->typ = ctype_unknown; ret->sref = sRef_undefined; } } } } else /* its not a pointer */ { if (!ctype_isUnknown (tr)) { voptgenerror (FLG_TYPE, message ("Arrow access of non-pointer (%t): %s->%s", t, exprNode_unparse (s), f), loc); ret->typ = ctype_unknown; ret->sref = sRef_undefined; } else { cstring sn = cstring_copy (f); DPRINTF (("Here: %s", exprNode_unparse (s))); exprNode_checkUse (ret, s->sref, s->loc); exprNode_checkUse (ret, sRef_makePointer (s->sref), s->loc); cstring_markOwned (sn); ret->sref = sRef_makeArrow (s->sref, sn); ret->kind = XPR_ARROW; return (ret); } } return (ret); } BADEXIT; } exprNode exprNode_arrowAccess (/*@only@*/ exprNode s, /*@only@*/ lltok arrow, /*@only@*/ cstring f) { exprNode res = exprNode_arrowAccessAux (s, lltok_getLoc (arrow), f); lltok_free (arrow); return res; } /* ** only postOp's in C: i++ and i-- */ exprNode exprNode_postOp (/*@only@*/ exprNode e, /*@only@*/ lltok op) { /* check modification also */ /* cstring opname = lltok_unparse (op);*/ ctype t; exprNode ret = exprNode_createPartialCopy (e); ret->loc = fileloc_update (ret->loc, lltok_getLoc (op)); ret->kind = XPR_POSTOP; ret->edata = exprData_makeUop (e, op); if (!exprNode_isDefined (e)) { return ret; } checkMacroParen (e); exprNode_checkUse (ret, e->sref, e->loc); exprNode_checkSet (ret, e->sref); t = exprNode_getType (e); if (sRef_isUnsafe (e->sref)) { voptgenerror (FLG_MACROPARAMS, message ("Operand of %s is macro parameter (non-functional): %s%s", lltok_unparse (op), exprNode_unparse (e), lltok_unparse (op)), e->loc); sRef_makeSafe (e->sref); sRef_makeSafe (ret->sref); } if (ctype_isForceRealNumeric (&t) || ctype_isRealAP (t)) { ret->typ = e->typ; } else { if (ctype_isRealAbstract (t)) { if (ctype_isRealNumAbstract (t)) { ; /* Allow operations on numabstract types */ } else { voptgenerror (FLG_ABSTRACT, message ("Operand of %s is abstract type (%t): %s", lltok_unparse (op), t, exprNode_unparse (e)), e->loc); } } else { voptgenerror (FLG_TYPE, message ("Operand of %s is non-numeric (%t): %s", lltok_unparse (op), t, exprNode_unparse (e)), e->loc); } ret->typ = ctype_unknown; } /* if (ctype_isZero (t)) e->typ = ctype_int; */ exprNode_checkModify (e, ret); /* added 7/11/2000 D.L */ /* updateEnvironmentForPostOp (e); */ /* start modifications */ /* added by Seejo on 4/16/2000 */ /* Arithmetic operations on pointers wil modify the size/len/null terminated status */ if ((sRef_isPossiblyNullTerminated (e->sref)) || (sRef_isNullTerminated(e->sref))) { ret->sref = sRef_copy (e->sref); /* Operator : ++ */ if (lltok_getTok (op) == INC_OP) { if (sRef_getSize(e->sref) > 0) { sRef_setSize (ret->sref, sRef_getSize(e->sref) - 1); if (sRef_getLen(e->sref) == 1) { /* i.e. the first character is \0 */ /* Assumption: there is only 1 \0 in the buffer */ /* This will not be correct if there are 2 \0's in the buffer */ sRef_setNotNullTerminatedState(ret->sref); sRef_resetLen(ret->sref); } else { sRef_setNullTerminatedState(ret->sref); sRef_setLen (ret->sref, sRef_getLen(e->sref) - 1); } if (sRef_isNullTerminated (ret->sref)) printf ("ret->sref is Null Terminated\n"); else if (sRef_isPossiblyNullTerminated (ret->sref)) printf ("ret->sref is Possibly Null Terminated\n"); else if (sRef_isNotNullTerminated (ret->sref)) printf ("ret->sref is Not Null Terminated\n"); else {} } } /* Operator : -- */ if (lltok_getTok (op) == DEC_OP) { if (sRef_getSize(e->sref) >= 0) { sRef_setSize (ret->sref, sRef_getSize(e->sref) + 1); sRef_setLen (ret->sref, sRef_getLen(e->sref) + 1); } } } /* end modifications */ return ret; } exprNode exprNode_preOp (/*@only@*/ exprNode e, /*@only@*/ lltok op) { bool checkMod = FALSE; ctype te, tr; int opid = lltok_getTok (op); exprNode ret = exprNode_createSemiCopy (e); exprNode_copySets (ret, e); multiVal_free (ret->val); ret->val = multiVal_undefined; ret->loc = fileloc_update (ret->loc, lltok_getLoc (op)); ret->kind = XPR_PREOP; ret->edata = exprData_makeUop (e, op); if (exprNode_isError (e)) { return ret; } checkMacroParen (e); te = exprNode_getType (e); tr = ctype_realType (te); if (opid != TAMPERSAND) { exprNode_checkUse (ret, e->sref, e->loc); if (ctype_isRealAbstract (tr) && (!(ctype_isRealBool (te) && (opid == TEXCL)))) { if (ctype_isRealNumAbstract (tr)) { ; /* no warning for numabstract types */ } else { if (optgenerror (FLG_ABSTRACT, message ("Operand of %s is abstract type (%t): %s", lltok_unparse (op), tr, exprNode_unparse (ret)), e->loc)) { tr = te = ctype_unknown; ret->typ = ctype_unknown; sRef_setNullError (e->sref); } } } } switch (opid) { case INC_OP: case DEC_OP: /* should also check modification! */ if (sRef_isMacroParamRef (e->sref)) { voptgenerror (FLG_MACROPARAMS, message ("Operand of %s is macro parameter (non-functional): %s", lltok_unparse (op), exprNode_unparse (ret)), e->loc); } else { exprNode_checkSet (ret, e->sref); } if (ctype_isForceRealNumeric (&tr) || ctype_isRealAP (tr)) { } else { if (context_msgStrictOps ()) { voptgenerror (FLG_STRICTOPS, message ("Operand of %s is non-numeric (%t): %s", lltok_unparse (op), te, exprNode_unparse (ret)), e->loc); } ret->typ = ctype_int; } /* start modifications */ /* added by Seejo on 4/16/2000 */ /* Arithmetic operations on pointers wil modify the size/len/null terminated status */ if ((sRef_isPossiblyNullTerminated (e->sref)) || (sRef_isNullTerminated(e->sref))) { ret->sref = sRef_copy (e->sref); /* Operator : ++ */ if (lltok_getTok (op) == INC_OP) { if (sRef_getSize(e->sref) > 0) { sRef_setSize (ret->sref, sRef_getSize(e->sref) - 1); if (sRef_getLen(e->sref) == 1) { /* i.e. the first character is \0 */ /* Assumption: there is only 1 \0 in the buffer */ /* This will not be correct if there are 2 \0's in the buffer */ sRef_setNotNullTerminatedState(ret->sref); sRef_resetLen (ret->sref); } else { sRef_setNullTerminatedState(ret->sref); sRef_setLen (ret->sref, sRef_getLen(e->sref) - 1); } } } /* Operator : -- */ if (lltok_getTok (op) == DEC_OP) { if (sRef_getSize(e->sref) >= 0) { sRef_setSize (ret->sref, sRef_getSize(e->sref) + 1); sRef_setLen (ret->sref, sRef_getLen(e->sref) + 1); } } } /* end modifications */ checkMod = TRUE; break; case TMINUS: case TPLUS: if (ctype_isForceRealNumeric (&tr)) { if (opid == TMINUS) { ret->val = multiVal_invert (exprNode_getValue (e)); } else { ret->val = multiVal_copy (exprNode_getValue (e)); } } else { if (context_msgStrictOps ()) { voptgenerror (FLG_STRICTOPS, message ("Operand of %s is non-numeric (%t): %s", lltok_unparse (op), te, exprNode_unparse (ret)), e->loc); } ret->typ = ctype_int; } break; case TEXCL: /* maybe this should be restricted */ guardSet_flip (ret->guards); if (ctype_isRealBool (te) || ctype_isUnknown (te)) { ; } else { if (ctype_isRealPointer (tr)) { if (sRef_isKnown (e->sref)) { ret->guards = guardSet_addFalseGuard (ret->guards, e->sref); } voptgenerror2n (FLG_BOOLOPS, FLG_PTRNEGATE, message ("Operand of %s is non-boolean (%t): %s", lltok_unparse (op), te, exprNode_unparse (ret)), e->loc); } else { voptgenerror (FLG_BOOLOPS, message ("Operand of %s is non-boolean (%t): %s", lltok_unparse (op), te, exprNode_unparse (ret)), e->loc); } ret->typ = ctype_bool; } break; case TTILDE: if (ctype_isForceRealInt (&tr)) { } else { if (context_msgStrictOps ()) { voptgenerror (FLG_STRICTOPS, message ("Operand of %s is non-integer (%t): %s", lltok_unparse (op), te, exprNode_unparse (ret)), e->loc); } if (ctype_isInt (e->typ)) { ret->typ = e->typ; } else { ret->typ = ctype_int; } } break; case TAMPERSAND: ret->typ = ctype_makePointer (e->typ); if (sRef_isKnown (e->sref)) { ret->sref = sRef_makeAddress (e->sref); } break; case TMULT: if (ctype_isAP (tr)) { ret->typ = ctype_baseArrayPtr (e->typ); } else { if (ctype_isKnown (te)) { if (ctype_isFunction (te)) { ret->typ = e->typ; voptgenerror (FLG_FCNDEREF, message ("Dereference of function type (%t): %s", te, exprNode_unparse (ret)), e->loc); } else { voptgenerror (FLG_TYPE, message ("Dereference of non-pointer (%t): %s", te, exprNode_unparse (ret)), e->loc); ret->typ = ctype_unknown; } } else { ret->typ = ctype_unknown; } } if (sRef_isKnown (e->sref)) { DPRINTF (("Checking possibly null: %s", sRef_unparseFull (e->sref))); if (sRef_possiblyNull (e->sref)) { DPRINTF (("Checking possibly null: %s", sRef_unparse (e->sref))); if (!usymtab_isGuarded (e->sref) && !context_inProtectVars ()) { if (optgenerror (FLG_NULLDEREF, message ("Dereference of %s pointer %q: %s", sRef_nullMessage (e->sref), sRef_unparse (e->sref), exprNode_unparse (ret)), e->loc)) { sRef_showNullInfo (e->sref); sRef_setNotNull (e->sref, e->loc); /* suppress future messages */ } } } ret->sref = sRef_makePointer (e->sref); } break; default: llbug (message ("exprNode_preOp: unhandled op: %s", lltok_unparse (op))); } if (checkMod) { exprNode_checkModify (e, ret); } return ret; } /* ** any reason to disallow sizeof (abstract type) ? */ /* ** used by both sizeof */ static ctype sizeof_resultType (void) { static ctype sizet = ctype_unknown; if (ctype_isUnknown (sizet)) { if (usymtab_existsType (cstring_makeLiteralTemp ("size_t"))) { sizet = uentry_getAbstractType (usymtab_lookup (cstring_makeLiteralTemp ("size_t"))); } else { sizet = ctype_ulint; } } return sizet; } exprNode exprNode_sizeofType (/*@only@*/ qtype qt) { exprNode ret = exprNode_create (sizeof_resultType ()); ctype ct = qtype_getType (qt); ret->kind = XPR_SIZEOFT; ret->edata = exprData_makeSizeofType (qt); voptgenerror (FLG_SIZEOFTYPE, message ("Parameter to sizeof is type %s: %s", ctype_unparse (ct), exprNode_unparse (ret)), ret->loc); return (ret); } exprNode exprNode_alignofType (/*@only@*/ qtype qt) { exprNode ret = exprNode_create (sizeof_resultType ()); ctype ct = qtype_getType (qt); ret->kind = XPR_ALIGNOFT; ret->edata = exprData_makeSizeofType (qt); voptgenerror (FLG_SIZEOFTYPE, message ("Parameter to alignof is type %s: %s", ctype_unparse (ct), exprNode_unparse (ret)), ret->loc); return (ret); } exprNode exprNode_offsetof (qtype qt, cstringList s) { exprNode ret = exprNode_create (sizeof_resultType ()); ctype ct = qtype_getType (qt); ret->kind = XPR_OFFSETOF; ret->edata = exprData_makeOffsetof (qt, s); if (!ctype_isRealSU (ct)) { voptgenerror (FLG_TYPE, message ("First parameter to offsetof is not a " "struct or union type (type %s): %s", ctype_unparse (ct), exprNode_unparse (ret)), ret->loc); } else { ctype lt = ct; cstringList_elements (s, el) { uentryList fields; uentry fld; if (ctype_isUndefined (lt)) { break; } else if (!ctype_isRealSU (lt)) { voptgenerror (FLG_TYPE, message ("Inner offsetof type is not a " "struct or union type (type %s before field %s): %s", ctype_unparse (lt), el, exprNode_unparse (ret)), ret->loc); break; } else { fields = ctype_getFields (ctype_realType (lt)); fld = uentryList_lookupField (fields, el); DPRINTF (("Try: %s / %s", ctype_unparse (lt), el)); if (uentry_isUndefined (fld)) { if (ctype_equal (lt, ct)) { voptgenerror (FLG_TYPE, message ("Field %s in offsetof is not the " "name of a field of %s: %s", el, ctype_unparse (ct), exprNode_unparse (ret)), ret->loc); } else { voptgenerror (FLG_TYPE, message ("Deep field %s in offsetof is not the " "name of a field of %s: %s", el, ctype_unparse (lt), exprNode_unparse (ret)), ret->loc); } } else { lt = uentry_getType (fld); } } } end_cstringList_elements; /* Should report error if its a bit field - behavior is undefined! */ } return (ret); } /*@only@*/ exprNode exprNode_sizeofExpr (/*@only@*/ exprNode e) { exprNode ret; if (exprNode_isUndefined (e)) { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (g_currentloc)); ret->edata = exprData_makeSingle (e); ret->typ = sizeof_resultType (); ret->kind = XPR_SIZEOF; } else { uentry u = exprNode_getUentry (e); ret = exprNode_createPartialCopy (e); ret->edata = exprData_makeSingle (e); ret->typ = sizeof_resultType (); ret->kind = XPR_SIZEOF; if (uentry_isValid (u) && uentry_isRefParam (u) && ctype_isRealArray (uentry_getType (u))) { voptgenerror (FLG_SIZEOFFORMALARRAY, message ("Parameter to sizeof is an array-type function parameter: %s", exprNode_unparse (ret)), ret->loc); } } /* ** sizeof (x) doesn't "really" use x */ return (ret); } /*@only@*/ exprNode exprNode_alignofExpr (/*@only@*/ exprNode e) { exprNode ret; if (exprNode_isUndefined (e)) { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (g_currentloc)); } else { ret = exprNode_createPartialCopy (e); } ret->edata = exprData_makeSingle (e); ret->typ = sizeof_resultType (); ret->kind = XPR_ALIGNOF; /* ** sizeof (x) doesn't "really" use x */ return (ret); } /*@only@*/ exprNode exprNode_cast (/*@only@*/ lltok tok, /*@only@*/ exprNode e, /*@only@*/ qtype q) { ctype c; ctype t; exprNode ret; if (exprNode_isError (e)) { qtype_free (q); lltok_free (tok); return exprNode_undefined; } checkMacroParen (e); c = qtype_getType (q); t = exprNode_getType (e); ret = exprNode_createPartialCopy (e); ret->loc = fileloc_update (ret->loc, lltok_getLoc (tok)); ret->typ = c; ret->kind = XPR_CAST; ret->edata = exprData_makeCast (tok, e, q); ret->sref = sRef_copy (e->sref); DPRINTF (("Cast: -> %s", sRef_unparseFull (ret->sref))); if (!sRef_isConst (e->sref)) { usymtab_addForceMustAlias (ret->sref, e->sref); } DPRINTF (("Cast 2: -> %s", sRef_unparseFull (ret->sref))); sRef_setTypeFull (ret->sref, c); DPRINTF (("Cast 2: -> %s", sRef_unparseFull (ret->sref))); /* ** we allow ** abstract -> void ** 0 <-> abstract * ** void * <-> abstract * (if FLG_ABSTVOIDP) ** abstract * <-> void * (if FLG_ABSTVOIDP) */ if (ctype_isVoid (c)) /* cast to void is always okay --- discard value */ { /* evans 2002-07-19: added this warning */ DPRINTF (("Checking: %s / %s", exprNode_unparse (ret), sRef_unparseFull (ret->sref))); if (sRef_isFresh (ret->sref)) { voptgenerror (FLG_MUSTFREEFRESH, message ("New fresh storage %q(type %s) cast to void (not released): %s", sRef_unparseOpt (ret->sref), ctype_unparse (exprNode_getType (ret)), exprNode_unparse (ret)), exprNode_loc (ret)); } } else if (ctype_isRealAP (c)) /* casting to array or pointer */ { ctype bc = ctype_getBaseType (c); ctype bt = ctype_getBaseType (t); ctype rt = ctype_realType (t); if (ctype_isFunction (ctype_baseArrayPtr (ctype_realType (c))) && (ctype_isArrayPtr (rt) && !ctype_isFunction (ctype_realType (ctype_baseArrayPtr (rt))))) { voptgenerror (FLG_CASTFCNPTR, message ("Cast from function pointer type (%t) to " "non-function pointer (%t): %s", c, t, exprNode_unparse (ret)), e->loc); } if (!ctype_isFunction (ctype_baseArrayPtr (c)) && (ctype_isArrayPtr (rt) && ctype_isFunction (ctype_realType (ctype_baseArrayPtr (rt))))) { voptgenerror (FLG_CASTFCNPTR, message ("Cast from non-function pointer type (%t) to " "function pointer (%t): %s", c, t, exprNode_unparse (ret)), e->loc); } if (exprNode_isZero (e) && context_getFlag (FLG_ZEROPTR) && !(ctype_isRealAbstract (bc) && context_hasAccess (ctype_typeId (bc)))) { ; /* okay to cast zero */ } else { if (ctype_isRealAbstract (bc) && !context_hasAccess (ctype_typeId (bc))) { if (ctype_isVoidPointer (t) || ctype_isUnknown (t)) { vnoptgenerror (FLG_ABSTVOIDP, message ("Cast to underlying abstract type %t: %s", c, exprNode_unparse (ret)), e->loc); } else { voptgenerror (FLG_ABSTRACT, message ("Cast to underlying abstract type %t: %s", c, exprNode_unparse (ret)), e->loc); } } if (ctype_isRealAbstract (bt) && !context_hasAccess (ctype_typeId (bt))) { if (ctype_isUnknown (c) || ctype_isVoidPointer (c)) { vnoptgenerror (FLG_ABSTVOIDP, message ("Cast from underlying abstract type %t: %s", t, exprNode_unparse (ret)), e->loc); } else { voptgenerror (FLG_ABSTRACT, message ("Cast from underlying abstract type %t: %s", t, exprNode_unparse (ret)), e->loc); } } } } else { ctype bt = ctype_realType (ctype_getBaseType (t)); ctype bc = ctype_realType (ctype_getBaseType (c)); if (ctype_isAbstract (bt) && !context_hasAccess (ctype_typeId (bt))) { if (ctype_match (c, t)) { if (ctype_equal (c, t)) { voptgenerror (FLG_TYPE, message ("Redundant cast involving abstract type %t: %s", bt, exprNode_unparse (ret)), e->loc); } } else { voptgenerror (FLG_ABSTRACT, message ("Cast from abstract type %t: %s", bt, exprNode_unparse (ret)), e->loc); } } if (ctype_isAbstract (bc) && !context_hasAccess (ctype_typeId (bc))) { if (ctype_match (c, t)) { ; } else { if (ctype_isNumAbstract (bc)) { if (exprNode_isNumLiteral (e)) { voptgenerror (FLG_NUMABSTRACTCAST, message ("Cast from literal to numabstract type %t: %s", bc, exprNode_unparse (ret)), e->loc); } else { voptgenerror (FLG_NUMABSTRACT, message ("Cast to numabstract type %t: %s", bc, exprNode_unparse (ret)), e->loc); } } else { DPRINTF (("No access to: %s / %d", ctype_unparse (bc), ctype_typeId (bc))); DPRINTF (("Context %s %s", bool_unparse (context_inFunctionLike ()), context_unparse ())); voptgenerror (FLG_ABSTRACT, message ("Cast to abstract type %t: %s", bc, exprNode_unparse (ret)), e->loc); } } } } if (ctype_isAbstract (c)) { if (sRef_isExposed (e->sref) || sRef_isOnly (e->sref)) { /* okay, cast exposed to abstract */ sRef_clearExKindComplete (ret->sref, fileloc_undefined); } else { if (ctype_isVisiblySharable (t) && sRef_isExternallyVisible (e->sref) && !(ctype_isAbstract (t) && context_hasAccess (ctype_typeId (t)))) { voptgenerror (FLG_CASTEXPOSE, message ("Cast to abstract type from externally visible " "mutable storage exposes rep of %s: %s", ctype_unparse (c), exprNode_unparse (e)), e->loc); } } } return (ret); } static bool evaluationOrderUndefined (lltok op) { int opid = lltok_getTok (op); return (opid != AND_OP && opid != OR_OP); } static bool checkIntegral (/*@notnull@*/ exprNode e1, /*@notnull@*/ exprNode e2, /*@notnull@*/ exprNode ret, lltok op) { bool error = FALSE; ctype te1 = exprNode_getType (e1); ctype te2 = exprNode_getType (e2); ctype tr1 = ctype_realishType (te1); ctype tr2 = ctype_realishType (te2); if (ctype_isForceRealInt (&tr1) && ctype_isForceRealInt (&tr2)) { ; } else { if (context_msgStrictOps ()) { if (!ctype_isInt (tr1) && !ctype_isInt (tr2)) { if (ctype_sameName (te1, te2)) { error = optgenerror (FLG_STRICTOPS, message ("Operands of %s are non-integer (%t): %s", lltok_unparse (op), te1, exprNode_unparse (ret)), e1->loc); } else { error = optgenerror (FLG_STRICTOPS, message ("Operands of %s are non-integers (%t, %t): %s", lltok_unparse (op), te1, te2, exprNode_unparse (ret)), e1->loc); } } else if (!ctype_isInt (tr1)) { error = optgenerror (FLG_STRICTOPS, message ("Left operand of %s is non-integer (%t): %s", lltok_unparse (op), te1, exprNode_unparse (ret)), e1->loc); } else /* !ctype_isInt (te2) */ { error = optgenerror (FLG_STRICTOPS, message ("Right operand of %s is non-integer (%t): %s", lltok_unparse (op), te2, exprNode_unparse (ret)), e2->loc); } } } return !error; } /* ** returns exprNode representing e1 op e2 ** ** uses msg if there are errors ** can be used for both assignment ops and regular ops ** ** modifies e1 */ static /*@only@*/ /*@notnull@*/ exprNode exprNode_makeOp (/*@keep@*/ exprNode e1, /*@keep@*/ exprNode e2, /*@keep@*/ lltok op) { ctype te1, te2, tr1, tr2, tret; int opid = lltok_getTok (op); bool hasError = FALSE; exprNode ret; if (exprNode_isError (e1)) { ret = exprNode_createPartialNVCopy (e2); } else { ret = exprNode_createPartialNVCopy (e1); } ret->val = multiVal_undefined; ret->kind = XPR_OP; ret->edata = exprData_makeOp (e1, e2, op); if (exprNode_isError (e1) || exprNode_isError (e2)) { if (opid == TLT || opid == TGT || opid == LE_OP || opid == GE_OP || opid == EQ_OP || opid == NE_OP || opid == AND_OP || opid == OR_OP) { ret->typ = ctype_bool; } if (exprNode_isDefined (e1)) { exprNode_checkUse (ret, e1->sref, e1->loc); } if (exprNode_isDefined (e2)) { exprNode_mergeUSs (ret, e2); exprNode_checkUse (ret, e2->sref, e2->loc); } return ret; } tret = ctype_unknown; te1 = exprNode_getType (e1); DPRINTF (("te1 = %s / %s", exprNode_unparse (e1), ctype_unparse (te1))); te2 = exprNode_getType (e2); tr1 = ctype_realishType (te1); tr2 = ctype_realishType (te2); if (opid == OR_OP) { exprNode_produceGuards (e2); ret->guards = guardSet_or (ret->guards, e2->guards); } else if (opid == AND_OP) { exprNode_produceGuards (e2); /* evans 2003-08-13: need to produce guards for expression */ /* Shouldn't this have already happened? */ DPRINTF (("Anding guards: %s / %s", guardSet_unparse (ret->guards), guardSet_unparse (e2->guards))); ret->guards = guardSet_and (ret->guards, e2->guards); } else { /* no guards */ } if (opid == EQ_OP || opid == NE_OP) { exprNode temp1 = e1, temp2 = e2; /* could do NULL == x */ if (exprNode_isNullValue (e1) || exprNode_isUnknownConstant (e1)) { temp1 = e2; temp2 = e1; } if (exprNode_isNullValue (temp2) || exprNode_isUnknownConstant (temp2)) { reflectNullTest (temp1, (opid == NE_OP)); guardSet_free (ret->guards); ret->guards = guardSet_copy (temp1->guards); } } if (opid == TLT || opid == TGT || opid == LE_OP || opid == GE_OP || opid == EQ_OP || opid == NE_OP || opid == AND_OP || opid == OR_OP) { tret = ctype_bool; } if (anyAbstract (tr1, tr2) && (!((ctype_isRealBool (te1) || ctype_isRealBool (te2)) && (opid == AND_OP || opid == OR_OP || opid == EQ_OP || opid == NE_OP)))) { if (abstractOpError (tr1, tr2, op, e1, e2, e1->loc, e2->loc)) { tret = ctype_unknown; goto skiprest; } } if (ctype_isUnknown (te1) || ctype_isUnknown (te2)) { /* unknown types, no comparisons possible */ goto skiprest; } switch (opid) { case TMULT: /* multiplication and division: */ case TDIV: /* */ case MUL_ASSIGN: /* numeric, numeric -> numeric */ case DIV_ASSIGN: /* */ if (opid == TMULT || opid == MUL_ASSIGN) { ret->val = multiVal_multiply (exprNode_getValue (e1), exprNode_getValue (e2)); } else { ret->val = multiVal_divide (exprNode_getValue (e1), exprNode_getValue (e2)); } tret = checkNumerics (tr1, tr2, te1, te2, e1, e2, op); break; case TPLUS: /* addition and subtraction: */ case TMINUS: /* pointer, int -> pointer */ case SUB_ASSIGN: /* int, pointer -> pointer */ case ADD_ASSIGN: /* numeric, numeric -> numeric */ if (opid == TPLUS || opid == ADD_ASSIGN) { ret->val = multiVal_add (exprNode_getValue (e1), exprNode_getValue (e2)); } else { ret->val = multiVal_subtract (exprNode_getValue (e1), exprNode_getValue (e2)); } tr1 = ctype_fixArrayPtr (tr1); if ((ctype_isRealPointer (tr1) && !exprNode_isNullValue (e1)) && (!ctype_isRealPointer (tr2) && ctype_isRealInt (tr2))) { /* pointer + int */ if (context_msgPointerArith ()) { voptgenerror (FLG_POINTERARITH, message ("Pointer arithmetic (%t, %t): %s", te1, te2, exprNode_unparse (ret)), e1->loc); } /* ** Swap terms so e1 is always the pointer */ if (ctype_isRealPointer (tr1)) { ; } else { exprNode_swap (e1, e2); } if (sRef_possiblyNull (e1->sref) && !usymtab_isGuarded (e1->sref)) { voptgenerror (FLG_NULLPOINTERARITH, message ("Pointer arithmetic involving possibly " "null pointer %s: %s", exprNode_unparse (e1), exprNode_unparse (ret)), e1->loc); } ret->sref = sRef_copy (e1->sref); /* start modifications */ /* added by Seejo on 4/16/2000 */ /* Arithmetic operations on pointers wil modify the size/len/null terminated status */ if ((sRef_isPossiblyNullTerminated (e1->sref)) || (sRef_isNullTerminated(e1->sref))) { int val; /*drl 1-4-2002 added ugly fixed to stop program from crashing on point + int +int one day I'll fix this or ask Seejo wtf the codes supposed to do. */ if (!multiVal_isInt (e2->val) ) break; /*end drl*/ val = (int) multiVal_forceInt (e2->val); /* Operator : + or += */ if ((lltok_getTok (op) == TPLUS) || (lltok_getTok(op) == ADD_ASSIGN)) { if (sRef_getSize(e1->sref) >= val) {/* Incrementing the pointer by val should not result in a size < 0 (size = 0 is ok !) */ sRef_setSize (ret->sref, sRef_getSize(e1->sref) - val); if (sRef_getLen(e1->sref) == val) { /* i.e. the character at posn val is \0 */ sRef_setNotNullTerminatedState(ret->sref); sRef_resetLen (ret->sref); } else { sRef_setNullTerminatedState(ret->sref); sRef_setLen (ret->sref, sRef_getLen(e1->sref) - val); } } } /* Operator : - or -= */ if ((lltok_getTok (op) == TMINUS) || (lltok_getTok (op) == SUB_ASSIGN)) { if (sRef_getSize(e1->sref) >= 0) { sRef_setSize (ret->sref, sRef_getSize(e1->sref) + val); sRef_setLen (ret->sref, sRef_getLen(e1->sref) + val); } } } /* end modifications */ sRef_setNullError (ret->sref); /* ** Fixed for 2.2c: the alias state of ptr + int is dependent, ** since is points to storage that should not be deallocated ** through this pointer. */ if (sRef_isOnly (ret->sref) || sRef_isFresh (ret->sref)) { sRef_setAliasKind (ret->sref, AK_DEPENDENT, exprNode_loc (ret)); } tret = e1->typ; } else if ((!ctype_isRealPointer(tr1) && ctype_isRealInt (tr1)) && (ctype_isRealPointer (tr2) && !exprNode_isNullValue (e2))) { if (context_msgPointerArith ()) { voptgenerror (FLG_POINTERARITH, message ("Pointer arithmetic (%t, %t): %s", te1, te2, exprNode_unparse (ret)), e1->loc); } if (sRef_possiblyNull (e1->sref) && !usymtab_isGuarded (e1->sref)) { voptgenerror (FLG_NULLPOINTERARITH, message ("Pointer arithmetic involving possibly " "null pointer %s: %s", exprNode_unparse (e2), exprNode_unparse (ret)), e2->loc); } ret->sref = sRef_copy (e2->sref); /* start modifications */ /* added by Seejo on 4/16/2000 */ /* Arithmetic operations on pointers wil modify the size/len/null terminated status */ if ((sRef_isPossiblyNullTerminated (e2->sref)) || (sRef_isNullTerminated(e2->sref))) { if (multiVal_isDefined (e1->val)) { int val = (int) multiVal_forceInt (e1->val); /* Operator : + or += */ if ((lltok_getTok (op) == TPLUS) || (lltok_getTok(op) == ADD_ASSIGN)) { if (sRef_getSize(e2->sref) >= val) {/* Incrementing the pointer by val should not result in a size < 0 (size = 0 is ok !) */ sRef_setSize (ret->sref, sRef_getSize(e2->sref) - val); if (sRef_getLen(e2->sref) == val) { /* i.e. the character at posn val is \0 */ sRef_setNotNullTerminatedState(ret->sref); sRef_resetLen (ret->sref); } else { sRef_setNullTerminatedState(ret->sref); sRef_setLen (ret->sref, sRef_getLen(e2->sref) - val); } } } /* Operator : - or -= */ if ((lltok_getTok (op) == TMINUS) || (lltok_getTok (op) == SUB_ASSIGN)) { if (sRef_getSize(e2->sref) >= 0) { sRef_setSize (ret->sref, sRef_getSize(e2->sref) + val); sRef_setLen (ret->sref, sRef_getLen(e2->sref) + val); } } } } /* end modifications */ sRef_setNullError (ret->sref); /* ** Fixed for 2.2c: the alias state of ptr + int is dependent, ** since is points to storage that should not be deallocated ** through this pointer. */ if (sRef_isOnly (ret->sref) || sRef_isFresh (ret->sref)) { sRef_setAliasKind (ret->sref, AK_DEPENDENT, exprNode_loc (ret)); } tret = e2->typ; ret->sref = e2->sref; } else { tret = checkNumerics (tr1, tr2, te1, te2, e1, e2, op); } break; case LEFT_ASSIGN: case RIGHT_ASSIGN: case LEFT_OP: case RIGHT_OP: case TAMPERSAND: /* bitwise & */ case AND_ASSIGN: case TCIRC: /* ^ (XOR) */ case TBAR: case XOR_ASSIGN: case OR_ASSIGN: { bool reported = FALSE; /* ** Shift Operator */ if (opid == LEFT_OP || opid == LEFT_ASSIGN || opid == RIGHT_OP || opid == RIGHT_ASSIGN) { /* ** evans 2002-01-01: fixed this to follow ISO 6.5.7. */ if (!ctype_isUnsigned (tr2) && !exprNode_isNonNegative (e2)) { reported = optgenerror (FLG_SHIFTNEGATIVE, message ("Right operand of %s may be negative (%t): %s", lltok_unparse (op), te2, exprNode_unparse (ret)), e2->loc); } if (!ctype_isUnsigned (tr1) && !exprNode_isNonNegative (e1)) { reported = optgenerror (FLG_SHIFTIMPLEMENTATION, message ("Left operand of %s may be negative (%t): %s", lltok_unparse (op), te1, exprNode_unparse (ret)), e1->loc); } /* ** Should check size of right operand also... */ } else { if (!ctype_isUnsigned (tr1)) { if (exprNode_isNonNegative (e1)) { ; } else { reported = optgenerror (FLG_BITWISEOPS, message ("Left operand of %s is not unsigned value (%t): %s", lltok_unparse (op), te1, exprNode_unparse (ret)), e1->loc); if (reported) { te1 = ctype_uint; } } } else { if (!ctype_isUnsigned (tr2)) { if (!exprNode_isNonNegative (e2)) { reported = optgenerror (FLG_BITWISEOPS, message ("Right operand of %s is not unsigned value (%t): %s", lltok_unparse (op), te2, exprNode_unparse (ret)), e2->loc); } } } } if (!reported) { if (!checkIntegral (e1, e2, ret, op)) { te1 = ctype_unknown; } } DPRINTF (("Set: %s", ctype_unparse (te1))); /* ** tret is the widest type of te1 and te2 */ tret = ctype_widest (te1, te2); break; } case MOD_ASSIGN: case TPERCENT: if (checkIntegral (e1, e2, ret, op)) { tret = te1; } else { tret = ctype_unknown; } break; case EQ_OP: case NE_OP: case TLT: /* comparisons */ case TGT: /* numeric, numeric -> bool */ DPRINTF (("Here we go: %s / %s", ctype_unparse (tr1), ctype_unparse (tr2))); if ((ctype_isReal (tr1) && !ctype_isInt (tr1)) || (ctype_isReal (tr2) && !ctype_isInt (tr2))) { ctype rtype = tr1; bool fepsilon = FALSE; if (!ctype_isReal (rtype) || ctype_isInt (rtype)) { rtype = tr2; } if (opid == TLT || opid == TGT) { uentry ue1 = exprNode_getUentry (e1); uentry ue2 = exprNode_getUentry (e2); /* ** FLT_EPSILON, etc. really is a variable, not ** a constant. */ if (uentry_isVariable (ue1)) { cstring uname = uentry_rawName (ue1); if (cstring_equalLit (uname, "FLT_EPSILON") || cstring_equalLit (uname, "DBL_EPSILON") || cstring_equalLit (uname, "LDBL_EPSILON")) { fepsilon = TRUE; } } if (uentry_isVariable (ue2)) { cstring uname = uentry_rawName (ue2); if (cstring_equalLit (uname, "FLT_EPSILON") || cstring_equalLit (uname, "DBL_EPSILON") || cstring_equalLit (uname, "LDBL_EPSILON")) { fepsilon = TRUE; } } } if (fepsilon) { ; /* Don't complain. */ } else { if (opid == EQ_OP || opid == NE_OP) { voptgenerror (FLG_REALCOMPARE, message ("Dangerous equality comparison involving %s types: %s", ctype_unparse (rtype), exprNode_unparse (ret)), ret->loc); } else { voptgenerror (FLG_REALRELATECOMPARE, message ("Possibly dangerous relational comparison involving %s types: %s", ctype_unparse (rtype), exprNode_unparse (ret)), ret->loc); } } } /*@fallthrough@*/ case LE_OP: case GE_OP: /* ** Types should match. */ DPRINTF (("Match types: %s / %s", exprNode_unparse (e1), exprNode_unparse (e2))); if (!exprNode_matchTypes (e1, e2)) { hasError = gentypeerror (te1, e1, te2, e2, message ("Operands of %s have incompatible types (%t, %t): %s", lltok_unparse (op), te1, te2, exprNode_unparse (ret)), e1->loc); } if (hasError || (ctype_isForceRealNumeric (&tr1) && ctype_isForceRealNumeric (&tr2)) || (ctype_isRealPointer (tr1) && ctype_isRealPointer (tr2))) { ; /* okay */ } else { if ((ctype_isRealNumeric (tr1) && ctype_isRealPointer (tr2)) || (ctype_isRealPointer (tr1) && ctype_isRealNumeric (tr2))) { voptgenerror (FLG_PTRNUMCOMPARE, message ("Comparison of pointer and numeric (%t, %t): %s", te1, te2, exprNode_unparse (ret)), e1->loc); } else { (void) checkNumerics (tr1, tr2, te1, te2, e1, e2, op); } tret = ctype_bool; } /* certain comparisons on unsigned's and zero look suspicious */ if (opid == TLT || opid == LE_OP || opid == GE_OP) { if ((ctype_isUnsigned (tr1) && exprNode_isZero (e2)) || (ctype_isUnsigned (tr2) && exprNode_isZero (e1))) { voptgenerror (FLG_UNSIGNEDCOMPARE, message ("Comparison of unsigned value involving zero: %s", exprNode_unparse (ret)), e1->loc); } } /* EQ_OP should NOT be used with booleans (unless one is FALSE) */ if ((opid == EQ_OP || opid == NE_OP) && ctype_isDirectBool (tr1) && ctype_isDirectBool (tr2)) { /* ** is one a variable? */ if (uentry_isVariable (exprNode_getUentry (e1)) || uentry_isVariable (exprNode_getUentry (e2))) { /* ** comparisons with FALSE are okay */ if (exprNode_isFalseConstant (e1) || exprNode_isFalseConstant (e2)) { ; } else { voptgenerror (FLG_BOOLCOMPARE, message ("Use of %q with %s variables (risks inconsistency because " "of multiple true values): %s", cstring_makeLiteral ((opid == EQ_OP) ? "==" : "!="), context_printBoolName (), exprNode_unparse (ret)), e1->loc); } } } break; case AND_OP: /* bool, bool -> bool */ case OR_OP: if (ctype_isForceRealBool (&tr1) && ctype_isForceRealBool (&tr2)) { ; } else { if (context_maybeSet (FLG_BOOLOPS)) { if (!ctype_isRealBool (te1) && !ctype_isRealBool (te2)) { if (ctype_sameName (te1, te2)) { voptgenerror (FLG_BOOLOPS, message ("Operands of %s are non-boolean (%t): %s", lltok_unparse (op), te1, exprNode_unparse (ret)), e1->loc); } else { voptgenerror (FLG_BOOLOPS, message ("Operands of %s are non-booleans (%t, %t): %s", lltok_unparse (op), te1, te2, exprNode_unparse (ret)), e1->loc); } } else if (!ctype_isRealBool (te1)) { voptgenerror (FLG_BOOLOPS, message ("Left operand of %s is non-boolean (%t): %s", lltok_unparse (op), te1, exprNode_unparse (ret)), e1->loc); } else if (!ctype_isRealBool (te2)) { voptgenerror (FLG_BOOLOPS, message ("Right operand of %s is non-boolean (%t): %s", lltok_unparse (op), te2, exprNode_unparse (ret)), e2->loc); } else { ; } } tret = ctype_bool; } break; default: llfatalbug (cstring_makeLiteral ("There has been a problem in the parser. This is believed to result " "from a problem with bison v. 1.25. Please try rebuidling Splint " "using the pre-compiled grammar files by commenting out the " "BISON= line in the top-level Makefile.")); } skiprest: ret->typ = tret; DPRINTF (("Return type %s: %s", exprNode_unparse (ret), ctype_unparse (tret))); exprNode_checkUse (ret, e1->sref, e1->loc); exprNode_mergeUSs (ret, e2); exprNode_checkUse (ret, e2->sref, e2->loc); return ret; } /*@only@*/ exprNode exprNode_op (/*@only@*/ exprNode e1, /*@keep@*/ exprNode e2, /*@only@*/ lltok op) { exprNode ret; checkMacroParen (e1); checkMacroParen (e2); if (evaluationOrderUndefined (op) && context_maybeSet (FLG_EVALORDER)) { checkExpressionDefined (e1, e2, op); } ret = exprNode_makeOp (e1, e2, op); return (ret); } static void exprNode_checkAssignMod (exprNode e1, exprNode ret) { /* ** This is somewhat bogus! ** ** Assigning to a nested observer in a non-observer datatype ** should not produce an error. */ sRef ref = exprNode_getSref (e1); DPRINTF (("Check assign mod: %s", sRef_unparseFull (ref))); if (sRef_isObserver (ref) || ((sRef_isFileStatic (ref) || sRef_isFileOrGlobalScope (ref)) && ctype_isArray (ctype_realType (sRef_getType (ref))))) { sRef base = sRef_getBase (ref); if (sRef_isValid (base) && sRef_isObserver (base)) { exprNode_checkModify (e1, ret); } else { exprNode_checkModifyVal (e1, ret); } } else { exprNode_checkModify (e1, ret); } } exprNode exprNode_assign (/*@only@*/ exprNode e1, /*@only@*/ exprNode e2, /*@only@*/ lltok op) { bool isalloc = FALSE; bool isjustalloc = FALSE; bool noalias = FALSE; exprNode ret; DPRINTF (("%s [%s] <- %s [%s]", exprNode_unparse (e1), ctype_unparse (e1->typ), exprNode_unparse (e2), ctype_unparse (e2->typ))); if (lltok_getTok (op) != TASSIGN) { ret = exprNode_makeOp (e1, e2, op); DPRINTF (("Here goes: %s %s", ctype_unparse (e1->typ), ctype_unparse (e2->typ))); if (exprNode_isDefined (e1) && exprNode_isDefined (e2)) { if (ctype_isNumeric (e2->typ) || ctype_isNumeric (e1->typ)) { /* Its a pointer arithmetic expression like ptr += i */ noalias = TRUE; } } } else { ret = exprNode_createPartialCopy (e1); ret->kind = XPR_ASSIGN; ret->edata = exprData_makeOp (e1, e2, op); if (!exprNode_isError (e2)) { ret->sets = sRefSet_union (ret->sets, e2->sets); ret->msets = sRefSet_union (ret->msets, e2->msets); ret->uses = sRefSet_union (ret->uses, e2->uses); } } checkExpressionDefined (e1, e2, op); if (exprNode_isError (e1)) { if (!exprNode_isError (e2)) { ret->loc = fileloc_update (ret->loc, e2->loc); } else { ret->loc = fileloc_update (ret->loc, g_currentloc); } } if (!exprNode_isError (e2)) { checkMacroParen (e2); } if (exprNode_isDefined (e1)) { if (sRef_isMacroParamRef (e1->sref)) { if (context_inIterDef ()) { uentry ue = sRef_getUentry (e1->sref); if (uentry_isYield (ue)) { ; } else { if (fileloc_isDefined (e1->loc)) { voptgenerror (FLG_MACROPARAMS, message ("Assignment to non-yield iter parameter: %q", sRef_unparse (e1->sref)), e1->loc); } else { voptgenerror (FLG_MACROPARAMS, message ("Assignment to non-yield iter parameter: %q", sRef_unparse (e1->sref)), g_currentloc); } } } else { if (fileloc_isDefined (e1->loc)) { voptgenerror (FLG_MACROASSIGN, message ("Assignment to macro parameter: %q", sRef_unparse (e1->sref)), e1->loc); } else { voptgenerror (FLG_MACROASSIGN, message ("Assignment to macro parameter: %q", sRef_unparse (e1->sref)), g_currentloc); } exprNode_checkAssignMod (e1, ret); /* evans 2001-07-22 */ } } else { exprNode_checkAssignMod (e1, ret); } if (exprNode_isDefined (e2)) { if (lltok_getTok (op) == TASSIGN) { ctype te1 = exprNode_getType (e1); ctype te2 = exprNode_getType (e2); if (ctype_isVoid (te2)) { (void) gentypeerror (te2, e2, te1, e1, message ("Assignment of void value to %t: %s %s %s", te1, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e1->loc); } else if (!ctype_forceMatch (te1, te2)) { if (exprNode_matchLiteral (te1, e2)) { DPRINTF (("Literals match: %s / %s", ctype_unparse (te1), exprNode_unparse (e2))); if (ctype_isNumAbstract (te1)) { if (!context_flagOn (FLG_NUMABSTRACTLIT, e1->loc)) { (void) llgenhinterror (FLG_NUMABSTRACT, message ("Assignment of %t literal to numabstract type %t: %s %s %s", te2, te1, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), cstring_makeLiteral ("Use +numabstractlit to allow numeric literals to be used as numabstract values"), e1->loc); } } } else { (void) gentypeerror (te2, e2, te1, e1, message ("Assignment of %t to %t: %s %s %s", te2, te1, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e1->loc); } } else { /* Type checks okay */ } } exprNode_mergeUSs (ret, e2); exprNode_checkUse (ret, e2->sref, e2->loc); DPRINTF (("Do assign! %s %s", exprNode_unparse (e1), exprNode_unparse (e2))); if (noalias) { ; } else { doAssign (e1, e2, FALSE); } ret->sref = e1->sref; } else { if (exprNode_isDefined (e2)) { exprNode_mergeUSs (ret, e2); exprNode_checkUse (ret, e2->sref, e2->loc); } } if (sRef_isPointer (e1->sref) && !sRef_isMacroParamRef (e1->sref)) { exprNode_checkUse (ret, sRef_getBase (e1->sref), e1->loc); } isjustalloc = sRef_isJustAllocated (e1->sref); isalloc = sRef_isAllocated (e1->sref); if (sRef_isField (e1->sref)) { sRef root = sRef_getRootBase (sRef_getBase (e1->sref)); if (!sRef_isAllocated (root) && !sRef_isMacroParamRef (root)) { exprNode_checkUse (ret, root, e1->loc); } } /* ** be careful! this defines e1->sref. */ /* evans 2001-07-22: removed if (!sRef_isMacroParamRef (e1->sref)) */ DPRINTF (("Setting: %s -> %s", exprNode_unparse (ret), sRef_unparse (e1->sref))); exprNode_checkSet (ret, e1->sref); if (isjustalloc) { sRef_setAllocatedComplete (e1->sref, exprNode_isDefined (e2) ? e2->loc : e1->loc); } else { if (isalloc) { sRef_setAllocatedShallowComplete (e1->sref, exprNode_loc (e2)); } } } return ret; } exprNode exprNode_cond (/*@keep@*/ exprNode pred, /*@keep@*/ exprNode ifclause, /*@keep@*/ exprNode elseclause) { exprNode ret; if (!exprNode_isError (pred)) { ret = exprNode_createPartialCopy (pred); checkMacroParen (pred); exprNode_checkPred (cstring_makeLiteralTemp ("conditional"), pred); if (!exprNode_isError (ifclause)) { checkMacroParen (ifclause); /* update macro counts! */ if (!exprNode_isError (elseclause)) { checkMacroParen (elseclause); if (!exprNode_matchTypes (ifclause, elseclause)) { if (gentypeerror (exprNode_getType (ifclause), ifclause, exprNode_getType (elseclause), elseclause, message ("Conditional clauses are not of same type: " "%s (%t), %s (%t)", exprNode_unparse (ifclause), exprNode_getType (ifclause), exprNode_unparse (elseclause), exprNode_getType (elseclause)), ifclause->loc)) { ret->sref = sRef_undefined; ret->typ = ctype_unknown; } } else { /* for now...should merge the states */ ret->sref = ifclause->sref; ret->typ = ifclause->typ; if (exprNode_isNullValue (ifclause)) { ret->typ = elseclause->typ; } } exprNode_checkUse (ret, pred->sref, pred->loc); exprNode_checkUse (ifclause, ifclause->sref, ifclause->loc); exprNode_checkUse (elseclause, elseclause->sref, elseclause->loc); exprNode_mergeCondUSs (ret, ifclause, elseclause); } else { ret->typ = ifclause->typ; exprNode_checkUse (pred, pred->sref, pred->loc); exprNode_checkUse (ifclause, ifclause->sref, ifclause->loc); exprNode_mergeCondUSs (ret, ifclause, exprNode_undefined); } } else { if (!exprNode_isError (elseclause)) { ret->typ = elseclause->typ; exprNode_checkUse (pred, pred->sref, pred->loc); exprNode_checkUse (elseclause, elseclause->sref, elseclause->loc); exprNode_mergeCondUSs (ret, exprNode_undefined, elseclause); } } } else /* pred is error */ { if (!exprNode_isError (ifclause)) { ret = exprNode_createSemiCopy (ifclause); checkMacroParen (ifclause); /* update macro counts! */ if (!exprNode_isError (elseclause)) { checkMacroParen (elseclause); ret->typ = ifclause->typ; if (!ctype_forceMatch (ifclause->typ, elseclause->typ)) { if (gentypeerror (exprNode_getType (ifclause), ifclause, exprNode_getType (elseclause), elseclause, message ("Conditional clauses are not of same type: " "%s (%t), %s (%t)", exprNode_unparse (ifclause), exprNode_getType (ifclause), exprNode_unparse (elseclause), exprNode_getType (elseclause)), ifclause->loc)) { ret->typ = ctype_unknown; } } exprNode_checkUse (ifclause, ifclause->sref, ifclause->loc); exprNode_checkUse (elseclause, elseclause->sref, elseclause->loc); exprNode_mergeCondUSs (ret, ifclause, elseclause); } } else if (!exprNode_isError (elseclause)) /* pred, if errors */ { ret = exprNode_createSemiCopy (ifclause); ret->typ = elseclause->typ; checkMacroParen (elseclause); exprNode_checkUse (elseclause, elseclause->sref, elseclause->loc); exprNode_mergeCondUSs (ret, exprNode_undefined, elseclause); } else /* all errors! */ { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (g_currentloc)); } } ret->kind = XPR_COND; ret->edata = exprData_makeCond (pred, ifclause, elseclause); if (exprNode_isDefined (ifclause) && exprNode_isDefined (elseclause)) { exprNode_combineControl (ret, ifclause, elseclause); } return (ret); } exprNode exprNode_condIfOmit (/*@keep@*/ exprNode pred, /*@keep@*/ exprNode elseclause) { exprNode ifclause = exprNode_createPartialCopy (pred); if (!context_flagOn (FLG_GNUEXTENSIONS, exprNode_loc (pred))) { (void) llgenhinterror (FLG_SYNTAX, message ("Conditionals with Omitted Operands is not supported by ISO C99"), message ("Use +gnuextensions to allow compound statement " "expressions (and other GNU language extensions) " "without this warning"), exprNode_loc (pred)); } return exprNode_cond(pred, ifclause, elseclause); } exprNode exprNode_vaArg (/*@only@*/ lltok tok, /*@only@*/ exprNode arg, /*@only@*/ qtype qt) { ctype totype = qtype_getType (qt); exprNode ret = exprNode_createPartialLocCopy (arg, fileloc_copy (lltok_getLoc (tok))); ctype targ; /* ** check use of va_arg : , type -> type */ if (exprNode_isError (arg)) { } else { targ = exprNode_getType (arg); /* ** arg should have be a pointer */ if (!ctype_isUA (targ) || (!typeId_equal (ctype_typeId (targ), usymtab_getTypeId (cstring_makeLiteralTemp ("va_list"))))) { voptgenerror (FLG_TYPE, message ("First argument to va_arg is not a va_list (type %t): %s", targ, exprNode_unparse (arg)), arg->loc); } exprNode_checkSet (ret, arg->sref); } /* ** return type is totype */ ret->typ = totype; ret->kind = XPR_VAARG; ret->edata = exprData_makeCast (tok, arg, qt); return (ret); } exprNode exprNode_labelMarker (/*@only@*/ cstring label) { exprNode ret = exprNode_createPlain (ctype_undefined); ret->kind = XPR_LABEL; ret->edata = exprData_makeLiteral (label); ret->isJumpPoint = TRUE; return (ret); /* for now, ignore label */ } exprNode exprNode_notReached (/*@returned@*/ exprNode stmt) { if (exprNode_isDefined (stmt)) { stmt->isJumpPoint = TRUE; /* This prevent stray no return path errors, etc. */ stmt->exitCode = XK_MUSTEXIT; } return (stmt); } bool exprNode_isDefaultMarker (exprNode e) { if (exprNode_isDefined (e)) { return (e->kind == XPR_DEFAULT || e->kind == XPR_FTDEFAULT); } return FALSE; } bool exprNode_isCaseMarker (exprNode e) { if (exprNode_isDefined (e)) { return (e->kind == XPR_FTCASE || e->kind == XPR_CASE); } return FALSE; } bool exprNode_isLabelMarker (exprNode e) { if (exprNode_isDefined (e)) { return (e->kind == XPR_LABEL); } return FALSE; } exprNode exprNode_caseMarker (/*@only@*/ exprNode test, bool fallThrough) { exprNode ret = exprNode_createPartialCopy (test); ret->kind = fallThrough ? XPR_FTCASE : XPR_CASE; if (exprNode_isError (test)) { return ret; } exprNode_checkUse (ret, test->sref, test->loc); usymtab_setExitCode (ret->exitCode); if (ret->mustBreak) { usymtab_setMustBreak (); } ret->edata = exprData_makeSingle (test); ret->isJumpPoint = TRUE; return ret; } # if 0 exprNode exprNode_caseStatement (/*@only@*/ exprNode test, /*@only@*/ exprNode stmt, bool fallThrough) { exprNode ret = exprNode_createPartialCopy (test); ret->kind = fallThrough ? XPR_FTCASE : XPR_CASE; ret->edata = exprData_makePair (test, stmt); ret->isJumpPoint = TRUE; if (exprNode_isError (test)) { return ret; } exprNode_checkUse (ret, test->sref, test->loc); if (exprNode_isError (stmt)) { return ret; } exprNode_mergeUSs (ret, stmt); ret->exitCode = stmt->exitCode; ret->mustBreak = stmt->mustBreak; ret->canBreak = stmt->canBreak; usymtab_setExitCode (ret->exitCode); if (ret->mustBreak) { usymtab_setMustBreak (); } return ret; } # endif /*@notnull@*/ /*@only@*/ exprNode exprNode_defaultMarker (/*@only@*/ lltok def, bool fallThrough) { exprNode ret = exprNode_createTok (def); ret->isJumpPoint = TRUE; ret->kind = fallThrough ? XPR_FTDEFAULT : XPR_DEFAULT; return (ret); } bool exprNode_mayEscape (exprNode e) { if (exprNode_isDefined (e)) { return exitkind_couldEscape (e->exitCode); } return FALSE; } static bool exprNode_mustBreak (exprNode e) { if (exprNode_isDefined (e)) { return e->mustBreak; } return FALSE; } bool exprNode_mustEscape (exprNode e) { if (exprNode_isDefined (e)) { return exitkind_mustEscape (e->exitCode) || exprNode_mustBreak (e); } return FALSE; } bool exprNode_errorEscape (exprNode e) { if (exprNode_isDefined (e)) { return exitkind_isError (e->exitCode); } return FALSE; } exprNode exprNode_concat (/*@only@*/ exprNode e1, /*@only@*/ exprNode e2) { exprNode ret = exprNode_createPartialCopy (e1); DPRINTF (("Concat: %s / %s", exprNode_unparse (e1), exprNode_unparse (e2))); ret->edata = exprData_makePair (e1, e2); ret->kind = XPR_STMTLIST; if (exprNode_isDefined (e1)) { ret->isJumpPoint = e1->isJumpPoint; ret->canBreak = e1->canBreak; } else { if (exprNode_isDefined (e2)) { ret->loc = fileloc_update (ret->loc, e2->loc); } } if (exprNode_isDefined (e2)) { ret->exitCode = e2->exitCode; ret->mustBreak = e2->mustBreak; if (e2->canBreak) ret->canBreak = TRUE; } /* ** if e1 must return, then e2 is unreachable! */ if (exprNode_isDefined (e1) && exprNode_isDefined (e2)) { if ((exprNode_mustEscape (e1) || exprNode_mustBreak (e1)) && !(e2->isJumpPoint)) { if (context_getFlag (FLG_UNREACHABLE)) { exprNode nr = e2; if (e2->kind == XPR_STMT) { nr = exprData_getUopNode (e2->edata); } if ((nr->kind == XPR_TOK && lltok_isSemi (exprData_getTok (nr->edata)))) { /* okay to have unreachable ";" */ ret->exitCode = XK_MUSTEXIT; ret->canBreak = TRUE; } else { if (optgenerror (FLG_UNREACHABLE, message ("Unreachable code: %s", exprNode_unparseFirst (nr)), exprNode_loc (nr))) { ret->isJumpPoint = TRUE; ret->mustBreak = FALSE; ret->exitCode = XK_ERROR; DPRINTF (("Jump point: %s", exprNode_unparse (ret))); } else { ret->exitCode = XK_MUSTEXIT; ret->canBreak = TRUE; } } } } else { if ((e2->kind == XPR_CASE || e2->kind == XPR_DEFAULT)) { /* ** We want a warning anytime we have: ** case xxx: ... ** yyy; <<<- no break or return ** case zzz: ... */ exprNode lastStmt = exprNode_lastStatement (e1); if (exprNode_isDefined (lastStmt) && !exprNode_mustEscape (lastStmt) && !exprNode_mustBreak (lastStmt) && !exprNode_isCaseMarker (lastStmt) && !exprNode_isDefaultMarker (lastStmt) && !exprNode_isLabelMarker (lastStmt)) { voptgenerror (FLG_CASEBREAK, cstring_makeLiteral ("Fall through case (no preceding break)"), e2->loc); } } } } exprNode_mergeUSs (ret, e2); usymtab_setExitCode (ret->exitCode); if (ret->mustBreak) { usymtab_setMustBreak (); } DPRINTF (("==> %s", exprNode_unparse (ret))); return ret; } exprNode exprNode_createTok (/*@only@*/ lltok t) { exprNode ret = exprNode_create (ctype_unknown); ret->kind = XPR_TOK; ret->edata = exprData_makeTok (t); return ret; } exprNode exprNode_statement (/*@only@*/ exprNode e, /*@only@*/ lltok t) { if (!exprNode_isError (e)) { exprChecks_checkStatementEffect(e); } return (exprNode_statementError (e, t)); } static exprNode exprNode_statementError (/*@only@*/ exprNode e, /*@only@*/ lltok t) { exprNode ret = exprNode_createPartialCopy (e); if (!exprNode_isError (e)) { if (e->kind != XPR_ASSIGN) { exprNode_checkUse (ret, e->sref, e->loc); } ret->exitCode = e->exitCode; ret->canBreak = e->canBreak; ret->mustBreak = e->mustBreak; } ret->edata = exprData_makeUop (e, t); ret->kind = XPR_STMT; return ret; } exprNode exprNode_checkExpr (/*@returned@*/ exprNode e) { if (!exprNode_isError (e)) { if (e->kind != XPR_ASSIGN) { exprNode_checkUse (e, e->sref, e->loc); } } return e; } void exprNode_produceGuards (exprNode pred) { if (!exprNode_isError (pred)) { if (ctype_isRealPointer (pred->typ)) { pred->guards = guardSet_addTrueGuard (pred->guards, pred->sref); } exprNode_checkUse (pred, pred->sref, pred->loc); exprNode_resetSref (pred); } } exprNode exprNode_compoundStatementExpression (/*@only@*/ lltok tlparen, /*@only@*/ exprNode e) { exprNode laststmt; DPRINTF (("Compound: %s", exprNode_unparse (e))); if (!context_flagOn (FLG_GNUEXTENSIONS, exprNode_loc (e))) { (void) llgenhinterror (FLG_SYNTAX, message ("Compound statement expressions is not supported by ISO C99"), message ("Use +gnuextensions to allow compound statement expressions (and other GNU language extensions) " "without this warning"), exprNode_loc (e)); } /* ** The type of a compoundStatementExpression is the type of the last statement */ llassert (exprNode_isBlock (e)); laststmt = exprNode_lastStatement (e); DPRINTF (("Last statement: %s / %s", exprNode_unparse (laststmt), ctype_unparse (exprNode_getType (laststmt)))); DPRINTF (("e: %s", exprNode_unparse (e))); e->typ = exprNode_getType (laststmt); return exprNode_addParens (tlparen, e); } exprNode exprNode_makeBlock (/*@only@*/ exprNode e) { exprNode ret = exprNode_createPartialCopy (e); if (!exprNode_isError (e)) { ret->exitCode = e->exitCode; ret->canBreak = e->canBreak; ret->mustBreak = e->mustBreak; } DPRINTF (("Block e: %s", exprNode_unparse (e))); ret->edata = exprData_makeSingle (e); ret->kind = XPR_BLOCK; DPRINTF (("Block: %s", exprNode_unparse (ret))); return ret; } bool exprNode_isBlock (exprNode e) { return (exprNode_isDefined (e) && ((e)->kind == XPR_BLOCK)); } bool exprNode_isStatement (exprNode e) { return (exprNode_isDefined (e) && ((e)->kind == XPR_STMT)); } bool exprNode_isAssign (exprNode e) { if (exprNode_isDefined (e)) { return (e->kind == XPR_ASSIGN); } return FALSE; } bool exprNode_isEmptyStatement (exprNode e) { return (exprNode_isDefined (e) && (e->kind == XPR_TOK) && (lltok_isSemi (exprData_getTok (e->edata)))); } bool exprNode_isMultiStatement (exprNode e) { return (exprNode_isDefined (e) && ((e->kind == XPR_FOR) || (e->kind == XPR_FORPRED) || (e->kind == XPR_IF) || (e->kind == XPR_IFELSE) || (e->kind == XPR_WHILE) || (e->kind == XPR_WHILEPRED) || (e->kind == XPR_DOWHILE) || (e->kind == XPR_BLOCK) || (e->kind == XPR_STMT) || (e->kind == XPR_STMTLIST) || (e->kind == XPR_SWITCH))); } void exprNode_checkIfPred (exprNode pred) { exprNode_checkPred (cstring_makeLiteralTemp ("if"), pred); } exprNode exprNode_if (/*@only@*/ exprNode pred, /*@only@*/ exprNode tclause) { exprNode ret; bool emptyErr = FALSE; if (context_maybeSet (FLG_IFEMPTY)) { if (exprNode_isEmptyStatement (tclause)) { emptyErr = optgenerror (FLG_IFEMPTY, cstring_makeLiteral ("Body of if statement is empty"), exprNode_loc (tclause)); } } if (!emptyErr && context_maybeSet (FLG_IFBLOCK)) { if (exprNode_isDefined (tclause) && !exprNode_isBlock (tclause)) { voptgenerror (FLG_IFBLOCK, message ("Body of if statement is not a block: %s", exprNode_unparse (tclause)), exprNode_loc (tclause)); } } if (exprNode_isError (pred)) { if (exprNode_isError (tclause)) { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (g_currentloc)); } else { ret = exprNode_createPartialCopy (tclause); } } else { if (exprNode_mustEscape (pred)) { voptgenerror (FLG_UNREACHABLE, message ("Predicate always exits: %s", exprNode_unparse (pred)), exprNode_loc (pred)); } exprNode_checkUse (pred, pred->sref, pred->loc); if (!exprNode_isError (tclause)) { exprNode_mergeCondUSs (pred, tclause, exprNode_undefined); } ret = exprNode_createPartialCopy (pred); } ret->kind = XPR_IF; ret->edata = exprData_makePair (pred, tclause); ret->exitCode = XK_UNKNOWN; if (exprNode_isDefined (tclause)) { ret->exitCode = exitkind_makeConditional (tclause->exitCode); ret->canBreak = tclause->canBreak; ret->sets = sRefSet_union (ret->sets, tclause->sets); ret->msets = sRefSet_union (ret->msets, tclause->msets); ret->uses = sRefSet_union (ret->uses, tclause->uses); } ret->mustBreak = FALSE; return ret; } exprNode exprNode_ifelse (/*@only@*/ exprNode pred, /*@only@*/ exprNode tclause, /*@only@*/ exprNode eclause) { exprNode ret; bool tEmptyErr = FALSE; bool eEmptyErr = FALSE; if (context_maybeSet (FLG_IFEMPTY)) { if (exprNode_isEmptyStatement (tclause)) { tEmptyErr = optgenerror (FLG_IFEMPTY, cstring_makeLiteral ("Body of if clause of if statement is empty"), exprNode_loc (tclause)); } if (exprNode_isEmptyStatement (eclause)) { eEmptyErr = optgenerror (FLG_IFEMPTY, cstring_makeLiteral ("Body of else clause of if statement is empty"), exprNode_loc (eclause)); } } if (context_maybeSet (FLG_IFBLOCK)) { if (!tEmptyErr && exprNode_isDefined (tclause) && !exprNode_isBlock (tclause)) { voptgenerror (FLG_IFBLOCK, message ("Body of if clause of if statement is not a block: %s", exprNode_unparse (tclause)), exprNode_loc (tclause)); } if (!eEmptyErr && exprNode_isDefined (eclause) && !exprNode_isBlock (eclause) && !(eclause->kind == XPR_IF) && !(eclause->kind == XPR_IFELSE)) { voptgenerror (FLG_IFBLOCK, message ("Body of else clause of if statement is not a block: %s", exprNode_unparse (eclause)), exprNode_loc (eclause)); } } if (context_maybeSet (FLG_ELSEIFCOMPLETE)) { if (exprNode_isDefined (eclause) && (eclause->kind == XPR_IF)) { voptgenerror (FLG_ELSEIFCOMPLETE, message ("Incomplete else if logic (no final else): %s", exprNode_unparse (eclause)), exprNode_loc (eclause)); } } if (exprNode_isError (pred)) { if (exprNode_isError (tclause)) { if (exprNode_isError (eclause)) { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (g_currentloc)); } else { ret = exprNode_createPartialCopy (eclause); } } else { ret = exprNode_createPartialCopy (tclause); } } else /* pred is okay */ { ret = exprNode_createPartialCopy (pred); if (exprNode_mustEscape (pred)) { voptgenerror (FLG_UNREACHABLE, message ("Predicate always exits: %s", exprNode_unparse (pred)), exprNode_loc (pred)); } exprNode_checkUse (ret, pred->sref, pred->loc); exprNode_mergeCondUSs (ret, tclause, eclause); } ret->kind = XPR_IFELSE; ret->edata = exprData_makeCond (pred, tclause, eclause); if (exprNode_isDefined (tclause) && exprNode_isDefined (eclause)) { exprNode_combineControl (ret, tclause, eclause); ret->loc = fileloc_update (ret->loc, eclause->loc); } return ret; } /* ** *allpaths <- TRUE iff all executions paths must go through the switch */ static bool checkSwitchExpr (exprNode test, /*@dependent@*/ exprNode e, /*@out@*/ bool *allpaths) { exprNodeSList el = exprNode_flatten (e); bool mustReturn = TRUE; /* find a branch that doesn't */ bool thisReturn = FALSE; bool hasDefault = FALSE; bool hasAllMembers = FALSE; bool inSwitch = FALSE; bool isEnumSwitch = FALSE; bool canBreak = FALSE; bool fallThrough = FALSE; ctype ct = ctype_unknown; enumNameSList usedEnums; enumNameList enums; if (exprNode_isDefined (test)) { ctype ttype; ct = test->typ; ttype = ctype_realType (ct); if (ctype_isEnum (ttype)) { isEnumSwitch = TRUE; enums = ctype_elist (ttype); usedEnums = enumNameSList_new (); } } exprNodeSList_elements (el, current) { DPRINTF ((message("checkSwitchExpr current = %s ", exprNode_unparse(current) ) )); if (exprNode_isDefined (current)) { switch (current->kind) { case XPR_FTDEFAULT: case XPR_DEFAULT: if (hasDefault) { voptgenerror (FLG_DUPLICATECASES, message ("Duplicate default cases in switch"), exprNode_loc (current)); } /*@fallthrough@*/ case XPR_FTCASE: case XPR_CASE: if (current->kind == XPR_DEFAULT || current->kind == XPR_FTDEFAULT) { hasDefault = TRUE; } else { if (isEnumSwitch) { exprNode st = exprData_getSingle (current->edata); uentry ue = exprNode_getUentry (st); if (uentry_isValid (ue)) { cstring cname = uentry_rawName (ue); if (enumNameList_member (/*@-usedef@*/enums/*@=usedef@*/, cname)) { if (enumNameSList_member (/*@-usedef@*/usedEnums/*@=usedef@*/, cname)) { voptgenerror (FLG_DUPLICATECASES, message ("Duplicate case in switch: %s", cname), current->loc); } else { enumNameSList_addh (usedEnums, cname); } } else { voptgenerror (FLG_TYPE, message ("Case in switch not %s member: %s", ctype_unparse (ct), cname), current->loc); } } } } if (inSwitch && !fallThrough) { if (!thisReturn || canBreak) { mustReturn = FALSE; } } fallThrough = TRUE; inSwitch = TRUE; thisReturn = FALSE; canBreak = FALSE; /*@switchbreak@*/ break; default: thisReturn = thisReturn || exprNode_mustEscape (current); canBreak = canBreak || current->canBreak; if (canBreak) fallThrough = FALSE; } } } end_exprNodeSList_elements; if (inSwitch) /* check the last one! */ { if (!thisReturn || canBreak) { mustReturn = FALSE; } } if (isEnumSwitch) { if (!hasDefault && (enumNameSList_size (/*@-usedef@*/usedEnums/*@=usedef@*/) != enumNameList_size (/*@-usedef@*/enums/*@=usedef@*/))) { enumNameSList unused = enumNameSList_subtract (enums, usedEnums); voptgenerror (FLG_MISSCASE, message ("Missing case%s in switch: %q", cstring_makeLiteralTemp ((enumNameSList_size (unused) > 1) ? "s" : ""), enumNameSList_unparse (unused)), g_currentloc); enumNameSList_free (unused); *allpaths = FALSE; /* evans 2002-01-01 */ } else { hasAllMembers = TRUE; *allpaths = TRUE; } enumNameSList_free (usedEnums); } else { *allpaths = hasDefault; } exprNodeSList_free (el); return ((hasDefault || hasAllMembers) && mustReturn); } exprNode exprNode_switch (/*@only@*/ exprNode e, /*@only@*/ exprNode s) { exprNode ret = exprNode_createPartialCopy (e); bool allpaths; DPRINTF (("Switch: %s", exprNode_unparse (s))); ret->kind = XPR_SWITCH; ret->edata = exprData_makePair (e, s); if (!exprNode_isError (s)) { exprNode fs = exprNode_firstStatement (s); ret->loc = fileloc_update (ret->loc, s->loc); if (exprNode_isUndefined (fs) || exprNode_isCaseMarker (fs) || exprNode_isLabelMarker (fs) || exprNode_isDefaultMarker (fs)) { ; } else { voptgenerror (FLG_FIRSTCASE, message ("Statement after switch is not a case: %s", exprNode_unparse (fs)), fs->loc); } } if (!exprNode_isError (e)) { if (checkSwitchExpr (e, s, &allpaths)) { ret->exitCode = XK_MUSTRETURN; } else { ret->exitCode = e->exitCode; } ret->canBreak = e->canBreak; ret->mustBreak = e->mustBreak; } /* ** forgot this! ** exprNode.c:3883,32: Variable allpaths used before definition */ else { allpaths = FALSE; } DPRINTF (("Context exit switch!")); context_exitSwitch (ret, allpaths); DPRINTF (("Context exit switch done!")); return ret; } static void checkInfiniteLoop (/*@notnull@*/ exprNode test, /*@notnull@*/ exprNode body) { sRefSet tuses = test->uses; if (!sRefSet_isEmpty (test->uses)) { sRefSet sets = sRefSet_newCopy (body->sets); bool hasError = TRUE; bool innerState = FALSE; sRefSet tuncon = sRefSet_undefined; sets = sRefSet_union (sets, test->sets); sets = sRefSet_union (sets, body->msets); sets = sRefSet_union (sets, test->msets); sRefSet_allElements (tuses, el) { if (sRef_isUnconstrained (el)) { tuncon = sRefSet_insert (tuncon, el); } else { if (sRefSet_member (sets, el)) { hasError = FALSE; break; } } if (sRef_isInternalState (el) || sRef_isFileStatic (sRef_getRootBase (el))) { innerState = TRUE; } } end_sRefSet_allElements ; if (hasError) { sRefSet suncon = sRefSet_undefined; bool sinner = FALSE; sRefSet_allElements (sets, el) { if (sRef_isUnconstrained (el)) { suncon = sRefSet_insert (suncon, el); } else if (sRef_isInternalState (el)) { sinner = TRUE; } else { ; } } end_sRefSet_allElements ; if (sinner && innerState) { ; } else if (sRefSet_isEmpty (tuncon) && sRefSet_isEmpty (suncon)) { voptgenerror (FLG_INFLOOPS, message ("Suspected infinite loop. No value used in loop test (%q) " "is modified by test or loop body.", sRefSet_unparsePlain (tuses)), test->loc); } else { if (sRefSet_isEmpty (tuncon)) { voptgenerror (FLG_INFLOOPSUNCON, message ("Suspected infinite loop. No condition values " "modified. Modification possible through " "unconstrained calls: %q", sRefSet_unparsePlain (suncon)), test->loc); } else { voptgenerror (FLG_INFLOOPSUNCON, message ("Suspected infinite loop. No condition values " "modified. Possible undetected dependency through " "unconstrained calls in loop test: %q", sRefSet_unparsePlain (tuncon)), test->loc); } } } sRefSet_free (sets); } } exprNode exprNode_while (/*@keep@*/ exprNode t, /*@keep@*/ exprNode b) { exprNode ret; bool emptyErr = FALSE; if (context_maybeSet (FLG_WHILEEMPTY)) { if (exprNode_isEmptyStatement (b)) { emptyErr = optgenerror (FLG_WHILEEMPTY, cstring_makeLiteral ("Body of while statement is empty"), exprNode_loc (b)); } } if (!emptyErr && context_maybeSet (FLG_WHILEBLOCK)) { if (exprNode_isDefined (b) && !exprNode_isBlock (b)) { if (context_inIterDef () && (b->kind == XPR_STMTLIST || b->kind == XPR_TOK)) { ; /* no error */ } else { voptgenerror (FLG_WHILEBLOCK, message ("Body of while statement is not a block: %s", exprNode_unparse (b)), exprNode_loc (b)); } } } if (exprNode_isError (t)) { if (exprNode_isError (b)) { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (g_currentloc)); } else { ret = exprNode_createPartialCopy (b); } } else { exprNode test; ret = exprNode_createPartialCopy (t); llassert (t->kind == XPR_WHILEPRED); test = exprData_getSingle (t->edata); if (!exprNode_isError (b) && exprNode_isDefined (test)) { if (context_maybeSet (FLG_INFLOOPS) || context_maybeSet (FLG_INFLOOPSUNCON)) { /* ** check that some variable in the predicate is set by the body ** if the predicate uses any variables */ checkInfiniteLoop (test, b); } exprNode_mergeUSs (ret, b); if (exprNode_isDefined (b)) { ret->exitCode = exitkind_makeConditional (b->exitCode); } } } ret->edata = exprData_makePair (t, b); ret->kind = XPR_WHILE; if (exprNode_isDefined (t) && exprNode_mustEscape (t)) { voptgenerror (FLG_ALWAYSEXITS, message ("Predicate always exits: %s", exprNode_unparse (t)), exprNode_loc (t)); } ret->exitCode = XK_NEVERESCAPE; /* ** If loop is infinite, and there is no break inside, ** exit code is never reach. */ if (exprNode_knownIntValue (t)) { if (!exprNode_isZero (t)) { if (exprNode_isDefined (b)) { if (!b->canBreak) { /* Really, it means never reached. */ ret->exitCode = XK_MUSTEXIT; } } } } else { ; } ret->canBreak = FALSE; ret->mustBreak = FALSE; return ret; } /* ** do { b } while (t); ** ** note: body passed as first argument */ exprNode exprNode_doWhile (/*@only@*/ exprNode b, /*@only@*/ exprNode t) { exprNode ret; DPRINTF (("Do while: %s / %s", exprNode_unparse (b), exprNode_unparse (t))); if (exprNode_isError (t)) { if (exprNode_isError (b)) { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (g_currentloc)); } else { ret = exprNode_createPartialCopy (b); ret->exitCode = exitkind_makeConditional (b->exitCode); exprNode_checkUse (ret, b->sref, b->loc); ret->exitCode = b->exitCode; ret->canBreak = b->canBreak; ret->mustBreak = FALSE; } } else { DPRINTF (("Do while: %s / %s", exitkind_unparse (t->exitCode), exitkind_unparse (b->exitCode))); ret = exprNode_createPartialCopy (t); exprNode_checkPred (cstring_makeLiteralTemp ("while"), t); if (!exprNode_isError (b)) { /* ** forgot the copy's --- why wasn't this detected?? */ ret->sets = sRefSet_copyInto (ret->sets, b->sets); ret->msets = sRefSet_copyInto (ret->msets, b->msets); ret->uses = sRefSet_copyInto (ret->uses, b->uses); /* left this out --- causes and aliasing bug (infinite loop) should be detected?? */ exprNode_checkUse (ret, b->sref, b->loc); exprNode_mergeUSs (ret, t); exprNode_checkUse (ret, t->sref, t->loc); /* evans 2001-10-05: while loop can break */ ret->exitCode = exitkind_makeConditional (b->exitCode); DPRINTF (("Do while: %s", exitkind_unparse (ret->exitCode))); ret->canBreak = b->canBreak; /* Always FALSE for doWhile loops - break's when test is false */ ret->mustBreak = FALSE; /* b->mustBreak; */ } } context_exitDoWhileClause (t); ret->kind = XPR_DOWHILE; ret->edata = exprData_makePair (t, b); return ret; } bool exprNode_loopMustExec (exprNode forPred) { /* ** Returns true if it is obvious that the loop always executes at least once ** ** For now, we only identify the most obvious cases. Should be true anytime ** we can prove init => !test. */ if (exprNode_isDefined (forPred)) { exprNode init, test, inc; exprData edata; llassert (forPred->kind == XPR_FORPRED); edata = forPred->edata; init = exprData_getTripleInit (edata); test = exprData_getTripleTest (edata); inc = exprData_getTripleInc (edata); if (exprNode_isAssign (init)) { exprNode loopVar = exprData_getOpA (init->edata); exprNode loopInit = exprData_getOpB (init->edata); if (exprNode_isDefined (test) && test->kind == XPR_OP) { exprNode testVar = exprData_getOpA (test->edata); exprNode testVal = exprData_getOpB (test->edata); lltok comp = exprData_getOpTok (test->edata); int opid = lltok_getTok (comp); DPRINTF (("Same storage: %s / %s", exprNode_unparse (loopVar), exprNode_unparse (testVar))); if (exprNode_sameStorage (loopVar, testVar)) { multiVal valinit = exprNode_getValue (loopInit); multiVal valtest = exprNode_getValue (testVal); DPRINTF (("Values: %s / %s", multiVal_unparse (valinit), multiVal_unparse (valtest))); if (multiVal_isInt (valinit) && multiVal_isInt (valtest)) { long v1 = multiVal_forceInt (valinit); long v2 = multiVal_forceInt (valtest); DPRINTF (("Here: %ld %ld", v1, v2)); if ((opid == EQ_OP && v1 < v2) || (opid == NE_OP && v1 != v2) || (opid == TLT && v1 <= v2) || (opid == TGT && v1 >= v2) || (opid == LE_OP && v1 < v2) || (opid == GE_OP && v1 > v2)) { DPRINTF (("mustexec if inc")); return TRUE; } } } } } } DPRINTF (("loop must exec: FALSE")); return FALSE; } exprNode exprNode_for (/*@keep@*/ exprNode inc, /*@keep@*/ exprNode body) { exprNode ret; bool emptyErr = FALSE; if (context_maybeSet (FLG_FOREMPTY)) { if (exprNode_isEmptyStatement (body)) { emptyErr = optgenerror (FLG_FOREMPTY, cstring_makeLiteral ("Body of for statement is empty"), exprNode_loc (body)); } } if (!emptyErr && context_maybeSet (FLG_FORBLOCK)) { if (exprNode_isDefined (body) && !exprNode_isBlock (body)) { if (context_inIterDef () && (body->kind == XPR_STMTLIST || body->kind == XPR_TOK)) { ; /* no error */ } else { voptgenerror (FLG_FORBLOCK, message ("Body of for statement is not a block: %s", exprNode_unparse (body)), exprNode_loc (body)); } } } /* ** for ud purposes: (alreadly) init -> test -> (now) LOOP: body + inc + test */ if (exprNode_isError (body)) { ret = exprNode_createPartialCopy (inc); } else { ret = exprNode_createPartialCopy (body); ret->exitCode = exitkind_makeConditional (body->exitCode); exprNode_mergeUSs (inc, body); if (exprNode_isDefined (inc)) { exprNode tmp; context_setMessageAnnote (cstring_makeLiteral ("in post loop increment")); tmp = exprNode_effect (exprData_getTripleInc (inc->edata)); exprNode_freeShallow (tmp); context_clearMessageAnnote (); context_setMessageAnnote (cstring_makeLiteral ("in post loop test")); tmp = exprNode_effect (exprData_getTripleTest (inc->edata)); exprNode_freeShallow (tmp); context_clearMessageAnnote (); ret->uses = sRefSet_copyInto (ret->uses, inc->uses); ret->sets = sRefSet_copyInto (ret->sets, inc->sets); ret->msets = sRefSet_copyInto (ret->msets, inc->msets); } } ret->kind = XPR_FOR; ret->edata = exprData_makePair (inc, body); if (exprNode_isDefined (inc)) { exprNode test = exprData_getTripleTest (inc->edata); if (exprNode_isUndefined (test)) { if (exprNode_isDefined (body)) { if (!body->canBreak) { /* Really, it means never reached. */ ret->exitCode = XK_MUSTEXIT; } } } } return (ret); } /* ** for (init; test; inc) ** ==> ** init; ** while (test) { body; inc; } ** ** Now: check use of init (may set vars for test) ** check use of test ** no checks on inc _*/ /*@observer@*/ guardSet exprNode_getForGuards (exprNode pred) { exprNode test; if (exprNode_isError (pred)) return guardSet_undefined; llassert (pred->kind == XPR_FORPRED); test = exprData_getTripleTest (pred->edata); if (!exprNode_isError (test)) { return (test->guards); } return guardSet_undefined; } exprNode exprNode_whilePred (/*@only@*/ exprNode test) { exprNode ret = exprNode_createSemiCopy (test); if (exprNode_isDefined (test)) { exprNode_copySets (ret, test); exprNode_checkPred (cstring_makeLiteralTemp ("while"), test); exprNode_checkUse (ret, test->sref, test->loc); exprNode_produceGuards (test); ret->guards = guardSet_copy (test->guards); } ret->edata = exprData_makeSingle (test); ret->kind = XPR_WHILEPRED; return ret; } exprNode exprNode_forPred (/*@only@*/ exprNode init, /*@only@*/ exprNode test, /*@only@*/ exprNode inc) { exprNode ret; /* ** for ud purposes: init -> test -> LOOP: [ body, inc ] */ exprNode_checkPred (cstring_makeLiteralTemp ("for"), test); if (!exprNode_isError (inc)) { ret = exprNode_createPartialCopy (inc); } else { if (!exprNode_isError (init)) { ret = exprNode_createPartialCopy (init); } else if (!exprNode_isError (test)) { ret = exprNode_createPartialCopy (test); } else { ret = exprNode_createUnknown (); } } exprNode_mergeUSs (ret, init); if (exprNode_isDefined (init)) { exprNode_checkUse (ret, init->sref, init->loc); } exprNode_mergeUSs (ret, test); if (exprNode_isDefined (test)) { exprNode_checkUse (ret, test->sref, test->loc); } ret->kind = XPR_FORPRED; ret->edata = exprData_makeFor (init, test, inc); return (ret); } /*@notnull@*/ /*@only@*/ exprNode exprNode_goto (/*@only@*/ cstring label) { exprNode ret = exprNode_createUnknown (); if (context_inMacro ()) { voptgenerror (FLG_MACROSTMT, message ("Macro %s uses goto (not functional)", context_inFunctionName ()), g_currentloc); } ret->kind = XPR_GOTO; ret->edata = exprData_makeLiteral (label); ret->mustBreak = TRUE; ret->exitCode = XK_GOTO; ret->canBreak = TRUE; return ret; } exprNode exprNode_continue (/*@only@*/ lltok l, int qcontinue) { exprNode ret = exprNode_createLoc (ctype_unknown, fileloc_copy (lltok_getLoc (l))); ret->kind = XPR_CONTINUE; ret->edata = exprData_makeTok (l); ret->canBreak = TRUE; ret->mustBreak = TRUE; if (qcontinue == QSAFEBREAK) { ; /* no checking */ } else if (qcontinue == QINNERCONTINUE) { if (!context_inDeepLoop ()) { voptgenerror (FLG_LOOPLOOPCONTINUE, cstring_makeLiteral ("Continue statement marked with innercontinue " "is not inside a nested loop"), exprNode_loc (ret)); } } else if (qcontinue == BADTOK) { if (context_inDeepLoop ()) { voptgenerror (FLG_LOOPLOOPCONTINUE, cstring_makeLiteral ("Continue statement in nested loop"), exprNode_loc (ret)); } } else { llbuglit ("exprNode_continue: bad qcontinue"); } return ret; } exprNode exprNode_break (/*@only@*/ lltok l, int bqual) { exprNode ret = exprNode_createLoc (ctype_unknown, fileloc_copy (lltok_getLoc (l))); clause breakClause = context_breakClause (); ret->kind = XPR_BREAK; ret->edata = exprData_makeTok (l); ret->canBreak = TRUE; ret->mustBreak = TRUE; if (breakClause == NOCLAUSE) { voptgenerror (FLG_SYNTAX, cstring_makeLiteral ("Break not inside while, for or switch statement"), exprNode_loc (ret)); } else { if (bqual != BADTOK) { switch (bqual) { case QSAFEBREAK: break; case QINNERBREAK: if (breakClause == SWITCHCLAUSE) { if (!context_inDeepSwitch ()) { voptgenerror (FLG_SYNTAX, cstring_makeLiteral ("Break preceded by innerbreak is not in a deep switch"), exprNode_loc (ret)); } } else { if (!context_inDeepLoop ()) { voptgenerror (FLG_SYNTAX, cstring_makeLiteral ("Break preceded by innerbreak is not in a deep loop"), exprNode_loc (ret)); } } break; case QLOOPBREAK: if (breakClause == SWITCHCLAUSE) { voptgenerror (FLG_SYNTAX, cstring_makeLiteral ("Break preceded by loopbreak is breaking a switch"), exprNode_loc (ret)); } break; case QSWITCHBREAK: if (breakClause != SWITCHCLAUSE) { voptgenerror (FLG_SYNTAX, message ("Break preceded by switchbreak is breaking %s", cstring_makeLiteralTemp ((breakClause == WHILECLAUSE || breakClause == DOWHILECLAUSE) ? "a while loop" : (breakClause == FORCLAUSE) ? "a for loop" : (breakClause == ITERCLAUSE) ? "an iterator" : "")), exprNode_loc (ret)); } break; BADDEFAULT; } } else { if (breakClause == SWITCHCLAUSE) { clause nextBreakClause = context_nextBreakClause (); switch (nextBreakClause) { case NOCLAUSE: break; case WHILECLAUSE: case DOWHILECLAUSE: case FORCLAUSE: case ITERCLAUSE: voptgenerror (FLG_LOOPSWITCHBREAK, cstring_makeLiteral ("Break statement in switch inside loop"), exprNode_loc (ret)); break; case SWITCHCLAUSE: voptgenerror (FLG_SWITCHSWITCHBREAK, cstring_makeLiteral ("Break statement in switch inside switch"), exprNode_loc (ret)); break; BADDEFAULT; } } else { if (context_inDeepLoop ()) { voptgenerror (FLG_LOOPLOOPBREAK, cstring_makeLiteral ("Break statement in nested loop"), exprNode_loc (ret)); } else { if (context_inDeepLoopSwitch ()) { voptgenerror (FLG_SWITCHLOOPBREAK, cstring_makeLiteral ("Break statement in loop inside switch"), exprNode_loc (ret)); } } } } } return ret; } exprNode exprNode_nullReturn (/*@only@*/ lltok t) { fileloc loc = lltok_getLoc (t); exprNode ret = exprNode_createLoc (ctype_unknown, fileloc_copy (loc)); context_returnFunction (); exprChecks_checkNullReturn (loc); ret->kind = XPR_NULLRETURN; ret->edata = exprData_makeTok (t); ret->exitCode = XK_MUSTRETURN; return ret; } exprNode exprNode_return (/*@only@*/ exprNode e) { exprNode ret; if (exprNode_isError (e)) { ret = exprNode_createUnknown (); } else { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (e->loc)); exprNode_checkUse (ret, e->sref, e->loc); exprNode_checkReturn (e); } context_returnFunction (); ret->kind = XPR_RETURN; ret->edata = exprData_makeSingle (e); ret->exitCode = XK_MUSTRETURN; return (ret); } exprNode exprNode_comma (/*@only@*/ exprNode e1, /*@only@*/ exprNode e2) { exprNode ret; if (exprNode_isError (e1)) { if (exprNode_isError (e2)) { ret = exprNode_createLoc (ctype_unknown, fileloc_copy (g_currentloc)); } else { ret = exprNode_createPartialCopy (e2); exprNode_checkUse (ret, e2->sref, e2->loc); ret->sref = e2->sref; } } else { ret = exprNode_createPartialCopy (e1); exprNode_checkUse (ret, e1->sref, e1->loc); if (!exprNode_isError (e2)) { exprNode_mergeUSs (ret, e2); exprNode_checkUse (ret, e2->sref, e2->loc); ret->sref = e2->sref; } } ret->kind = XPR_COMMA; ret->edata = exprData_makePair (e1, e2); if (exprNode_isDefined (e1)) { if (exprNode_isDefined (e2)) { ret->typ = e2->typ; if (exprNode_mustEscape (e1) || e1->mustBreak) { voptgenerror (FLG_UNREACHABLE, message ("Second clause of comma expression is unreachable: %s", exprNode_unparse (e2)), exprNode_loc (e2)); } ret->exitCode = exitkind_combine (e1->exitCode, e2->exitCode); ret->mustBreak = e1->mustBreak || e2->mustBreak; ret->canBreak = e1->canBreak || e2->canBreak; } else { if (exprNode_mustEscape (e1) || e1->mustBreak) { voptgenerror (FLG_UNREACHABLE, message ("Second clause of comma expression is unreachable: %s", exprNode_unparse (e2)), exprNode_loc (e2)); } ret->exitCode = e1->exitCode; ret->canBreak = e1->canBreak; } } else { if (exprNode_isDefined (e2)) { ret->exitCode = e2->exitCode; ret->mustBreak = e2->mustBreak; ret->canBreak = e2->canBreak; } } return (ret); } static bool exprNode_checkOneInit (/*@notnull@*/ exprNode el, exprNode val) { ctype t1 = exprNode_getType (el); ctype t2 = exprNode_getType (val); bool hasError = FALSE; DPRINTF (("Check one init: %s / %s", exprNode_unparse (el), exprNode_unparse (val))); if (ctype_isUnknown (t1)) { voptgenerror (FLG_IMPTYPE, message ("Variable has unknown (implicitly int) type: %s", exprNode_unparse (el)), el->loc); t1 = ctype_int; el->typ = ctype_int; } if (exprNode_isDefined (val) && val->kind == XPR_INITBLOCK) { exprNodeList vals = exprData_getArgs (val->edata); DPRINTF (("Check one init: %s", exprNodeList_unparse (vals))); DPRINTF (("Type: %s", ctype_unparse (t1))); if (ctype_isRealAP (t1)) { int i = 0; int nerrors = 0; if (ctype_isFixedArray (t1)) { size_t nelements = ctype_getArraySize (t1); DPRINTF (("Checked array: %s / %d", ctype_unparse (t1), nelements)); if (exprNode_isStringLiteral (val)) { exprNode_checkStringLiteralLength (t1, val); } else { if (exprNodeList_size (vals) != size_toInt (nelements)) { hasError = optgenerror (exprNodeList_size (vals) > size_toInt (nelements) ? FLG_INITSIZE : FLG_INITALLELEMENTS, message ("Initializer block for " "%s has %d element%&, but declared as %s: %q", exprNode_unparse (el), exprNodeList_size (vals), ctype_unparse (t1), exprNodeList_unparse (vals)), val->loc); } } } exprNodeList_elements (vals, oneval) { cstring istring = message ("%d", i); exprNode newel = exprNode_arrayFetch (exprNode_fakeCopy (el), exprNode_numLiteral (ctype_int, istring, fileloc_copy (el->loc), i)); if (exprNode_isDefined (newel)) { if (exprNodeList_size (vals) == 1 && ctype_isString (exprNode_getType (oneval)) && ctype_isChar (exprNode_getType (newel))) { exprNode_freeIniter (newel); } else { if (exprNode_checkOneInit (newel, oneval)) { hasError = TRUE; nerrors++; if (nerrors > 3 && exprNodeList_size (vals) > 6) { llgenmsg (message ("Additional initialization errors " "for %s not reported", exprNode_unparse (el)), exprNode_loc (el)); exprNode_freeIniter (newel); break; } else { exprNode_freeIniter (newel); } } else { exprNode_freeIniter (newel); } } } cstring_free (istring); i++; /*@-branchstate@*/ } end_exprNodeList_elements; /*@=branchstate@*/ } else if (ctype_isStruct (ctype_realType (t1))) { uentryList fields = ctype_getFields (t1); int i = 0; if (uentryList_size (fields) != exprNodeList_size (vals)) { if (uentryList_size (fields) > exprNodeList_size (vals)) { hasError = optgenerror (FLG_FULLINITBLOCK, message ("Initializer block for " "%s has %d field%&, but %s has %d field%&: %q", exprNode_unparse (el), exprNodeList_size (vals), ctype_unparse (t1), uentryList_size (fields), exprNodeList_unparse (vals)), val->loc); } else { hasError = optgenerror (FLG_TYPE, message ("Initializer block for " "%s has %d field%&, but %s has %d field%&: %q", exprNode_unparse (el), exprNodeList_size (vals), ctype_unparse (t1), uentryList_size (fields), exprNodeList_unparse (vals)), val->loc); } } else { exprNodeList_elements (vals, oneval) { uentry thisfield = uentryList_getN (fields, i); exprNode newel = exprNode_fieldAccessAux (exprNode_fakeCopy (el), exprNode_loc (el), uentry_getName (thisfield)); if (exprNode_isDefined (newel)) { if (exprNode_checkOneInit (newel, oneval)) { hasError = TRUE; } exprNode_freeIniter (newel); } i++; } end_exprNodeList_elements; } } /* evans 2001-12-30: added to fix bug reported by Jim Zelenka */ else if (ctype_isUnion (ctype_realType (t1))) { uentryList fields = ctype_getFields (t1); int i = 0; /* ** Union initializers set the first member always. */ DPRINTF (("Union initializer: %s / %s", exprNode_unparse (el), ctype_unparse (ctype_realType (t1)))); if (exprNodeList_size (vals) != 1) { hasError = optgenerror (FLG_TYPE, message ("Initializer block for union " "%s has %d elements, union initializers should have one element: %q", exprNode_unparse (el), exprNodeList_size (vals), exprNodeList_unparse (vals)), val->loc); } else { exprNode oneval = exprNodeList_head (vals); uentry thisfield = uentryList_getN (fields, i); exprNode newel = exprNode_fieldAccessAux (exprNode_fakeCopy (el), exprNode_loc (el), uentry_getName (thisfield)); if (exprNode_isDefined (newel)) { if (exprNode_checkOneInit (newel, oneval)) { hasError = TRUE; } exprNode_freeIniter (newel); } } } else { hasError = optgenerror (FLG_TYPE, message ("Initializer block used for " "%s where %t is expected: %s", exprNode_unparse (el), t1, exprNode_unparse (val)), val->loc); } } else { if (exprNode_isDefined (val)) { doAssign (el, val, TRUE); if (!exprNode_matchType (t1, val)) { hasError = gentypeerror (t1, val, t2, el, message ("Initial value of %s is type %t, " "expects %t: %s", exprNode_unparse (el), t2, t1, exprNode_unparse (val)), val->loc); } } } return hasError; } static /*@notnull@*/ exprNode exprNode_makeInitializationAux (/*@temp@*/ idDecl t) { exprNode ret; DPRINTF (("Initialization: %s", idDecl_unparse (t))); if (usymtab_exists (idDecl_observeId (t))) { uentry ue = usymtab_lookup (idDecl_observeId (t)); ret = exprNode_createId (ue); } else { uentry ue; DPRINTF (("Unrecognized: %s", idDecl_unparse (t))); ue = uentry_makeUnrecognized (idDecl_observeId (t), fileloc_copy (g_currentloc)); ret = exprNode_fromIdentifierAux (ue); /* ** No error - this happens in old style declarations: voptgenerror (FLG_UNRECOG, message ("Unrecognized identifier in intializer: %s", idDecl_observeId (t)), g_currentloc); ** */ } exprData_free (ret->edata, ret->kind); ret->edata = exprData_undefined; ret->exitCode = XK_NEVERESCAPE; ret->mustBreak = FALSE; ret->kind = XPR_INIT; return ret; } exprNode exprNode_makeEmptyInitialization (/*@only@*/ idDecl t) { exprNode ret = exprNode_makeInitializationAux (t); llassert (ret->edata == exprData_undefined); ret->edata = exprData_makeInit (t, exprNode_undefined); return ret; } exprNode exprNode_makeInitialization (/*@only@*/ idDecl t, /*@only@*/ exprNode e) { uentry ue = usymtab_lookup (idDecl_observeId (t)); exprNode ret = exprNode_makeInitializationAux (t); fileloc loc = exprNode_loc (e); DPRINTF (("initialization: %s = %s", idDecl_unparse (t), exprNode_unparse (e))); if (exprNode_isError (e)) { e = exprNode_createUnknown (); /* error: assume initializer is defined */ sRef_setDefined (ret->sref, g_currentloc); ret->edata = exprData_makeInit (t, e); } else { ctype ct = ctype_realishType (ret->typ); /* ** evs - 9 Apr 1995 ** ** was addSafeUse --- what's the problem? ** ** int x = 3, y = x ? */ exprData_free (ret->edata, ret->kind); ret->edata = exprData_makeInit (t, e); DPRINTF (("ret: %s", exprNode_unparse (ret))); exprNode_checkUse (ret, e->sref, e->loc); if (ctype_isUnknown (e->typ) && uentry_isValid (ue)) { exprNode lhs = exprNode_createId (ue); /* ** static storage should be undefined before initializing */ if (uentry_isStatic (ue)) { sRef_setDefState (lhs->sref, SS_PARTIAL, fileloc_undefined); } (void) exprNode_checkOneInit (lhs, e); if (uentry_isStatic (ue)) { sRef_setDefState (lhs->sref, SS_DEFINED, fileloc_undefined); } exprNode_free (lhs); } else { if (!exprNode_matchType (ct, e)) { if (exprNode_isZero (e) && ctype_isArrayPtr (ct)) { ; } else { (void) gentypeerror (exprNode_getType (e), e, exprNode_getType (ret), ret, message ("Variable %q initialized to type %t, expects %t: %s", uentry_getName (ue), exprNode_getType (e), exprNode_getType (ret), exprNode_unparse (e)), e->loc); } } } if (uentry_isStatic (ue)) { sRef_setDefState (ret->sref, SS_PARTIAL, fileloc_undefined); } if (exprNode_isStringLiteral (e) && (ctype_isArray (ct)) && (ctype_isChar (ctype_realType (ctype_baseArrayPtr (ct))))) { /* ** If t is a char [], the literal is copied. */ exprNode_checkStringLiteralLength (ct, e); sRef_setDefState (ret->sref, SS_DEFINED, e->loc); ret->val = multiVal_copy (e->val); sRef_setNullTerminatedState (ret->sref); if (multiVal_isDefined (e->val)) { cstring slit = multiVal_forceString (e->val); sRef_setLen (ret->sref, size_toInt (cstring_length (slit) + 1)); } if (ctype_isFixedArray (ct)) { sRef_setSize (ret->sref, size_toInt (ctype_getArraySize (ct))); } } else { doAssign (ret, e, TRUE); } if (uentry_isStatic (ue)) { sRef_setDefState (ret->sref, SS_DEFINED, fileloc_undefined); } } if (context_inIterDef ()) { /* should check if it is yield */ uentry_setUsed (ue, loc); } else { ; } exprNode_mergeUSs (ret, e); DPRINTF (("Ret: %s %p %p", exprNode_unparse (ret), ret->requiresConstraints, ret->ensuresConstraints)); DPRINTF (("Ret: %s %s %s", exprNode_unparse (ret), constraintList_unparse (ret->requiresConstraints), constraintList_unparse (ret->ensuresConstraints))); return ret; } exprNode exprNode_iter (/*@observer@*/ uentry name, /*@only@*/ exprNodeList alist, /*@only@*/ exprNode body, /*@observer@*/ uentry end) { exprNode ret; cstring iname; llassert (uentry_isValid (name)); uentry_setUsed (name, exprNode_loc (body)); ret = exprNode_createPartialCopy (body); iname = uentry_getName (name); if (uentry_isInvalid (end)) { llerror (FLG_ITERBALANCE, message ("Iter %s not balanced with end_%s", iname, iname)); } else { cstring ename = uentry_getName (end); if (!cstring_equalPrefixLit (ename, "end_")) { llerror (FLG_ITERBALANCE, message ("Iter %s not balanced with end_%s: %s", iname, iname, ename)); } else { if (!cstring_equal (iname, cstring_suffix (ename, 4))) { llerror (FLG_ITERBALANCE, message ("Iter %s not balanced with end_%s: %s", iname, iname, ename)); } } cstring_free (ename); } context_exitIterClause (body); ret->kind = XPR_ITER; ret->edata = exprData_makeIter (name, alist, body, end); if (uentry_isIter (name)) { (void) checkArgsReal (name, body, uentry_getParams (name), alist, TRUE, ret); } cstring_free (iname); return ret; } exprNode exprNode_iterNewId (/*@only@*/ cstring s) { exprNode e = exprNode_new (); uentry ue = uentryList_getN (uentry_getParams (getCurrentIter ()), iterParamNo ()); llassert (processingIterVars ()); e->loc = context_getSaveLocation (); if (fileloc_isUndefined (e->loc)) { fileloc_free (e->loc); e->loc = fileloc_copy (g_currentloc); } e->uses = sRefSet_new (); e->sets = sRefSet_new (); e->msets = sRefSet_new (); e->kind = XPR_VAR; e->val = multiVal_unknown (); e->guards = guardSet_new (); e->sref = sRef_undefined; e->isJumpPoint = FALSE; e->exitCode = XK_NEVERESCAPE; /*> missing fields, detected by splint <*/ e->canBreak = FALSE; e->mustBreak = FALSE; e->etext = cstring_undefined; if (uentry_isYield (ue)) { uentry uue = uentry_makeVariable (s, uentry_getType (ue), fileloc_copy (e->loc), FALSE); sRef sr; uue = usymtab_supEntrySrefReturn (uue); sr = uentry_getSref (uue); sRef_mergeStateQuiet (sr, uentry_getSref (ue)); sr = uentry_getSref (uue); sRef_setDefined (sr, e->loc); e->typ = uentry_getType (uue); e->sref = sr; e->edata = exprData_makeId (uue); uentry_setUsed (uue, g_currentloc); } else { uentry uue; sRef_setGlobalScope (); uue = uentry_makeVariableLoc (s, ctype_unknown); e->typ = ctype_unknown; e->edata = exprData_makeId (uue); uentry_setUsed (uue, e->loc); uentry_setHasNameError (uue); if (context_getFlag (FLG_REPEATUNRECOG)) { uentry_markOwned (uue); } else { usymtab_supGlobalEntry (uue); } sRef_clearGlobalScope (); voptgenerror (FLG_UNRECOG, message ("Unrecognized identifier: %s", s), e->loc); } cstring_free (s); exprNode_defineConstraints(e); return (e); } exprNode exprNode_iterExpr (/*@returned@*/ exprNode e) { if (!processingIterVars ()) { llcontbuglit ("checkIterParam: not in iter"); return e; } if (uentry_isYield (uentryList_getN (uentry_getParams (getCurrentIter ()), iterParamNo ()))) { if (exprNode_isDefined (e)) { if (fileloc_isDefined (e->loc)) { voptgenerror (FLG_ITERYIELD, message ("Yield parameter is not simple identifier: %s", exprNode_unparse (e)), e->loc); } else { voptgenerror (FLG_ITERYIELD, message ("Yield parameter is not simple identifier: %s", exprNode_unparse (e)), g_currentloc); } } } return e; } exprNode exprNode_iterId (/*@observer@*/ uentry c) { uentry ue; llassert (processingIterVars ()); ue = uentryList_getN (uentry_getParams (getCurrentIter ()), iterParamNo ()); if (uentry_isYield (ue)) { ctype ct = uentry_getType (ue); exprNode e = exprNode_createPlain (ct); cstring name = uentry_getName (c); uentry le = uentry_makeVariable (name, ct, fileloc_undefined, FALSE); uentry_setUsed (ue, g_currentloc); uentry_setHasNameError (ue); cstring_free (name); e->kind = XPR_VAR; e->edata = exprData_makeId (le); e->loc = context_getSaveLocation (); e->sref = uentry_getSref (le); usymtab_supEntrySref (le); if (!context_inHeader ()) { if (optgenerror (FLG_ITERYIELD, message ("Yield parameter shadows local declaration: %q", uentry_getName (c)), fileloc_isDefined (e->loc) ? e->loc : g_currentloc)) { uentry_showWhereDeclared (c); } } return e; } return (exprNode_fromIdentifierAux (c)); } exprNode exprNode_iterStart (/*@observer@*/ uentry name, /*@only@*/ exprNodeList alist) { exprNode ret = exprNode_create (ctype_unknown); ret->kind = XPR_ITERCALL; ret->edata = exprData_makeIterCall (name, alist); if (uentry_isIter (name)) { uentryList params = uentry_getParams (name); if (context_inIterDef () && uentryList_size (params) == exprNodeList_size (alist)) { int i = 0; exprNodeList_elements (alist, arg) { uentry parg = uentryList_getN (params, i); if (uentry_isYield (parg)) { uentry ue = exprNode_getUentry (arg); if (uentry_isValid (ue)) { ; } } i++; } end_exprNodeList_elements; } (void) checkArgsReal (name, ret, params, alist, TRUE, ret); checkUnspecCall (ret, params, alist); } return ret; } /*@exposed@*/ sRef exprNode_getSref (exprNode e) { if (exprNode_isDefined (e)) { if (sRef_isInvalid (e->sref)) { /*@-mods@*/ e->sref = sRef_makeUnknown (); sRef_setAliasKind (e->sref, AK_ERROR, fileloc_undefined); /*@=mods@*/ return e->sref; } else { return e->sref; } } else { return sRef_undefined; } } /*@observer@*/ cstring exprNode_unparseFirst (exprNode e) { if (exprNode_isDefined (e)) { cstring ret; if (e->kind == XPR_STMTLIST || e->kind == XPR_COMMA || e->kind == XPR_COND) { exprNode first = exprData_getPairA (e->edata); if (exprNode_isDefined (first)) { return (exprNode_unparseFirst (exprData_getPairA (e->edata))); } else { return (cstring_makeLiteralTemp ("...")); } } ret = cstring_elide (exprNode_unparse (e), 20); cstring_markOwned (ret); return (ret); } else { return cstring_makeLiteralTemp (""); } } /*@observer@*/ cstring exprNode_unparse (/*@temp@*/ exprNode e) { if (exprNode_isError (e)) { return cstring_makeLiteralTemp (""); } if (cstring_isDefined (e->etext)) { return e->etext; } else { cstring ret = exprNode_doUnparse (e); /*@-modifies@*/ /* benevolent */ e->etext = ret; /*@=modifies@*/ return ret; } } /*@observer@*/ fileloc exprNode_loc (exprNode e) { if (exprNode_isError (e)) { return (g_currentloc); } else { return (e->loc); } } /* ** executes exprNode e ** recursively rexecutes as though in original parse using ** information in e->edata */ static /*@only@*/ exprNodeList exprNodeList_effect (exprNodeList e) { exprNodeList ret = exprNodeList_new (); exprNodeList_elements (e, current) { exprNodeList_addh (ret, exprNode_effect (current)); } end_exprNodeList_elements; return ret; } static /*@only@*/ exprNode exprNode_effect (exprNode e) /*@globals internalState@*/ { bool innerEffect = inEffect; exprNode ret; exprData data; inEffect = TRUE; context_clearJustPopped (); if (exprNode_isError (e)) { ret = exprNode_undefined; } else { /* ** Turn off expose and dependent transfer checking. ** Need to pass exposed internal nodes, ** [ copying would be a waste! ] ** [ Actually, I think I wasted a lot more time than its worth ] ** [ trying to do this. ] */ /*@-exposetrans@*/ /*@-observertrans@*/ /*@-dependenttrans@*/ data = e->edata; switch (e->kind) { case XPR_PARENS: ret = exprNode_addParens (exprData_getUopTok (data), exprNode_effect (exprData_getUopNode (data))); break; case XPR_ASSIGN: ret = exprNode_assign (exprNode_effect (exprData_getOpA (data)), exprNode_effect (exprData_getOpB (data)), exprData_getOpTok (data)); break; case XPR_INITBLOCK: ret = exprNode_undefined; break; case XPR_CALL: ret = exprNode_functionCall (exprNode_effect (exprData_getFcn (data)), exprNodeList_effect (exprData_getArgs (data))); break; case XPR_EMPTY: ret = e; break; case XPR_LABEL: ret = e; break; case XPR_CONST: case XPR_VAR: { cstring id = exprData_getId (data); uentry ue = usymtab_lookupSafe (id); ret = exprNode_fromIdentifierAux (ue); ret->loc = fileloc_update (ret->loc, e->loc); break; } case XPR_BODY: ret = e; break; case XPR_FETCH: ret = exprNode_arrayFetch (exprNode_effect (exprData_getPairA (data)), exprNode_effect (exprData_getPairB (data))); break; case XPR_OP: /* ** evans 2002-03-15: for && and ||, need to do the guards also ** this is what cgrammar.y does - should be ** able to avoid duplication, but need to ** time with grammar productions. */ DPRINTF (("Effect: %s", exprNode_unparse (e))); if (lltok_getTok (exprData_getOpTok (data)) == AND_OP) { exprNode e1 = exprNode_effect (exprData_getOpA (data)); exprNode e2; exprNode_produceGuards (e1); context_enterAndClause (e1); e2 = exprNode_effect (exprData_getOpB (data)); ret = exprNode_op (e1, e2, exprData_getOpTok (data)); context_exitAndClause (ret, e2); } else if (lltok_getTok (exprData_getOpTok (data)) == OR_OP) { exprNode e1 = exprNode_effect (exprData_getOpA (data)); exprNode e2; exprNode_produceGuards (e1); context_enterOrClause (e1); e2 = exprNode_effect (exprData_getOpB (data)); ret = exprNode_op (e1, e2, exprData_getOpTok (data)); context_exitOrClause (ret, e2); } else { ret = exprNode_op (exprNode_effect (exprData_getOpA (data)), exprNode_effect (exprData_getOpB (data)), exprData_getOpTok (data)); } break; case XPR_POSTOP: ret = exprNode_postOp (exprNode_effect (exprData_getUopNode (data)), exprData_getUopTok (data)); break; case XPR_PREOP: ret = exprNode_preOp (exprNode_effect (exprData_getUopNode (data)), exprData_getUopTok (data)); break; case XPR_OFFSETOF: case XPR_SIZEOFT: case XPR_SIZEOF: case XPR_ALIGNOFT: case XPR_ALIGNOF: ret = e; break; case XPR_VAARG: ret = exprNode_vaArg (exprData_getCastTok (data), exprNode_effect (exprData_getCastNode (data)), exprData_getCastType (data)); break; case XPR_CAST: ret = exprNode_cast (exprData_getCastTok (data), exprNode_effect (exprData_getCastNode (data)), exprData_getCastType (data)); break; case XPR_ITERCALL: ret = exprNode_iterStart (exprData_getIterCallIter (data), exprNodeList_effect (exprData_getIterCallArgs (data))); break; case XPR_ITER: ret = exprNode_iter (exprData_getIterSname (data), exprNodeList_effect (exprData_getIterAlist (data)), exprNode_effect (exprData_getIterBody (data)), exprData_getIterEname (data)); break; case XPR_FOR: ret = exprNode_for (exprNode_effect (exprData_getPairA (data)), exprNode_effect (exprData_getPairB (data))); break; case XPR_FORPRED: ret = exprNode_forPred (exprNode_effect (exprData_getTripleInit (data)), exprNode_effect (exprData_getTripleTest (data)), exprNode_effect (exprData_getTripleInc (data))); break; case XPR_TOK: ret = exprNode_createTok (exprData_getTok (data)); break; case XPR_GOTO: ret = exprNode_goto (exprData_getLiteral (data)); ret->loc = fileloc_update (ret->loc, e->loc); break; case XPR_CONTINUE: ret = exprNode_continue (exprData_getTok (data), QSAFEBREAK); break; case XPR_BREAK: ret = exprNode_break (exprData_getTok (data), QSAFEBREAK); break; case XPR_RETURN: ret = exprNode_return (exprNode_effect (exprData_getSingle (data))); break; case XPR_NULLRETURN: ret = exprNode_nullReturn (exprData_getTok (data)); break; case XPR_COMMA: ret = exprNode_comma (exprNode_effect (exprData_getPairA (data)), exprNode_effect (exprData_getPairB (data))); break; case XPR_COND: ret = exprNode_cond (exprNode_effect (exprData_getTriplePred (data)), exprNode_effect (exprData_getTripleTrue (data)), exprNode_effect (exprData_getTripleFalse (data))); break; case XPR_IF: ret = exprNode_if (exprNode_effect (exprData_getPairA (data)), exprNode_effect (exprData_getPairB (data))); break; case XPR_IFELSE: ret = exprNode_ifelse (exprNode_effect (exprData_getTriplePred (data)), exprNode_effect (exprData_getTripleTrue (data)), exprNode_effect (exprData_getTripleFalse (data))); break; case XPR_WHILEPRED: ret = exprNode_whilePred (exprData_getSingle (data)); break; case XPR_WHILE: ret = exprNode_while (exprNode_effect (exprData_getPairA (data)), exprNode_effect (exprData_getPairB (data))); break; case XPR_DOWHILE: ret = exprNode_doWhile (exprNode_effect (exprData_getPairA (data)), exprNode_effect (exprData_getPairB (data))); break; case XPR_BLOCK: ret = exprNode_makeBlock (exprNode_effect (exprData_getSingle (data))); break; case XPR_STMT: ret = exprNode_statement (exprNode_effect (exprData_getUopNode (data)), exprData_getUopTok (data)); break; case XPR_STMTLIST: ret = exprNode_concat (exprNode_effect (exprData_getPairA (data)), exprNode_effect (exprData_getPairB (data))); break; case XPR_FTCASE: case XPR_CASE: ret = exprNode_caseMarker (exprNode_effect (exprData_getSingle (data)), TRUE); break; case XPR_FTDEFAULT: case XPR_DEFAULT: ret = exprNode_createTok (exprData_getTok (data)); break; case XPR_SWITCH: ret = exprNode_switch (exprNode_effect (exprData_getPairA (data)), exprNode_effect (exprData_getPairB (data))); break; case XPR_INIT: ret = exprNode_makeInitialization (exprData_getInitId (data), exprNode_effect (exprData_getInitNode (data))); break; case XPR_FACCESS: ret = exprNode_fieldAccessAux (exprNode_effect (exprData_getFieldNode (data)), exprNode_loc (exprData_getFieldNode (data)), cstring_copy (exprData_getFieldName (data))); break; case XPR_ARROW: ret = exprNode_arrowAccessAux (exprNode_effect (exprData_getFieldNode (data)), exprNode_loc (exprData_getFieldNode (data)), cstring_copy (exprData_getFieldName (data))); break; case XPR_STRINGLITERAL: ret = e; break; case XPR_NUMLIT: ret = e; break; case XPR_NODE: ret = e; break; /*@-branchstate@*/ } /*@=branchstate@*/ /*@=observertrans@*/ /*@=exposetrans@*/ /*@=dependenttrans@*/ } if (!innerEffect) { inEffect = FALSE; } return ret; } static /*@observer@*/ cstring exprNode_rootVarName (exprNode e) { cstring ret; exprData data; if (exprNode_isError (e)) { return cstring_undefined; } data = e->edata; switch (e->kind) { case XPR_PARENS: ret = exprNode_rootVarName (exprData_getUopNode (data)); break; case XPR_ASSIGN: ret = exprNode_rootVarName (exprData_getOpA (data)); break; case XPR_CONST: case XPR_VAR: ret = exprData_getId (data); break; case XPR_INIT: ret = idDecl_getName (exprData_getInitId (data)); break; case XPR_LABEL: case XPR_TOK: case XPR_ITERCALL: case XPR_EMPTY: case XPR_CALL: case XPR_INITBLOCK: case XPR_BODY: case XPR_FETCH: case XPR_OP: case XPR_POSTOP: case XPR_PREOP: case XPR_OFFSETOF: case XPR_ALIGNOFT: case XPR_ALIGNOF: case XPR_SIZEOFT: case XPR_SIZEOF: case XPR_VAARG: case XPR_CAST: case XPR_ITER: case XPR_FOR: case XPR_FORPRED: case XPR_BREAK: case XPR_RETURN: case XPR_NULLRETURN: case XPR_COMMA: case XPR_COND: case XPR_IF: case XPR_IFELSE: case XPR_WHILE: case XPR_WHILEPRED: case XPR_DOWHILE: case XPR_GOTO: case XPR_CONTINUE: case XPR_FTDEFAULT: case XPR_DEFAULT: case XPR_SWITCH: case XPR_FTCASE: case XPR_CASE: case XPR_BLOCK: case XPR_STMT: case XPR_STMTLIST: case XPR_FACCESS: case XPR_ARROW: case XPR_NODE: case XPR_NUMLIT: case XPR_STRINGLITERAL: ret = cstring_undefined; break; } return ret; } static /*@only@*/ cstring exprNode_doUnparse (exprNode e) { cstring ret; exprData data; if (exprNode_isError (e)) { static /*@only@*/ cstring error = cstring_undefined; if (!cstring_isDefined (error)) { error = cstring_makeLiteral (""); } return error; } data = e->edata; switch (e->kind) { case XPR_PARENS: ret = message ("(%s)", exprNode_unparse (exprData_getUopNode (e->edata))); break; case XPR_ASSIGN: ret = message ("%s %s %s", exprNode_unparse (exprData_getOpA (data)), lltok_unparse (exprData_getOpTok (data)), exprNode_unparse (exprData_getOpB (data))); break; case XPR_CALL: ret = message ("%s(%q)", exprNode_unparse (exprData_getFcn (data)), exprNodeList_unparse (exprData_getArgs (data))); break; case XPR_INITBLOCK: ret = message ("{ %q }", exprNodeList_unparse (exprData_getArgs (data))); break; case XPR_EMPTY: ret = cstring_undefined; break; case XPR_LABEL: ret = message ("%s:", exprData_getId (data)); break; case XPR_CONST: case XPR_VAR: ret = cstring_copy (exprData_getId (data)); break; case XPR_FETCH: ret = message ("%s[%s]", exprNode_unparse (exprData_getPairA (data)), exprNode_unparse (exprData_getPairB (data))); break; case XPR_BODY: ret = message (""); break; case XPR_OP: ret = message ("%s %s %s", exprNode_unparse (exprData_getOpA (data)), lltok_unparse (exprData_getOpTok (data)), exprNode_unparse (exprData_getOpB (data))); break; case XPR_PREOP: ret = message ("%s%s", lltok_unparse (exprData_getUopTok (data)), exprNode_unparse (exprData_getUopNode (data))); break; case XPR_POSTOP: ret = message ("%s%s", exprNode_unparse (exprData_getUopNode (data)), lltok_unparse (exprData_getUopTok (data))); break; case XPR_OFFSETOF: ret = message ("offsetof(%s,%q)", ctype_unparse (qtype_getType (exprData_getOffsetType (data))), cstringList_unparseSep (exprData_getOffsetName (data), cstring_makeLiteralTemp ("."))); break; case XPR_SIZEOFT: ret = message ("sizeof(%s)", ctype_unparse (qtype_getType (exprData_getType (data)))); break; case XPR_SIZEOF: ret = message ("sizeof(%s)", exprNode_unparse (exprData_getSingle (data))); break; case XPR_ALIGNOFT: ret = message ("alignof(%s)", ctype_unparse (qtype_getType (exprData_getType (data)))); break; case XPR_ALIGNOF: ret = message ("alignof(%s)", exprNode_unparse (exprData_getSingle (data))); break; case XPR_VAARG: ret = message ("va_arg(%s, %q)", exprNode_unparse (exprData_getCastNode (data)), qtype_unparse (exprData_getCastType (data))); break; case XPR_ITERCALL: ret = message ("%q(%q)", uentry_getName (exprData_getIterCallIter (data)), exprNodeList_unparse (exprData_getIterCallArgs (data))); break; case XPR_ITER: ret = message ("%q(%q) %s %q", uentry_getName (exprData_getIterSname (data)), exprNodeList_unparse (exprData_getIterAlist (data)), exprNode_unparse (exprData_getIterBody (data)), uentry_getName (exprData_getIterEname (data))); break; case XPR_CAST: ret = message ("(%q)%s", qtype_unparse (exprData_getCastType (data)), exprNode_unparse (exprData_getCastNode (data))); break; case XPR_FOR: ret = message ("%s %s", exprNode_unparse (exprData_getPairA (data)), exprNode_unparse (exprData_getPairB (data))); break; case XPR_FORPRED: ret = message ("for (%s; %s; %s)", exprNode_unparse (exprData_getTripleInit (data)), exprNode_unparse (exprData_getTripleTest (data)), exprNode_unparse (exprData_getTripleInc (data))); break; case XPR_GOTO: ret = message ("goto %s", exprData_getLiteral (data)); break; case XPR_CONTINUE: ret = cstring_makeLiteral ("continue"); break; case XPR_BREAK: ret = cstring_makeLiteral ("break"); break; case XPR_RETURN: ret = message ("return %s", exprNode_unparse (exprData_getSingle (data))); break; case XPR_NULLRETURN: ret = cstring_makeLiteral ("return"); break; case XPR_COMMA: ret = message ("%s, %s", exprNode_unparse (exprData_getPairA (data)), exprNode_unparse (exprData_getPairB (data))); break; case XPR_COND: ret = message ("%s ? %s : %s", exprNode_unparse (exprData_getTriplePred (data)), exprNode_unparse (exprData_getTripleTrue (data)), exprNode_unparse (exprData_getTripleFalse (data))); break; case XPR_IF: ret = message ("if (%s) %s", exprNode_unparse (exprData_getPairA (data)), exprNode_unparse (exprData_getPairB (data))); break; case XPR_IFELSE: ret = message ("if (%s) %s else %s", exprNode_unparse (exprData_getTriplePred (data)), exprNode_unparse (exprData_getTripleTrue (data)), exprNode_unparse (exprData_getTripleFalse (data))); break; case XPR_WHILE: ret = message ("while (%s) %s", exprNode_unparse (exprData_getPairA (data)), exprNode_unparse (exprData_getPairB (data))); break; case XPR_WHILEPRED: ret = cstring_copy (exprNode_unparse (exprData_getSingle (data))); break; case XPR_TOK: ret = cstring_copy (lltok_unparse (exprData_getTok (data))); break; case XPR_DOWHILE: ret = message ("do { %s } while (%s)", exprNode_unparse (exprData_getPairB (data)), exprNode_unparse (exprData_getPairA (data))); break; case XPR_BLOCK: ret = message ("{ %s }", exprNode_unparse (exprData_getSingle (data))); /* evans 2002-02-20 was unparseFirst! */ break; case XPR_STMT: ret = message ("%s;", exprNode_unparse (exprData_getUopNode (data))); break; case XPR_STMTLIST: if (exprNode_isStatement (exprData_getPairA (data))) { /* ** statement expressions already print the ; */ ret = message ("%s %s", exprNode_unparse (exprData_getPairA (data)), exprNode_unparse (exprData_getPairB (data))); } else { ret = message ("%s; %s", exprNode_unparse (exprData_getPairA (data)), exprNode_unparse (exprData_getPairB (data))); } break; case XPR_FTDEFAULT: case XPR_DEFAULT: ret = cstring_makeLiteral ("default:"); break; case XPR_SWITCH: ret = message ("switch (%s) %s", exprNode_unparse (exprData_getPairA (data)), exprNode_unparse (exprData_getPairB (data))); break; case XPR_FTCASE: case XPR_CASE: ret = message ("case %s:", exprNode_unparse (exprData_getSingle (data))); break; case XPR_INIT: if (exprNode_isError (exprData_getInitNode (data))) { ret = message ("%q", idDecl_unparseC (exprData_getInitId (data))); } else { ret = message ("%q = %s", idDecl_unparseC (exprData_getInitId (data)), exprNode_unparse (exprData_getInitNode (data))); } break; case XPR_FACCESS: ret = message ("%s.%s", exprNode_unparse (exprData_getFieldNode (data)), exprData_getFieldName (data)); break; case XPR_ARROW: ret = message ("%s->%s", exprNode_unparse (exprData_getFieldNode (data)), exprData_getFieldName (data)); break; case XPR_STRINGLITERAL: if (ctype_isWideString (e->typ)) { ret = message ("L\"%s\"", exprData_getLiteral (data)); } else { ret = message ("\"%s\"", exprData_getLiteral (data)); } break; case XPR_NUMLIT: ret = cstring_copy (exprData_getLiteral (data)); break; case XPR_NODE: ret = cstring_makeLiteral (""); break; } return ret; } bool exprNode_isInitializer (exprNode e) { return (exprNode_isDefined (e) && e->kind == XPR_INIT); } bool exprNode_isCharLiteral (exprNode e) { if (exprNode_isDefined (e)) { return (multiVal_isChar (exprNode_getValue (e))); } else { return FALSE; } } bool exprNode_isNumLiteral (exprNode e) { if (exprNode_isDefined (e)) { return (multiVal_isInt (exprNode_getValue (e))); } else { return FALSE; } } static bool exprNode_isFalseConstant (exprNode e) { if (exprNode_isDefined (e)) { cstring s = exprNode_rootVarName (e); if (cstring_equal (s, context_getFalseName ())) { return TRUE; } } return FALSE; } bool exprNode_matchLiteral (ctype expected, exprNode e) { if (exprNode_isDefined (e)) { multiVal m = exprNode_getValue (e); if (multiVal_isDefined (m)) { if (multiVal_isInt (m)) { long int val = multiVal_forceInt (m); if (ctype_isNumAbstract (expected) && context_flagOn (FLG_NUMABSTRACTLIT, exprNode_loc (e))) { return TRUE; } if (ctype_isDirectBool (ctype_realishType (expected))) { if (val == 0) { return FALSE; /* really?! return TRUE; allow use of 0 for FALSE */ } else { return FALSE; } } if (ctype_isRealInt (expected)) { /* ** unsigned <- [ constant >= 0 is okay ] */ if (ctype_isUnsigned (expected)) { if (val < 0) { return FALSE; } } /* ** No checks on sizes of integers...maybe add ** these later. */ DPRINTF (("Here: %s => %s", exprNode_unparse (e), ctype_unparse (expected))); DPRINTF (("Type: %s / %s", ctype_unparse (exprNode_getType (e)), bool_unparse (ctype_isInt (exprNode_getType (e))))); if (context_getFlag (FLG_NUMLITERAL) && (ctype_isRegularInt (exprNode_getType (e)) || val == 0)) { return TRUE; } else { if (val == 0) { return TRUE; } else { return FALSE; /* evs 2000-05-17: previously, always returned TRUE */ } } } else if (ctype_isChar (expected)) { return FALSE; } else if (ctype_isArrayPtr (expected)) { /* ** evans 2001-10-14: We allow 0 to match any pointer, but only if the type matches or is void *. */ if (val == 0) { if (ctype_match (exprNode_getType (e), expected) || ctype_isVoidPointer (exprNode_getType (e))) { return TRUE; } } else { return FALSE; } } else if (ctype_isAnyFloat (expected)) { return (context_getFlag (FLG_NUMLITERAL)); } else { return FALSE; } } else if (multiVal_isDouble (m)) { if (ctype_isAnyFloat (expected)) { return TRUE; } } else if (multiVal_isChar (m)) { /*signed? */ char val = multiVal_forceChar (m); if (ctype_isChar (expected)) { if (ctype_isUnsigned (expected) && ((int) val) < 0) { return FALSE; } else { return TRUE; } } } else { return FALSE; } } } return FALSE; } bool exprNode_matchType (ctype expected, exprNode e) { ctype actual; if (!exprNode_isDefined (e)) return TRUE; actual = ctype_realishType (exprNode_getType (e)); if (ctype_match (ctype_realishType (expected), actual)) { return TRUE; } llassert (!exprNode_isError (e)); return (exprNode_matchLiteral (expected, e)); } static bool exprNode_matchTypes (exprNode e1, exprNode e2) { ctype t1; ctype t2; if (!exprNode_isDefined (e1)) return TRUE; if (!exprNode_isDefined (e2)) return TRUE; /* ** realish type --- keep bools, bools */ t1 = ctype_realishType (exprNode_getType (e1)); t2 = ctype_realishType (exprNode_getType (e2)); if (ctype_match (t1, t2)) { return TRUE; } DPRINTF (("Matching literal! %s %s %s %s", ctype_unparse (t1), exprNode_unparse (e2), ctype_unparse (t2), exprNode_unparse (e1))); return (exprNode_matchLiteral (t1, e2) || exprNode_matchLiteral (t2, e1)); } /* ** pass e as ct */ static bool exprNode_matchArgType (ctype ct, exprNode e) { ctype et; if (!exprNode_isDefined (e)) { return TRUE; } et = ctype_realType (exprNode_getType (e)); if (ctype_matchArg (ct, et)) return TRUE; llassert (!exprNode_isError (e)); return (exprNode_matchLiteral (ct, e)); } static /*@only@*/ exprNodeSList exprNode_flatten (/*@dependent@*/ exprNode e) /*@*/ { if (exprNode_isDefined (e)) { if (e->kind == XPR_STMTLIST) { return (exprNodeSList_append (exprNode_flatten (exprData_getPairA (e->edata)), exprNode_flatten (exprData_getPairB (e->edata)))); } else if (e->kind == XPR_BLOCK) { return (exprNode_flatten (exprData_getSingle (e->edata))); } else { return (exprNodeSList_singleton (e)); } } return exprNodeSList_new (); } static /*@exposed@*/ exprNode exprNode_lastStatement (/*@returned@*/ exprNode e) { if (exprNode_isDefined (e)) { if (e->kind == XPR_STMTLIST) { exprNode b = exprData_getPairB (e->edata); if (exprNode_isDefined (b)) { return exprNode_lastStatement (b); } else { return exprNode_lastStatement (exprData_getPairA (e->edata)); } } else if (e->kind == XPR_BLOCK) { return (exprNode_lastStatement (exprData_getSingle (e->edata))); } else { return (e); } } return exprNode_undefined; } static /*@exposed@*/ exprNode exprNode_firstStatement (/*@returned@*/ exprNode e) { if (exprNode_isDefined (e)) { if (e->kind == XPR_STMTLIST) { exprNode b = exprData_getPairA (e->edata); if (exprNode_isDefined (b)) { return exprNode_firstStatement (b); } else { return exprNode_firstStatement (exprData_getPairB (e->edata)); } } else if (e->kind == XPR_BLOCK) { return (exprNode_firstStatement (exprData_getSingle (e->edata))); } else { return (e); } } return exprNode_undefined; } static void exprNode_mergeUSs (exprNode res, exprNode other) { if (exprNode_isDefined (res) && exprNode_isDefined (other)) { res->msets = sRefSet_union (res->msets, other->msets); res->sets = sRefSet_union (res->sets, other->sets); res->uses = sRefSet_union (res->uses, other->uses); } } static void exprNode_mergeCondUSs (exprNode res, exprNode other1, exprNode other2) { if (exprNode_isDefined (res)) { if (exprNode_isDefined (other1)) { res->sets = sRefSet_union (res->sets, other1->sets); res->msets = sRefSet_union (res->msets, other1->msets); res->uses = sRefSet_union (res->uses, other1->uses); } if (exprNode_isDefined (other2)) { res->sets = sRefSet_union (res->sets, other2->sets); res->msets = sRefSet_union (res->msets, other2->msets); res->uses = sRefSet_union (res->uses, other2->uses); } } } /* ** modifies e->uses ** ** Reports errors is s is not defined. */ static void exprNode_addUse (exprNode e, /*@exposed@*/ sRef s) { if (exprNode_isDefined (e)) { e->uses = sRefSet_insert (e->uses, s); } } void exprNode_checkUse (exprNode e, /*@exposed@*/ sRef s, fileloc loc) { if (sRef_isKnown (s) && !sRef_isConst (s)) { /* ** need to check all outer types are useable */ DPRINTF (("Check use: %s / %s", exprNode_unparse (e), sRef_unparse (s))); exprNode_addUse (e, s); if (!context_inProtectVars ()) { /* ** only report the deepest error */ sRef errorRef = sRef_undefined; sRef lastRef = sRef_undefined; bool deadRef = FALSE; bool unuseable = FALSE; bool errorMaybe = FALSE; while (sRef_isValid (s) && sRef_isKnown (s)) { ynm readable = sRef_isValidLvalue (s); DPRINTF (("Readable: %s / %s", sRef_unparseFull (s), ynm_unparse (readable))); if (!(ynm_toBoolStrict (readable))) { if (ynm_isMaybe (readable)) { lastRef = errorRef; errorRef = s; DPRINTF (("Setting ERROR: %s", sRef_unparseFull (s))); deadRef = sRef_isPossiblyDead (errorRef); unuseable = sRef_isUnuseable (errorRef); errorMaybe = TRUE; } else { lastRef = errorRef; errorRef = s; DPRINTF (("Setting ERROR: %s", sRef_unparseFull (s))); deadRef = sRef_isDead (errorRef); unuseable = sRef_isUnuseable (errorRef); errorMaybe = FALSE; } /* if (!sRef_isPartial (s)) { DPRINTF (("Defining! %s", sRef_unparseFull (s))); sRef_setDefined (s, loc); DPRINTF (("Defining! %s", sRef_unparseFull (s))); } */ } s = sRef_getBaseSafe (s); } /* end while */ if (sRef_isValid (errorRef)) { if (sRef_isValid (lastRef) && sRef_isField (lastRef) && sRef_isPointer (errorRef)) { errorRef = lastRef; DPRINTF (("errorRef: %s", sRef_unparseFull (errorRef))); } if (deadRef) { if (sRef_isThroughArrayFetch (errorRef)) { if (optgenerror (FLG_STRICTUSERELEASED, message ("%q %q may be used after being released", sRef_unparseKindNamePlain (errorRef), sRef_unparse (errorRef)), loc)) { sRef_showRefKilled (errorRef); if (sRef_isKept (errorRef)) { sRef_clearAliasState (errorRef, loc); } } } else { DPRINTF (("HERE: %s", sRef_unparseFull (errorRef))); if (optgenerror (FLG_USERELEASED, message ("%q %q %qused after being released", sRef_unparseKindNamePlain (errorRef), sRef_unparse (errorRef), cstring_makeLiteral (errorMaybe ? "may be " : "")), loc)) { sRef_showRefKilled (errorRef); if (sRef_isKept (errorRef)) { sRef_clearAliasState (errorRef, loc); } } } } else if (unuseable) { if (optgenerror (FLG_USEDEF, message ("%q %q%qused in inconsistent state", sRef_unparseKindName (errorRef), sRef_unparseOpt (errorRef), cstring_makeLiteral (errorMaybe ? "may be " : "")), loc)) { sRef_showStateInconsistent (errorRef); } } else { DPRINTF (("HERE: %s", sRef_unparseFull (errorRef))); if (optgenerror (FLG_USEDEF, message ("%q %q%qused before definition", sRef_unparseKindName (errorRef), sRef_unparseOpt (errorRef), cstring_makeLiteral (errorMaybe ? "may be " : "")), loc)) { ; } DPRINTF (("Error: %s", sRef_unparseFull (errorRef))); } sRef_setDefined (errorRef, loc); if (sRef_isAddress (errorRef)) { sRef_setDefined (sRef_getRootBase (errorRef), loc); } } /* end is error */ } } setCodePoint (); } static void checkSafeUse (exprNode e, /*@exposed@*/ sRef s) { if (exprNode_isDefined (e) && sRef_isKnown (s)) { e->uses = sRefSet_insert (e->uses, s); } } static void exprNode_checkSetAny (exprNode e, /*@dependent@*/ cstring name) { if (exprNode_isDefined (e)) { e->sets = sRefSet_insert (e->sets, sRef_makeUnconstrained (name)); } } void exprNode_checkSet (exprNode e, /*@exposed@*/ sRef s) { sRef defines = sRef_undefined; if (sRef_isValid (s) && !sRef_isNothing (s)) { uentry ue = sRef_getBaseUentry (s); if (uentry_isValid (ue)) { uentry_setLset (ue); } if (!ynm_toBoolStrict (sRef_isWriteable (s))) { voptgenerror (FLG_USEDEF, message ("Attempt to set unuseable storage: %q", sRef_unparse (s)), exprNode_loc (e)); } if (sRef_isMeaningful (s)) { if (sRef_isDead (s)) { sRef base = sRef_getBaseSafe (s); if (sRef_isValid (base) && sRef_isDead (base)) { sRef_setPartial (s, exprNode_loc (e)); } defines = s; /* okay - modifies for only param */ } else if (sRef_isPartial (s)) { sRef eref = exprNode_getSref (e); if (!sRef_isPartial (eref)) { /* ** should do something different here??? */ sRef_setDefinedComplete (eref, exprNode_loc (e)); } else { sRef_setPartialDefinedComplete (eref, exprNode_loc (e)); } if (sRef_isMeaningful (eref)) { defines = eref; } else { defines = s; } } else if (sRef_isAllocated (s)) { sRef eref = exprNode_getSref (e); if (!sRef_isAllocated (eref)) { sRef_setDefinedComplete (eref, exprNode_loc (e)); } else { sRef base = sRef_getBaseSafe (eref); if (sRef_isValid (base)) { sRef_setPdefined (base, exprNode_loc (e)); } } defines = s; } else { sRef_setDefinedNCComplete (s, exprNode_loc (e)); defines = s; } } else /* not meaningful...but still need to insert it */ { defines = s; } } if (exprNode_isDefined (e) && sRef_isValid (defines)) { e->sets = sRefSet_insert (e->sets, defines); } } void exprNode_checkMSet (exprNode e, /*@exposed@*/ sRef s) { if (sRef_isValid (s) && !sRef_isNothing (s)) { uentry ue = sRef_getBaseUentry (s); if (uentry_isValid (ue)) { uentry_setLset (ue); } if (!ynm_toBoolStrict (sRef_isWriteable (s))) { voptgenerror (FLG_USEDEF, message ("Attempt to set unuseable storage: %q", sRef_unparse (s)), exprNode_loc (e)); } if (sRef_isMeaningful (s)) { sRef_setDefinedComplete (s, exprNode_loc (e)); } if (exprNode_isDefined (e)) { e->msets = sRefSet_insert (e->msets, s); } } } static void checkUnspecCall (/*@notnull@*/ /*@dependent@*/ exprNode fcn, uentryList params, exprNodeList args) { checkAnyCall (fcn, cstring_undefined, params, args, FALSE, sRefSet_undefined, FALSE, 0); } static void checkOneArg (uentry ucurrent, /*@notnull@*/ exprNode current, /*@dependent@*/ exprNode fcn, bool isSpec, int argno, int totargs) { setCodePoint (); if (uentry_isYield (ucurrent)) { sRef_setDefined (exprNode_getSref (current), exprNode_loc (current)); exprNode_checkSet (current, current->sref); } else { if (uentry_isSefParam (ucurrent)) { sRefSet sets = current->sets; sRef ref = exprNode_getSref (current); if (sRef_isMacroParamRef (ref)) { uentry ue = sRef_getUentry (ref); if (!uentry_isSefParam (ue)) { voptgenerror (FLG_SEFPARAMS, message ("Parameter %d to %s is declared sef, but " "the argument is a macro parameter declared " "without sef: %s", argno, exprNode_unparse (fcn), exprNode_unparse (current)), exprNode_loc (current)); } } if (!sRefSet_isEmpty (sets)) { sRefSet reported = sRefSet_undefined; sRefSet_realElements (current->sets, el) { if (sRefSet_isSameNameMember (reported, el)) { ; /* don't report again */ } else { if (sRef_isUnconstrained (el)) { voptgenerror (FLG_SEFUNSPEC, message ("Parameter %d to %s is declared sef, but " "the argument calls unconstrained function %s " "(no guarantee it will not modify something): %s", argno, exprNode_unparse (fcn), sRef_unconstrainedName (el), exprNode_unparse (current)), exprNode_loc (current)); } else { voptgenerror (FLG_SEFPARAMS, message ("Parameter %d to %s is declared sef, but " "the argument may modify %q: %s", argno, exprNode_unparse (fcn), sRef_unparse (el), exprNode_unparse (current)), exprNode_loc (current)); } } } end_sRefSet_realElements; } } transferChecks_passParam (current, ucurrent, isSpec, fcn, argno, totargs); exprNode_mergeUSs (fcn, current); } } static void checkAnyCall (/*@dependent@*/ exprNode fcn, /*@dependent@*/ cstring fname, uentryList pn, exprNodeList args, bool hasMods, sRefSet mods, bool isSpec, int specialArgs) { int paramno = 0; int nargs = exprNodeList_size (args); setCodePoint (); /* ** concat all args ud's to f, add each arg sref as a use unless ** it was specified as "out", in which case it is a def. */ uentryList_reset (pn); /* ** aliasing checks: ** ** if paramn is only or unique, no other arg may alias argn */ exprNodeList_elements (args, current) { paramno++; if (exprNode_isDefined (current)) { if ((!uentryList_isUndefined (pn) && !uentryList_isFinished (pn))) { uentry ucurrent = uentryList_current (pn); if (specialArgs == 0 || (paramno < specialArgs)) { checkOneArg (ucurrent, current, fcn, isSpec, paramno, nargs); if (context_maybeSet (FLG_ALIASUNIQUE)) { if (uentry_isOnly (ucurrent) || uentry_isUnique (ucurrent)) { checkUniqueParams (fcn, current, args, paramno, ucurrent); } } } } else /* uentry is undefined */ { if (specialArgs == 0) { exprNode_checkUseParam (current); } exprNode_mergeUSs (fcn, current); } } uentryList_advanceSafe (pn); } end_exprNodeList_elements; if (hasMods) { setCodePoint (); sRefSet_allElements (mods, s) { sRef fb; sRef rb = sRef_getRootBase (s); if (sRef_isFileOrGlobalScope (rb)) { context_usedGlobal (rb); } fb = sRef_fixBaseParam (s, args); if (!sRef_isMacroParamRef (fb)) { if (sRef_isNothing (fb)) { ; } else { if (sRef_isValid (fb)) { uentry ue = sRef_getBaseUentry (s); if (uentry_isValid (ue)) { uentry_setLset (ue); } } fcn->sets = sRefSet_insert (fcn->sets, fb); } } sRef_clearDerivedComplete (s); } end_sRefSet_allElements; setCodePoint (); } else { if (context_hasMods ()) { if (context_maybeSet (FLG_MODUNCON)) { voptgenerror (FLG_MODUNCON, message ("Undetected modification possible " "from call to unconstrained function %s: %s", fname, exprNode_unparse (fcn)), exprNode_loc (fcn)); } } else { if (context_maybeSet (FLG_MODUNCONNOMODS) && !(context_inIterDef () || context_inIterEnd ())) { voptgenerror (FLG_MODUNCONNOMODS, message ("Undetected modification possible " "from call to unconstrained function %s: %s", fname, exprNode_unparse (fcn)), exprNode_loc (fcn)); } } exprNode_checkSetAny (fcn, fname); } } void exprNode_checkUseParam (exprNode current) { if (exprNode_isDefined (current)) { exprNode_checkUse (current, current->sref, current->loc); } } static ctype checkNumerics (ctype tr1, ctype tr2, ctype te1, ctype te2, /*@notnull@*/ exprNode e1, /*@notnull@*/ exprNode e2, lltok op) { ctype ret = tr1; if (!ctype_match (tr1, tr2)) { if ((ctype_isRealInt (tr1) || ctype_isReal (tr1)) && (ctype_isRealInt (tr2) || ctype_isReal (tr2))) { DPRINTF (("No error: [%s] %s / [%s] %s", exprNode_unparse (e1), ctype_unparse (tr1), exprNode_unparse (e2), ctype_unparse (tr2))); /* ** evans 2003-06-15: changed this so if either type is a literal, ** the other type is used. ** (Need to look at the ISO C99 rules on this...) */ if (exprNode_isNumLiteral (e1)) { ret = tr2; } else if (exprNode_isNumLiteral (e2)) { ret = tr1; } else { ret = ctype_biggerType (tr1, tr2); } } else { if (ctype_isNumAbstract (tr1) && exprNode_isNumLiteral (e2) && context_flagOn (FLG_NUMABSTRACTLIT, e1->loc)) { ret = tr1; /* No error */ } else if (ctype_isNumAbstract (tr2) && exprNode_isNumLiteral (e1) && context_flagOn (FLG_NUMABSTRACTLIT, e1->loc)) { ret = tr2; } else { if (gentypeerror (tr1, e1, tr2, e2, message ("Incompatible types for %s (%s, %s): %s %s %s", lltok_unparse (op), ctype_unparse (te1), ctype_unparse (te2), exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e1->loc)) { ret = ctype_unknown; } else { ret = ctype_biggerType (tr1, tr2); } } } } else { if (ctype_isNumAbstract (tr1)) { ret = tr1; } else if (ctype_isForceRealNumeric (&tr1) && ctype_isForceRealNumeric (&tr2)) { ret = ctype_resolveNumerics (tr1, tr2); } else if (!context_msgStrictOps ()) { if (ctype_isPointer (tr1)) { if (ctype_isPointer (tr2) && !exprNode_isNullValue (e2)) { ret = ctype_int; } else if (ctype_isInt (tr2)) { ret = te1; } else { ret = ctype_unknown; } } else if (ctype_isPointer (tr2)) { if (ctype_isPointer (tr1)) { ret = ctype_int; } else if (ctype_isInt (tr1)) { ret = te2; } else { ret = ctype_unknown; } } else { ret = ctype_resolveNumerics (tr1, tr2); } } else { int opid = lltok_getTok (op); bool comparop = (opid == EQ_OP || opid == NE_OP || opid == TLT || opid == TGT || opid == LE_OP || opid == GE_OP); if (!ctype_isNumeric (tr1) && !ctype_isNumeric (tr2)) { if (comparop && ((ctype_isEnum (tr1) && ctype_isEnum (tr2)) || (ctype_isBool (tr1) && ctype_isBool (tr2)) || (ctype_isChar (tr1) && ctype_isChar (tr2)))) { ; /* no error */ } else { if (ctype_sameName (te1, te2)) { voptgenerror (FLG_STRICTOPS, message ("Operands of %s are non-numeric (%t): %s %s %s", lltok_unparse (op), te1, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e1->loc); } else { voptgenerror (FLG_STRICTOPS, message ("Operands of %s are non-numerics (%t, %t): %s %s %s", lltok_unparse (op), te1, te2, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e1->loc); } } } else if (!ctype_isNumeric (tr1)) { voptgenerror (FLG_STRICTOPS, message ("Right operand of %s is non-numeric (%t): %s %s %s", lltok_unparse (op), te1, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e1->loc); } else { if (!ctype_isNumeric (tr2)) { voptgenerror (FLG_STRICTOPS, message ("Left operand of %s is non-numeric (%t): %s %s %s", lltok_unparse (op), te2, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), e2->loc); } } ret = ctype_unknown; } } return ret; } static bool abstractOpError (ctype tr1, ctype tr2, lltok op, /*@notnull@*/ exprNode e1, /*@notnull@*/ exprNode e2, fileloc loc1, fileloc loc2) { if (ctype_isRealAbstract (tr1) && ctype_isRealAbstract (tr2)) { if (ctype_match (tr1, tr2)) { if (ctype_isRealNumAbstract (tr1)) { ; /* No warning for numabstract types */ } else { if (lltok_isEqOp (op) || lltok_isNotEqOp (op)) { return optgenerror (FLG_ABSTRACTCOMPARE, message ("Object equality comparison (%s) on objects of abstract type (%t): %s %s %s", lltok_unparse (op), tr1, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), loc1); } else { return optgenerror (FLG_ABSTRACT, message ("Operands of %s are abstract type (%t): %s %s %s", lltok_unparse (op), tr1, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), loc1); } } } else { if (ctype_isRealNumAbstract (tr1) && ctype_isRealNumAbstract (tr2)) { return optgenerror (FLG_NUMABSTRACT, message ("Operands of %s are different numabstract types (%t, %t): %s %s %s", lltok_unparse (op), tr1, tr2, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), loc1); } else { return optgenerror (FLG_ABSTRACT, message ("Operands of %s are abstract types (%t, %t): %s %s %s", lltok_unparse (op), tr1, tr2, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), loc1); } } } else if (ctype_isRealAbstract (tr1) && !ctype_isRealNumAbstract (tr1)) { return optgenerror (FLG_ABSTRACT, message ("Left operand of %s is abstract type (%t): %s %s %s", lltok_unparse (op), tr1, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), loc1); } else { if (ctype_isRealAbstract (tr2) && !ctype_isRealNumAbstract (tr2)) { return optgenerror (FLG_ABSTRACT, message ("Right operand of %s is abstract type (%t): %s %s %s", lltok_unparse (op), tr2, exprNode_unparse (e1), lltok_unparse (op), exprNode_unparse (e2)), loc2); } } return FALSE; } /* ** e1 <= e2 ** ** requies e1 and e2 and not error exprNode's. ** ** Checks: ** ** If e1 is a component of an abstract type, and e2 is mutable and client-visible, ** the rep of the abstract type is exposed. ** ** The order is very important: ** ** check rep expose (move into check transfer) ** check transfer ** setup aliases */ /* ** This isn't really a sensible procedure, but the indententation ** was getting too deep. */ static void checkOneRepExpose (sRef ysr, sRef base, /*@notnull@*/ exprNode e1, /*@notnull@*/ exprNode e2, ctype ct, sRef s2b) { if (!(sRef_isOnly (ysr) || sRef_isKeep (ysr) || sRef_isOwned (ysr) || sRef_isExposed (ysr))) { if (sRef_isAnyParam (base) && !sRef_isExposed (base) && !sRef_isObserver (base)) /* evans 2001-07-11: added isObserver */ { if (sRef_isIReference (ysr)) { if (sRef_sameName (base, sRef_getRootBase (e2->sref))) { voptgenerror (FLG_ASSIGNEXPOSE, message ("Assignment of mutable component of parameter %q " "to component of abstract " "type %s exposes rep: %s = %s", sRef_unparse (base), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } else { voptgenerror (FLG_ASSIGNEXPOSE, message ("Assignment of mutable component of parameter %q " "(through alias %q) to component of abstract " "type %s exposes rep: %s = %s", sRef_unparse (base), sRef_unparse (e2->sref), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } } else { if (sRef_sameName (base, sRef_getRootBase (e2->sref))) { voptgenerror (FLG_ASSIGNEXPOSE, message ("Assignment of mutable parameter %q " "to component of abstract type %s " "exposes rep: %s = %s", sRef_unparse (base), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } else { voptgenerror (FLG_ASSIGNEXPOSE, message ("Assignment of mutable parameter %q " "(through alias %q) to " "component of abstract type %s exposes " "rep: %s = %s", sRef_unparse (base), sRef_unparse (e2->sref), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } } } if (sRef_isFileOrGlobalScope (s2b)) { if (sRef_sameName (base, sRef_getRootBase (e2->sref))) { voptgenerror (FLG_REPEXPOSE, message ("Assignment of global %q " "to component of " "abstract type %s exposes rep: %s = %s", sRef_unparse (base), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } else { voptgenerror (FLG_REPEXPOSE, message ("Assignment of global %q (through alias %q) " "to component of " "abstract type %s exposes rep: %s = %s", sRef_unparse (base), sRef_unparse (e2->sref), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } } } } static void doAssign (/*@notnull@*/ exprNode e1, /*@notnull@*/ exprNode e2, bool isInit) { DPRINTF (("Do assign: %s <- %s", exprNode_unparse (e1), exprNode_unparse (e2))); DPRINTF (("Ctype: %s", ctype_unparse (exprNode_getType (e1)))); if (ctype_isRealFunction (exprNode_getType (e1)) && !ctype_isRealPointer (exprNode_getType (e1))) { voptgenerror (FLG_TYPE, message ("Invalid left-hand side of assignment (function type %s): %s", ctype_unparse (exprNode_getType (e1)), exprNode_unparse (e1)), e1->loc); } if (context_getFlag (FLG_ASSIGNEXPOSE) && ctype_isMutable (e2->typ)) { ctype t2 = exprNode_getType (e2); sRef sr = sRef_getRootBase (e1->sref); ctype ct = sRef_getType (sr); if (ctype_isAbstract (t2) && !ctype_isNumAbstract (t2) && !(uentry_isMutableDatatype (usymtab_getTypeEntry (ctype_typeId (t2))))) { /* it is immutable, okay to reference */ goto donerepexpose; } if (ctype_isAbstract (ct) && sRef_isIReference (e1->sref)) { sRef s2b = sRef_getRootBase (e2->sref); sRef s1 = e1->sref; sRef s1b = sRef_getRootBase (s1); sRefSet aliases; aliases = usymtab_canAlias (e2->sref); if (!sRef_similar (s2b, s1b) && !sRef_isExposed (s1) && !(sRef_isOnly (s2b) || sRef_isKeep (s2b) || sRef_isExposed (s2b))) { if (sRef_isAnyParam (s2b) && !sRef_isOnly (s2b) && !sRef_isOwned (s2b) && !sRef_isKeep (s2b) && !sRef_isExposed (s2b)) { if (sRef_isIReference (e2->sref)) { voptgenerror (FLG_ASSIGNEXPOSE, message ("Assignment of mutable component of parameter %q " "to component of abstract type %s exposes rep: %s = %s", sRef_unparse (s2b), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } else { voptgenerror (FLG_ASSIGNEXPOSE, message ("Assignment of mutable parameter %q to " "component of abstract type %s exposes rep: %s = %s", sRef_unparse (s2b), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } } if (sRef_isFileOrGlobalScope (s2b)) { voptgenerror (FLG_ASSIGNEXPOSE, message ("Assignment of global %q to component of " "abstract type %s exposes rep: %s = %s", sRef_unparse (s2b), ctype_unparse (ct), exprNode_unparse (e1), exprNode_unparse (e2)), e1->loc); } sRefSet_realElements (aliases, ysr) { sRef base = sRef_getRootBase (ysr); if (sRef_similar (ysr, s2b) || sRef_similar (s1b, base) || sRef_sameName (base, s1b)) { ; /* error already reported or same sref */ } else { checkOneRepExpose (ysr, base, e1, e2, ct, s2b); } } end_sRefSet_realElements; } sRefSet_free (aliases); } } donerepexpose: /* ** function variables don't really work... */ if (!ctype_isFunction (ctype_realType (e2->typ))) { if (isInit) { DPRINTF (("Check init: %s / %s", exprNode_unparse (e1), exprNode_unparse (e2))); transferChecks_initialization (e1, e2); } else { transferChecks_assign (e1, e2); } } else { sRef fref = e2->sref; sRef_setDefState (e1->sref, sRef_getDefState (fref), e1->loc); sRef_setNullState (e1->sref, sRef_getNullState (fref), e1->loc); /* Need to typecheck the annotation on the parameters */ if (ctype_isRealFunction (e1->typ)) { uentryList e1p = ctype_argsFunction (ctype_realType (e1->typ)); uentryList e2p = ctype_argsFunction (ctype_realType (e2->typ)); if (!uentryList_isMissingParams (e1p) && !uentryList_isMissingParams (e2p) && uentryList_size (e1p) > 0) { if (uentryList_size (e1p) == uentryList_size (e2p)) { int n = 0; uentryList_elements (e1p, el1) { uentry el2; el2 = uentryList_getN (e2p, n); n++; uentry_checkMatchParam (el1, el2, n, e2); } end_uentryList_elements; } } } } if (exprNode_isStringLiteral (e2)) { exprNode_checkStringLiteralLength (exprNode_getType (e1), e2); } if (isInit && sRef_isFileOrGlobalScope (e1->sref)) { ; } else { DPRINTF (("Update aliases: %s / %s", exprNode_unparse (e1), exprNode_unparse (e2))); updateAliases (e1, e2); } } static void checkMacroParen (exprNode e) { if (exprNode_isError (e) || e->kind == XPR_CAST) { ; } else { if (sRef_isUnsafe (e->sref) && !exprNode_isInParens (e)) { voptgenerror (FLG_MACROPARENS, message ("Macro parameter used without parentheses: %s", exprNode_unparse (e)), e->loc); } } } static void reflectNullTest (/*@notnull@*/ exprNode e, bool isnull) { if (isnull) { e->guards = guardSet_addTrueGuard (e->guards, e->sref); } else { e->guards = guardSet_addFalseGuard (e->guards, e->sref); } } /* ** e1 <= e2 ** ** if e2 is a parameter or global derived location which ** can be modified (that is, e2 is a mutable abstract type, ** or a derived pointer), then e1 can alias e2. ** ** e1 can alias everything which e2 can alias. ** ** Also, if e1 is guarded, remove from guard sets! */ static void updateAliases (/*@notnull@*/ exprNode e1, /*@notnull@*/ exprNode e2) { if (!context_inProtectVars ()) { /* ** depends on types of e1 and e2 */ sRef s1 = e1->sref; sRef s2 = e2->sref; ctype t1 = exprNode_getType (e1); /* handle pointer sRefs, record fields, arrays, etc... */ if (!ctype_isRealSU (t1)) { DPRINTF (("Copying real! %s", ctype_unparse (t1))); sRef_copyRealDerivedComplete (s1, s2); } else { /* ** Fields should alias */ DPRINTF (("Not COPYING!: %s", ctype_unparse (t1))); } if (ctype_isMutable (t1) && sRef_isKnown (s1)) { usymtab_clearAlias (s1); usymtab_addMustAlias (s1, s2); DPRINTF (("Add must alias: %s / %s", sRef_unparse (s1), sRef_unparse (s2))); } else { DPRINTF (("Not mutable: %s", ctype_unparse (t1))); } if (sRef_possiblyNull (s1) && usymtab_isGuarded (s1)) { usymtab_unguard (s1); } } } exprNode exprNode_updateLocation (/*@returned@*/ exprNode e, /*@temp@*/ fileloc loc) { if (exprNode_isDefined (e)) { e->loc = fileloc_update (e->loc, loc); } else { e = exprNode_createLoc (ctype_unknown, fileloc_copy (loc)); } return (e); } static void checkUniqueParams (exprNode fcn, /*@notnull@*/ exprNode current, exprNodeList args, int paramno, uentry ucurrent) { int iparamno = 0; sRef thisref = exprNode_getSref (current); /* ** Check if any argument could match this argument. */ exprNodeList_elements (args, icurrent) { iparamno++; if (iparamno != paramno) { sRef sr = exprNode_getSref (icurrent); if (sRef_similarRelaxed (thisref, sr)) { if (!sRef_isConst (thisref) && !sRef_isConst (sr)) { voptgenerror (FLG_ALIASUNIQUE, message ("Parameter %d (%s) to function %s is declared %s but " "is aliased by parameter %d (%s)", paramno, exprNode_unparse (current), exprNode_unparse (fcn), alkind_unparse (uentry_getAliasKind (ucurrent)), iparamno, exprNode_unparse (icurrent)), current->loc); } } else { sRefSet aliases = usymtab_canAlias (sr); sRefSet_allElements (aliases, asr) { if (ctype_isUnknown (sRef_getType (thisref))) { sRef_setType (thisref, uentry_getType (ucurrent)); } if (sRef_similarRelaxed (thisref, asr)) { if (sRef_isExternal (asr)) { if (sRef_isLocalState (thisref)) { ; /* okay */ } else { sRef base = sRef_getRootBase (asr); if (!sRef_similar (sRef_getBase (asr), thisref)) { if (sRef_isUnique (base) || sRef_isOnly (base) || sRef_isKept (base) || (sRef_isAddress (asr) && sRef_isLocalVar (base)) || (sRef_isAddress (thisref) && sRef_isLocalVar (sRef_getRootBase (thisref)))) { ; /* okay, no error */ } else { voptgenerror (FLG_MAYALIASUNIQUE, message ("Parameter %d (%s) to function %s is declared %s but " "may be aliased externally by parameter %d (%s)", paramno, exprNode_unparse (current), exprNode_unparse (fcn), alkind_unparse (uentry_getAliasKind (ucurrent)), iparamno, exprNode_unparse (icurrent)), current->loc); } } } } else { voptgenerror (FLG_ALIASUNIQUE, message ("Parameter %d (%s) to function %s is declared %s but " "is aliased externally by parameter %d (%s) through " "alias %q", paramno, exprNode_unparse (current), exprNode_unparse (fcn), alkind_unparse (uentry_getAliasKind (ucurrent)), iparamno, exprNode_unparse (icurrent), sRef_unparse (asr)), current->loc); } } } end_sRefSet_allElements; sRefSet_free (aliases); } } } end_exprNodeList_elements; } long exprNode_getLongValue (exprNode e) { long value; if (exprNode_hasValue (e) && multiVal_isInt (exprNode_getValue (e))) { value = multiVal_forceInt (exprNode_getValue (e)); } else { value = 0; /* Unknown value */ } return value; } /*@only@*/ fileloc exprNode_getNextSequencePoint (exprNode e) { /* ** Returns the location of the sequence point following e. ** ** Only works for statements (for now). */ if (exprNode_isDefined (e) && e->kind == XPR_STMT) { lltok t = exprData_getUopTok (e->edata); return fileloc_copy(lltok_getLoc (t)); } else { /* drl possible problem : warning fix llcontbug (message ("Cannot get next sequence point: %s", exprNode_unparse (e))); */ return fileloc_undefined; } } exprNode exprNode_createNew(ctype c) { exprNode ret; ret = exprNode_createPlain (c); return ret; } bool exprNode_isInitBlock (exprNode e) { return (exprNode_isDefined(e) && e->kind == XPR_INITBLOCK); } /*drl 3/2/2003 moved this function out of constraint.c */ exprNode exprNode_copyConstraints (/*@returned@*/ exprNode dst, exprNode src) { llassert (exprNode_isDefined (dst) ); llassert (exprNode_isDefined (src) ); constraintList_free (dst->ensuresConstraints); constraintList_free (dst->requiresConstraints); constraintList_free (dst->trueEnsuresConstraints); constraintList_free (dst->falseEnsuresConstraints); dst->ensuresConstraints = constraintList_copy (src->ensuresConstraints); dst->requiresConstraints = constraintList_copy (src->requiresConstraints); dst->trueEnsuresConstraints = constraintList_copy (src->trueEnsuresConstraints); dst->falseEnsuresConstraints = constraintList_copy (src->falseEnsuresConstraints); return dst; } void exprNode_revealState (exprNode e) { if (exprNode_isDefined (e)) { llmsg (message ("%s: State of %s: %s", fileloc_unparse (exprNode_loc (e)), exprNode_unparse (e), sRef_unparseFull (e->sref))); } else { llmsg (message ("%s: Reveal state undefined", fileloc_unparse (g_currentloc))); } }