From 3f6f2d3f90de9a4f506b196f9fac1d5909693d5f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 3 Sep 2024 15:33:33 +0200 Subject: [PATCH] Fix Yul stack output using standard json in the presence of warnings. --- Changelog.md | 1 + libsolidity/interface/StandardCompiler.cpp | 26 ++++---- libyul/YulStack.h | 1 + .../standard_yul_output_warning/input.json | 20 ++++++ .../standard_yul_output_warning/output.json | 61 +++++++++++++++++++ 5 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 test/cmdlineTests/standard_yul_output_warning/input.json create mode 100644 test/cmdlineTests/standard_yul_output_warning/output.json diff --git a/Changelog.md b/Changelog.md index 04bfa929530b..299a7a5bddb2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -32,6 +32,7 @@ Bugfixes: * SMTChecker: Fix formatting of unary minus expressions in invariants. * SMTChecker: Fix internal compiler error when reporting proved targets for BMC engine. * SMTChecker: Fix SMT logic error when assigning to an array of contracts or functions. + * Standard JSON Interface: For Yul input, properly produce output artifacts in case of warnings. * TypeChecker: Fix segfault when assigning nested tuple to tuple. * Yul IR Code Generation: Deterministic order of Yul subobjects. * Yul Optimizer: Fix Yul source locations always referring to unoptimized source, even in optimized outputs. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 385e48c2236e..0194aa937d5e 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -1620,7 +1620,7 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) std::string const& sourceContents = _inputsAndSettings.sources.begin()->second; // Inconsistent state - stop here to receive error reports from users - if (!stack.parseAndAnalyze(sourceName, sourceContents) && stack.errors().empty()) + if (!stack.parseAndAnalyze(sourceName, sourceContents) && !stack.hasErrors()) { output["errors"].emplace_back(formatError( Error::Type::InternalCompilerError, @@ -1630,22 +1630,20 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) return output; } - if (!stack.errors().empty()) + for (auto const& error: stack.errors()) { - for (auto const& error: stack.errors()) - { - auto err = std::dynamic_pointer_cast(error); + auto err = std::dynamic_pointer_cast(error); - output["errors"].emplace_back(formatErrorWithException( - stack, - *error, - err->type(), - "general", - "" - )); - } - return output; + output["errors"].emplace_back(formatErrorWithException( + stack, + *error, + err->type(), + "general", + "" + )); } + if (stack.hasErrors()) + return output; std::string contractName = stack.parserResult()->name; diff --git a/libyul/YulStack.h b/libyul/YulStack.h index 746e0c49fa72..0dc90baf890d 100644 --- a/libyul/YulStack.h +++ b/libyul/YulStack.h @@ -134,6 +134,7 @@ class YulStack: public langutil::CharStreamProvider /// @returns the errors generated during parsing, analysis (and potentially assembly). langutil::ErrorList const& errors() const { return m_errors; } + bool hasErrors() const { return m_errorReporter.hasErrors(); } /// Pretty-print the input after having parsed it. std::string print( diff --git a/test/cmdlineTests/standard_yul_output_warning/input.json b/test/cmdlineTests/standard_yul_output_warning/input.json new file mode 100644 index 000000000000..f6f519d82f21 --- /dev/null +++ b/test/cmdlineTests/standard_yul_output_warning/input.json @@ -0,0 +1,20 @@ +{ + "language": "Yul", + "sources": + { + "C": + { + "content": "{ let x := tload(0) tstore(add(x, 1), 0) }" + } + }, + "settings": + { + "outputSelection": + { + "*": { + "*": ["evm.bytecode", "evm.assembly", "irOptimized"], + "": [ "*" ] + } + } + } +} diff --git a/test/cmdlineTests/standard_yul_output_warning/output.json b/test/cmdlineTests/standard_yul_output_warning/output.json new file mode 100644 index 000000000000..3234be1817fd --- /dev/null +++ b/test/cmdlineTests/standard_yul_output_warning/output.json @@ -0,0 +1,61 @@ +{ + "contracts": { + "C": { + "object": { + "evm": { + "assembly": " /* \"C\":99:100 */ + 0x00 + /* \"C\":95:96 */ + 0x01 + /* \"C\":60:68 */ + dup2 + tload + /* \"C\":88:97 */ + add + /* \"C\":81:101 */ + tstore + /* \"C\":27:117 */ + stop +", + "bytecode": { + "functionDebugData": {}, + "generatedSources": [], + "linkReferences": {}, + "object": "", + "opcodes":"", + "sourceMap":"" + } + }, + "irOptimized": "object \"object\" { + code { + { + let x := tload(0) + tstore(add(x, 1), 0) + } + } +} +" + } + } + }, + "errors": [ + { + "component": "general", + "formattedMessage": "Warning: Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. + --> C:1:21: + | +1 | { let x := tload(0) tstore(add(x, 1), 0) } + | ^^^^^^ + +", + "message": "Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe.", + "severity": "warning", + "sourceLocation": { + "end": 26, + "file": "C", + "start": 20 + }, + "type": "Warning" + } + ] +}