/* This file is part of cqual.
   Copyright (C) 2002-2003 The Regents of the University of California.

cqual 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, or (at your option)
any later version.

cqual 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.

You should have received a copy of the GNU General Public License
along with cqual; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */

/* This file defines data structures and functions to be used for
   Steensgaard's alias analysis style restrict inference/checking */

#include <assert.h>
#include <stdio.h>
#include "regions.h"
#include "aloctypes.h"
#include "utils.h"
#include "flags.h"
#include "qerror.h"
#include "rinf_analyze.h"

/* #define DEBUG */

DEFINE_BAG(alocreftype_set, alocreftype);
#define scan_alocreftype_set(var,scanner,set) \
for (alocreftype_set_scan(set, &scanner), var = alocreftype_set_next(&scanner); \
     var; \
     var = alocreftype_set_next(&scanner))

DEFINE_BAG(alocfntype_set, alocfntype);
#define scan_alocfntype_set(var,scanner,set) \
for (alocfntype_set_scan(set, &scanner), var = alocfntype_set_next(&scanner); \
     var; \
     var = alocfntype_set_next(&scanner))

DEFINE_BAG(aloctype_set, aloctype);
#define scan_aloctype_set(var,scanner,set) \
for (aloctype_set_scan(set, &scanner), var = aloctype_set_next(&scanner); \
     var; \
     var = aloctype_set_next(&scanner))

DEFINE_BAG(effecttype_set, effecttype);
#define scan_effecttype_set(var,scanner,set) \
for (effecttype_set_scan(set, &scanner), var = effecttype_set_next(&scanner); \
     var; \
     var = effecttype_set_next(&scanner))

struct Aloctype 
{
  enum { aloctype_var, aloctype_link } kind;
  union 
  {
    struct {
      alocreftype tau;
      alocfntype lam;
      alocreftype_set refcontexts;
      alocfntype_set fncontexts;
      int num_equiv;
    } var;
    aloctype link;
  } u;
};

struct Alocreftype
{
  enum { alocref_ref, alocref_var, alocref_link } kind;
  int visited;
  int reachcomputed;
  aloctype_set contexts;
  effecttype_set singles;
  effecttype_set reaches;
  union
  {
    struct {
      aloctype pointsto;
    } ref;
    struct {
      const char * name;
      int num_equiv;
      alocreftype_set ub;
    } var;
    alocreftype link;
  } u;
};

struct Alocfntype
{
  enum { alocfn_fn, alocfn_var, alocfn_link } kind;
  aloctype_set contexts;
  union
  {
    struct {
      aloctype * params;  /* Change this to a list? */
      aloctype returns;
      effecttype eff;
    } fn;
    struct {
      const char * name;
      int num_equiv;
      alocfntype_set ub;
    } var;
    alocfntype link;
  } u;
};

typedef enum { effconstr_read, effconstr_write, effconstr_any } effconstr_kind;

struct Effecttype
{
  enum { efftype_single, efftype_reach, efftype_union, efftype_inter, 
	 efftype_var, efftype_link } kind;
  int visited;
  union
  {
    struct {
      effconstr_kind kind;
      alocreftype base;
      effecttype_set ub;
    } single;
    struct {
      effconstr_kind kind;
      alocreftype base;
      effecttype_set ub;
    } reach;
    struct {
      effecttype e1;
      effecttype e2;
    } u;
    struct {
      effecttype e1;
      effecttype e2;
      effecttype_set ub;
      int visited1, visited2;
    } inter;
    struct {
      const char * name;
      effecttype_set ub;
      int num_equiv;
    } var;
    effecttype link;
  } u;
};

typedef struct Checked_leq_const {
  int satisfied;
  effecttype e1;
  effecttype e2;
} *checked_leq_const;

struct Rinf_const {
  location loc;
  struct {
    int rhs_invoked;
    checked_leq_const condition1;
    checked_leq_const condition2;
    checked_leq_const condition3;
    checked_leq_const condition4;
    checked_leq_const condition5;
    alocreftype r_al;
    alocreftype old_al;
  } one;
  struct {
    int rhs_invoked;
    checked_leq_const condition;
    alocreftype old_al;
    effecttype body;
  } two;
};

effecttype effecttype_empty = NULL;
region aloctype_region;
static enum { state_gen, state_reach, state_done } state;
static int next_ref = 0;
static int next_fn = 0;
static int next_effecttype = 0;
static dd_list constraints = NULL;

static alocreftype ecr_alocreftype(alocreftype alref);
static aloctype ecr_aloctype(aloctype al);
static alocfntype ecr_alocfntype(alocfntype alfn);

void init_aloctypes(void)
{
  assert(aloctype_region == NULL);
  aloctype_region = newregion();
  state = state_gen;
  constraints = dd_new_list(aloctype_region);
}

/**************************************************************************
 *                                                                        *
 * Aloctype                                                               *
 *                                                                        *
 **************************************************************************/

aloctype make_aloctype(alocreftype tau, alocfntype lam)
{
  aloctype al;
  aloctype_set * contexts;

  assert(state == state_gen);

  al = ralloc(aloctype_region, struct Aloctype);
  al->kind = aloctype_var;
  al->u.var.tau = tau;
  al->u.var.lam = lam;
  al->u.var.refcontexts = empty_alocreftype_set(aloctype_region);
  al->u.var.fncontexts = empty_alocfntype_set(aloctype_region);
  al->u.var.num_equiv = 1;

  tau = ecr_alocreftype(tau);
  if (tau->kind == alocref_ref)
    contexts = &tau->contexts;
  else
    {
      assert(tau->kind == alocref_var);
      contexts = &tau->contexts;
    }
  aloctype_set_insert(aloctype_region, contexts, al);
 
  lam = ecr_alocfntype(lam);
  if (lam->kind == alocfn_fn)
    contexts = &lam->contexts;
  else
    {
      assert(lam->kind == alocfn_var);
      contexts = &lam->contexts;
    }
  aloctype_set_insert(aloctype_region, contexts, al);

  return al;
}

aloctype make_aloctype_fresh(void)
{
  assert(state == state_gen);

  return make_aloctype(alocreftype_fresh(), alocfntype_fresh());
}

static aloctype ecr_aloctype(aloctype al)
{
  assert(al);

  if (al->kind == aloctype_link)
    {
      aloctype ecr = al, cur, temp;
      
      /* Find root */
      while (ecr->kind == aloctype_link)
	ecr = ecr->u.link;
      
      /* Compress path */
      cur = al;
      while (cur->u.link != ecr)
	{
	  temp = cur->u.link;
	  cur->u.link = ecr;
	  cur = temp;
	}
      return ecr;
    }
  else
    return al;
}

void unify_aloctype(aloctype al1, aloctype al2)
{
  aloctype new_ecr, new_link;
  alocreftype tau1, tau2;
  alocfntype lam1, lam2;

#ifdef DEBUG
  printf("\nUnify aloc:\n");
  print_aloctype(printf, al1, 10);
  printf(" = ");
  print_aloctype(printf, al2, 10);
  printf("\n");
#endif

  assert(al1 && al2);

  al1 = ecr_aloctype(al1);
  al2 = ecr_aloctype(al2);

  if (al1 == al2) return;
  
  /* assert(al1->kind == aloctype_var && al2->kind == aloctype_var); */
  if (al1->kind != aloctype_var || al2->kind != aloctype_var)
    {
      printf("%d %d\n", al1->kind, al2->kind);
      assert(al1->kind == aloctype_var && al2->kind == aloctype_var);
    }
  
  tau1 = al1->u.var.tau;
  tau2 = al2->u.var.tau;
  lam1 = al1->u.var.lam;
  lam2 = al2->u.var.lam;

  if (al1->u.var.num_equiv <= al2->u.var.num_equiv)
    {
      new_ecr = al2;
      new_link = al1;
    }
  else
    {
      new_ecr = al1;
      new_link = al2;
    }

  new_ecr->u.var.num_equiv += new_link->u.var.num_equiv;
  new_ecr->u.var.refcontexts = 
    alocreftype_set_union(new_ecr->u.var.refcontexts, 
			  new_link->u.var.refcontexts);
  new_ecr->u.var.fncontexts = 
    alocfntype_set_union(new_ecr->u.var.fncontexts, 
			 new_link->u.var.fncontexts);
  
  new_link->kind = aloctype_link;      
  new_link->u.link = new_ecr;

  unify_alocreftype(tau1, tau2);
  unify_alocfntype(lam1, lam2);
}

void mkleq_aloctype(aloctype al1, aloctype al2)
{
  alocreftype tau1, tau2;
  alocfntype lam1, lam2;
  
  assert(al1 && al2);

  al1 = ecr_aloctype(al1);
  al2 = ecr_aloctype(al2);

#ifdef DEBUG
  printf("\nMkleq aloc:\n");
  print_aloctype(printf, al1, 10);
  printf(" <= ");
  print_aloctype(printf, al2, 10);
  printf("\n");
#endif

  if (al1 == al2) return;
  
  assert(al1->kind == aloctype_var && al2->kind == aloctype_var);

  tau1 = al1->u.var.tau;
  tau2 = al2->u.var.tau;
  lam1 = al1->u.var.lam;
  lam2 = al2->u.var.lam;

  mkleq_alocreftype(tau1, tau2);
  mkleq_alocfntype(lam1, lam2);
}

alocreftype proj_tau(aloctype al)
{
  al = ecr_aloctype(al);
  assert(al->kind == aloctype_var);

  assert(al->u.var.tau);

  return al->u.var.tau;
}

alocfntype proj_lam(aloctype al)
{
  al = ecr_aloctype(al);
  assert(al->kind == aloctype_var);

  assert(al->u.var.lam);

  return al->u.var.lam;
}

int print_aloctype(printf_func pf, aloctype al, int depth)
{
  int result;

  if (depth <= 0)
    return pf("*");

  al = ecr_aloctype(al);
  assert(al->kind == aloctype_var);

  result = print_alocreftype(pf, al->u.var.tau, depth);
  result += pf(" x ");
  result += print_alocfntype(pf, al->u.var.lam, depth);

  return result;
}

/**************************************************************************
 *                                                                        *
 * Alocreftype                                                            *
 *                                                                        *
 **************************************************************************/

alocreftype alocreftype_fresh(void)
{
  const char *name;

  assert(state == state_gen);

  name = rstrcat(aloctype_region, "ref", 
		 inttostr(aloctype_region, next_ref++));

  return alocreftype_var(name);
}

alocreftype alocreftype_var(const char * name)
{
  alocreftype alref;
  
  assert(state == state_gen);

  alref = ralloc(aloctype_region, struct Alocreftype);
  alref->kind = alocref_var;
  alref->u.var.name = name;
  alref->u.var.num_equiv = 1;
  alref->reachcomputed = 0;
  alref->visited = 0;
  alref->u.var.ub = empty_alocreftype_set(aloctype_region);
  alref->contexts = empty_aloctype_set(aloctype_region);
  alref->singles = empty_effecttype_set(aloctype_region);
  alref->reaches = empty_effecttype_set(aloctype_region);
  return alref;
}

alocreftype alocreftype_ref(aloctype pointsto)
{
  alocreftype alref;
  
  assert(pointsto);
  assert(state == state_gen);

  alref = ralloc(aloctype_region, struct Alocreftype);
  alref->kind = alocref_ref;
  alref->u.ref.pointsto = pointsto;
  alref->reachcomputed = 0;
  alref->visited = 0;
  alref->contexts = empty_aloctype_set(aloctype_region);
  alref->singles = empty_effecttype_set(aloctype_region);
  alref->reaches = empty_effecttype_set(aloctype_region);

  pointsto = ecr_aloctype(pointsto);
  assert(pointsto->kind == aloctype_var);
  alocreftype_set_insert(aloctype_region, &pointsto->u.var.refcontexts, alref);

  return alref;
}

static alocreftype ecr_alocreftype(alocreftype alref)
{
  assert(alref);

  if (alref->kind == alocref_link)
    {
      alocreftype ecr = alref, cur, temp;
      
      /* Find root */
      while (ecr->kind == alocref_link)
	ecr = ecr->u.link;
      
      /* Compress path */
      cur = alref;
      while (cur->u.link != ecr)
	{
	  temp = cur->u.link;
	  cur->u.link = ecr;
	  cur = temp;
	}
      return ecr;
    }
  else
    return alref;
}

/* Walks down the graph to reset reachability information.
   Ignore functions.
*/
static void reset_reachcomputed(alocreftype alref)
{
  assert(state == state_reach);

  alref = ecr_alocreftype(alref);
  assert(alref->kind == alocref_ref || alref->kind == alocref_var);

  if (alref->visited) return;

  alref->visited = 1;
  alref->reachcomputed = 0;

  if (alref->kind == alocref_ref)
    {
      aloctype al;

      al = ecr_aloctype(alref->u.ref.pointsto);
      assert(al->kind == aloctype_var);
      reset_reachcomputed(al->u.var.tau);
    }
}

/* Walks down the graph to reset visited information.
   Ignore functions.
*/
static void clean_visited(alocreftype alref)
{
  assert(state == state_reach);

  alref = ecr_alocreftype(alref);
  assert(alref->kind == alocref_ref || alref->kind == alocref_var);
  
  if (!alref->visited) return;

  alref->visited = 0;

  if (alref->kind == alocref_ref)
    {
      aloctype al;

      al = ecr_aloctype(alref->u.ref.pointsto);
      assert(al->kind == aloctype_var);
      clean_visited(al->u.var.tau);
    }
}

void unify_alocreftype(alocreftype alref1, alocreftype alref2)
{
  assert(alref1 && alref2);

#ifdef DEBUG
  printf("\nUnify alocref:\n");
  print_alocreftype(printf, alref1, 10);
  printf(" = ");
  print_alocreftype(printf, alref2, 10);
  printf("\n");
#endif

  alref1 = ecr_alocreftype(alref1);
  alref2 = ecr_alocreftype(alref2);

  if (alref1 == alref2)
    return;
  else if (alref1->kind == alocref_var && alref2->kind == alocref_var)
    {
      alocreftype new_ecr, new_link;

      assert(state == state_gen);
      
      if (alref1->u.var.num_equiv <= alref2->u.var.num_equiv)
	{
	  new_ecr = alref2;
	  new_link = alref1;
	}
      else
	{
	  new_ecr = alref1;
	  new_link = alref2;
	}

      new_ecr->u.var.num_equiv += new_link->u.var.num_equiv;
      new_ecr->u.var.ub = 
	alocreftype_set_union(new_ecr->u.var.ub, new_link->u.var.ub);
      new_ecr->contexts =
	aloctype_set_union(new_ecr->contexts, new_link->contexts);
      new_ecr->singles =
	effecttype_set_union(new_ecr->singles, new_link->singles);
      new_ecr->reaches =
	effecttype_set_union(new_ecr->reaches, new_link->reaches);

      new_link->kind = alocref_link;      
      new_link->u.link = new_ecr;

      if (state == state_reach)
	{
#ifdef DEBUG	  
	  printf("  Resetting reach for ");
	  print_alocreftype(printf, new_ecr, 10);
	  printf("\n");
#endif
	  reset_reachcomputed(new_ecr);
	  clean_visited(new_ecr);
	}
    }
  else if (alref1->kind == alocref_var && alref2->kind == alocref_ref)
    unify_alocreftype(alref2, alref1);
  else if (alref1->kind == alocref_ref && alref2->kind == alocref_var)
    {
      alocreftype_set ub = alref2->u.var.ub;
      alocreftype_set_scanner ss;
      alocreftype b;
      
      assert(state == state_gen);

      alref1->contexts =
	aloctype_set_union(alref1->contexts, alref2->contexts);
      alref1->singles =
	effecttype_set_union(alref1->singles, alref2->singles);
      alref1->reaches =
	effecttype_set_union(alref1->reaches, alref2->reaches);

      alref2->kind = alocref_link;
      alref2->u.link = alref1;

      if (state == state_reach)
	{
#ifdef DEBUG	  
	  printf("  Resetting reach for ");
	  print_alocreftype(printf, alref1, 10);
	  printf("\n");
#endif
	  reset_reachcomputed(alref1);
	  clean_visited(alref1);
	}

      scan_alocreftype_set(b, ss, ub)
	unify_alocreftype(alref1, b);
    }
  else
    {
      aloctype pointsto;

      assert(alref1->kind == alocref_ref && alref2->kind == alocref_ref);

      pointsto = alref2->u.ref.pointsto;

      alref1->contexts =
	aloctype_set_union(alref1->contexts, alref2->contexts);
      alref1->singles =
	effecttype_set_union(alref1->singles, alref2->singles);
      alref1->reaches =
	effecttype_set_union(alref1->reaches, alref2->reaches);

      alref2->kind = alocref_link;
      alref2->u.link = alref1;

      if (state == state_reach)
	{
	  /* This must have come from restrict, thus poinsto should have
	     been already unified.
	  */
	  assert(ecr_aloctype(alref1->u.ref.pointsto) == 
		 ecr_aloctype(pointsto));
#ifdef DEBUG	  
	  printf("  Resetting reach for ");
	  print_alocreftype(printf, alref1, 10);
	  printf("\n");
#endif
	  reset_reachcomputed(alref1);
	  clean_visited(alref1);
	}
      else
	unify_aloctype(alref1->u.ref.pointsto, pointsto);
    }
}

void mkleq_alocreftype(alocreftype alref1, alocreftype alref2)
{
  assert(alref1 && alref2);

#ifdef DEBUG
  printf("\nMkleq alocref:\n");
  print_alocreftype(printf, alref1, 10);
  printf(" <= ");
  print_alocreftype(printf, alref2, 10);
  printf("\n");
#endif

  alref1 = ecr_alocreftype(alref1);
  alref2 = ecr_alocreftype(alref2);

  if (alref1 == alref2)
    return;
  else if (alref1->kind == alocref_var)
    alocreftype_set_insert(aloctype_region, &alref1->u.var.ub, alref2);
  else
    {
      assert(alref1->kind == alocref_ref);
      unify_alocreftype(alref1, alref2);
    }
}

aloctype deref(alocreftype tau)
{
  aloctype al;

  assert(tau);

  tau = ecr_alocreftype(tau);
  
  if (tau->kind == alocref_ref)
    return tau->u.ref.pointsto;
  else
    {
      assert(tau->kind == alocref_var);
  
      al = make_aloctype_fresh();
      unify_alocreftype(alocreftype_ref(al), tau);
      return al;
    }
}

int print_alocreftype(printf_func pf, alocreftype alref, int depth)
{
  int result;

  alref = ecr_alocreftype(alref);
  
  result = 0;
  depth--;
  switch (alref->kind)
    {
    case alocref_ref:
      result += pf("ref(");
      result += print_aloctype(pf, alref->u.ref.pointsto, depth);
      /* result += pf(")"); */
      result += pf(")@%p", alref);
      break;
    case alocref_var:
      result += pf("%s", alref->u.var.name);
      break;
    default:
      fail("Unexpected alocreftype kind %x", alref->kind);
    }

  return result;
}

/**************************************************************************
 *                                                                        *
 * Alocfntype                                                             *
 *                                                                        *
 **************************************************************************/

alocfntype alocfntype_fresh(void)
{
  const char *name;
  
  assert(state == state_gen);
  
  name = rstrcat(aloctype_region, "fn", 
		 inttostr(aloctype_region, next_fn++));
  return alocfntype_var(name);
}

alocfntype alocfntype_var(const char * name)
{
  alocfntype alfn;

  assert(state == state_gen);

  alfn = ralloc(aloctype_region, struct Alocfntype);
  alfn->kind = alocfn_var;
  alfn->u.var.name = name;
  alfn->u.var.num_equiv = 1;
  alfn->u.var.ub = empty_alocfntype_set(aloctype_region);
  alfn->contexts = empty_aloctype_set(aloctype_region);
  return alfn;
}

alocfntype alocfntype_fn(aloctype * params, effecttype eff, aloctype returns)
{
  alocfntype alfn;
  int i;

  assert(eff && returns);
  assert(state == state_gen);

  alfn = ralloc(aloctype_region, struct Alocfntype);
  alfn->kind = alocfn_fn;
  alfn->u.fn.params = params;
  alfn->u.fn.eff = eff;
  alfn->u.fn.returns = returns;
  alfn->contexts = empty_aloctype_set(aloctype_region);

  for (i=0; i < NUM_ALOCFN_PARAMS; i++)
    {
      aloctype param;

      param = ecr_aloctype(params[i]);
      assert(param->kind == aloctype_var);
      alocfntype_set_insert(aloctype_region, &param->u.var.fncontexts, alfn);
    }
  returns = ecr_aloctype(returns);
  assert(returns->kind == aloctype_var);
  alocfntype_set_insert(aloctype_region, &returns->u.var.fncontexts, alfn);

  return alfn;
}

static alocfntype ecr_alocfntype(alocfntype alfn)
{
  assert(alfn);

  if (alfn->kind == alocfn_link)
    {
      alocfntype ecr = alfn, cur, temp;
      
      /* Find root */
      while (ecr->kind == alocfn_link)
	ecr = ecr->u.link;
      
      /* Compress path */
      cur = alfn;
      while (cur->u.link != ecr)
	{
	  temp = cur->u.link;
	  cur->u.link = ecr;
	  cur = temp;
	}
      return ecr;
    }
  else
    return alfn;
}

void unify_alocfntype(alocfntype alfn1, alocfntype alfn2)
{
  assert(alfn1 && alfn2);

#ifdef DEBUG
  printf("\nUnify alocfn:\n");
  print_alocfntype(printf, alfn1, 10);
  printf(" = ");
  print_alocfntype(printf, alfn2, 10);
  printf("\n");
#endif
  alfn1 = ecr_alocfntype(alfn1);
  alfn2 = ecr_alocfntype(alfn2);

  if (alfn1 == alfn2)
    return;
  else if (alfn1->kind == alocfn_var && alfn2->kind == alocfn_var)
    {
      alocfntype new_ecr, new_link;
      
      if (alfn1->u.var.num_equiv <= alfn2->u.var.num_equiv)
	{
	  new_ecr = alfn2;
	  new_link = alfn1;
	}
      else
	{
	  new_ecr = alfn1;
	  new_link = alfn2;
	}

      new_ecr->u.var.num_equiv += new_link->u.var.num_equiv;
      new_ecr->u.var.ub = 
	alocfntype_set_union(new_ecr->u.var.ub, new_link->u.var.ub);
      new_ecr->contexts =
	aloctype_set_union(new_ecr->contexts, new_link->contexts);

      new_link->kind = alocfn_link;      
      new_link->u.link = new_ecr;
    }
  else if (alfn1->kind == alocfn_var && alfn2->kind == alocfn_fn)
    unify_alocfntype(alfn2, alfn1);
  else if (alfn1->kind == alocfn_fn && alfn2->kind == alocfn_var)
    {
      alocfntype_set ub = alfn2->u.var.ub;
      alocfntype_set_scanner ss;
      alocfntype b;

      alfn1->contexts =
	aloctype_set_union(alfn1->contexts, alfn2->contexts);

      alfn2->kind = alocfn_link;
      alfn2->u.link = alfn1;

      scan_alocfntype_set(b, ss, ub)
	unify_alocfntype(alfn1, b);
    }
  else
    {
      int i;
      aloctype * params2;
      effecttype eff2;
      aloctype returns2;
      aloctype * params1;
      effecttype eff1;
      aloctype returns1;
      
      assert(alfn1->kind == alocfn_fn && alfn2->kind == alocfn_fn);

      params2 = alfn2->u.fn.params;
      eff2 = alfn2->u.fn.eff;
      returns2 = alfn2->u.fn.returns;
      params1 = alfn1->u.fn.params;
      eff1 = alfn1->u.fn.eff;
      returns1 = alfn1->u.fn.returns;

      alfn1->contexts =
	aloctype_set_union(alfn1->contexts, alfn2->contexts);

      alfn2->kind = alocfn_link;
      alfn2->u.link = alfn1;

      for (i=0; i < NUM_ALOCFN_PARAMS; i++) {
	unify_aloctype(params1[i], params2[i]);
      }
      unify_effecttype(eff1, eff2);
      unify_aloctype(returns1, returns2);
    }
}

void mkleq_alocfntype(alocfntype alfn1, alocfntype alfn2)
{
  assert(alfn1 && alfn2);

#ifdef DEBUG
  printf("\nMkleq alocfn:\n");
  print_alocfntype(printf, alfn1, 10);
  printf(" <= ");
  print_alocfntype(printf, alfn2, 10);
  printf("\n");
#endif

  alfn1 = ecr_alocfntype(alfn1);
  alfn2 = ecr_alocfntype(alfn2);

  if (alfn1 == alfn2)
    return;
  else if (alfn1->kind == alocfn_var)
    alocfntype_set_insert(aloctype_region, &alfn1->u.var.ub, alfn2);
  else
    {
      assert(alfn1->kind == alocfn_fn);
      unify_alocfntype(alfn1, alfn2);
    }
}

int print_alocfntype(printf_func pf, alocfntype alfn, int depth)
{
  int result, i;

  alfn = ecr_alocfntype(alfn);
  
  result = 0;
  depth--;
  switch (alfn->kind)
    {
    case alocfn_fn:
      result += pf("fn(");
      for (i=0; i < NUM_ALOCFN_PARAMS; i++)
	{
	  result += print_aloctype(pf, alfn->u.fn.params[i], depth);
	  result += pf(", ");
	}
      result += print_aloctype(pf, alfn->u.fn.returns, depth);
      result += pf(")");
      break;
    case alocfn_var:
      result += pf("%s", alfn->u.var.name);
      break;
    default:
      fail("Unexpected alocfntype kind %x", alfn->kind);
    }

  return result;
}

/**************************************************************************
 *                                                                        *
 * Effecttypes                                                            *
 *                                                                        *
 **************************************************************************/

static effecttype ecr_effecttype(effecttype e)
{
  if (e == NULL)
    return e;
  
  if (e->kind == efftype_link)
    {
      effecttype ecr = e, cur, temp;
      
      /* Find root */
      while (ecr && ecr->kind == efftype_link)
	ecr = ecr->u.link;
      
      /* Compress path */
      cur = e;
      while (cur->u.link != ecr)
	{
	  temp = cur->u.link;
	  cur->u.link = ecr;
	  cur = temp;
	}
      return ecr;
    }
  else
    return e;
}

static effecttype effecttype_single(alocreftype base, effconstr_kind kind)
{
  effecttype efftype;

  /* XXX */
  /* assert(state == state_gen); */
  
  efftype = ralloc(aloctype_region, struct Effecttype);
  efftype->kind = efftype_single;
  efftype->u.single.kind = kind;
  efftype->u.single.base = base;
  efftype->u.single.ub = empty_effecttype_set(aloctype_region);

  base = ecr_alocreftype(base);
  assert(base->kind == alocref_ref || base->kind == alocref_var);
  effecttype_set_insert(aloctype_region, &base->singles, efftype);

  return efftype;
}

effecttype effecttype_read(alocreftype base)
{
  return effecttype_single(base, effconstr_read);
}

effecttype effecttype_write(alocreftype base)
{
  return effecttype_single(base, effconstr_write);
}

effecttype effecttype_any(alocreftype base)
{
  return effecttype_single(base, effconstr_any);
}

static effecttype effecttype_reach(alocreftype base, effconstr_kind kind)
{
  effecttype efftype;
  
  assert(state == state_gen);

  efftype = ralloc(aloctype_region, struct Effecttype);
  efftype->kind = efftype_reach;
  efftype->u.reach.kind = kind;
  efftype->u.reach.base = base;
  efftype->u.reach.ub = empty_effecttype_set(aloctype_region);

  base = ecr_alocreftype(base);
  assert(base->kind == alocref_ref || base->kind == alocref_var);
  effecttype_set_insert(aloctype_region, &base->reaches, efftype);

  return efftype;
}

effecttype effecttype_read_reach(alocreftype base)
{
  return effecttype_reach(base, effconstr_read);
}

effecttype effecttype_write_reach(alocreftype base)
{
  return effecttype_reach(base, effconstr_write);
}

effecttype effecttype_any_reach(alocreftype base)
{
  return effecttype_reach(base, effconstr_any);
}

effecttype effecttype_union(effecttype e1, effecttype e2)
{
  assert(state == state_gen);
  
  e1 = ecr_effecttype(e1);
  e2 = ecr_effecttype(e2);
  
  if (e1 == e2)
    return e1;
  else if (e1 == NULL)
    return e2;
  else if (e2 == NULL)
    return e1;
  else
    {
      effecttype e;

      e = ralloc(aloctype_region, struct Effecttype);
      e->kind = efftype_union;
      e->u.u.e1 = e1;
      e->u.u.e2 = e2;
      e->visited = 0;
      
      return e;
    }
}

static void add_to_effecttype_ub(effecttype e, effecttype to_add)
{
  e = ecr_effecttype(e);
  switch (e->kind)
    {
    case efftype_single:
      effecttype_set_insert(aloctype_region, &e->u.single.ub, to_add);
      break;
    case efftype_reach:
      effecttype_set_insert(aloctype_region, &e->u.reach.ub, to_add);
      break;
    case efftype_inter:
      effecttype_set_insert(aloctype_region, &e->u.inter.ub, to_add);
      break;
    case efftype_var:
      effecttype_set_insert(aloctype_region, &e->u.var.ub, to_add);
      break;
    default:
      fail("Unexpected effecttype kind %x\n", e->kind);
    }
}

effecttype effecttype_inter(effecttype e1, effecttype e2)
{
  assert(state == state_gen);
  
  e1 = ecr_effecttype(e1);
  e2 = ecr_effecttype(e2);

  if (e1 == NULL)
    return NULL;
  else
    {
      effecttype e;
      
      assert(e2 != NULL);

      /* Get rid of any unions in e1 and e2 */
      if (e1->kind == efftype_union)
	{
	  effecttype temp;

	  temp = effecttype_fresh();
	  mkleq_effecttype(e1, temp);
	  e1 = temp;
	}
      if (e2->kind == efftype_union)
	{
	  effecttype temp;

	  temp = effecttype_fresh();
	  mkleq_effecttype(e2, temp);
	  e2 = temp;
	}

      e = ralloc(aloctype_region, struct Effecttype);
      e->kind = efftype_inter;
      e->u.inter.e1 = e1;
      e->u.inter.e2 = e2;
      e->u.inter.ub = empty_effecttype_set(aloctype_region);
      e->u.inter.visited1 = 0;
      e->u.inter.visited2 = 0;
      e->visited = 0;

      add_to_effecttype_ub(e1, e);
      add_to_effecttype_ub(e2, e);

      return e;
    }
}

effecttype effecttype_var(const char * name)
{
  effecttype e;

  assert(state == state_gen);

  e = ralloc(aloctype_region, struct Effecttype);
  e->kind = efftype_var;
  e->u.var.name = name;
  e->u.var.ub = empty_effecttype_set(aloctype_region);
  e->u.var.num_equiv = 1;

  return e;
}

effecttype effecttype_fresh(void)
{
  const char *name;

  name = rstrcat(aloctype_region, "efty",
		 inttostr(aloctype_region, next_effecttype++));
  return effecttype_var(name);
}

void mkleq_effecttype(effecttype e1, effecttype e2)
{
#ifdef DEBUG
  printf("\nMkleq effecttype:\n");
  print_effecttype(printf, e1);
  printf("<=");
  print_effecttype(printf, e2);
  printf("\n");
#endif

  e1 = ecr_effecttype(e1);
  e2 = ecr_effecttype(e2);

  assert(e2->kind == efftype_var);

  if (state == state_reach)
    {
      alocreftype alref;

      /* This must have come from invoking the rhs of the 2nd type
	 conditional constraint. */
      assert(e1->kind == efftype_single); 
      alref = ecr_alocreftype(e1->u.single.base);
#ifdef DEBUG	  
      printf("  Resetting reach for ");
      print_alocreftype(printf, alref, 10);
      printf("\n");
#endif
      reset_reachcomputed(alref);
      clean_visited(alref);
    }

  if (e1 == NULL)
    return;
  else
    switch (e1->kind)
      {
      case efftype_union:
	mkleq_effecttype(e1->u.u.e1, e2);
	mkleq_effecttype(e1->u.u.e2, e2);
	break;
      default:
	add_to_effecttype_ub(e1, e2);
      }
}

void mkeq_effecttype(effecttype e1, effecttype e2)
{
  mkleq_effecttype(e1, e2);
  mkleq_effecttype(e2, e1);
}

void unify_effecttype(effecttype e1, effecttype e2)
{
  effecttype new_ecr, new_link;

#ifdef DEBUG
  printf("\nUnify effecttype:\n");
  print_effecttype(printf, e1);
  printf("=");
  print_effecttype(printf, e2);
  printf("\n");
#endif

  /* This is all right because induced unification between alocreftype should
     not propagate!  */
  assert(state == state_gen);

  e1 = ecr_effecttype(e1);
  e2 = ecr_effecttype(e2);

  if (e1 == e2)
    return;

  assert(e1->kind == efftype_var && e2->kind == efftype_var);
  assert(state == state_gen);

  if (e1->u.var.num_equiv <= e2->u.var.num_equiv)
    {
      new_ecr = e2;
      new_link = e1;
    }
  else
    {
      new_ecr = e1;
      new_link = e2;
    }

  new_ecr->u.var.num_equiv += new_link->u.var.num_equiv;
  new_ecr->u.var.ub = effecttype_set_union(new_ecr->u.var.ub,
					   new_link->u.var.ub);
  new_link->kind = efftype_link;
  new_link->u.link = new_ecr;
}

int print_effecttype(printf_func pf, effecttype e)
{
  int result;

  result = 0;
  e = ecr_effecttype(e);
  if (e == NULL)
    return pf("0");
  switch(e->kind)
    {
    case efftype_single:
      switch (e->u.single.kind)
	{
	case effconstr_read:
	  result += pf("read(");
	  break;
	case effconstr_write:
	  result += pf("write(");
	  break;
	case effconstr_any:
	  result += pf("any(");
	  break;
	default:
	  fail("Unexpected constructor kind %x\n", e->u.single.kind);
	}
      result += print_alocreftype(pf, e->u.single.base, 10);
      result += pf(")");
      break;
    case efftype_reach:
      switch (e->u.reach.kind)
	{
	case effconstr_read:
	  result += pf("read_r(");
	  break;
	case effconstr_write:
	  result += pf("write_r(");
	  break;
	case effconstr_any:
	  result += pf("any_r(");
	  break;
	default:
	  fail("Unexpected constructor kind %x\n", e->u.reach.kind);
	}
      result += print_alocreftype(pf, e->u.reach.base, 10);
      result += pf(")");
      break;
    case efftype_union:
      result += print_effecttype(pf, e->u.u.e1);
      result += pf(" + ");
      result += print_effecttype(pf, e->u.u.e2);
      break;
    case efftype_inter:
      result += pf("[(");
      result += print_effecttype(pf, e->u.inter.e1);
      result += pf(") & (");
      result += print_effecttype(pf, e->u.inter.e2);
      result += pf(")]");
      break;
    case efftype_var:
      result += pf("%s", e->u.var.name);
      break;
    default:
      fail("Unexpected effecttype kind %x\n", e->kind);
    }
  return result;
}

/**************************************************************************
 *                                                                        *
 * Restrict Inference                                                     *
 *                                                                        *
 **************************************************************************/

#define MARK_READ (1 << 0)
#define MARK_WRITE (1 << 1)
#define MARK_ANY (MARK_READ | MARK_WRITE)

static effecttype normalize_effecttype(effecttype e)
{
  e = ecr_effecttype(e);
  
  if (e && e->kind == efftype_union)
    {
      effecttype temp;
      
      temp = effecttype_fresh();
      mkleq_effecttype(e, temp);
      return temp;
    }
  else
    return e;
}

static checked_leq_const mkchecked_leq_const(effecttype e1, effecttype e2)
{
  checked_leq_const new_const;

  assert(state == state_gen);
  
  new_const = ralloc(aloctype_region, struct Checked_leq_const);
  new_const->satisfied = 0;
  new_const->e1 = e1;
  new_const->e2 = e2;

  return new_const;
}

int print_leq_const(printf_func pf, checked_leq_const c)
{
  int result;
  
  result = print_effecttype(pf, c->e1);
  result += pf(" <= ");
  result += print_effecttype(pf, c->e2);
  return result;
}

/* This sets up all constraints needed for one restrict:
   1.)
   (r_al \in env) v (r_al \in pointsto_type) v (r_al \in r_type) v 
   (old_al \in r_body) v (wr(top_al) \in r_body) => r_al = old_al
   2.)
   r_al \in r_body => old_al \in body
*/
rinf_const mk_rinf_const(location loc,
			 alocreftype r_al, alocreftype old_al, 
			 alocreftype top_al,
			 effecttype r_type, effecttype pointsto_type,
			 effecttype r_body, effecttype body, effecttype env)
{
  rinf_const new_const;

  assert(state == state_gen);

  r_type = normalize_effecttype(r_type);
  pointsto_type = normalize_effecttype(pointsto_type);
  r_body = normalize_effecttype(r_body);
  body = normalize_effecttype(body);
  env = normalize_effecttype(env);

#ifdef DEBUG
  printf("\nMk_rinf_const:\n");
  printf("Restricted location: ");
  print_alocreftype(printf, r_al, 10);
  printf("\n");
  printf("Old location: ");
  print_alocreftype(printf, old_al, 10);
  printf("\n");
  printf("Topmost location: ");
  print_alocreftype(printf, top_al, 10);
  printf("\n");
  printf("Return type effect: ");
  print_effecttype(printf, r_type);
  printf("\n");
  printf("Points-to type effect: ");
  print_effecttype(printf, pointsto_type);
  printf("\n");
  printf("Restricted body effect: ");
  print_effecttype(printf, r_body);
  printf("\n");
  printf("Body effect: ");
  print_effecttype(printf, body);
  printf("\n");
  printf("Environment: ");
  print_effecttype(printf, env);
  printf("\n");
#endif

  new_const = ralloc(aloctype_region, struct Rinf_const);

  new_const->loc = loc;

  new_const->one.condition1 = 
    mkchecked_leq_const(effecttype_any(r_al), env);
  new_const->one.condition2 = 
    mkchecked_leq_const(effecttype_any(r_al), pointsto_type);
  new_const->one.condition3 = 
    mkchecked_leq_const(effecttype_any(r_al), r_type);
  new_const->one.condition4 = 
    mkchecked_leq_const(effecttype_any(old_al), r_body);
  new_const->one.condition5 = 
    mkchecked_leq_const(effecttype_write(top_al), r_body);
  new_const->one.r_al = r_al;
  new_const->one.old_al = old_al;

  new_const->two.condition =
    mkchecked_leq_const(effecttype_any(r_al), r_body);
  new_const->two.old_al = old_al;
  new_const->two.body = body;

  new_const->one.rhs_invoked = 0;
  new_const->two.rhs_invoked = 0;
  
  dd_add_last(aloctype_region, constraints, new_const);

  return new_const;
}

static int sorting_cmp(const void *i1, const void *i2)
{
  checked_leq_const *c1;
  checked_leq_const *c2;
  effecttype e1;
  effecttype e2;
  alocreftype alref1;
  alocreftype alref2;

  c1 = (checked_leq_const *) i1;
  c2 = (checked_leq_const *) i2;
  e1 = ecr_effecttype((*c1)->e1);
  e2 = ecr_effecttype((*c2)->e1);
  
  assert(e1->kind == efftype_single && e2->kind == efftype_single);
  alref1 = ecr_alocreftype(e1->u.single.base);
  alref2 = ecr_alocreftype(e2->u.single.base);
  
  if ((intptr_t) alref1 > (intptr_t) alref2)
    return 1;
  else if ((intptr_t) alref1 < (intptr_t) alref2)
    return -1;
  return 0;
}

static void mark_reachable_upward(effecttype lb, effecttype ub, int mark)
{
  effecttype_set_scanner ss;
  effecttype b;

#ifdef DEBUG
  printf("Propagating mark %d from \n", mark);
  print_effecttype(printf, lb);
  printf(" to ");
  print_effecttype(printf, ub);
  printf("\n");
#endif

  ub = ecr_effecttype(ub);
  lb = ecr_effecttype(lb);
  
  switch (ub->kind)
    {
    case efftype_single:
      {
	assert(!ub->visited);
	ub->visited = mark;
	scan_effecttype_set(b, ss, ub->u.single.ub)
	  mark_reachable_upward(ub, b, mark);
      }
      break;
    case efftype_reach:
      {
	int old_mark = ub->visited;

	ub->visited |= mark;
	if (ub->visited != old_mark)
	  scan_effecttype_set(b, ss, ub->u.reach.ub)
	    mark_reachable_upward(ub, b, mark);
      }
      break;
    case efftype_inter:
      {
	int old_mark = ub->visited;
	
	if (lb == ecr_effecttype(ub->u.inter.e1))
	  ub->u.inter.visited1 |= mark;
	if (lb == ecr_effecttype(ub->u.inter.e2))
	  ub->u.inter.visited2 |= mark;
	
	ub->visited = ub->u.inter.visited1 & ub->u.inter.visited2;
	if (ub->visited != old_mark)
	  scan_effecttype_set(b, ss, ub->u.inter.ub)
	    mark_reachable_upward(ub, b, ub->visited);
      }
      break;
    case efftype_var:
      {
	int old_mark = ub->visited;
	ub->visited |= mark;
	if (ub->visited != old_mark)
	  scan_effecttype_set(b, ss, ub->u.var.ub)
	    mark_reachable_upward(ub, b, mark);
      }
      break;
    default:
      fail("Unexpected effecttype kind %x\n", ub->kind);
    }
}

static void unmark_reachable_upward(effecttype ub)
{
  effecttype_set_scanner ss;
  effecttype b;

  ub = ecr_effecttype(ub);
  
  if (ub->visited == 0 && 
      (ub->kind != efftype_inter || (ub->u.inter.visited1 == 0 &&
				     ub->u.inter.visited2 == 0)))
    return;

  switch (ub->kind)
    {
    case efftype_single:
      {
	ub->visited = 0;
	scan_effecttype_set(b, ss, ub->u.single.ub)
	  unmark_reachable_upward(b);
      }
      break;
    case efftype_reach:
      {
	ub->visited = 0;
	scan_effecttype_set(b, ss, ub->u.reach.ub)
	  unmark_reachable_upward(b);
      }
      break;
    case efftype_inter:
      {
	ub->u.inter.visited1 = 0;
	ub->u.inter.visited2 = 0;
       	ub->visited = 0;
	scan_effecttype_set(b, ss, ub->u.inter.ub)
	  unmark_reachable_upward(b);
      }
      break;
    case efftype_var:
      {
	ub->visited = 0;;
	scan_effecttype_set(b, ss, ub->u.var.ub)
	  unmark_reachable_upward(b);
      }
      break;
    default:
      fail("Unexpected effecttype kind %x\n", ub->kind);
    }
}

static void clean_reachable_upward(alocreftype alref, int orig)
{
  alref = ecr_alocreftype(alref);
  assert(alref->kind == alocref_ref || alref->kind == alocref_var);

  if (!alref->visited)
    return;

  alref->visited = 0;

  /* First clean everything reachable via poinsto relationship */
  {
    aloctype_set_scanner ss;
    aloctype c;

    scan_aloctype_set(c, ss, alref->contexts)
      {
	aloctype al;
	alocreftype_set_scanner ss2;
	alocreftype c2;
	
	/* Ignore function contexts */
	al = ecr_aloctype(c);
	assert(al->kind == aloctype_var);
	scan_alocreftype_set(c2, ss2, al->u.var.refcontexts)
	  clean_reachable_upward(c2, 0);
      }
  }
  
  /* Now do cleaning around the effect graph */
  {
    effecttype_set_scanner ss;
    effecttype b;

    if (orig)
      {
	scan_effecttype_set(b, ss, alref->singles)
	  {
	    effecttype e;

	    e = ecr_effecttype(b);
	    assert(e->kind == efftype_single);
	    unmark_reachable_upward(b);
	  }
      }
    
    scan_effecttype_set(b, ss, alref->reaches)
      {
	effecttype e;

	e = ecr_effecttype(b);
	assert(e->kind == efftype_reach);
	unmark_reachable_upward(b);
      }
  }
}

static void compute_reachable_upward(alocreftype alref, int orig)
{
  alref = ecr_alocreftype(alref);
  assert(alref->kind == alocref_ref || alref->kind == alocref_var);
#ifdef DEBUG
  if (orig)
    printf("Computing orig reachability from ");
  else
    printf("Computing indirect reachability from ");
  print_alocreftype(printf, alref, 10);
  printf("\n");
#endif  

  if ((orig && alref->reachcomputed) || alref->visited)
    return;

  if (orig)
    alref->reachcomputed = 1;
  alref->visited = 1;

  /* First get everything reachable via poinsto relationship */
  {
    aloctype_set_scanner ss;
    aloctype c;

    scan_aloctype_set(c, ss, alref->contexts)
      {
	aloctype al;
	alocreftype_set_scanner ss2;
	alocreftype c2;
	
	/* Ignore function contexts */
	al = ecr_aloctype(c);
	assert(al->kind == aloctype_var);
	scan_alocreftype_set(c2, ss2, al->u.var.refcontexts)
	  compute_reachable_upward(c2, 0);
      }
  }
  
  /* Now do reachability around the effect graph */
  {
    effecttype_set_scanner ss;
    effecttype b;

    if (orig)
      {
	scan_effecttype_set(b, ss, alref->singles)
	  {
	    effecttype e;

	    e = ecr_effecttype(b);
	    assert(e->kind == efftype_single);
	    switch (e->u.single.kind)
	      {
	      case effconstr_read:		
		mark_reachable_upward(NULL, b, MARK_READ);
		break;
	      case effconstr_write:
		mark_reachable_upward(NULL, b, MARK_WRITE);
		break;
	      case effconstr_any:
		mark_reachable_upward(NULL, b, MARK_ANY);
		break;
	      default:
		fail("Unexpected effconstr kind %x", e->u.single.kind);
	      }
	  }
      }

    scan_effecttype_set(b, ss, alref->reaches)
      {
	effecttype e;

	e = ecr_effecttype(b);
	assert(e->kind == efftype_reach);
	switch (e->u.reach.kind)
	  {
	  case effconstr_read:		
	    mark_reachable_upward(NULL, b, MARK_READ);
	    break;
	  case effconstr_write:
	    mark_reachable_upward(NULL, b, MARK_WRITE);
	    break;
	  case effconstr_any:
	    mark_reachable_upward(NULL, b, MARK_ANY);
	    break;
	  default:
	    fail("Unexpected effconstr kind %x", e->u.reach.kind);
	  }	    
      }
  }

#ifdef DEBUG
  if (orig)
    printf("Done computing orig reachability from ");
  else
    printf("Done computing indirect reachability from ");
  print_alocreftype(printf, alref, 10);
  printf("\n");
#endif  
}

static int iseffectreached(effecttype e1, effecttype e2)
{
  e1 = ecr_effecttype(e1);
  e2 = ecr_effecttype(e2);

  assert(e1->kind == efftype_single);
  switch (e1->u.single.kind)
    {
    case effconstr_read:
      return e2->visited & MARK_READ;
    case effconstr_write:
      return e2->visited & MARK_WRITE;
    case effconstr_any:
      return e2->visited & MARK_ANY;
    default:
      fail("Unexpected effconstr kind %x", e1->u.single.kind);
    }
}

void check_rinf_consts(void)
{
  int some_rhs_invoked;
  dd_list_pos cur;
  checked_leq_const *leq_consts_array;
  region scratch_region;
  
  state = state_reach;

  printf("Computing constraints...\n");

  do {
    int i, j, last = 0, array_size;
    alocreftype cur_al = NULL;

#ifdef DEBUG
    printf("\nIteration\n");
#endif

    scratch_region = newregion();

    /* Build up the worklist array */
    {
      dd_list leq_consts;

      leq_consts = dd_new_list(scratch_region);
      dd_scan(cur, constraints)
	{
	  rinf_const c = DD_GET(rinf_const, cur);
      
	  if (!c->one.rhs_invoked)
	    /* Inference hasn't failed on this restrict yet */
	    {
	      dd_add_last(scratch_region, leq_consts, c->one.condition1);
	      dd_add_last(scratch_region, leq_consts, c->one.condition2);
	      dd_add_last(scratch_region, leq_consts, c->one.condition3);
	      dd_add_last(scratch_region, leq_consts, c->one.condition4);
	      dd_add_last(scratch_region, leq_consts, c->one.condition5);
	    }
	  
	  if (!c->two.rhs_invoked)
	    dd_add_last(scratch_region, leq_consts, c->two.condition);
	}

      array_size = dd_length(leq_consts);
      leq_consts_array = rarrayalloc(scratch_region, 
				     array_size, checked_leq_const);
      i = 0;
      dd_scan(cur, leq_consts)
	{
	  leq_consts_array[i] = DD_GET(checked_leq_const, cur);
	  i++;
	}
      assert(i == array_size);
      if (i == 0) break;
      qsort(leq_consts_array, array_size, sizeof(checked_leq_const), sorting_cmp);
    }

    /* Compute reachability */
    for (i = 0; i < array_size; i++)
      {
	effecttype e1;

	e1 = ecr_effecttype(leq_consts_array[i]->e1);
	assert(e1->kind == efftype_single);
	
	if (i == 0)
	  /* The first one always needs to be computed */
	  {
	    cur_al = ecr_alocreftype(e1->u.single.base);
#ifdef DEBUG
	    printf("\n%d Cur_al: ", i);
	    print_alocreftype(printf, cur_al, 10);
	    printf("\n");
#endif
	    compute_reachable_upward(cur_al, 1);
	    last = 0;
	  }
	else if (cur_al != ecr_alocreftype(e1->u.single.base))
	  /* We handle cur_al and go to the next alocreftype */
	  {
	    for (j = last; j < i; j++)
	      leq_consts_array[j]->satisfied = 
		iseffectreached(leq_consts_array[j]->e1, 
				leq_consts_array[j]->e2);
	    clean_reachable_upward(cur_al, 1);

	    cur_al = ecr_alocreftype(e1->u.single.base);
#ifdef DEBUG
	    printf("\n%d Cur_al: ", i);
	    print_alocreftype(printf, cur_al, 10);
	    printf("\n");
#endif
	    compute_reachable_upward(cur_al, 1);
	    last = 0;
	    compute_reachable_upward(cur_al, 1);
	    last = i;
	  }
      }    
    /* Handle the last cur_al */
    for (j = last; j < array_size; j++)
      {
#ifdef DEBUG
	if (leq_consts_array[j]->satisfied != 
	    iseffectreached(leq_consts_array[j]->e1, leq_consts_array[j]->e2))
	  {
	    printf("Constraint\n  ");
	    print_leq_const(printf, leq_consts_array[j]);
	    printf("satisfied\n");
	  }
#endif
	leq_consts_array[j]->satisfied = 
	  iseffectreached(leq_consts_array[j]->e1, leq_consts_array[j]->e2);
      }
    clean_reachable_upward(cur_al, 1);

    /* Go back to the list of rinf constraints and invoke any rhs */
    some_rhs_invoked = 0;
    dd_scan(cur, constraints)
      {
	rinf_const c = DD_GET(rinf_const, cur);

	if (!c->one.rhs_invoked && 
	    (c->one.condition1->satisfied || c->one.condition2->satisfied || 
	     c->one.condition3->satisfied || c->one.condition4->satisfied || 
	     c->one.condition5->satisfied))
	  {
	    unify_alocreftype(c->one.r_al, c->one.old_al);
	    c->one.rhs_invoked = some_rhs_invoked = 1;
	  }
	  
	if (!c->two.rhs_invoked && c->two.condition->satisfied)
	  {
	    mkleq_effecttype(effecttype_any(c->two.old_al), c->two.body);
	    c->two.rhs_invoked = some_rhs_invoked = 1;
	  }
      }	
    
    deleteregion(scratch_region);
  } while (some_rhs_invoked);

  state = state_done;

  /* Finally mark inferred restricts */
  {
    int inferred, total, used, used_inferred;

    inferred = total = used = used_inferred = 0;
    dd_scan(cur, constraints)
      {
	rinf_const c = DD_GET(rinf_const, cur);

	if (is_rinf_satisfied(c) && is_rinf_used(c))
	  {
	    used_inferred++;
	  }

	if (is_rinf_satisfied(c))
	  {
	    inferred++;
	    /* report_rinf(c->loc, sev_err, "Restrict Inferred"); */
	  }
	else
	  report_rinf(c->loc, sev_err, "Restrict Failed");

	if (is_rinf_used(c))
	  {
	    used++;
	  }

	total++;
      }
    
    printf("Restrict Inferred: %d/%d\n", inferred, total);
    printf("Restrict Used: %d/%d\n", used_inferred, used);
  }
}

bool is_rinf_used(rinf_const c)
{
  assert(state == state_done);

  if (c->two.rhs_invoked)
    return TRUE;
  else
    return FALSE;
}

bool is_rinf_satisfied(rinf_const c)
{
  assert(state == state_done);

  if (!c->one.rhs_invoked)
    return TRUE;
  else
    return FALSE;
}


int print_rinf_info(printf_func pf, rinf_const c)
{
  int result;
  assert(state == state_done);

  result = 0;
  if (!c->one.rhs_invoked)
    result += pf("Restricted\n");
  else
    {
      result += pf("Shared because: \n");
      if (c->one.condition1->satisfied)
	result += pf(" * Escape to env\n");
      if (c->one.condition2->satisfied)
	result += pf(" * Escape to pointsto type\n");
      if (c->one.condition3->satisfied)
	result += pf(" * Escape to return type\n");
      if (c->one.condition4->satisfied)
	result += pf(" * Old location used\n");
      if (c->one.condition5->satisfied)
	result += pf(" * Pointer written\n");
    }
  if (c->two.rhs_invoked)
    result += pf("Used\n");
  else
    result += pf("Unused\n");

  return result;
}
