/*
	This file is part of solidity.
	solidity 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 3 of the License, or
	(at your option) any later version.
	solidity 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 solidity.  If not, see .
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace dev;
using namespace dev::solidity;
bool PostTypeChecker::check(ASTNode const& _astRoot)
{
	_astRoot.accept(*this);
	return Error::containsOnlyWarnings(m_errorReporter.errors());
}
bool PostTypeChecker::visit(ContractDefinition const&)
{
	solAssert(!m_currentConstVariable, "");
	solAssert(m_constVariableDependencies.empty(), "");
	return true;
}
void PostTypeChecker::endVisit(ContractDefinition const&)
{
	solAssert(!m_currentConstVariable, "");
	for (auto declaration: m_constVariables)
		if (auto identifier = findCycle(*declaration))
			m_errorReporter.typeError(
				declaration->location(),
				"The value of the constant " + declaration->name() +
				" has a cyclic dependency via " + identifier->name() + "."
			);
	m_constVariables.clear();
	m_constVariableDependencies.clear();
}
bool PostTypeChecker::visit(VariableDeclaration const& _variable)
{
	solAssert(!m_currentConstVariable, "");
	if (_variable.isConstant())
	{
		m_currentConstVariable = &_variable;
		m_constVariables.push_back(&_variable);
	}
	return true;
}
void PostTypeChecker::endVisit(VariableDeclaration const& _variable)
{
	if (_variable.isConstant())
	{
		solAssert(m_currentConstVariable == &_variable, "");
		m_currentConstVariable = nullptr;
	}
}
bool PostTypeChecker::visit(Identifier const& _identifier)
{
	if (m_currentConstVariable)
		if (auto var = dynamic_cast(_identifier.annotation().referencedDeclaration))
			if (var->isConstant())
				m_constVariableDependencies[m_currentConstVariable].insert(var);
	return true;
}
VariableDeclaration const* PostTypeChecker::findCycle(VariableDeclaration const& _startingFrom)
{
	auto visitor = [&](VariableDeclaration const& _variable, CycleDetector& _cycleDetector, size_t _depth)
	{
		if (_depth >= 256)
			m_errorReporter.fatalDeclarationError(_variable.location(), "Variable definition exhausting cyclic dependency validator.");
		// Iterating through the dependencies needs to be deterministic and thus cannot
		// depend on the memory layout.
		// Because of that, we sort by AST node id.
		vector dependencies(
			m_constVariableDependencies[&_variable].begin(),
			m_constVariableDependencies[&_variable].end()
		);
		sort(dependencies.begin(), dependencies.end(), [](VariableDeclaration const* _a, VariableDeclaration const* _b) -> bool
		{
			return _a->id() < _b->id();
		});
		for (auto v: dependencies)
			if (_cycleDetector.run(*v))
				return;
	};
	return CycleDetector(visitor).run(_startingFrom);
}