
// Copyright (c) 1999-2005 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.

#include <stdio.h>
#include <assert.h>

#include "root.h"

#include "mars.h"
#include "init.h"
#include "identifier.h"
#include "scope.h"
#include "attrib.h"
#include "dsymbol.h"
#include "declaration.h"
#include "aggregate.h"
#include "module.h"
#include "id.h"

Scope *Scope::freelist = NULL;

void *Scope::operator new(size_t size)
{
    if (freelist)
    {
	Scope *s = freelist;
	freelist = s->enclosing;
	//printf("freelist %p\n", s);
	assert(s->flags & SCOPEfree);
	s->flags &= ~SCOPEfree;
	return s;
    }

    void *p = ::operator new(size);
    //printf("new %p\n", p);
    return p;
}

Scope::Scope()
{   // Create root scope

    //printf("Scope::Scope() %p\n", this);
    this->module = NULL;
    this->scopesym = NULL;
    this->sd = NULL;
    this->enclosing = NULL;
    this->parent = NULL;
    this->sw = NULL;
    this->tf = NULL;
    this->sbreak = NULL;
    this->scontinue = NULL;
    this->fes = NULL;
    this->structalign = global.structalign;
    this->func = NULL;
    this->slabel = NULL;
    this->linkage = LINKd;
    this->protection = PROTpublic;
    this->explicitProtection = 0;
    this->stc = 0;
    this->offset = 0;
    this->inunion = 0;
    this->incontract = 0;
    this->nofree = 0;
    this->noctor = 0;
    this->noaccesscheck = 0;
    this->intypeof = 0;
    this->parameterSpecialization = 0;
    this->callSuper = 0;
    this->flags = 0;
    this->anonAgg = NULL;
    this->lastdc = NULL;
    this->lastoffset = 0;
    this->docbuf = NULL;
}

Scope::Scope(Scope *enclosing)
{
    //printf("Scope::Scope(enclosing = %p) %p\n", enclosing, this);
    assert(!(enclosing->flags & SCOPEfree));
    this->module = enclosing->module;
    this->func   = enclosing->func;
    this->parent = enclosing->parent;
    this->scopesym = NULL;
    this->sd = NULL;
    this->sw = enclosing->sw;
    this->tf = enclosing->tf;
    this->sbreak = enclosing->sbreak;
    this->scontinue = enclosing->scontinue;
    this->fes = enclosing->fes;
    this->structalign = enclosing->structalign;
    this->enclosing = enclosing;
#ifdef DEBUG
    if (enclosing->enclosing)
	assert(!(enclosing->enclosing->flags & SCOPEfree));
    if (this == enclosing->enclosing)
    {
	printf("this = %p, enclosing = %p, enclosing->enclosing = %p\n", this, enclosing, enclosing->enclosing);
    }
    assert(this != enclosing->enclosing);
#endif
    this->slabel = NULL;
    this->linkage = enclosing->linkage;
    this->protection = enclosing->protection;
    this->explicitProtection = enclosing->explicitProtection;
    this->stc = enclosing->stc;
    this->offset = 0;
    this->inunion = enclosing->inunion;
    this->incontract = enclosing->incontract;
    this->nofree = 0;
    this->noctor = enclosing->noctor;
    this->noaccesscheck = enclosing->noaccesscheck;
    this->intypeof = enclosing->intypeof;
    this->parameterSpecialization = enclosing->parameterSpecialization;
    this->callSuper = enclosing->callSuper;
    this->flags = 0;
    this->anonAgg = NULL;
    this->lastdc = NULL;
    this->lastoffset = 0;
    this->docbuf = enclosing->docbuf;
    assert(this != enclosing);
}

Scope *Scope::createGlobal(Module *module)
{
    Scope *sc;

    sc = new Scope();
    sc->module = module;
    sc->scopesym = new ScopeDsymbol();
    sc->scopesym->symtab = new DsymbolTable();

    // Add top level package as member of this global scope
    Dsymbol *m = module;
    while (m->parent)
	m = m->parent;
    m->addMember(NULL, sc->scopesym, 1);
    m->parent = NULL;			// got changed by addMember()

    // Create the module scope underneath the global scope
    sc = sc->push(module);
    sc->parent = module;
    return sc;
}

Scope *Scope::push()
{
    //printf("Scope::push()\n");
    Scope *s = new Scope(this);
    assert(this != s);
    return s;
}

Scope *Scope::push(ScopeDsymbol *ss)
{
    //printf("Scope::push(%s)\n", ss->toChars());
    Scope *s = push();
    s->scopesym = ss;
    return s;
}

Scope *Scope::pop()
{
    //printf("Scope::pop() %p nofree = %d\n", this, nofree);
    Scope *enc = enclosing;

    if (enclosing)
	enclosing->callSuper |= callSuper;

    if (!nofree)
    {	enclosing = freelist;
	freelist = this;
	flags |= SCOPEfree;
    }

    return enc;
}

void Scope::mergeCallSuper(Loc loc, unsigned cs)
{
    // This does a primitive flow analysis to support the restrictions
    // regarding when and how constructors can appear.
    // It merges the results of two paths.
    // The two paths are callSuper and cs; the result is merged into callSuper.

    if (cs != callSuper)
    {	int a;
	int b;

	callSuper |= cs & (CSXany_ctor | CSXlabel);
	if (cs & CSXreturn)
	{
	}
	else if (callSuper & CSXreturn)
	{
	    callSuper = cs | (callSuper & (CSXany_ctor | CSXlabel));
	}
	else
	{
	    a = (cs        & (CSXthis_ctor | CSXsuper_ctor)) != 0;
	    b = (callSuper & (CSXthis_ctor | CSXsuper_ctor)) != 0;
	    if (a != b)
		error(loc, "one path skips constructor");
	    callSuper |= cs;
	}
    }
}

Dsymbol *Scope::search(Loc loc, Identifier *ident, Dsymbol **pscopesym)
{   Dsymbol *s;
    Scope *sc;

    //printf("Scope::search(%p, '%s')\n", this, ident->toChars());
    if (ident == Id::empty)
    {
	// Look for module scope
	for (sc = this; sc; sc = sc->enclosing)
	{
	    assert(sc != sc->enclosing);
	    if (sc->scopesym)
	    {
		s = sc->scopesym->isModule();
		if (s)
		{
		    //printf("\tfound %s.%s\n", s->parent ? s->parent->toChars() : "", s->toChars());
		    if (pscopesym)
			*pscopesym = sc->scopesym;
		    return s;
		}
	    }
	}
	return NULL;
    }

    for (sc = this; sc; sc = sc->enclosing)
    {
	assert(sc != sc->enclosing);
	if (sc->scopesym)
	{
	    //printf("\tlooking in scopesym '%s', kind = '%s'\n", sc->scopesym->toChars(), sc->scopesym->kind());
	    s = sc->scopesym->search(loc, ident, 0);
	    if (s)
	    {
		if ((global.params.warnings ||
		    global.params.Dversion > 1) &&
		    ident == Id::length &&
		    sc->scopesym->isArrayScopeSymbol() &&
		    sc->enclosing &&
		    sc->enclosing->search(loc, ident, NULL))
		{
		    warning(s->loc, "array 'length' hides other 'length' name in outer scope");
		}

		//printf("\tfound %s.%s, kind = '%s'\n", s->parent ? s->parent->toChars() : "", s->toChars(), s->kind());
		if (pscopesym)
		    *pscopesym = sc->scopesym;
		return s;
	    }
	}
    }

    return NULL;
}

Dsymbol *Scope::insert(Dsymbol *s)
{   Scope *sc;

    for (sc = this; sc; sc = sc->enclosing)
    {
	//printf("\tsc = %p\n", sc);
	if (sc->scopesym)
	{
	    //printf("\t\tsc->scopesym = %p\n", sc->scopesym);
	    if (!sc->scopesym->symtab)
		sc->scopesym->symtab = new DsymbolTable();
	    return sc->scopesym->symtab->insert(s);
	}
    }
    assert(0);
    return NULL;
}

/********************************************
 * Search enclosing scopes for ClassDeclaration.
 */

ClassDeclaration *Scope::getClassScope()
{   Scope *sc;

    for (sc = this; sc; sc = sc->enclosing)
    {
	ClassDeclaration *cd;
	
	if (sc->scopesym)
	{
	    cd = sc->scopesym->isClassDeclaration();
	    if (cd)
		return cd;
	}
    }
    return NULL;
}

/********************************************
 * Search enclosing scopes for ClassDeclaration.
 */

AggregateDeclaration *Scope::getStructClassScope()
{   Scope *sc;

    for (sc = this; sc; sc = sc->enclosing)
    {
	AggregateDeclaration *ad;
	
	if (sc->scopesym)
	{
	    ad = sc->scopesym->isClassDeclaration();
	    if (ad)
		return ad;
	    else
	    {	ad = sc->scopesym->isStructDeclaration();
		if (ad)
		    return ad;
	    }
	}
    }
    return NULL;
}

/*******************************************
 * For TemplateDeclarations, we need to remember the Scope
 * where it was declared. So mark the Scope as not
 * to be free'd.
 */

void Scope::setNoFree()
{   Scope *sc;
    //int i = 0;

    //printf("Scope::setNoFree(this = %p)\n", this);
    for (sc = this; sc; sc = sc->enclosing)
    {
	//printf("\tsc = %p\n", sc);
	sc->nofree = 1;

	assert(!(flags & SCOPEfree));
	//assert(sc != sc->enclosing);
	//assert(!sc->enclosing || sc != sc->enclosing->enclosing);
	//if (++i == 10)
	    //assert(0);
    }
}
