Catch panic.

This commit is contained in:
chriseth 2020-10-20 15:30:46 +02:00
parent b78443ac75
commit b965446182
21 changed files with 619 additions and 80 deletions

View File

@ -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.

View File

@ -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(...)`."
);
}
}

View File

@ -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")

View File

@ -1494,6 +1494,8 @@ private:
* Syntax:
* try <call> 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<ASTPointer<TryCatchClause>> 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:

View File

@ -971,31 +971,46 @@ bool ContractCompiler::visit(TryStatement const& _tryStatement)
void ContractCompiler::handleCatch(vector<ASTPointer<TryCatchClause>> const& _catchClauses)
{
// Stack is empty.
ASTPointer<TryCatchClause> structured{};
ASTPointer<TryCatchClause> error{};
ASTPointer<TryCatchClause> panic{};
ASTPointer<TryCatchClause> 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: <selector>
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: <selector>
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<ASTPointer<TryCatchClause>> 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: <selector>
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: <code> <success>
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)
{

View File

@ -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 <functionName>() -> sig {
if gt(returndatasize(), 3) {
returndatacopy(0, 0, 4)
sig := <shr224>(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 <functionName>() -> ret {
if lt(returndatasize(), 0x44) { leave }
returndatacopy(0, 0, 4)
let sig := <shr224>(mload(0))
if iszero(eq(sig, 0x<ErrorSignature>)) { leave }
let data := mload(<freeMemoryPointer>)
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 <functionName>() -> 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";

View File

@ -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.
///

View File

@ -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";

View File

@ -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.

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,11 @@
contract C {
function f() public {
try this.f() {
} catch Error(string memory) {
} catch Panic(uint) {
}
}
}
// ====
// EVMVersion: >=byzantium
// ----

View File

@ -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(...)`.

View File

@ -0,0 +1,11 @@
contract C {
function f() public {
try this.f() {
} catch Panic() {
}
}
}
// ====
// EVMVersion: >=byzantium
// ----
// TypeError 1271: (72-97): Expected `catch Panic(uint ...) { ... }`.

View File

@ -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 ...) { ... }`.