/*
	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 .
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include 
#include 
#include 
#include 
namespace solidity::yul
{
struct Dialect;
struct Block;
class YulString;
class NameDispenser;
struct OptimiserStepContext
{
	Dialect const& dialect;
	NameDispenser& dispenser;
	std::set const& reservedIdentifiers;
	/// The value nullopt represents creation code
	std::optional expectedExecutionsPerDeployment;
};
/**
 * Construction to create dynamically callable objects out of the
 * statically callable optimiser steps.
 */
struct OptimiserStep
{
	explicit OptimiserStep(std::string _name): name(std::move(_name)) {}
	virtual ~OptimiserStep() = default;
	virtual void run(OptimiserStepContext&, Block&) const = 0;
	/// @returns non-nullopt if the step cannot be run, for example because it requires
	/// an SMT solver to be loaded, but none is available. In that case, the string
	/// contains a human-readable reason.
	virtual std::optional invalidInCurrentEnvironment() const = 0;
	std::string name;
};
template 
struct OptimiserStepInstance: public OptimiserStep
{
private:
	template
	struct HasInvalidInCurrentEnvironmentMethod
	{
	private:
		template static auto test(int) -> decltype(U::invalidInCurrentEnvironment(), std::true_type());
		template static std::false_type test(...);
	public:
		static constexpr bool value = decltype(test(0))::value;
	};
public:
	OptimiserStepInstance(): OptimiserStep{Step::name} {}
	void run(OptimiserStepContext& _context, Block& _ast) const override
	{
		Step::run(_context, _ast);
	}
	std::optional invalidInCurrentEnvironment() const override
	{
		if constexpr (HasInvalidInCurrentEnvironmentMethod::value)
			return Step::invalidInCurrentEnvironment();
		else
			return std::nullopt;
	}
};
}