//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// IdTable.cc -- 
//
// Author           : Richard C. Bilson
// Created On       : Sun May 17 17:04:02 2015
// Last Modified By : Rob Schluntz
// Last Modified On : Wed Oct 07 12:21:13 2015
// Update Count     : 73
//

#include <cassert>

#include "SynTree/Declaration.h"
#include "ResolvExpr/typeops.h"
#include "Indexer.h"
#include "Mangler.h"
#include "IdTable.h"
#include "SemanticError.h"

using std::string;

namespace SymTab {
	IdTable::IdTable() : scopeLevel( 0 ) {
	}

	void IdTable::enterScope() {
		scopeLevel++;
	}

	void IdTable::leaveScope() {
		for ( OuterTableType::iterator outer = table.begin(); outer != table.end(); ++outer ) {
			for ( InnerTableType::iterator inner = outer->second.begin(); inner != outer->second.end(); ++inner ) {
				std::stack< DeclEntry >& entry = inner->second;
				// xxx - should be while?
				if ( ! entry.empty() && entry.top().second == scopeLevel ) {
					entry.pop();
				} // if
			} // for
		} // for

		scopeLevel--;
		assert( scopeLevel >= 0 );
	}

	void IdTable::addDecl( DeclarationWithType *decl ) {
		const string &name = decl->get_name();
		string manglename;
		if ( decl->get_linkage() == LinkageSpec::C ) {
			manglename = name;
		} else if ( LinkageSpec::isOverridable( decl->get_linkage() ) ) {
			// mangle the name without including the appropriate suffix
			// this will make it so that overridable routines are placed
			// into the same "bucket" as their user defined versions.
			manglename = Mangler::mangle( decl, false );
		} else {
			manglename = Mangler::mangle( decl );
		} // if

		InnerTableType &declTable = table[ name ];
		InnerTableType::iterator it = declTable.find( manglename );

		if ( it == declTable.end() ) {
			// first time this name mangling has been defined
			declTable[ manglename ].push( DeclEntry( decl, scopeLevel ) );
		} else {
			std::stack< DeclEntry >& entry = it->second;
			if ( ! entry.empty() && entry.top().second == scopeLevel ) {
				// if we're giving the same name mangling to things of
				//  different types then there is something wrong
				Declaration *old = entry.top().first;
				assert( (dynamic_cast<ObjectDecl*>( decl ) && dynamic_cast<ObjectDecl*>( old ) )
				  || (dynamic_cast<FunctionDecl*>( decl ) && dynamic_cast<FunctionDecl*>( old ) ) );

				if ( LinkageSpec::isOverridable( old->get_linkage() ) ) {
					// new definition shadows the autogenerated one, even at the same scope
					declTable[ manglename ].push( DeclEntry( decl, scopeLevel ) );
				} else if ( decl->get_linkage() != LinkageSpec::C || ResolvExpr::typesCompatible( decl->get_type(), entry.top().first->get_type(), Indexer() ) ) {
					// typesCompatible doesn't really do the right thing here. When checking compatibility of function types,
					// we should ignore outermost pointer qualifiers, except _Atomic?
					FunctionDecl *newentry = dynamic_cast< FunctionDecl* >( decl );
					FunctionDecl *oldentry = dynamic_cast< FunctionDecl* >( old );
					if ( newentry && oldentry ) {
						if ( newentry->get_statements() && oldentry->get_statements() ) {
							throw SemanticError( "duplicate function definition for 1 ", decl );
						} // if
					} else {
						// two objects with the same mangled name defined in the same scope.
						// both objects must be marked extern or both must be intrinsic for this to be okay
						// xxx - perhaps it's actually if either is intrinsic then this is okay?
						//       might also need to be same storage class?
						ObjectDecl *newobj = dynamic_cast< ObjectDecl* >( decl );
						ObjectDecl *oldobj = dynamic_cast< ObjectDecl* >( old );
						if (newobj->get_storageClass() != DeclarationNode::Extern && oldobj->get_storageClass() != DeclarationNode::Extern ) {
							throw SemanticError( "duplicate definition for 3 ", decl );
						} // if
					} // if
				} else {
					throw SemanticError( "duplicate definition for ", decl );
				} // if
			} else {
				// new scope level - shadow existing definition
				declTable[ manglename ].push( DeclEntry( decl, scopeLevel ) );
			} // if
		} // if
		// this ensures that no two declarations with the same unmangled name both have C linkage
		for ( InnerTableType::iterator i = declTable.begin(); i != declTable.end(); ++i ) {
			if ( ! i->second.empty() && i->second.top().first->get_linkage() == LinkageSpec::C && declTable.size() > 1 ) {
				InnerTableType::iterator j = i;
				for ( j++; j != declTable.end(); ++j ) {
					if ( ! j->second.empty() && j->second.top().first->get_linkage() == LinkageSpec::C ) {
						throw SemanticError( "invalid overload of C function " );
					} // if
				} // for
			} // if
		} // for
	}

	void IdTable::lookupId( const std::string &id, std::list< DeclarationWithType* >& decls ) const {
		OuterTableType::const_iterator outer = table.find( id );
		if ( outer == table.end() ) return;
		const InnerTableType &declTable = outer->second;
		for ( InnerTableType::const_iterator it = declTable.begin(); it != declTable.end(); ++it ) {
			const std::stack< DeclEntry >& entry = it->second;
			if ( ! entry.empty() ) {
				decls.push_back( entry.top().first );
			} // if
		} // for
	}

	DeclarationWithType * IdTable::lookupId( const std::string &id) const {
		DeclarationWithType* result = 0;
		int depth = -1;

		OuterTableType::const_iterator outer = table.find( id );
		if ( outer == table.end() ) return 0;
		const InnerTableType &declTable = outer->second;
		for ( InnerTableType::const_iterator it = declTable.begin(); it != declTable.end(); ++it ) {
			const std::stack< DeclEntry >& entry = it->second;
			if ( ! entry.empty() && entry.top().second > depth ) {
				result = entry.top().first;
				depth = entry.top().second;
			} // if
		} // for
		return result;
	}

	void IdTable::dump( std::ostream &os ) const {
		for ( OuterTableType::const_iterator outer = table.begin(); outer != table.end(); ++outer ) {
			for ( InnerTableType::const_iterator inner = outer->second.begin(); inner != outer->second.end(); ++inner ) {
#if 0
				const std::stack< DeclEntry >& entry = inner->second;
				if ( ! entry.empty() ) { // && entry.top().second == scopeLevel ) {
					os << outer->first << " (" << inner->first << ") (" << entry.top().second << ")" << std::endl;
				} else {
					os << outer->first << " (" << inner->first << ") ( entry-empty)" << std::endl;
				} // if
#endif
#if 0
				std::stack<DeclEntry> stack = inner->second;
				os << "dumping a stack" << std::endl;
				while ( ! stack.empty()) {
					DeclEntry d = stack.top();
					os << outer->first << " (" << inner->first << ") (" << d.second << ") " << std::endl;
					stack.pop();
				} // while
#endif
			} // for
		} // for
#if 0
		for ( OuterTableType::const_iterator outer = table.begin(); outer != table.end(); ++outer ) {
			for ( InnerTableType::const_iterator inner = outer->second.begin(); inner != outer->second.end(); ++inner ) {
				const std::stack< DeclEntry >& entry = inner->second;
				if ( ! entry.empty() && entry.top().second == scopeLevel ) {
					os << outer->first << " (" << inner->first << ") (" << scopeLevel << ")" << std::endl;
				} // if
			} // for
		} // for
#endif
	}
} // namespace SymTab

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
