diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index ae82582be365..3970bd4c4d05 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -456,7 +456,7 @@ std::optional checkOptimizerDetail(Json::Value const& _details, std return {}; } -std::optional checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, std::string& _optimiserSetting, std::string& _cleanupSetting) +std::optional checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, std::string& _optimiserSetting, std::string& _cleanupSetting, bool _runYulOptimizer) { if (_details.isMember(_name)) { @@ -482,6 +482,9 @@ std::optional checkOptimizerDetailSteps(Json::Value const& _details _cleanupSetting = fullSequence.substr(delimiterPos + 1); else solAssert(_cleanupSetting == OptimiserSettings::DefaultYulOptimiserCleanupSteps); + + if (!_runYulOptimizer && OptimiserSuite::isEmptyOptimizerSequence(fullSequence)) + return formatFatalError(Error::Type::JSONError, "If Yul optimizer is disabled, only an empty optimizerSteps sequence is accepted."); } else return formatFatalError(Error::Type::JSONError, "\"settings.optimizer.details." + _name + "\" must be a string"); @@ -606,36 +609,22 @@ std::variant parseOptimizerSettings(Json::Value { if (!settings.runYulOptimiser) { - auto const& yulDetails = details["yulDetails"]; - if (yulDetails.isObject() && yulDetails.isMember("optimizerSteps")) - { - auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps); - auto isWhiteSpaceOrNewlineOnly = [](std::string const& _sequence) { - return !std::any_of(_sequence.begin(), _sequence.end(), [](char const step){ - return step != ' ' && step != '\n'; - }); - }; - if ( - !error && - isWhiteSpaceOrNewlineOnly(settings.yulOptimiserSteps) && - isWhiteSpaceOrNewlineOnly(settings.yulOptimiserCleanupSteps) - ) - return { std::move(settings) }; - else - return formatFatalError(Error::Type::JSONError, "\"If Yul optimizer is disabled, only an empty optimizerSteps sequence (\":\") is accepted.\""); - } - return formatFatalError(Error::Type::JSONError, "\"Providing yulDetails requires Yul optimizer to be enabled."); + if (checkKeys(details["yulDetails"], {"optimizerSteps"}, "settings.optimizer.details.yulDetails")) + return formatFatalError(Error::Type::JSONError, "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled."); + if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps, settings.runYulOptimiser)) + *error; + return {std::move(settings)}; } if (auto result = checkKeys(details["yulDetails"], {"stackAllocation", "optimizerSteps"}, "settings.optimizer.details.yulDetails")) return *result; if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation)) return *error; - if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps)) + if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps, settings.runYulOptimiser)) return *error; } } - return { std::move(settings) }; + return {std::move(settings)}; } } diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index 4e123307cf9d..6c89cadbdfd9 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -34,12 +34,14 @@ #include #include #include +#include #include #include using namespace solidity; +using namespace solidity::frontend; using namespace solidity::yul; using namespace solidity::langutil; @@ -169,11 +171,18 @@ void YulStack::optimize(Object& _object, bool _isCreation) if (!m_optimiserSettings.runYulOptimiser) { // Yul optimizer disabled, but empty sequence (:) explicitly provided - if (m_optimiserSettings.yulOptimiserSteps.empty() && m_optimiserSettings.yulOptimiserCleanupSteps.empty()) + if (OptimiserSuite::isEmptyOptimizerSequence(m_optimiserSettings.yulOptimiserSteps + ":" + m_optimiserSettings.yulOptimiserCleanupSteps)) return std::make_tuple(true, "", ""); // Yul optimizer disabled, and no sequence explicitly provided (assumes default sequence) else + { + yulAssert( + m_optimiserSettings.yulOptimiserSteps == OptimiserSettings::DefaultYulOptimiserSteps && + m_optimiserSettings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps + ); return std::make_tuple(true, "u", ""); + } + } return std::make_tuple( m_optimiserSettings.optimizeStackAllocation, diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 58561ee10a15..7231c4bc55b4 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -383,6 +383,24 @@ void OptimiserSuite::validateSequence(std::string_view _stepAbbreviations) assertThrow(nestingLevel == 0, OptimizerException, "Unbalanced brackets"); } +bool OptimiserSuite::isEmptyOptimizerSequence(std::string const& _sequence) +{ + size_t delimiterCount{0}; + for (char const step: _sequence) + switch (step) + { + case ':': + ++delimiterCount; + break; + case ' ': + case '\n': + break; + default: + return false; + } + return delimiterCount == 1; +} + void OptimiserSuite::runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable) { validateSequence(_stepAbbreviations); diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index abed2720e2a5..6e3886da3b2d 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -76,6 +76,10 @@ class OptimiserSuite /// Ensures that specified sequence of step abbreviations is well-formed and can be executed. /// @throw OptimizerException if the sequence is invalid static void validateSequence(std::string_view _stepAbbreviations); + /// Check whether the provided sequence is empty provided that the allowed characters are + /// whitespace, newline and : + static bool isEmptyOptimizerSequence(std::string const& _sequence); + void runSequence(std::vector const& _steps, Block& _ast); void runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable = false); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 8d6564567990..3ed1b7b634ca 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -33,6 +33,7 @@ #include using namespace solidity::langutil; +using namespace solidity::yul; namespace po = boost::program_options; @@ -1228,8 +1229,8 @@ void CommandLineParser::processArgs() if (m_args.count(g_strYulOptimizations)) { OptimiserSettings optimiserSettings = m_options.optimiserSettings(); - if (!optimiserSettings.runYulOptimiser && !isEmptyOptimizerSequence(m_args[g_strYulOptimizations].as())) - solThrow(CommandLineValidationError, "--" + g_strYulOptimizations + " is invalid with any sequence other than empty (:) if Yul optimizer is disabled."); + if (!optimiserSettings.runYulOptimiser && !OptimiserSuite::isEmptyOptimizerSequence(m_args[g_strYulOptimizations].as())) + solThrow(CommandLineValidationError, "--" + g_strYulOptimizations + " is invalid with a non-empty sequence if Yul optimizer is disabled."); try { @@ -1485,24 +1486,6 @@ size_t CommandLineParser::countEnabledOptions(std::vector const& _o return count; } -bool CommandLineParser::isEmptyOptimizerSequence(std::string const& _sequence) const -{ - size_t delimiterCount{0}; - for (char const step: _sequence) - switch (step) - { - case ':': - ++delimiterCount; - break; - case ' ': - case '\n': - break; - default: - return false; - } - return delimiterCount == 1; -} - std::string CommandLineParser::joinOptionNames(std::vector const& _optionNames, std::string _separator) { return util::joinHumanReadable( diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 42356484340f..b95973562e51 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -287,7 +287,6 @@ class CommandLineParser void checkMutuallyExclusive(std::vector const& _optionNames); size_t countEnabledOptions(std::vector const& _optionNames) const; - bool isEmptyOptimizerSequence(std::string const& _sequence) const; static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); CommandLineOptions m_options; diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json index 6109931cccac..acf3b74ef1de 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json @@ -1,12 +1,9 @@ { - "errors": - [ + "sources": + { + "A": { - "component": "general", - "formattedMessage": "\"If Yul optimizer is disabled, only an empty optimizerSteps sequence (\":\") is accepted.\"", - "message": "\"If Yul optimizer is disabled, only an empty optimizerSteps sequence (\":\") is accepted.\"", - "severity": "error", - "type": "JSONError" + "id": 0 } - ] + } } diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json index 433e3941a1c8..9de7beea915a 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json @@ -1,16 +1,16 @@ { - "language": "Solidity", - "sources": { - "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol"]} - }, - "settings": { - "optimizer": { - "details": { - "yul": false, - "yulDetails": { - "optimizerSteps": ":" - } - } - } - } + "language": "Solidity", + "sources": { + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol"]} + }, + "settings": { + "optimizer": { + "details": { + "yul": false, + "yulDetails": { + "optimizerSteps": ":" + } + } + } + } } diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json index 4e6d7b6f4ddc..2470015e1fb0 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json @@ -3,8 +3,8 @@ [ { "component": "general", - "formattedMessage": "\"Providing yulDetails requires Yul optimizer to be enabled.", - "message": "\"Providing yulDetails requires Yul optimizer to be enabled.", + "formattedMessage": "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled.", + "message": "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled.", "severity": "error", "type": "JSONError" } diff --git a/test/cmdlineTests/yul_optimizer_steps_disabled/err b/test/cmdlineTests/yul_optimizer_steps_disabled/err index f2ef0a919fa0..1abbab8bde55 100644 --- a/test/cmdlineTests/yul_optimizer_steps_disabled/err +++ b/test/cmdlineTests/yul_optimizer_steps_disabled/err @@ -1 +1 @@ -Error: --yul-optimizations is invalid with any sequence other than empty (:) if Yul optimizer is disabled. +Error: --yul-optimizations is invalid with a non-empty sequence if Yul optimizer is disabled. diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 422e4fc9d6af..28ca2c48a1a0 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -602,7 +602,7 @@ BOOST_AUTO_TEST_CASE(valid_empty_optimizer_sequences_without_optimize) BOOST_AUTO_TEST_CASE(invalid_optimizer_sequence_without_optimize) { string const invalidSequence{"u: "}; - string const expectedErrorMessage{"--yul-optimizations is invalid with any sequence other than empty (:) if Yul optimizer is disabled."}; + string const expectedErrorMessage{"--yul-optimizations is invalid with a non-empty sequence if Yul optimizer is disabled."}; vector commandLineOptions{"solc", "contract.sol", "--yul-optimizations=" + invalidSequence}; auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage);