diff --git a/Changelog.md b/Changelog.md index 495864c6e..baeb7312b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### 0.8.1 (unreleased) +Language Features: + * Possibility to use ``catch Panic(uint code)`` to catch a panic failure from an external call. + Compiler Features: * Parser: Report meaningful error if parsing a version pragma failed. * SMTChecker: Support ABI functions as uninterpreted functions. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ea71d0d40..ca8c44592 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -997,6 +997,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) } } + TryCatchClause const* panicClause = nullptr; TryCatchClause const* errorClause = nullptr; TryCatchClause const* lowLevelClause = nullptr; for (size_t i = 1; i < _tryStatement.clauses().size(); ++i) @@ -1029,7 +1030,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) ); } } - else if (clause.errorName() == "Error") + else if (clause.errorName() == "Error" || clause.errorName() == "Panic") { if (!m_evmVersion.supportsReturndata()) m_errorReporter.typeError( @@ -1040,26 +1041,46 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) "). You need at least a Byzantium-compatible EVM or use `catch { ... }`." ); - if (errorClause) - m_errorReporter.typeError( - 1036_error, - clause.location(), - SecondarySourceLocation{}.append("The first clause is here:", errorClause->location()), - "This try statement already has an \"Error\" catch clause." - ); - errorClause = &clause; - if ( - !clause.parameters() || - clause.parameters()->parameters().size() != 1 || - *clause.parameters()->parameters().front()->type() != *TypeProvider::stringMemory() - ) - m_errorReporter.typeError(2943_error, clause.location(), "Expected `catch Error(string memory ...) { ... }`."); + if (clause.errorName() == "Error") + { + if (errorClause) + m_errorReporter.typeError( + 1036_error, + clause.location(), + SecondarySourceLocation{}.append("The first clause is here:", errorClause->location()), + "This try statement already has an \"Error\" catch clause." + ); + errorClause = &clause; + if ( + !clause.parameters() || + clause.parameters()->parameters().size() != 1 || + *clause.parameters()->parameters().front()->type() != *TypeProvider::stringMemory() + ) + m_errorReporter.typeError(2943_error, clause.location(), "Expected `catch Error(string memory ...) { ... }`."); + } + else + { + if (panicClause) + m_errorReporter.typeError( + 6732_error, + clause.location(), + SecondarySourceLocation{}.append("The first clause is here:", panicClause->location()), + "This try statement already has a \"Panic\" catch clause." + ); + panicClause = &clause; + if ( + !clause.parameters() || + clause.parameters()->parameters().size() != 1 || + *clause.parameters()->parameters().front()->type() != *TypeProvider::uint256() + ) + m_errorReporter.typeError(1271_error, clause.location(), "Expected `catch Panic(uint ...) { ... }`."); + } } else m_errorReporter.typeError( 3542_error, clause.location(), - "Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`." + "Invalid catch clause name. Expected either `catch (...)`, `catch Error(...)`, or `catch Panic(...)`." ); } } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 8081b2c8b..61f038abd 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -841,7 +841,15 @@ TryCatchClause const* TryStatement::successClause() const return m_clauses[0].get(); } -TryCatchClause const* TryStatement::structuredClause() const +TryCatchClause const* TryStatement::panicClause() const +{ + for (size_t i = 1; i < m_clauses.size(); ++i) + if (m_clauses[i]->errorName() == "Panic") + return m_clauses[i].get(); + return nullptr; +} + +TryCatchClause const* TryStatement::errorClause() const { for (size_t i = 1; i < m_clauses.size(); ++i) if (m_clauses[i]->errorName() == "Error") diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index cff1b512d..6edbc717e 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1494,6 +1494,8 @@ private: * Syntax: * try returns (uint x, uint y) { * // success code + * } catch Panic(uint errorCode) { + * // panic * } catch Error(string memory cause) { * // error code, reason provided * } catch (bytes memory lowLevelData) { @@ -1524,7 +1526,8 @@ public: std::vector> const& clauses() const { return m_clauses; } TryCatchClause const* successClause() const; - TryCatchClause const* structuredClause() const; + TryCatchClause const* panicClause() const; + TryCatchClause const* errorClause() const; TryCatchClause const* fallbackClause() const; private: diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 475f52603..9ce2b743d 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -971,31 +971,46 @@ bool ContractCompiler::visit(TryStatement const& _tryStatement) void ContractCompiler::handleCatch(vector> const& _catchClauses) { // Stack is empty. - ASTPointer structured{}; + ASTPointer error{}; + ASTPointer panic{}; ASTPointer fallback{}; for (size_t i = 1; i < _catchClauses.size(); ++i) if (_catchClauses[i]->errorName() == "Error") - structured = _catchClauses[i]; + error = _catchClauses[i]; + else if (_catchClauses[i]->errorName() == "Panic") + panic = _catchClauses[i]; else if (_catchClauses[i]->errorName().empty()) fallback = _catchClauses[i]; else solAssert(false, ""); - solAssert(_catchClauses.size() == 1ul + (structured ? 1 : 0) + (fallback ? 1 : 0), ""); + solAssert(_catchClauses.size() == 1ul + (error ? 1 : 0) + (panic ? 1 : 0) + (fallback ? 1 : 0), ""); evmasm::AssemblyItem endTag = m_context.newTag(); evmasm::AssemblyItem fallbackTag = m_context.newTag(); - if (structured) + evmasm::AssemblyItem panicTag = m_context.newTag(); + if (error || panic) + // Note that this function returns zero on failure, which is not a problem yet, + // but will be a problem once we allow user-defined errors. + m_context.callYulFunction(m_context.utilFunctions().returnDataSelectorFunction(), 0, 1); + // stack: + if (error) { solAssert( - structured->parameters() && - structured->parameters()->parameters().size() == 1 && - structured->parameters()->parameters().front() && - *structured->parameters()->parameters().front()->annotation().type == *TypeProvider::stringMemory(), + error->parameters() && + error->parameters()->parameters().size() == 1 && + error->parameters()->parameters().front() && + *error->parameters()->parameters().front()->annotation().type == *TypeProvider::stringMemory(), "" ); solAssert(m_context.evmVersion().supportsReturndata(), ""); + // stack: + m_context << Instruction::DUP1 << selectorFromSignature32("Error(string)") << Instruction::EQ; + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(panicTag); + m_context << Instruction::POP; // remove selector + // Try to decode the error message. // If this fails, leaves 0 on the stack, otherwise the pointer to the data string. m_context.callYulFunction(m_context.utilFunctions().tryDecodeErrorMessageFunction(), 0, 1); @@ -1006,9 +1021,42 @@ void ContractCompiler::handleCatch(vector> const& _ca m_context.adjustStackOffset(1); m_context << decodeSuccessTag; - structured->accept(*this); + error->accept(*this); m_context.appendJumpTo(endTag); + m_context.adjustStackOffset(1); } + m_context << panicTag; + if (panic) + { + solAssert( + panic->parameters() && + panic->parameters()->parameters().size() == 1 && + panic->parameters()->parameters().front() && + *panic->parameters()->parameters().front()->annotation().type == *TypeProvider::uint256(), + "" + ); + solAssert(m_context.evmVersion().supportsReturndata(), ""); + + // stack: + m_context << selectorFromSignature32("Panic(uint256)") << Instruction::EQ; + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(fallbackTag); + + m_context.callYulFunction(m_context.utilFunctions().tryDecodePanicDataFunction(), 0, 2); + m_context << Instruction::SWAP1; + // stack: + AssemblyItem decodeSuccessTag = m_context.appendConditionalJump(); + m_context << Instruction::POP; + m_context.appendJumpTo(fallbackTag); + m_context.adjustStackOffset(1); + + m_context << decodeSuccessTag; + panic->accept(*this); + m_context.appendJumpTo(endTag); + m_context.adjustStackOffset(1); + } + if (error || panic) + m_context << Instruction::POP; // selector m_context << fallbackTag; if (fallback) { diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 198550015..2c8c18987 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -3985,19 +3985,36 @@ string YulUtilFunctions::panicFunction(util::PanicCode _code) }); } +string YulUtilFunctions::returnDataSelectorFunction() +{ + string const functionName = "return_data_selector"; + solAssert(m_evmVersion.supportsReturndata(), ""); + + return m_functionCollector.createFunction(functionName, [&]() { + return util::Whiskers(R"( + function () -> sig { + if gt(returndatasize(), 3) { + returndatacopy(0, 0, 4) + sig := (mload(0)) + } + } + )") + ("functionName", functionName) + ("shr224", shiftRightFunction(224)) + .render(); + }); +} + string YulUtilFunctions::tryDecodeErrorMessageFunction() { string const functionName = "try_decode_error_message"; + solAssert(m_evmVersion.supportsReturndata(), ""); return m_functionCollector.createFunction(functionName, [&]() { return util::Whiskers(R"( function () -> ret { if lt(returndatasize(), 0x44) { leave } - returndatacopy(0, 0, 4) - let sig := (mload(0)) - if iszero(eq(sig, 0x)) { leave } - let data := mload() returndatacopy(data, 4, sub(returndatasize(), 4)) @@ -4021,14 +4038,32 @@ string YulUtilFunctions::tryDecodeErrorMessageFunction() } )") ("functionName", functionName) - ("shr224", shiftRightFunction(224)) - ("ErrorSignature", FixedHash<4>(util::keccak256("Error(string)")).hex()) ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) ("roundUp", roundUpFunction()) .render(); }); } +string YulUtilFunctions::tryDecodePanicDataFunction() +{ + string const functionName = "try_decode_panic_data"; + solAssert(m_evmVersion.supportsReturndata(), ""); + + return m_functionCollector.createFunction(functionName, [&]() { + return util::Whiskers(R"( + function () -> success, data { + if gt(returndatasize(), 0x23) { + returndatacopy(0, 4, 0x20) + success := 1 + data := mload(0) + } + } + )") + ("functionName", functionName) + .render(); + }); +} + string YulUtilFunctions::extractReturndataFunction() { string const functionName = "extract_returndata"; diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 5831ba2ff..2d29f9b8a 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -440,13 +440,21 @@ public: /// Reverts with ``Panic(uint256)`` and the given code. std::string panicFunction(util::PanicCode _code); - /// Returns the name of a function that decodes an error message. - /// signature: () -> arrayPtr - /// - /// Returns a newly allocated `bytes memory` array containing the decoded error message - /// or 0 on failure. + /// @returns the name of a function that returns the return data selector. + /// Returns zero if return data is too short. + std::string returnDataSelectorFunction(); + + /// @returns the name of a function that tries to abi-decode a string from offset 4 in the + /// return data. On failure, returns 0, otherwise a pointer to the newly allocated string. + /// Does not check the return data signature. + /// signature: () -> ptr std::string tryDecodeErrorMessageFunction(); + /// @returns the name of a function that tries to abi-decode a uint256 value from offset 4 in the + /// return data. + /// Does not check the return data signature. + /// signature: () -> success, value + std::string tryDecodePanicDataFunction(); /// Returns a function name that returns a newly allocated `bytes` array that contains the return data. /// diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index ee1a24bb3..5b46e2f9b 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2969,46 +2969,53 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement) { - if (_tryStatement.structuredClause()) - handleCatchStructuredAndFallback(*_tryStatement.structuredClause(), _tryStatement.fallbackClause()); - else if (_tryStatement.fallbackClause()) - handleCatchFallback(*_tryStatement.fallbackClause()); - else - rethrow(); -} + string const runFallback = m_context.newYulVariable(); + m_code << "let " << runFallback << " := 1\n"; -void IRGeneratorForStatements::handleCatchStructuredAndFallback( - TryCatchClause const& _structured, - TryCatchClause const* _fallback -) -{ - solAssert( - _structured.parameters() && - _structured.parameters()->parameters().size() == 1 && - _structured.parameters()->parameters().front() && - *_structured.parameters()->parameters().front()->annotation().type == *TypeProvider::stringMemory(), - "" - ); - solAssert(m_context.evmVersion().supportsReturndata(), ""); + // This function returns zero on "short returndata". We have to add a success flag + // once we implement custom error codes. + if (_tryStatement.errorClause() || _tryStatement.panicClause()) + m_code << "switch " << m_utils.returnDataSelectorFunction() << "()\n"; - // Try to decode the error message. - // If this fails, leaves 0 on the stack, otherwise the pointer to the data string. - string const dataVariable = m_context.newYulVariable(); - - m_code << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n"; - m_code << "switch iszero(" << dataVariable << ") \n"; - m_code << "case 0 { // decoding success\n"; - if (_structured.parameters()) + if (TryCatchClause const* errorClause = _tryStatement.errorClause()) { - solAssert(_structured.parameters()->parameters().size() == 1, ""); - IRVariable const& var = m_context.addLocalVariable(*_structured.parameters()->parameters().front()); - define(var) << dataVariable << "\n"; + m_code << "case " << selectorFromSignature32("Error(string)") << " {\n"; + string const dataVariable = m_context.newYulVariable(); + m_code << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n"; + m_code << "if " << dataVariable << " {\n"; + m_code << runFallback << " := 0\n"; + if (errorClause->parameters()) + { + solAssert(errorClause->parameters()->parameters().size() == 1, ""); + IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front()); + define(var) << dataVariable << "\n"; + } + errorClause->accept(*this); + m_code << "}\n"; + m_code << "}\n"; } - _structured.accept(*this); - m_code << "}\n"; - m_code << "default { // decoding failure\n"; - if (_fallback) - handleCatchFallback(*_fallback); + if (TryCatchClause const* panicClause = _tryStatement.panicClause()) + { + m_code << "case " << selectorFromSignature32("Panic(uint256)") << " {\n"; + string const success = m_context.newYulVariable(); + string const code = m_context.newYulVariable(); + m_code << "let " << success << ", " << code << " := " << m_utils.tryDecodePanicDataFunction() << "()\n"; + m_code << "if " << success << " {\n"; + m_code << runFallback << " := 0\n"; + if (panicClause->parameters()) + { + solAssert(panicClause->parameters()->parameters().size() == 1, ""); + IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front()); + define(var) << code << "\n"; + } + panicClause->accept(*this); + m_code << "}\n"; + m_code << "}\n"; + } + + m_code << "if " << runFallback << " {\n"; + if (_tryStatement.fallbackClause()) + handleCatchFallback(*_tryStatement.fallbackClause()); else rethrow(); m_code << "}\n"; diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 2af463132..e03215966 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -100,10 +100,6 @@ public: private: /// Handles all catch cases of a try statement, except the success-case. void handleCatch(TryStatement const& _tryStatement); - void handleCatchStructuredAndFallback( - TryCatchClause const& _structured, - TryCatchClause const* _fallback - ); void handleCatchFallback(TryCatchClause const& _fallback); /// Generates code to rethrow an exception. diff --git a/test/libsolidity/semanticTests/tryCatch/malformed_error.sol b/test/libsolidity/semanticTests/tryCatch/malformed_error.sol new file mode 100644 index 000000000..bcac7c1c6 --- /dev/null +++ b/test/libsolidity/semanticTests/tryCatch/malformed_error.sol @@ -0,0 +1,89 @@ +contract C { + function f(uint size) public pure { + assembly { + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(4, 0x20) + mstore(0x24, 7) + mstore(0x44, "abcdefg") + revert(0, size) + } + } + function a() public returns (uint) { + try this.f(3) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory) { + assert(false); + } catch { + assert(true); + } + } + function b() public returns (uint) { + try this.f(6) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory) { + assert(false); + } catch { + assert(true); + } + } + function b2() public returns (uint) { + try this.f(0x43) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory) { + assert(false); + } catch { + assert(true); + } + } + function b3() public returns (string memory) { + try this.f(0x4a) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory) { + assert(false); + } catch { + assert(true); + } + } + function c() public returns (string memory) { + try this.f(0x4b) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory er) { + assert(true); + return er; + } catch { + assert(false); + } + } + function d() public returns (string memory) { + try this.f(0x100) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory er) { + assert(true); + return er; + } catch { + assert(false); + } + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// a() -> 0x00 +// b() -> 0x00 +// b2() -> 0x00 +// b3() -> 0x20, 0x00 +// c() -> 0x20, 7, "abcdefg" +// d() -> 0x20, 7, "abcdefg" diff --git a/test/libsolidity/semanticTests/tryCatch/malformed_panic.sol b/test/libsolidity/semanticTests/tryCatch/malformed_panic.sol new file mode 100644 index 000000000..a90cca08f --- /dev/null +++ b/test/libsolidity/semanticTests/tryCatch/malformed_panic.sol @@ -0,0 +1,55 @@ +contract C { + function f(uint size) public pure { + assembly { + mstore(0, 0x4e487b7100000000000000000000000000000000000000000000000000000000) + mstore(4, 0x43) + revert(0, size) + } + } + function a() public returns (uint) { + try this.f(3) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch { + assert(true); + } + } + function b() public returns (uint) { + try this.f(6) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch { + assert(true); + } + } + function c() public returns (uint) { + try this.f(0x24) { + assert(false); + } catch Panic(uint c) { + assert(true); + return c; + } catch { + assert(false); + } + } + function d() public returns (uint) { + try this.f(0x100) { + assert(false); + } catch Panic(uint c) { + assert(true); + return c; + } catch { + assert(false); + } + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// a() -> 0x00 +// b() -> 0x00 +// c() -> 0x43 +// d() -> 0x43 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/tryCatch/malformed_panic_2.sol b/test/libsolidity/semanticTests/tryCatch/malformed_panic_2.sol new file mode 100644 index 000000000..bb7e48f32 --- /dev/null +++ b/test/libsolidity/semanticTests/tryCatch/malformed_panic_2.sol @@ -0,0 +1,53 @@ +contract C { + function f(uint size) public pure { + assembly { + mstore(0, 0x4e487b7100000000000000000000000000000000000000000000000000000000) + mstore(4, 0x43) + revert(0, size) + } + } + function a() public returns (uint) { + try this.f(3) { + assert(false); + } catch Panic(uint) { + assert(false); + } + // Error will be re-thrown, since there is no low-level catch clause + assert(false); + } + function b() public returns (uint) { + try this.f(6) { + assert(false); + } catch Panic(uint) { + assert(false); + } + // Error will be re-thrown, since there is no low-level catch clause + assert(false); + } + function c() public returns (uint) { + try this.f(0x24) { + assert(false); + } catch Panic(uint c) { + assert(true); + return c; + } + assert(false); + } + function d() public returns (uint) { + try this.f(0x100) { + assert(false); + } catch Panic(uint c) { + assert(true); + return c; + } + assert(false); + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// a() -> FAILURE, hex"4e487b" +// b() -> FAILURE, hex"4e487b710000" +// c() -> 0x43 +// d() -> 0x43 diff --git a/test/libsolidity/semanticTests/tryCatch/malformed_panic_3.sol b/test/libsolidity/semanticTests/tryCatch/malformed_panic_3.sol new file mode 100644 index 000000000..52a76f181 --- /dev/null +++ b/test/libsolidity/semanticTests/tryCatch/malformed_panic_3.sol @@ -0,0 +1,59 @@ +contract C { + function f(uint size) public pure { + assembly { + mstore(0, 0x4e487b7100000000000000000000000000000000000000000000000000000000) + mstore(4, 0x43) + revert(0, size) + } + } + function a() public returns (uint) { + try this.f(3) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory) { + assert(false); + } + // Error will be re-thrown, since there is no low-level catch clause + assert(false); + } + function b() public returns (uint) { + try this.f(6) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory) { + assert(false); + } + // Error will be re-thrown, since there is no low-level catch clause + assert(false); + } + function c() public returns (uint) { + try this.f(0x24) { + assert(false); + } catch Panic(uint c) { + assert(true); + return c; + } catch Error(string memory) { + assert(false); + } + } + function d() public returns (uint) { + try this.f(0x100) { + assert(false); + } catch Panic(uint c) { + assert(true); + return c; + } catch Error(string memory) { + assert(false); + } + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// a() -> FAILURE, hex"4e487b" +// b() -> FAILURE, hex"4e487b710000" +// c() -> 0x43 +// d() -> 0x43 diff --git a/test/libsolidity/semanticTests/tryCatch/malformed_panic_4.sol b/test/libsolidity/semanticTests/tryCatch/malformed_panic_4.sol new file mode 100644 index 000000000..2efce6853 --- /dev/null +++ b/test/libsolidity/semanticTests/tryCatch/malformed_panic_4.sol @@ -0,0 +1,62 @@ +contract C { + function f(uint size) public pure { + assembly { + mstore(0, 0x4e487b7100000000000000000000000000000000000000000000000000000000) + mstore(4, 0x43) + revert(0, size) + } + } + function a() public returns (uint) { + try this.f(3) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory) { + assert(false); + } catch { + assert(true); + } + } + function b() public returns (uint) { + try this.f(6) { + assert(false); + } catch Panic(uint) { + assert(false); + } catch Error(string memory) { + assert(false); + } catch { + assert(true); + } + } + function c() public returns (uint) { + try this.f(0x24) { + assert(false); + } catch Panic(uint c) { + assert(true); + return c; + } catch Error(string memory) { + assert(false); + } catch { + assert(false); + } + } + function d() public returns (uint) { + try this.f(0x100) { + assert(false); + } catch Panic(uint c) { + assert(true); + return c; + } catch Error(string memory) { + assert(false); + } catch { + assert(false); + } + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// a() -> 0x00 +// b() -> 0x00 +// c() -> 0x43 diff --git a/test/libsolidity/semanticTests/tryCatch/panic.sol b/test/libsolidity/semanticTests/tryCatch/panic.sol new file mode 100644 index 000000000..9211b4fa4 --- /dev/null +++ b/test/libsolidity/semanticTests/tryCatch/panic.sol @@ -0,0 +1,34 @@ +contract C { + function uf(bool b, uint x, uint y) public pure returns (uint) { + require(b, "failure"); + return x - y; + } + function onlyPanic(bool b, uint x, uint y) public returns (uint r, uint code) { + try this.uf(b, x, y) returns (uint b) { + r = b; + } catch Panic(uint c) { + code = c; + } + } + function panicAndError(bool b, uint x, uint y) public returns (uint r, uint code, string memory msg_) { + try this.uf(b, x, y) returns (uint b) { + r = b; + } catch Panic(uint c) { + code = c; + } catch Error(string memory _errmsg) { + msg_ = _errmsg; + } + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// onlyPanic(bool,uint256,uint256): true, 7, 6 -> 1, 0x00 +// onlyPanic(bool,uint256,uint256): true, 6, 7 -> 0x00, 0x11 +// onlyPanic(bool,uint256,uint256): false, 7, 6 -> FAILURE, hex"08c379a0", 0x20, 7, "failure" +// onlyPanic(bool,uint256,uint256): false, 6, 7 -> FAILURE, hex"08c379a0", 0x20, 7, "failure" +// panicAndError(bool,uint256,uint256): true, 7, 6 -> 1, 0x00, 0x60, 0x00 +// panicAndError(bool,uint256,uint256): true, 6, 7 -> 0x00, 0x11, 0x60, 0x00 +// panicAndError(bool,uint256,uint256): false, 7, 6 -> 0x00, 0x00, 0x60, 7, "failure" +// panicAndError(bool,uint256,uint256): false, 6, 7 -> 0x00, 0x00, 0x60, 7, "failure" diff --git a/test/libsolidity/syntaxTests/tryCatch/double_panic.sol b/test/libsolidity/syntaxTests/tryCatch/double_panic.sol new file mode 100644 index 000000000..27ce26330 --- /dev/null +++ b/test/libsolidity/syntaxTests/tryCatch/double_panic.sol @@ -0,0 +1,13 @@ +contract C { + function f() public { + try this.f() { + } catch Panic(bytes memory) { + } catch Panic(uint) { + } + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError 1271: (72-109): Expected `catch Panic(uint ...) { ... }`. +// TypeError 6732: (110-139): This try statement already has a "Panic" catch clause. diff --git a/test/libsolidity/syntaxTests/tryCatch/double_panic_correct.sol b/test/libsolidity/syntaxTests/tryCatch/double_panic_correct.sol new file mode 100644 index 000000000..926e944df --- /dev/null +++ b/test/libsolidity/syntaxTests/tryCatch/double_panic_correct.sol @@ -0,0 +1,12 @@ +contract C { + function f() public { + try this.f() { + } catch Panic(uint) { + } catch Panic(uint) { + } + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError 6732: (102-131): This try statement already has a "Panic" catch clause. diff --git a/test/libsolidity/syntaxTests/tryCatch/error_and_panic.sol b/test/libsolidity/syntaxTests/tryCatch/error_and_panic.sol new file mode 100644 index 000000000..70e75e7d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/tryCatch/error_and_panic.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + try this.f() { + } catch Error(string memory) { + } catch Panic(uint) { + } + } +} +// ==== +// EVMVersion: >=byzantium +// ---- diff --git a/test/libsolidity/syntaxTests/tryCatch/invalid_error_name.sol b/test/libsolidity/syntaxTests/tryCatch/invalid_error_name.sol index 0cf78c5e7..d777065e6 100644 --- a/test/libsolidity/syntaxTests/tryCatch/invalid_error_name.sol +++ b/test/libsolidity/syntaxTests/tryCatch/invalid_error_name.sol @@ -7,5 +7,5 @@ contract C { } } // ---- -// TypeError 3542: (93-119): Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`. -// TypeError 3542: (120-143): Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`. +// TypeError 3542: (93-119): Invalid catch clause name. Expected either `catch (...)`, `catch Error(...)`, or `catch Panic(...)`. +// TypeError 3542: (120-143): Invalid catch clause name. Expected either `catch (...)`, `catch Error(...)`, or `catch Panic(...)`. diff --git a/test/libsolidity/syntaxTests/tryCatch/wrong_panic.sol b/test/libsolidity/syntaxTests/tryCatch/wrong_panic.sol new file mode 100644 index 000000000..379a2c7c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/tryCatch/wrong_panic.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + try this.f() { + } catch Panic() { + } + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError 1271: (72-97): Expected `catch Panic(uint ...) { ... }`. diff --git a/test/libsolidity/syntaxTests/tryCatch/wrong_panic_2.sol b/test/libsolidity/syntaxTests/tryCatch/wrong_panic_2.sol new file mode 100644 index 000000000..6ccf784ab --- /dev/null +++ b/test/libsolidity/syntaxTests/tryCatch/wrong_panic_2.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + try this.f() { + } catch Panic(bytes memory) { + } + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError 1271: (72-109): Expected `catch Panic(uint ...) { ... }`.