/* $Id$
 * ResolveTypes: perform type analysis.
 *
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include <cassert>
#include <algorithm>
#include "frontend/visitor/ResolveTypes.hpp"

#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/ConstReal.hpp"
#include "frontend/ast/Entity.hpp"
#include "frontend/ast/SymbolDeclaration.hpp"
#include "frontend/ast/ValDeclaration.hpp"
#include "frontend/ast/SignalDeclaration.hpp"
#include "frontend/ast/ConstantDeclaration.hpp"
#include "frontend/ast/Expression.hpp"
#include "frontend/ast/IfStat.hpp"
#include "frontend/ast/NullStat.hpp"
#include "frontend/ast/ForLoopStat.hpp"
#include "frontend/ast/WhileLoopStat.hpp"
#include "frontend/ast/NextStat.hpp"
#include "frontend/ast/VarAssignStat.hpp"
#include "frontend/ast/WaitStat.hpp"
#include "frontend/ast/ExitStat.hpp"
#include "frontend/ast/SigAssignStat.hpp"
#include "frontend/ast/WaveFormElem.hpp"
#include "frontend/ast/ReturnStat.hpp"
#include "frontend/ast/ProcCallStat.hpp"
#include "frontend/ast/AssertStat.hpp"
#include "frontend/ast/VarDeclaration.hpp"
#include "frontend/ast/DiscreteRange.hpp"
#include "frontend/ast/CaseStat.hpp"
#include "frontend/ast/CaseAlternative.hpp"
#include "frontend/ast/Others.hpp"
#include "frontend/ast/Architecture.hpp"
#include "frontend/ast/AssociationElement.hpp"
#include "frontend/ast/FunctionDeclaration.hpp"
#include "frontend/ast/ProcedureDeclaration.hpp"
#include "frontend/ast/CompInstStat.hpp"
#include "frontend/ast/Package.hpp"
#include "frontend/ast/PackageBody.hpp"
#include "frontend/ast/Process.hpp"
#include "frontend/ast/SubprogBody.hpp"
#include "frontend/ast/CondalSigAssign.hpp"
#include "frontend/ast/EnumerationType.hpp"
#include "frontend/ast/PhysicalType.hpp"
#include "frontend/ast/PhysicalTypeUnit.hpp"
#include "frontend/ast/RangeConstraintType.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include "frontend/ast/RecordType.hpp"
#include "frontend/ast/Aggregate.hpp"
#include "frontend/ast/Subscript.hpp"
#include "frontend/ast/TypeConversion.hpp"
#include "frontend/ast/ConstantDeclaration.hpp"
#include "frontend/reporting/ErrorRegistry.hpp"
#include "frontend/reporting/CompileError.hpp"
#include "util/MiscUtil.hpp"
#include "frontend/ast/SimpleName.hpp"
#include "frontend/ast/SelectedName.hpp"
#include "frontend/ast/AttributeName.hpp"
#include "frontend/ast/FunctionCall.hpp"
#include "frontend/ast/Slice.hpp"
#include "frontend/ast/TemporaryName.hpp"
#include "frontend/ast/ReturnStat.hpp"
#include "frontend/ast/AttributeSpecification.hpp"
#include "frontend/visitor/LookupTypes.hpp"
#include "frontend/visitor/UnconstraintBounds.hpp"

#define SLICE_DEBUG 0
#define SELECTION_DEBUG 0
#define FUNCCALLS_DEBUG 0

namespace ast {

void
ResolveTypes::visit(CompInstStat &node)
{
	if (node.genericMap != NULL) {
		for (std::list<AssociationElement*>::const_iterator i = 
			node.genericMap->begin(); i != node.genericMap->end();
			i++) {
	
			//FIXME this may fail for positional association.
			//      (because there, the formal is missing, but
			//      the wanted type is the projected argument's
			//      type)
			this->typeCandidates.clear();
			(*i)->accept(*this);
			if (! this->needUniqueType(**i)) {
				return;
			}
			// pin down type
			(*i)->accept(*this);
			assert(this->typeCandidates.size() == 1);
		}
	}

	if (node.portMap != NULL) {
		for (std::list<AssociationElement*>::const_iterator i = 
			node.portMap->begin(); i != node.portMap->end();
			i++) {
	
			//FIXME same as above.
			this->typeCandidates.clear();
			(*i)->accept(*this);

			bool ret = this->needUniqueType(**i);
			if (! ret) {
				// error already reported, skip.
				continue;
			}

			// pin down type
			(*i)->accept(*this);
			assert(this->typeCandidates.size() == 1);
		}
	}
	this->typeCandidates.clear();
}

void
ResolveTypes::visit(AssociationElement &node)
{
	bool mustSingle = (this->typeCandidates.size() == 1);

	if (node.formal != NULL) {
		node.formal->accept(*this);
	}
	
	if (node.actual != NULL) {
		node.actual->accept(*this);
	}

	if (mustSingle) {
		this->needUniqueType(node);
	}
}

void
ResolveTypes::visit(UnconstrainedArrayType &node)
{
	assert(node.indexTypes != NULL);
	node.baseType = BASE_TYPE_ARRAY;
	assert(node.elementType != NULL);
	node.elementType->accept(*this);
}

void
ResolveTypes::visit(DiscreteRange &node)
{
	if (node.rangeName != NULL) {
		this->processDRByName(node);
		return;
	}

	assert(node.from != NULL);
	assert(node.to != NULL);

	// gather types of from.
	typeSetT backup = this->typeCandidates;
	node.from->accept(*this);
	typeSetT fromT = this->typeCandidates;
	this->typeCandidates = backup;
	node.to->accept(*this);

	//propagate a RangeConstraintType based on the left bound 
	//(usually the index type) with an additional constraint
	if ((fromT.size() == 1) && (this->typeCandidates.size() == 1)) {
		const TypeDeclaration *left = *fromT.begin();
		const TypeDeclaration *right = *this->typeCandidates.begin();

		enum BaseType resolved = (left->baseType && right->baseType);
		node.baseType = ResolveTypes::transformBaseType(resolved, 
								node.location);

		if (node.baseType != BASE_TYPE_RANGE_INT) {
			CompileError *ce = new CompileError(node, 
				"Not a discrete type in discrete range.");
			ErrorRegistry::addError(ce);
		}

		// if backup is empty, we'll need to deliver all possible
		// types -> keep types from right hand side.
		if (! backup.empty()) {
			this->typeCandidates = backup;
		}

		// generate range type
		SubtypeIndication *rangeType 
				= new SubtypeIndication(
						left, 
						Location("temporary"));
		rangeType->constraint = &node;
		node.type = rangeType;

		return;
	} else {
		// can this happen? at what place?
		std::cerr << "BOOM: The unexpected happened at " 
			<< node.location << std::endl;

		std::cerr << "left:" << std::endl;
		this->debugPrintTypes(fromT);
		std::cerr << "right: " << std::endl;
		this->debugPrintTypes(this->typeCandidates);
		std::cerr << "wanted: " << std::endl;
		this->debugPrintTypes(backup);
		assert(false);
	}
}

void
ResolveTypes::processDRByName(DiscreteRange &node)
{
	assert(node.rangeName != NULL);
	bool mustSingle = this->typeCandidates.size() == 1;
	node.rangeName->accept(*this);

	if (mustSingle) {
		this->needUniqueType(node);
	}
}

void 
ResolveTypes::visit(VarAssignStat &node)
{
	assert(node.target);
	assert(node.source);

	// traverse to target
	this->typeCandidates.clear();
	node.target->accept(*this);
	if (! this->needUniqueType(node)) {
		return;
	}

	// pin down type of target
	node.target->accept(*this);
	assert(this->typeCandidates.size() == 1);

	//traverse to source
	node.source->accept(*this);
	if (! this->needUniqueType(node)) {
		return;
	}
}

void
ResolveTypes::visit(SigAssignStat &node)
{
	assert(node.target);
	assert(node.waveForm);

	// traverse to target
	this->typeCandidates.clear();
	node.target->accept(*this);

	if (! this->needUniqueType(node)) {
		return;
	}

	// pin down type of target
	node.target->accept(*this);
	assert(this->typeCandidates.size() == 1);

	typeSetT backup = this->typeCandidates;
	//traverse to every element of the waveform.
	for (std::list<WaveFormElem*>::const_iterator i = 
		node.waveForm->begin();
		i != node.waveForm->end(); i++) {

		this->typeCandidates = backup;
		(*i)->accept(*this);

		if (! this->needUniqueType(node)) {
			return;
		}
	}
}

void
ResolveTypes::visit(WaveFormElem &node)
{
	assert(node.value != NULL);
	node.value->accept(*this);
	
	if (node.delay != NULL) {
		typeSetT backup = this->typeCandidates;
		this->typeCandidates.clear();

		const TypeDeclaration *t = 
			this->symbolTable.getStdStandardType("time");
		this->typeCandidates.insert(t);
		node.delay->accept(*this);
		this->needUniqueType(*node.delay);

		this->typeCandidates = backup;
	}
}

void
ResolveTypes::visit(SimpleName &node)
{
	bool mustSingle = (this->typeCandidates.size() == 1);

	SymbolFilter tf = SymbolFilter(node.candidates, this->typeCandidates);
	tf.apply();

	if (mustSingle) {
		this->needUniqueType(node);
	} else {
		this->needNotEmpty(node.location);
	}
}

void
ResolveTypes::visit(SelectedName &node)
{
	/** nested class to transform a record element symbol to the 
	 *  parent record type.
	 */
	class SelectionFilter : public SymbolFilter {
	public:
		/** c'tor
		 *  @param cands candidate symbols
		 *  @param wantTypes list of wanted types
		 */
		SelectionFilter(std::list<Symbol*> &cands,
				typeSetT &wantTypes
				) : SymbolFilter (cands, wantTypes) {}
	private:
		/** operation to transform an element to a parent record type.
		 *  @param element RecordTypeElement symbol
		 *  @return parent RecordType.
		 */
		virtual const TypeDeclaration*
		operator()(Symbol *element) const {

			assert(element);
			if (element->type != SYMBOL_ELEMENT) {
				return NULL;
			}

			RecordTypeElement *rel = 
				dynamic_cast<RecordTypeElement*>(
						&(element->declaration));
			assert(rel);
			assert(rel->parent);
			return rel->parent;
		}

	};

	assert(node.prefix);
	assert(node.name);

#if SELECTION_DEBUG
	std::cerr << "Selection enter (prefix at " << node.prefix->location
		<< "=" 
		<< node.prefix << ")" 
		<< std::endl;
	ResolveTypes::debugPrintTypes(this->typeCandidates);
#endif

	bool mustSingle = (this->typeCandidates.size() == 1);

	// 1. filter on current types.
	SymbolFilter tf = SymbolFilter(node.candidates, this->typeCandidates);
	tf.apply();

#if SELECTION_DEBUG
	std::cerr << "Selection after SymbolFilter" << std::endl;
	ResolveTypes::debugPrintTypes(this->typeCandidates);
#endif

	size_t numSyms = node.candidates.size();

	typeSetT backup = this->typeCandidates;
	this->typeCandidates.clear();

	// build list of possible record types of prefix
	SelectionFilter sf = SelectionFilter(node.candidates, 
						this->typeCandidates);
	sf.apply();

#if SELECTION_DEBUG
	std::cerr << "prefix candidates (before traversal)" << std::endl;
	ResolveTypes::debugPrintTypes(this->typeCandidates);
#endif

	// traverse to prefix
	node.prefix->accept(*this);

#if SELECTION_DEBUG
	std::cerr << "prefix candidates (after traversal)" << std::endl;
	ResolveTypes::debugPrintTypes(this->typeCandidates);
#endif

	// prefix types were filtered, filter out candidate symbols which
	// still match.
	sf.apply();

	this->needNotEmpty(node.prefix->location);

	// restore old candidates 
	this->typeCandidates = backup;
	if (node.candidates.size() != numSyms) {
#if SELECTION_DEBUG
		std::cerr << "Selection: again" << std::endl;
#endif
		// candidates got reduced by the SelectionFilter. Filter by
		// wanted types again.
		tf.apply();

		// apply types to prefix
		backup = this->typeCandidates;
		this->typeCandidates.clear();
		// regenerate the only possible type the prefix may take
		sf.apply();
		// it *must* be one
		assert(this->typeCandidates.size() == 1);
		// apply it to the prefix
		node.prefix->accept(*this);

		this->typeCandidates = backup;
	}

	if (mustSingle) {
		this->needUniqueType(node);
	} else {
		this->needNotEmpty(node.location);
	}
}

void
ResolveTypes::visit(AttributeName &node)
{
	typedef void (ResolveTypes::*rtANMethT)(AttributeName &);

	// FIXME isn't that crack? cf. comments in BuiltinSymbolTable
	std::map<std::string, rtANMethT> attrCals;
	attrCals["range"] = &ResolveTypes::processRangeAttr;
	attrCals["left"] = &ResolveTypes::processLeftAttr;
	attrCals["right"] = &ResolveTypes::processRightAttr;
	attrCals["event"] = &ResolveTypes::processEventAttr;
	// TODO
	//
	// FIXME actually processEventAttr looks good to be 
	//       used as a more general way to resolve attributes.

	std::map<std::string, rtANMethT>::iterator i = 
		attrCals.find(*node.name);

	if (i == attrCals.end()) {
		std::cerr << "cannot find attribute <" << *node.name
			<< ">." << std::endl;
		assert(false);
	}
	
	(this->*(i->second))(node);
}

void
ResolveTypes::processRangeAttr(AttributeName &node)
{
	assert(node.prefix != NULL);

	bool mustSingle = this->typeCandidates.size() == 1;
	typeSetT backup = this->typeCandidates;

	this->typeCandidates.clear();
	node.prefix->accept(*this);
	IndexTypeFilter itf = 
		IndexTypeFilter(this->typeCandidates, backup, 1);
	itf.apply();

	if (mustSingle) {
		bool res = this->needUniqueType(node);
		if (res) {
			switch ((*backup.begin())->baseType) {
			case BASE_TYPE_INTEGER:
				node.baseType = BASE_TYPE_RANGE_INT;
				break;

			case BASE_TYPE_REAL:
				node.baseType = BASE_TYPE_RANGE_REAL;
				break;

			default:
				assert(false);
			}

			// pin down type...
			node.prefix->accept(*this);
		}
	}
	this->typeCandidates = backup;
}

void
ResolveTypes::processEventAttr(AttributeName &node)
{
	assert(node.prefix != NULL);

	// first traverse to prefix. Type must be clear from the
	// context.
	typeSetT backup = this->typeCandidates;
	this->typeCandidates.clear();
	node.prefix->accept(*this);

	// there may be only one type for the prefix!
	bool res = this->needUniqueType(node);
	if (res) {
		node.prefix->accept(*this);
	}

	this->typeCandidates = backup;
	// filter on symbols
	SymbolFilter tf = SymbolFilter(node.candidates, this->typeCandidates);
	tf.apply();
}

void
ResolveTypes::processLeftAttr(AttributeName &node)
{
	assert(false);
}

void
ResolveTypes::processRightAttr(AttributeName &node)
{
	assert(false);
}

void
ResolveTypes::visit(FunctionCall &node)
{
	assert(node.subprog);
	assert(node.arguments);

	bool mustSingle = (this->typeCandidates.size() == 1);

	SymbolFilter rf = SymbolFilter(node.subprog->candidates,
						this->typeCandidates);
	rf.apply();

	// minor check that no positional arguments are present after named
	// arguments (TODO should go somewhere else)
	// FIXME assert's, that no named areguments are present at all,
	// since these are currently not supported.
	bool positional = true;
	for (std::list<AssociationElement*>::const_iterator i = 
		node.arguments->begin(); i != node.arguments->end(); i++) {

		if ((*i)->formal) {
			positional = false;
			assert(false); //named arguments not yet supported.
		} else {
			if (! positional) {
				CompileError *ce = new CompileError(
						(*i)->formal->location,
						"Positional argument after "
						"named argument");
				ErrorRegistry::addError(ce);
				return;
			}
		}
	}

	typeSetT backup = this->typeCandidates;
	this->typeCandidates.clear();
	
	this->processSubprogCall(node, mustSingle);
	// filter again, to reduce return types.
	this->typeCandidates = backup;
	rf.apply();

	if (mustSingle && node.subprog->candidates.size() == 1) {
		node.definition = 
			dynamic_cast<FunctionDeclaration*>(
				&node.subprog->candidates.front()->declaration
				);
		assert(node.definition != NULL);
	}

	if (mustSingle) {
		this->needUniqueType(node);
	}
}

void
ResolveTypes::visit(ProcCallStat &node)
{
	assert(node.subprog);
	assert(node.arguments);

	this->typeCandidates.clear();
	this->processSubprogCall(node, true);
	if (node.subprog->candidates.size() != 1) {
		// error reported from AssociationElement list already
		return;
	}

	Symbol *sym = node.subprog->candidates.front();
	node.definition = 
		dynamic_cast<ProcedureDeclaration*>(&sym->declaration);
}

void
ResolveTypes::visit(SubtypeIndication &node)
{
	this->typeCandidates.clear();
	// ignore typeName, that's already handled in c'tor
	
	// FIXME that's too easy... constraint must be of a
	//       specific type!
	if (node.constraint != NULL) {
		node.constraint->accept(*this);
		this->typeCandidates.clear();
	}

	if (node.resolutionFunction != NULL) {
		SimpleName *sn =
			dynamic_cast<SimpleName*>(node.resolutionFunction);
		if (! sn) {
			// FIXME better error message. Imho this can
			// only happen for selected names. However
			// resolution functions should only be expanded names,
			// which in turn would result in SimpleName.
			CompileError *ce = 
				new CompileError(
					*node.resolutionFunction,
					"There is something wrong");
			ErrorRegistry::addError(ce);
			return;
		}
		this->processResolutionFunction(node, *sn);
	}

	if (node.indexConstraint == NULL) {
		return;
	}

	/* base must be unconstraint array */
	const UnconstrainedArrayType *base = 
		dynamic_cast<const UnconstrainedArrayType*>(
				this->findBaseType(node.declaration));
	assert(base != NULL);
	assert(base->indexTypes != NULL);

	if (node.indexConstraint->size() != base->indexTypes->size()) {
		CompileError *ce = new CompileError(node, 
			"wrong number of indices for index constraint.");
		ErrorRegistry::addError(ce);
		return;
	}

	std::list<TypeDeclaration*>::iterator j = base->indexTypes->begin();
	for (std::list<DiscreteRange*>::iterator i = 
		node.indexConstraint->begin();
		i != node.indexConstraint->end(); i++, j++) {

		this->typeCandidates.insert(*j);
		(*i)->accept(*this);
		this->typeCandidates.clear();
	}
}

void
ResolveTypes::processResolutionFunction(
	SubtypeIndication &type,
	SimpleName &resolver
)
{
	assert(type.resolutionFunction != NULL);
	
	// 1. filter by return type.
	this->typeCandidates.insert(&type);
	SymbolFilter sf = 
		SymbolFilter(resolver.candidates, this->typeCandidates);
	sf.apply();
	this->typeCandidates.clear();

	// 2. filter by parameter type (one dimensional array, element type
	//    must match this type.
	
	for (std::list<Symbol *>::iterator i = resolver.candidates.begin();
		i != resolver.candidates.end(); 
		/* nothing */) {

		FunctionDeclaration *fd = 
			dynamic_cast<FunctionDeclaration*>(
				&(*i)->declaration);
		if (fd == NULL) {
			i = resolver.candidates.erase(i);
			continue;
		}

		if ((fd->arguments == NULL) || (fd->arguments->size() != 1)) {
			i = resolver.candidates.erase(i);
			continue;
		}

		const ValDeclaration *arg = fd->arguments->front();
		if (! arg->isUnconstraint()) {
			i = resolver.candidates.erase(i);
			continue;
		}

		if (arg->storageClass != ValDeclaration::OBJ_CLASS_CONSTANT) {
			i = resolver.candidates.erase(i);
			continue;
		}

		const TypeDeclaration *elementT = 
			ResolveTypes::subscribedType(*arg->subtypeIndic,
							1,
							resolver.location);
		if (elementT == NULL) {
			i = resolver.candidates.erase(i);
			continue;
		}

		if (! ResolveTypes::baseTypeEqual(*elementT, type)) {
			i = resolver.candidates.erase(i);
			continue;
		}

		i++;
						
	}
	
	if (resolver.candidates.size() != 1) {
		CompileError *ce = 
			new CompileError(resolver,
					"Type Error for resolution function");
		ErrorRegistry::addError(ce);
	}
}


template <typename T>
void
ResolveTypes::processSubprogCall(T &node, bool mustSingle)
{
	assert(node.arguments);
	assert(node.subprog);

	/* FIXME this is not quite right yet.
	 * Example: 
	 * 	x : integer;
	 *
	 *	if 2 = x then
	 *	...
	 *
	 * Currently, 2 as first operand will resolve = uniquely to
	 * "="(anon, anon: universal_integer);
	 *
	 * Hence it will result in a type error, because x is of a different
	 * type (and as non-universal type not convertible).
	 */

	//! filter out symbols by number of arguments.
	/** FIXME currently this filter relies on no formal parts being 
	 *        present, because these could indicate individual 
	 *        association. 
	 *        For individual association, one approach would be to 
	 *        flatten every type (which has the caveat, that conversion
	 *        functions cannot be handled that way, since these need to
	 *        be done on the complete composite type, if no individual
	 *        association is used) and check again.
	 *        Another possible approach, would be to count the different
	 *        formal parts with different names.
	 *        This filter also doesn't take into account yet, that 
	 *        formal parts are a means of reducing the candidates as 
	 *        well (e.g. if a formal part with given name is not present
	 *        for a candidate).
	 */    
	class ArgcFilter {
	public:
		/** c'tor
		 *  @param cands candidate symbols of callable's
		 *  @param args current argument list
		 */
		ArgcFilter(std::list<Symbol*> &cands,
				const std::list<AssociationElement*> &args
				) : sc(cands), a(args) {}

		/** apply the filter */
		void apply(void) {
			for (std::list<Symbol*>::iterator i = 
				this->sc.begin(); i != this->sc.end();
				/* nothing */) {

				//FIXME see long comment above

				Callable *c = 
					dynamic_cast<Callable*>(
							&(*i)->declaration);

				if (c == NULL) {
					i = this->sc.erase(i);
					continue;
				}

				if (   (c->arguments == NULL) 
				    && this->a.empty()) {

				    	i++;
					continue;
				} 

				if (c->arguments == NULL) {
					// args not empty
					i = this->sc.erase(i);
					continue;
				}

				if (c->arguments->size() != this->a.size()) {
					i = this->sc.erase(i);
					continue;
				}
				i++;
			}
		}
	private:
		//!candidates
		std::list<Symbol*> &sc;
		//!arguments
		const std::list<AssociationElement*> &a;
	};

	// filter on number of arguments
	ArgcFilter argcf = ArgcFilter(
			node.subprog->candidates,
			*node.arguments);
	argcf.apply();

	ProjectPositionalArg paf = ProjectPositionalArg(
					node.subprog->candidates,
					this->typeCandidates,
					0);
	size_t firstSinglePos = node.arguments->size();

	for (std::list<AssociationElement*>::const_iterator i = 
		node.arguments->begin(); i != node.arguments->end(); 
		i++, paf.position++) {

		//TODO: named arguments.
		assert((*i)->formal == NULL);
		assert((*i)->actual); /* FIXME open associations */
		
		this->typeCandidates.clear();
		// fetch *all* possible types into typeCandidates.
		paf.apply();

		// if there's only a single type candidate, recall 
		// the position so that we can avoid duplicate traversal
		// for type pinning
		if ((this->typeCandidates.size() == 1)
		   && (paf.position < firstSinglePos)) {
		   	firstSinglePos = paf.position;
		}

#if FUNCCALLS_DEBUG
		std::cerr << (*i)->location << ": " << *i 
			<< " all possible types " << std::endl;
		this->debugPrintTypes(this->typeCandidates);
#endif /* FUNCCALLS_DEBUG */

		// traverse to actual part
		(*i)->accept(*this);

#if FUNCCALLS_DEBUG
		std::cerr << (*i)->location << ": " << *i 
			<< " reduced set" << std::endl;
		this->debugPrintTypes(this->typeCandidates);
#endif /* FUNCCALLS_DEBUG */

		// now typeCandidates contains the set of possbile types, that
		// can be matched by the argument. filter the possible types
		// again (actually possible types are reduced not by 
		// actual types, but rather by a reduced set of possible types).
		paf.apply();
	}

	this->typeCandidates.clear();

	if (! mustSingle) {
		return;
	}

	// check if unique
	if (1 < node.subprog->candidates.size()) {
		std::string msg = "Ambiguous subprogram call of <";
		msg += *node.subprog->name + ">. ";

#if FUNCCALLS_DEBUG
		std::cerr << "TYPE ERROR: " << node << std::endl;
#endif /* FUNCCALLS_DEBUG */
		CompileError *ce = new CompileError(node, msg);
		ErrorRegistry::addError(ce);
		return;
	}

	if (node.subprog->candidates.empty()) {
		return;
	}

	// now each argument *must* result in a single type!
	// pin these down.
	paf.position = 0;
	for (std::list<AssociationElement*>::const_iterator i = 
		node.arguments->begin(); i != node.arguments->end(); 
		i++, paf.position++) {

		// break early if other arguments already have been
		// pinned down.
		if (paf.position == firstSinglePos) {
			break;
		}

		//TODO: named arguments.
		assert((*i)->formal == NULL);
		assert((*i)->actual); /* FIXME open associations */
		
		this->typeCandidates.clear();
		// fetch *all* possible types into typeCandidates
		// (can be only one)
		paf.apply();
		assert(this->typeCandidates.size() == 1);

		// traverse to actual part to pin it down
		(*i)->accept(*this);
	}
	this->typeCandidates.clear();
}

void
ResolveTypes::visit(Subscript &node)
{
	// FIXME 6.4 (and 10.5) make it not explicitely clear, if the 
	//       index constraints may be used to eliminate possibile
	//       interpretions in the first place.
	//
	//       Is this valid?
	//
	//	 type foo is range 1 to 10;
	//	 type bar is range 1 to 20;
	//       type x is array(foo) of integer;
	//       type y is array(bar) of integer;
	//
	//       function f() return x;
	//       function f() return y;
	//
	//       var a : foo;
	//       var b : bar;
	//       var i : integer;
	//
	//       a := f(a);
	//       a := f(b);
	//
	// IMHO the only rule here would be that the prefix must be an array
	//      type, so wether it has the correct number of indices
	//      and wether the index types are set correct shouldn't matter.
	//
	// The current implementation eliminates candidates of f via 
	// index types though.
	assert(node.source != NULL);
	assert(node.indices != NULL);

	typeSetT wantTypes = this->typeCandidates;
	bool mustSingle = wantTypes.size() == 1;
	this->typeCandidates.clear();

	node.source->accept(*this);

	SubscriptFilter sf = SubscriptFilter(this->typeCandidates, wantTypes);
	sf.apply();

	typeSetT backup = this->typeCandidates;
	if (! mustSingle) {
		// fetch index types
		unsigned int nIdx = 1;
		for (std::list<Expression*>::iterator i = node.indices->begin();
			i != node.indices->end(); i++) {
			
			this->typeCandidates.clear();

			IndexTypeFilter itf = 
				IndexTypeFilter(backup, 
						this->typeCandidates, nIdx);
			itf.apply();
			(*i)->accept(*this);

			nIdx++;
		}
	}
	this->typeCandidates = backup;

	if (mustSingle) {
		// pin down source
		node.source->accept(*this);

		// and pin down index types
		unsigned int nIdx = 1;
		for (std::list<Expression*>::iterator i = node.indices->begin();
			i != node.indices->end(); i++) {

			this->typeCandidates.clear();
			IndexTypeFilter itf = 
				IndexTypeFilter(backup, 
						this->typeCandidates, nIdx);
			itf.apply();
			(*i)->accept(*this);

			if (! this->needUniqueType(**i)) {
				this->typeCandidates = backup;
				return;
			}
			nIdx++;
		}
	}

	this->typeCandidates = wantTypes;
	if (mustSingle) {
		bool ret = this->needUniqueType(node);
		if (! ret) {
			return;
		}

		// check number of indices
		const TypeDeclaration *d = 
			ResolveTypes::findBaseType(node.source->type);
		const UnconstrainedArrayType *ua = 
			dynamic_cast<const UnconstrainedArrayType*>(d);
		assert(ua != NULL);
		if (node.indices->size() < ua->indexTypes->size()) {
			CompileError *ce = 
				new CompileError(node, "Too few indices");
			ErrorRegistry::addError(ce);
		}

		if (node.indices->size() > ua->indexTypes->size()) {
			CompileError *ce = 
				new CompileError(node, "Too many indices");
			ErrorRegistry::addError(ce);
		}

	} else {
		this->needNotEmpty(node.location);
	}
}

void
ResolveTypes::visit(ConstInteger &node)
{
	if (node.physUnit != NULL) {
		// unit is a physical type. Let SimpleName handle this.
		node.physUnit->accept(*this);
		node.type = node.physUnit->type;
		return;
	}

	const TypeDeclaration *uInt = 
		this->symbolTable.getStdStandardType("__universal_integer__");
	assert(uInt);
	this->processUniversal(node, uInt, BASE_TYPE_INTEGER);
}

void
ResolveTypes::visit(ConstReal &node)
{
	const TypeDeclaration *uReal = 
		this->symbolTable.getStdStandardType("__universal_real__");
	assert(uReal);
	this->processUniversal(node, uReal, BASE_TYPE_REAL);
}

void
ResolveTypes::visit(Aggregate &node)
{
	//FIXME handles only array aggregates atm.
	assert(node.associations != NULL);
	bool isArray = false;

	for (typeSetT::iterator i = this->typeCandidates.begin(); 
		i != this->typeCandidates.end(); /* nothing */) {

		switch((*i)->baseType) {
		case BASE_TYPE_ARRAY:
			isArray = true;
			i++;
			continue;
		case BASE_TYPE_RECORD:
			isArray = false;
			i++;
			continue;
		default:
			/* fall through */
			break;
		}

		// not a composite type, remove.
		typeSetT::iterator j = i;
		i++;
		this->typeCandidates.erase(j);
	}

	if (! this->needUniqueType(node)) {
		return;
	}

	// typeCandidates contain exactly one type
	if (isArray) {
		this->processArrayAgg(node);
		node.baseType = BASE_TYPE_ARRAY;
		return;
	} 

	node.baseType = BASE_TYPE_RECORD;
	// TODO record Aggregates
	return;
}

void
ResolveTypes::processArrayAgg(Aggregate &node)
{
	// must have been filtered by caller.
	assert(this->typeCandidates.size() == 1);

	// determine subscripted type
	const TypeDeclaration *wanted = *(this->typeCandidates.begin());

	for (std::list<ElementAssociation*>::const_iterator i = 
		node.associations->begin(); i != node.associations->end();
		i++) {

		this->typeCandidates.clear();
		this->typeCandidates.insert(wanted);
		// don't dispatch, call directly instead.
		this->processArrayAssoc(**i);
	}

	// restore candidates
	this->typeCandidates.clear();
	this->typeCandidates.insert(wanted);

	if (ResolveTypes::isConstraintArray(node.type)) {
		return;
	}

	// unconstraint array as base type. Try to calculate bounds.
	UnconstraintBounds ub = UnconstraintBounds();
	node.accept(ub);
	assert(ub.bounds != NULL);

	// generate a new SubtypeIndication with the range constraint.
	SubtypeIndication *si = 
		new SubtypeIndication(node.type, node.location);
	si->indexConstraint = new std::list<DiscreteRange*>();
	si->indexConstraint->push_back(ub.bounds);
	node.type = si;
}

void
ResolveTypes::visit(ElementAssociation &node)
{
	assert(false); // use direct calls instead of dispatching.
}

void
ResolveTypes::visit(ReturnStat &node)
{
	if (node.result == NULL) {
		// no return expression, break early.
		return;
	}

	// determine return type
	// FIXME catch error that result is present, but 
	//       node refers to a Procedure
	LookupTypes lat = LookupTypes(false, false);

	assert(node.enclosingSubprog != NULL);
	node.enclosingSubprog->accept(lat);
	assert(lat.declaration != NULL);

	this->typeCandidates.clear();
	this->typeCandidates.insert(lat.declaration);
	
	// traverse to result expression (will report errors, since
	// there is only a single type in wantTypes)
	node.result->accept(*this);
	this->typeCandidates.clear();
}

void
ResolveTypes::visit(Process &node)
{
	if (node.sensitivityList != NULL) {
		for (std::list<Name*>::iterator i = 
			node.sensitivityList->begin();
			i != node.sensitivityList->end(); i++) {

			this->typeCandidates.clear();
			(*i)->accept(*this);
			if (! this->needUniqueType(**i)) {
				continue;
			}
			
			(*i)->accept(*this);
			this->needUniqueType(**i);
		}
	}

	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
	}
	this->typeCandidates.clear();

	if (node.seqStats != NULL) {
		this->listTraverse(*node.seqStats);
	}
}

void
ResolveTypes::processArrayAssoc(ElementAssociation &node)
{
	// need exactly one candidate. If that's not true, Aggregate should
	// have reported an error and not called this method.
	assert(this->typeCandidates.size() == 1); 

	if (node.choices != NULL) {
		// determine possible index types.
		typeSetT backup = this->typeCandidates;
		this->typeCandidates.clear();
		IndexTypeFilter itf = IndexTypeFilter(backup, 
							this->typeCandidates,
							1);
		itf.apply();
		typeSetT wantedIndexTypes = this->typeCandidates;

		for (std::list<Expression*>::const_iterator i = 
			node.choices->begin();
			i != node.choices->end(); i++) {

			this->typeCandidates = wantedIndexTypes;
			(*i)->accept(*this);
			// check if index types match.
			itf.apply();
			if (! this->needUniqueType(node)) {
				// type error, bail out immediately
				return;
			}

			assert((*i)->type != NULL);
		}
		this->typeCandidates = backup;
	}

	assert(node.actual != NULL);
	// getting tricky: 
	// * if it's a multidimensional array, remove the first index and 
	//   traverse to actual.
	// * otherwise determine the element type and traverse to actual.
	//
	// Examples: (0 => (0 => '1', 1 => '0'), 1 to 3 => (others => '1'))
	//           is valid for
	//           array(0 to 3, 0 to 1) of character;
	//           *and*
	//           at : array(0 to 1) of character;
	//           array(0 to 3) of at;
	const TypeDeclaration *wanted = *this->typeCandidates.begin();
	const TypeDeclaration *needed = 
		ResolveTypes::subscribedType(*wanted, 1, node.location);

	// traverse to actual
	this->typeCandidates.clear();
	this->typeCandidates.insert(needed);
	node.actual->accept(*this);
	this->typeCandidates.clear();
	this->typeCandidates.insert(wanted);
}

const TypeDeclaration *
ResolveTypes::subscribedType(
	const TypeDeclaration &array, 
	unsigned int nIdx,
	Location loc
)
{
	assert(array.baseType == BASE_TYPE_ARRAY);
	std::list<DiscreteRange*> idxConstraint = std::list<DiscreteRange*>();
	const UnconstrainedArrayType *uArr = 
		ResolveTypes::pickupIndexConstraint(&array, idxConstraint);

	assert(uArr->indexTypes != NULL);

	if (uArr->indexTypes->size() < nIdx) {
		// FIXME: issue error here.
		assert(false);
	}

	if (uArr->indexTypes->size() == nIdx) {
		// subscription to base type.
		return uArr->elementType;
	}

	// partial subscription. strip off first nIdx indices.
	std::list<TypeDeclaration*>::iterator i = uArr->indexTypes->begin();
	for (unsigned int c = nIdx; c > 0; c--) {
		i++;
	}

	std::list<TypeDeclaration*> *shiftIndices = 
					new std::list<TypeDeclaration*>();

	shiftIndices->insert(shiftIndices->end(), i, uArr->indexTypes->end());

	// that's the new anonymous base type.
	UnconstrainedArrayType *ua = new UnconstrainedArrayType(
			new std::string("__anonymous__"),
			shiftIndices,
			uArr->elementType,
			loc);

	if (idxConstraint.empty()) {
		// must have been an Unconstraint array already.
		return ua;
	}

	// FIXME must be checked somewhere (hello parser?)
	assert(idxConstraint.size() == uArr->indexTypes->size());
	// strip off nIdx indices from indexConstraint as well.
	for (std::list<DiscreteRange*>::iterator j = idxConstraint.begin();
		nIdx > 0; nIdx--) {

		assert(j != idxConstraint.end());
		j = idxConstraint.erase(j);
	}

	SubtypeIndication *si = new SubtypeIndication(ua, loc);
	si->indexConstraint = new std::list<DiscreteRange*>(idxConstraint);

	return si;
}


void
ResolveTypes::visit(Others &node)
{
	if (this->typeCandidates.size() == 1) {
		this->needUniqueType(node);
	}

	// otherwise others does not affect type candidates.
}

void
ResolveTypes::visit(Slice &node)
{
	assert(node.source);
#if SLICE_DEBUG
	std::cerr << "Slice before source" << std::endl;
	ResolveTypes::debugPrintTypes(this->typeCandidates);
#endif
	node.source->accept(*this);

#if SLICE_DEBUG
	std::cerr << "Slice after source" << std::endl;
	ResolveTypes::debugPrintTypes(this->typeCandidates);
#endif

	typeSetT backup = this->typeCandidates;
	this->typeCandidates.clear();

	// determine index types and store these in typeCandidates
	IndexTypeFilter itf = 
		IndexTypeFilter(backup, this->typeCandidates, 1);
	itf.apply();
#if SLICE_DEBUG
	std::cerr << "#indexTypes for " << *node.source << "=" 
		<< this->typeCandidates.size() << std::endl;
	std::cerr << "#source types=" << backup.size() << std::endl;
#endif

	// traverse to range and filter out types
	assert(node.range);
	node.range->accept(*this);
	itf.apply();
	this->typeCandidates = backup;

	this->needUniqueType(node);
}

void
ResolveTypes::visit(TemporaryName &node)
{
	// only traverse to prefix, do nothing else
	assert(node.prefix != NULL);
	node.prefix->accept(*this);

	// set the type in case it's known
	if (this->typeCandidates.size() == 1) {
		this->needUniqueType(node);
	}
}

void
ResolveTypes::visit(AttributeSpecification &node)
{
	this->typeCandidates.clear();
	assert(node.declaration != NULL);
	assert(node.declaration->type != NULL);

	this->typeCandidates.insert(node.declaration->type);

	assert(node.init != NULL);
	node.init->accept(*this);

	this->needUniqueType(node);
	this->typeCandidates.clear();
}

void
ResolveTypes::processUniversal(
	Expression &node,
	const TypeDeclaration *directMatch,
	enum BaseType icCompatible
)
{
	assert(directMatch != NULL);

	// no type candidates -> put directMatch (universal_int, U.real)
	// in there.
	// (implicit conversions are only allowed, if the wanted type
	// is known, LRM 7.3.5).
	if (this->typeCandidates.empty()) {
		this->typeCandidates.insert(directMatch);
		this->needUniqueType(node);
		return;
	}

	bool mustSingle = (this->typeCandidates.size() == 1);

	// check if there is a direct match for __universal_integer__
	typeSetT backup = this->typeCandidates;
	for (typeSetT::iterator i = backup.begin(); i != backup.end(); 
		/* nothing */) {

		if (! ResolveTypes::baseTypeEqual(**i, *directMatch)) {
			typeSetT::iterator j = i;
			i++;
			backup.erase(j);
			continue;
		}

		i++;
	}

	if (! backup.empty()) {
		// direct match against universal_integer found. return this.
		this->typeCandidates = backup;
		this->needUniqueType(node);
		return;
	}

	// node is of type (convertible) universal_integer. Filter out all 
	// types that are not integer based.
	for (typeSetT::iterator i = this->typeCandidates.begin(); 
		i != this->typeCandidates.end(); /* nothing */) {

		if ((*i)->baseType != icCompatible) {
			typeSetT::iterator j = i;
			i++;
			this->typeCandidates.erase(j);
			continue;
		}
		i++;
	}

	if (mustSingle) {
		this->needUniqueType(node);
	}
}

void
ResolveTypes::visit(RangeConstraintType &node)
{
	this->processConstraintType(node);
}

void
ResolveTypes::visit(PhysicalType &node)
{
	this->processConstraintType(node);
	if (node.baseType != BASE_TYPE_INTEGER) {
		node.baseType = BASE_TYPE_INTEGER;
		CompileError *err = new CompileError(node, 
				"Constraint must be an integral type.");
		ErrorRegistry::addError(err);
	}
}

void
ResolveTypes::visit(RecordType &node)
{
	// nothing to do, *don't* traverse to elements!
}

void
ResolveTypes::visit(FunctionDeclaration &node)
{
	// do *not* traverse to returnType
	this->process(node);
}

void
ResolveTypes::visit(ForLoopStat &node)
{
	// set type declaration of loop parameter specification from
	// discrete range.
	this->typeCandidates.clear();
	assert(node.range != NULL);
	node.range->accept(*this);

	bool ret = this->needUniqueType(node);
	if (! ret) {
		return;
	}

	const TypeDeclaration *t = *this->typeCandidates.begin();
	if (t->isUniversal) {
		t = this->symbolTable.getStdStandardType("integer");
		this->typeCandidates.clear();
		this->typeCandidates.insert(t);
	}

	// actually pin down type
	node.range->accept(*this);

	assert(node.loopVariable != NULL);
	assert(! this->typeCandidates.empty());
	t = *this->typeCandidates.begin();

	// check if it is a discrete type.
	switch (t->baseType) {
	case BASE_TYPE_INTEGER:
		break;
	
	default: {
		CompileError *ce = 
			new CompileError(*node.range,
				"For loop parameter must be discrete.");
		ErrorRegistry::addError(ce);
		return;
	    }
	}

	node.loopVariable->subtypeIndic = 
		new SubtypeIndication(t, node.loopVariable->location);
	this->typeCandidates.clear();

	// handle seqstats via process.
	this->process(node);
}

void
ResolveTypes::visit(WhileLoopStat &node)
{
	this->typeCandidates.clear();
	// condition must be of type boolean
	const TypeDeclaration *boolean = 
		this->symbolTable.getStdStandardType("boolean");

	assert(boolean != NULL);
	assert(node.condition != NULL);

	this->typeCandidates.insert(boolean);
	node.condition->accept(*this);
	this->typeCandidates.clear();

	if (node.loopStats != NULL) {
		this->listTraverse(*node.loopStats);
	}
}

void
ResolveTypes::visit(AssertStat &node)
{
	this->typeCandidates.clear();

	// condition must be of type boolean
	const TypeDeclaration *boolean = 
		this->symbolTable.getStdStandardType("boolean");

	assert(boolean != NULL);
	assert(node.condition != NULL);

	this->typeCandidates.insert(boolean);
	node.condition->accept(*this);
	this->typeCandidates.clear();

	if (node.report != NULL) {
		// report must be of type string (i.e. compatible to)
		const TypeDeclaration *strT = 
			this->symbolTable.getStdStandardType("string");
		assert(strT != NULL);
		this->typeCandidates.insert(strT);
		node.report->accept(*this);
		this->typeCandidates.clear();
	}

	if (node.severity != NULL) {
		// report must be of type SEVERITY_LEVEL
		const TypeDeclaration *sevLvl = 
			this->symbolTable.getStdStandardType(
							"severity_level");
		assert(sevLvl != NULL);
		this->typeCandidates.insert(sevLvl);
		node.severity->accept(*this);
		this->typeCandidates.clear();
	}
}

void
ResolveTypes::visit(WaitStat &node)
{
	this->typeCandidates.clear();
	// handle condition
	this->process(node);

	if (node.timeout != NULL) {
		// timeout must be of type "time"
		const TypeDeclaration *time = 
			this->symbolTable.getStdStandardType("time");
		this->typeCandidates.insert(time);

		node.timeout->accept(*this);
		this->needUniqueType(*node.timeout);
		this->typeCandidates.clear();
	}

	if (node.sensitivities == NULL) {
		return;
	}

	// node.sensitivities != NULL
	for (std::list<Name*>::iterator i = node.sensitivities->begin();
		i != node.sensitivities->end(); i++) {
		
		// TODO (probably in a separate step): the sensitivity list
		//      must consist of solely static names.
		(*i)->accept(*this);
		if (! this->needUniqueType(**i)) {
			this->typeCandidates.clear();
			continue;
		}

		(*i)->accept(*this);
		this->needUniqueType(**i);
		this->typeCandidates.clear();
	}
}

void
ResolveTypes::processAlternative(CaseAlternative &node)
{
	assert(node.isVals != NULL);
	assert(node.thenStats != NULL);
	typeSetT backup = this->typeCandidates;
	assert(backup.size() == 1);

	for (std::list<Expression*>::iterator i = node.isVals->begin();
		i != node.isVals->end(); i++) {

		this->typeCandidates = backup;
		(*i)->accept(*this);
		this->needUniqueType(**i);
	}

	this->typeCandidates.clear();
	this->listTraverse(*node.thenStats);
	this->typeCandidates = backup;
}

void
ResolveTypes::visit(CaseStat &node)
{
	assert(node.select != NULL);
	assert(node.alternatives != NULL);

	this->typeCandidates.clear();
	node.select->accept(*this);
	// TODO lrm, 8.8: base type must be discrete, or a one dimensional 
	//                array of a character type.
	bool r = this->needUniqueType(*node.select);
	if (! r) {
		// don't even bother alternatives on type error
		this->typeCandidates.clear();
		return;
	}
	// pin down type
	node.select->accept(*this);
	if (this->typeCandidates.size() != 1) {
		return;
	}

	for (std::list<CaseAlternative*>::iterator i = 
		node.alternatives->begin();
		i != node.alternatives->end(); i++) {

		this->processAlternative(**i);
	}

	this->typeCandidates.clear();
}

template <typename T>
void
ResolveTypes::processConstraintType(T &node)
{
	this->typeCandidates.clear();
	assert(node.constraint != NULL);

	if (node.constraint->rangeName != NULL) {
		// handle these via discrete range.
		node.constraint->accept(*this);
		if (! this->needUniqueType(node)) {
			return;
		}
		const TypeDeclaration *t = *this->typeCandidates.begin();
		switch(t->baseType) {
		case BASE_TYPE_RANGE_INT:
			node.baseType = BASE_TYPE_INTEGER;
			break;
		case BASE_TYPE_RANGE_REAL:
			node.baseType = BASE_TYPE_RANGE_REAL;
			break;
		default: {
			node.baseType = BASE_TYPE_UNSET;
			CompileError *ce = new CompileError(node.location,
				"invalid type for constraint.");
			ErrorRegistry::addError(ce);
		    }
			
		}
		return;
	}


	assert(node.constraint->from != NULL);
	assert(node.constraint->to != NULL);
	node.constraint->from->accept(*this);
	if (! this->needUniqueType(*node.constraint->from)) {
		this->typeCandidates.clear();
		node.baseType = BASE_TYPE_UNSET;
		return;
	}

	// traverse again, to pin down from type
	node.constraint->from->accept(*this);
	// FIXME can this happen?
	assert(this->typeCandidates.size() == 1);

	const TypeDeclaration *t = *this->typeCandidates.begin();
	enum BaseType left = t->baseType;


	this->typeCandidates.clear();
	node.constraint->to->accept(*this);
	if (! this->needUniqueType(*node.constraint->to)) {
		this->typeCandidates.clear();
		node.baseType = BASE_TYPE_UNSET;
		return;
	}

	// traverse again, to pin to from type
	node.constraint->to->accept(*this);
	assert(this->typeCandidates.size() == 1);

	// set base type
	t = *this->typeCandidates.begin();
	this->typeCandidates.clear();
	left = left && t->baseType;

	switch(left) {
	case BASE_TYPE_INTEGER:
	case BASE_TYPE_REAL:
		node.baseType = left;
		break;
	default: {
		node.baseType = BASE_TYPE_UNSET;
		CompileError *ce = new CompileError(node.location,
			"invalid type for constraint.");
		ErrorRegistry::addError(ce);
	    }
	}
}

bool
ResolveTypes::baseTypeEqual(
	const TypeDeclaration &t1,
	const TypeDeclaration &t2
)
{
	// determine base types.
	const TypeDeclaration *b1 = ResolveTypes::findBaseType(&t1);
	const TypeDeclaration *b2 = ResolveTypes::findBaseType(&t2); 

	// compare pointer!
	return b1 == b2;
}

const TypeDeclaration*
ResolveTypes::findBaseType(const TypeDeclaration* t)
{
	if (t == NULL) {
		return NULL;
	}

	const SubtypeIndication *s = 
			dynamic_cast<const SubtypeIndication*>(t);
	if (s == NULL) {
		// not a subtype
		return t;
	}

	// recurse
	assert(s->declaration);
	return ResolveTypes::findBaseType(s->declaration);
}

const UnconstrainedArrayType*
ResolveTypes::pickupIndexConstraint(
	const TypeDeclaration *constrainedArray,
	std::list<DiscreteRange*> &indexConstraint
)
{
	assert(constrainedArray != NULL);
	assert(constrainedArray->baseType == BASE_TYPE_ARRAY);

	const SubtypeIndication *sub = 
		dynamic_cast<const SubtypeIndication*>(constrainedArray);

	if ((sub != NULL) && (sub->indexConstraint != NULL)) {
		indexConstraint.insert(
			indexConstraint.end(), 
			sub->indexConstraint->begin(),
			sub->indexConstraint->end());
	}

	if (sub != NULL) {
		assert(sub->declaration != NULL);
		return ResolveTypes::pickupIndexConstraint(
						sub->declaration,
						indexConstraint);
	}

	const UnconstrainedArrayType *ua = 
		dynamic_cast<const UnconstrainedArrayType*>(constrainedArray);
	assert(ua != NULL);
	return ua;
}

bool
ResolveTypes::isConstraintArray(const TypeDeclaration *type)
{
	switch (type->baseType) {
	case BASE_TYPE_ARRAY:
		break;

	default:
		return false;
	}

	std::list<DiscreteRange *> dr;

	ResolveTypes::pickupIndexConstraint(type, dr);
	return ! dr.empty();
}

void
ResolveTypes::process(ValDeclaration& node)
{
	assert(node.subtypeIndic != NULL);
	this->typeCandidates.clear();
	node.subtypeIndic->accept(*this);

	this->typeCandidates.clear();
	if (node.init != NULL) {
		this->typeCandidates.insert(node.subtypeIndic);
		node.init->accept(*this);

		// for arrays, the node's subtype indication may
		// refer to an unconstraint array, and the bounds
		// are defined by the initializer. replace the
		// subtype indication then.
		if (this->needUniqueType(node)) {
			if (node.init->baseType != BASE_TYPE_ARRAY) {
				return;
			}

			// only constants may get the type inferred from the 
			// initializer.
			if (node.storageClass 
				!= ValDeclaration::OBJ_CLASS_CONSTANT) {
				return;
			}

			// and for constants, *only* those that are not
			// interface elements.
			const ConstantDeclaration *c = 
				dynamic_cast<const ConstantDeclaration*>(
								&node);

			assert(c != NULL);
			if (! c->fixedValue) {
				return;
			}


			SubtypeIndication *si = 
				dynamic_cast<SubtypeIndication*>(
					node.init->type);
			assert(si != NULL);
			util::MiscUtil::terminate(node.subtypeIndic);
			node.subtypeIndic = si;
#if 0
			std::cerr << "replaced subtype with " << si 
				<< std::endl;
			std::cerr << "initializer is" << node.init << std::endl;
#endif
		}
	}
}

void
ResolveTypes::process(LibUnit &node)
{
	//don't consider use- or library clauses
	//no need to call process here as well.

	//traverse to declarations.
	if (node.declarations != NULL) {
		this->listTraverse(*node.declarations);
	}
}

void
ResolveTypes::process(ConditionedStat &node)
{
	this->process(static_cast<SeqStat&>(node));
	if (node.condition == NULL) {
		return;
	}
	
	// condition must be of type boolean
	const TypeDeclaration *boolean = 
		this->symbolTable.getStdStandardType("boolean");
	
	assert(boolean != NULL);
	this->typeCandidates.insert(boolean);

	node.condition->accept(*this);
	this->typeCandidates.clear();
}

void
ResolveTypes::process(SeqStat &node)
{
	this->typeCandidates.clear();
}

bool
ResolveTypes::needUniqueType(AstNode &node) const
{
	if (this->typeCandidates.size() == 1) {
		return true;
	}

	if (this->typeCandidates.empty()) {
		std::string msg = "Type error for <";
		msg += util::MiscUtil::toString(node);
		msg += ">.";
		CompileError *ce = new CompileError(node, msg);
		ErrorRegistry::addError(ce);
		return false;
	}

	std::string msg = "Ambiguous Types for <";
		msg += util::MiscUtil::toString(node);
		msg += ">.";

	CompileError *ce = new CompileError(node, msg);
	ErrorRegistry::addError(ce);
	return false;
}

bool
ResolveTypes::needUniqueType(Expression &node) const
{
	bool ret = this->needUniqueType(static_cast<AstNode&>(node));
	if (ret) {
		const TypeDeclaration *t = *this->typeCandidates.begin();
		node.type = const_cast<TypeDeclaration*>(t);
		node.baseType = t->baseType;
	}

	return ret;
}

void
ResolveTypes::needNotEmpty(Location loc) 
{
	if (this->typeCandidates.empty()) {
		//FIXME: better error messages
		CompileError *ce = new CompileError(
					loc, 
					"Type error.");
		ErrorRegistry::addError(ce);
	}
}

enum BaseType
ResolveTypes::transformBaseType(enum BaseType rangeType, Location loc)
{
	switch(rangeType) {
	case BASE_TYPE_INTEGER:
		return BASE_TYPE_RANGE_INT;
	case BASE_TYPE_REAL:
		return BASE_TYPE_RANGE_REAL;
		break;
	default: {
		CompileError *ce = new CompileError(
			loc,
			"Wrong base type for discrete range.");
		ErrorRegistry::addError(ce);
		}
	}

	return BASE_TYPE_UNSET;
}

void
ResolveTypes::debugPrintTypes(const typeSetT &t)
{
	for (typeSetT::const_iterator i = t.begin(); i != t.end(); i++) {
		std::cerr << "\tcand=" << *i << std::endl;
	}
}

DiscreteRange *
ResolveTypes::determineIndexRangeAgg(
	const UnconstrainedArrayType *at,
	const std::list<ElementAssociation *> &assocs
)
{
	universal_integer lowerBound;
	universal_integer upperBound;

	assert(at->indexTypes != NULL);
	// TODO currently only 1-dimensional arrays supported.
	assert(at->indexTypes->size() == 1);

	DiscreteRange *maxBounds = 
		ResolveTypes::findRange(at->indexTypes->front());
	assert(maxBounds != NULL);
	// TODO direction
	assert(maxBounds->direction == DiscreteRange::DIRECTION_UP);

	// start with a NULL range...
	lowerBound = maxBounds->getUpperBound();
	upperBound = maxBounds->getLowerBound();

	if ((! assocs.empty()) && (assocs.front()->choices == NULL)) {
		// positional aggregate. lower bound is indextype'left
		lowerBound = upperBound;
		upperBound = lowerBound + assocs.size() - 1;

		ConstInteger *lb = new ConstInteger(lowerBound,
						Location("context"));
		ConstInteger *ub = new ConstInteger(upperBound,
						Location("context"));
		return new DiscreteRange(lb, ub, 
					DiscreteRange::DIRECTION_UP, 
					Location("context"));
	}

	// aggregate with choices.
	for (std::list<ElementAssociation *>::const_iterator i = 
		assocs.begin(); 
		i != assocs.end(); i++) {

		if ((*i)->choices != NULL) {
			for (std::list<Expression*>::const_iterator j =
				(*i)->choices->begin(); 
				j != (*i)->choices->end();
				j++) {

				const ConstInteger *c = 
					dynamic_cast<const ConstInteger*>(*j);
				assert(c != NULL);

				if (c->value < lowerBound) {
					lowerBound = c->value;
				}

				if (c->value > upperBound) {
					upperBound = c->value;
				}
			}
		} else {
			upperBound++;
		}
	}

	ConstInteger *lb = new ConstInteger(lowerBound, Location("context"));
	ConstInteger *ub = new ConstInteger(upperBound, Location("context"));
	return new DiscreteRange(lb, ub, DiscreteRange::DIRECTION_UP, 
				Location("context"));
}

DiscreteRange *
ResolveTypes::findRange(const TypeDeclaration *rangeType)
{
	const SubtypeIndication *sub;

	sub = dynamic_cast<const SubtypeIndication *>(rangeType);
	if (sub != NULL) {
		if (sub->constraint != NULL) {
			return sub->constraint;
		}
		return ResolveTypes::findRange(sub->declaration);
	}

	const RangeConstraintType *base;
	base = dynamic_cast<const RangeConstraintType *>(rangeType);
	assert(base != NULL);
	assert(base->constraint != NULL);

	return base->constraint;
}


template <typename T>
ResolveTypes::TypeFilter<T>::TypeFilter
(
	T &cands,
	typeSetT &wantTypes
) : 		candidates(cands),
		wantedTypes(wantTypes)
{
}

const TypeDeclaration*
ResolveTypes::SymbolFilter::operator()(Symbol *element) const
{
	LookupTypes lat = LookupTypes(false, false);
	element->declaration.accept(lat);

	return lat.declaration;
}

template <typename T>
void
ResolveTypes::TypeFilter<T>::apply(void)
{
	bool getAllPossibleTypes = this->wantedTypes.empty();
	typeSetT possibleTypes = typeSetT();

	for (typename T::iterator i = this->candidates.begin();
		i != this->candidates.end(); /* nothing */) {
		
		//determine type declaration via () operator.
		const TypeDeclaration *t = (*this)(*i);

		if (t == NULL) {
			//operation turned out that candidate not plausible
			typename T::iterator j = i;
			i++;
			this->candidates.erase(j);
			continue;
		}

		if (getAllPossibleTypes) {
			// don't perform type checking.
			possibleTypes.insert(t);
			i++;
			continue;
		}

		if (this->checkType(t)) {
			possibleTypes.insert(t);
			i++;
			continue;
		}

		//resulting type not in wanted types.
		typename T::iterator j = i;
		i++;
		this->candidates.erase(j);
	}

	this->wantedTypes = possibleTypes;
}

template <typename T>
bool
ResolveTypes::TypeFilter<T>::checkType(const TypeDeclaration *t) const
{
	assert(t);

	// must have wanted types, otherwise no type checking should be
	// performed but all possible types should get reported.
	assert(! this->wantedTypes.empty());
	
	for (typeSetT::const_iterator i = this->wantedTypes.begin();
		i != this->wantedTypes.end(); i++) {
		
		if (ResolveTypes::baseTypeEqual(**i, *t)) {
			return true;
		}
	}
	return false;
}

const TypeDeclaration*
ResolveTypes::ProjectPositionalArg::operator()(Symbol* element) const
{
	switch(element->type) {
	case SYMBOL_FUNCTION:
	case SYMBOL_PROCEDURE:
		break;
	default:
		/* only useful for functions/procedures */
		//FIXME: port map/generic map aspects!
		assert(false);
	}

	Callable *c = dynamic_cast<Callable*>(&element->declaration);
	assert(c);
	assert(c->arguments);

	if (c->arguments->size() <= this->position) {
		return NULL;
	}

	std::list<ValDeclaration*>::const_iterator i = c->arguments->begin();
	std::advance(i, this->position);

	const SubtypeIndication *s = (*i)->subtypeIndic;
	return s->declaration;
}

/** resolve the type of the source to a subscribed
 *  type.
 *  @param element source type 
 *  @return subscribed type.
 */
const TypeDeclaration*
ResolveTypes::SubscriptFilter::operator()(
	typeSetT::value_type element
) const 
{

	// not an array?
	if (element->baseType != BASE_TYPE_ARRAY) {
		return NULL;
	}

	const SubtypeIndication *si = 
		dynamic_cast<const SubtypeIndication*>(
						element);
	assert(si);
	const UnconstrainedArrayType *ua = 
		dynamic_cast<const UnconstrainedArrayType*>(
			ResolveTypes::findBaseType(si));

	assert(ua);
	assert(ua->elementType);
	
	return ua->elementType;
}

const TypeDeclaration*
ResolveTypes::IndexTypeFilter::operator()(typeSetT::value_type element) const
{
	if (element->baseType != BASE_TYPE_ARRAY) {
		return NULL;
	}

	const UnconstrainedArrayType *array = 
		dynamic_cast<const UnconstrainedArrayType*>(
			ResolveTypes::findBaseType(element));
	assert(array != NULL);
	assert(array->indexTypes != NULL);
	if (array->indexTypes->size() < this->nIdx) {
		return NULL;
	}
	
	
	bool found = false;
	std::list<TypeDeclaration*>::const_iterator i = 
					array->indexTypes->begin();

	for (unsigned int j = 1; i != array->indexTypes->end(); i++, j++) {
		if (this->nIdx <= j) {
			found = true;
			break;
		}
	}

	if (found) {
		return *i;
	}
	return NULL;
}

}; /* namespace ast */
