/*
** 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
*/
/*
** stateClauseList.c
*/

# include "splintMacros.nf"
# include "basic.h"

/*@constant int stateClauseListBASESIZE;@*/
# define stateClauseListBASESIZE MIDBASESIZE

static /*@notnull@*/ stateClauseList stateClauseList_new (void)
{
  stateClauseList s = (stateClauseList) dmalloc (sizeof (*s));
  
  s->nelements = 0;
  s->nspace = stateClauseListBASESIZE;
  s->elements = (stateClause *) 
    dmalloc (sizeof (*s->elements) * stateClauseListBASESIZE);

  return (s);
}

static void
stateClauseList_grow (stateClauseList s)
{
  int i;
  stateClause *newelements;

  llassert (stateClauseList_isDefined (s));

  s->nspace += stateClauseListBASESIZE; 
  
  newelements = (stateClause *) 
    dmalloc (sizeof (*newelements) * (s->nelements + s->nspace));
  
  for (i = 0; i < s->nelements; i++)
    {
      newelements[i] = s->elements[i];
    }
  
  sfree (s->elements);
  s->elements = newelements;
}

stateClauseList stateClauseList_add (stateClauseList s, stateClause el)
{
  DPRINTF (("Adding: %s", stateClause_unparse (el)));

  if (stateClauseList_isUndefined (s))
    {
      s = stateClauseList_new ();
    }
  else
    {
      stateClauseList_elements (s, cl)
	{
	  if (stateClause_sameKind (cl, el))
	    {
	      voptgenerror
		(FLG_SYNTAX,
		 message ("Multiple %q clauses for one function (ignoring second)",
			  stateClause_unparseKind (cl)),
		 g_currentloc);

	      stateClause_free (el);
	      return s;
	    }
	} end_stateClauseList_elements ;
    }

  if (s->nspace <= 0)
    {
      stateClauseList_grow (s);
    }
  
  s->nspace--;
  s->elements[s->nelements] = el;
  s->nelements++;

  return s;
}

cstring stateClauseList_unparse (stateClauseList s)
{
  cstring st = cstring_undefined;
  int i;
  
  if (stateClauseList_isDefined (s))
    {
      for (i = 0; i < stateClauseList_size (s); i++)
	{
	  if (i == 0)
	    {
	      st = message ("%q;", stateClause_unparse (s->elements[i]));
	    }
	  else
	    st = message ("%q %q;", st, stateClause_unparse (s->elements[i]));
	}
    }
  
  return (st);
}

stateClauseList stateClauseList_copy (stateClauseList s)
{
  if (stateClauseList_isDefined (s))
    {
      stateClauseList t = (stateClauseList) dmalloc (sizeof (*t));
      int i;
      
      t->nelements = s->nelements;
      t->nspace = 0;
      
      if (s->nelements > 0)
	{
	  t->elements = (stateClause *) dmalloc (sizeof (*t->elements) * t->nelements);
	  for (i = 0; i < s->nelements; i++) 
	    {
	      t->elements[i] = stateClause_copy (s->elements[i]); 
	    }
	}
      else
	{
	  t->elements = NULL;
	}

      return t;
    }
  else
    {
      return stateClauseList_undefined;
    }
}

void
stateClauseList_free (stateClauseList s)
{
  if (!stateClauseList_isUndefined (s)) 
    {
      int i;

      for (i = 0; i < s->nelements; i++)
	{
	  stateClause_free (s->elements[i]);  
	}

      sfree (s->elements);
      sfree (s);
    }
}

cstring stateClauseList_dump (stateClauseList s)
{
  cstring st = cstring_undefined;

  if (stateClauseList_isUndefined (s)) return st;
  
  stateClauseList_elements (s, current)
    {
      st = message ("%q%q$", st, stateClause_dump (current));
    } end_stateClauseList_elements;

  return st;
}

stateClauseList stateClauseList_undump (char **s)
{
  char c;
  stateClauseList pn = stateClauseList_new ();
  int paramno = 0;

  c = **s;

  while (c != '#' && c != '@')
    {
      stateClause sc = stateClause_undump (s);
      
      pn = stateClauseList_add (pn, sc);
      reader_checkChar (s, '$');
      c = **s;
      paramno++;
    }

  return pn;
}

# ifdef DEADCODE
int stateClauseList_compare (stateClauseList s1, stateClauseList s2)
{
  if (stateClauseList_isUndefined (s1)
      && stateClauseList_isUndefined (s2))
    {
      return 0;
    }
  else
    {
      if (s1 - s2 > 0) /* evans 2001-08-21: was (int) s1 > (int) s2) */
	{
	  return 1;
	}
      else
	{
	  return -1;
	}
    }
}
# endif /* DEADCODE */

static /*@exposed@*/ sRefSet
stateClauseList_getClause (stateClauseList s, stateClause k)
{
  stateClauseList_elements (s, el)
    {
      if (stateClause_matchKind (el, k))
	{
	  return stateClause_getRefs (el);
	}
    } end_stateClauseList_elements ;

  return sRefSet_undefined;
}

void stateClauseList_checkAll (uentry ue)
{
  stateClauseList clauses = uentry_getStateClauseList (ue);
  sRef res = uentry_getSref (ue);		  
  bool specialResult = FALSE;

  DPRINTF (("Check state clauses: %s", uentry_unparseFull (ue)));

  stateClauseList_elements (clauses, cl)
    {
      bool isPre = stateClause_isBeforeOnly (cl);

      if (stateClause_isGlobal (cl))
	{
	  ; 
	}
      else
	{
	  sRefSet refs = stateClause_getRefs (cl);
	  
	  sRefSet_allElements (refs, el)
	    {
	      sRef rb = sRef_getRootBase (el);

	      DPRINTF (("Check: %s", sRef_unparse (el)));

	      if (sRef_isResult (rb)) 
		{
		  /*
		  ** The result type is now know, need to set it:
		  */
		  
		  if (ctype_isUnknown (sRef_getType (rb)))
		    {
		      ctype utype = uentry_getType (ue);
		      llassert (ctype_isFunction (utype));

		      sRef_setTypeFull (rb, ctype_getReturnType (utype));
		      DPRINTF (("el: %s", sRef_unparseFull (el)));
		    }
		}
	      
	      if (stateClause_setsMetaState (cl))
		{
		  qual q = stateClause_getMetaQual (cl);
		  annotationInfo qa = qual_getAnnotationInfo (q);

		  if (!annotationInfo_matchesContextRef (qa, el))
		    {
		      if (optgenerror
			  (FLG_ANNOTATIONERROR,
			   message ("Attribute annotation %s used on inappropriate reference %q in %q clause of %q: %q",
				    qual_unparse (q),
				    sRef_unparse (el),
				    stateClause_unparseKind (cl),
				    uentry_getName (ue),
				    stateClause_unparse (cl)),
			   uentry_whereLast (ue)))
			{
			  /* annotationInfo_showContextError (ainfo, ue); */
			}
		    }
		}

	      if (sRef_isResult (rb))
		{
		  if (isPre)
		    {
		      voptgenerror
			(FLG_INCONDEFS,
			 message ("Function result is used in %q clause of %q "
				  "(%q applies to the state before function is "
				  "called, so should not use result): %q",
				  stateClause_unparseKind (cl),
				  uentry_getName (ue),
				  stateClause_unparseKind (cl),
				  sRef_unparse (el)),
			 uentry_whereLast (ue));
		    }
		  else
		    {
		      if (!sRef_isStateSpecial (res))
			{
			  DPRINTF (("Here we are: %s", sRef_unparseFull (res)));

			  if (!specialResult)
			    {
			      sstate pstate = sRef_getDefState (res);
			      
			      if (!sRef_makeStateSpecial (res))
				{
				  if (optgenerror
				      (FLG_INCONDEFS,
				       message ("Function result is used in %q clause of %q "
						"but was previously annotated with %s: %q",
						stateClause_unparseKind (cl),
						uentry_getName (ue),
						sstate_unparse (pstate),
						sRef_unparse (el)),
				       uentry_whereLast (ue)))
				    {
				      specialResult = TRUE;
				    }
				}			      
			    }
			}
		      
		      DPRINTF (("Fixing result type! %s", sRef_unparseFull (el)));
		      (void) sRef_fixResultType (el, sRef_getType (res), ue);
		    }
		}
	      else if (sRef_isParam (rb))
		{
		  DPRINTF (("Make special: %s", sRef_unparseFull (rb)));
		  
		  if (!sRef_makeStateSpecial (rb))
		    {
		      if (fileloc_isXHFile (uentry_whereLast (ue)))
			{
			  ; /* Okay to override in .xh files */
			}
		      else if (stateClause_isQual (cl))
			{
			  ; /* qual clauses don't interfere with definition state */
			}
		      else
			{
			  voptgenerror 
			    (FLG_INCONDEFS,
			     message ("Reference %q used in %q clause of %q, "
				      "but was previously annotated with %s: %q",
				      sRef_unparse (rb),
				      stateClause_unparseKind (cl),
				      uentry_getName (ue),
				      sstate_unparse (sRef_getDefState (res)),
				      sRef_unparse (el)),
			     uentry_whereLast (ue));
			}
		    }
		  
		  DPRINTF (("Made special: %s", sRef_unparseFull (rb)));
		}
	      else if (sRef_isInvalid (rb))
		{
		  /*@innercontinue@*/ continue;
		}
	      else 
		{
		  BADBRANCHCONT;
		  /*@innercontinue@*/ continue;
		}
	      
	      if (stateClause_isMemoryAllocation (cl))
		{
		  if (!ctype_isVisiblySharable (sRef_getType (el)))
		    {
		      voptgenerror
			(FLG_ANNOTATIONERROR,
			 /*@-sefparams@*/ /* This is okay because its fresh storage. */
			 message 
			 ("%q clauses includes %q of "
			  "non-dynamically allocated type %s",
			  cstring_capitalizeFree (stateClause_unparseKind (cl)),
			  sRef_unparse (el), 
			  ctype_unparse (sRef_getType (el))),
			 uentry_whereLast (ue));
		      /*@=sefparams@*/
		    }
		}
	      
	    } end_sRefSet_allElements ;
	}
    } end_stateClauseList_elements ;
}
  
void stateClauseList_checkEqual (uentry old, uentry unew)
{
  stateClauseList oldClauses = uentry_getStateClauseList (old);
  stateClauseList newClauses = uentry_getStateClauseList (unew);

  if (stateClauseList_isDefined (newClauses))
    {
      stateClauseList_elements (newClauses, cl)
	{
	  if (stateClause_isGlobal (cl))
	    {
	      ;
	    }
	  else
	    {
	      sRefSet sc = stateClauseList_getClause (oldClauses, cl);
	      
	      if (!sRefSet_equal (sc, stateClause_getRefs (cl)))
		{
		  if (optgenerror
		      (FLG_INCONDEFS,
		       message ("Function %q %rdeclared with inconsistent %q clause: %q",
				uentry_getName (old),
				uentry_isDeclared (old),
				stateClause_unparseKind (cl),
				sRefSet_unparsePlain (stateClause_getRefs (cl))),
		       g_currentloc))
		    {
		      uentry_showWhereLastExtra (old, sRefSet_unparsePlain (sc));
		    }
		}
	    }
	} end_stateClauseList_elements ;
	
      stateClauseList_elements (oldClauses, cl)
	{
	  if (stateClause_isGlobal (cl))
	    {
	      ; /* Don't handle globals for now */
	    }
	  else
	    {
	      sRefSet sc = stateClauseList_getClause (newClauses, cl);
	      
	      if (sRefSet_isUndefined (sc) && !sRefSet_isEmpty (stateClause_getRefs (cl)))
		{
		  if (optgenerror
		      (FLG_INCONDEFS,
		       message ("Function %q %rdeclared without %q clause (either "
				"use no special clauses in redeclaration, or "
				"they must match exactly: %q",
				uentry_getName (old),
				uentry_isDeclared (old),
				stateClause_unparseKind (cl),
				sRefSet_unparsePlain (stateClause_getRefs (cl))),
		       g_currentloc))
		    {
		      uentry_showWhereLastExtra (old, sRefSet_unparsePlain (sc));
		    }
		}
	    }
	} end_stateClauseList_elements ;
    }
}



