Skip to content

Commit 4e0438e

Browse files
committed
Code generation for errors.
1 parent efc16c7 commit 4e0438e

16 files changed

+280
-2
lines changed

libsolidity/codegen/CompilerUtils.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,22 @@ void CompilerUtils::revertWithStringData(Type const& _argumentType)
103103
m_context << Instruction::REVERT;
104104
}
105105

106+
void CompilerUtils::revertWithError(
107+
string const& _signature,
108+
vector<Type const*> const& _parameterTypes,
109+
vector<Type const*> const& _argumentTypes
110+
)
111+
{
112+
fetchFreeMemoryPointer();
113+
m_context << util::selectorFromSignature(_signature);
114+
m_context << Instruction::DUP2 << Instruction::MSTORE;
115+
m_context << u256(4) << Instruction::ADD;
116+
// Stack: <arguments...> <mem pos of encoding start>
117+
abiEncode(_argumentTypes, _parameterTypes);
118+
toSizeAfterFreeMemoryPointer();
119+
m_context << Instruction::REVERT;
120+
}
121+
106122
void CompilerUtils::returnDataToArray()
107123
{
108124
if (m_context.evmVersion().supportsReturndata())

libsolidity/codegen/CompilerUtils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ class CompilerUtils
6969
/// Stack post:
7070
void revertWithStringData(Type const& _argumentType);
7171

72+
void revertWithError(
73+
std::string const& _errorName,
74+
std::vector<Type const*> const& _parameterTypes,
75+
std::vector<Type const*> const& _argumentTypes
76+
);
77+
7278
/// Allocates a new array and copies the return data to it.
7379
/// If the EVM does not support return data, creates an empty array.
7480
void returnDataToArray();

libsolidity/codegen/ContractCompiler.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,15 @@ bool ContractCompiler::visit(EmitStatement const& _emit)
12811281
return false;
12821282
}
12831283

1284+
bool ContractCompiler::visit(RevertStatement const& _revert)
1285+
{
1286+
CompilerContext::LocationSetter locationSetter(m_context, _revert);
1287+
StackHeightChecker checker(m_context);
1288+
compileExpression(_revert.errorCall());
1289+
checker.check();
1290+
return false;
1291+
}
1292+
12841293
bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
12851294
{
12861295
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);

libsolidity/codegen/ContractCompiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class ContractCompiler: private ASTConstVisitor
117117
bool visit(Return const& _return) override;
118118
bool visit(Throw const& _throw) override;
119119
bool visit(EmitStatement const& _emit) override;
120+
bool visit(RevertStatement const& _revert) override;
120121
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
121122
bool visit(ExpressionStatement const& _expressionStatement) override;
122123
bool visit(PlaceholderStatement const&) override;

libsolidity/codegen/ExpressionCompiler.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <libsolidity/codegen/LValue.h>
3030

3131
#include <libsolidity/ast/AST.h>
32+
#include <libsolidity/ast/ASTUtils.h>
3233
#include <libsolidity/ast/TypeProvider.h>
3334

3435
#include <libevmasm/GasMeter.h>
@@ -914,7 +915,20 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
914915
}
915916
case FunctionType::Kind::Error:
916917
{
917-
solAssert(false, "");
918+
_functionCall.expression().accept(*this);
919+
vector<Type const*> argumentTypes;
920+
for (ASTPointer<Expression const> const& arg: _functionCall.sortedArguments())
921+
{
922+
arg->accept(*this);
923+
argumentTypes.push_back(arg->annotation().type);
924+
}
925+
solAssert(dynamic_cast<ErrorDefinition const*>(&function.declaration()), "");
926+
utils().revertWithError(
927+
function.externalSignature(),
928+
function.parameterTypes(),
929+
argumentTypes
930+
);
931+
break;
918932
}
919933
case FunctionType::Kind::BlockHash:
920934
{
@@ -1832,6 +1846,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
18321846
solAssert(
18331847
dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
18341848
dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
1849+
dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
18351850
category == Type::Category::TypeType ||
18361851
category == Type::Category::Module,
18371852
""

libsolidity/codegen/ir/IRGeneratorForStatements.cpp

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,29 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
10431043
}
10441044
case FunctionType::Kind::Error:
10451045
{
1046-
solAssert(false, "");
1046+
vector<string> arguments;
1047+
vector<Type const*> argumentTypes;
1048+
for (ASTPointer<Expression const> const& arg: _functionCall.sortedArguments())
1049+
{
1050+
argumentTypes.push_back(arg->annotation().type);
1051+
arguments += IRVariable(*arg).stackSlots();
1052+
}
1053+
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
1054+
solAssert(dynamic_cast<ErrorDefinition const*>(&functionType->declaration()), "");
1055+
Whiskers templ(R"({
1056+
let <pos> := <allocateUnbounded>()
1057+
mstore(<pos>, <selector>)
1058+
let <end> := <encode>(add(<pos>, 4) <data>)
1059+
revert(<pos>, sub(<end>, <pos>))
1060+
})");
1061+
templ("pos", m_context.newYulVariable());
1062+
templ("selector", util::selectorFromSignature(functionType->externalSignature()).str());
1063+
templ("end", m_context.newYulVariable());
1064+
templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
1065+
templ("encode", abi.tupleEncoder(argumentTypes, functionType->parameterTypes()));
1066+
templ("data", joinHumanReadablePrefixed(arguments));
1067+
m_code << templ.render();
1068+
break;
10471069
}
10481070
case FunctionType::Kind::Assert:
10491071
case FunctionType::Kind::Require:
@@ -1966,6 +1988,13 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
19661988
);
19671989
// the call will do the resolving
19681990
break;
1991+
case FunctionType::Kind::Error:
1992+
solAssert(
1993+
dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration),
1994+
"Error not found"
1995+
);
1996+
// The function call will resolve the selector.
1997+
break;
19691998
case FunctionType::Kind::DelegateCall:
19701999
define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
19712000
define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n";
@@ -2010,6 +2039,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
20102039
solAssert(
20112040
dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
20122041
dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
2042+
dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
20132043
category == Type::Category::TypeType ||
20142044
category == Type::Category::Module,
20152045
""
@@ -3107,6 +3137,62 @@ void IRGeneratorForStatements::rethrow()
31073137
m_code << "revert(0, 0) // rethrow\n"s;
31083138
}
31093139

3140+
void IRGeneratorForStatements::revertWithError(ASTPointer<Expression const> const& _error)
3141+
{
3142+
solAssert(_error, "");
3143+
3144+
bool usesString = _error->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory());
3145+
if (usesString && m_context.revertStrings() == RevertStrings::Strip)
3146+
{
3147+
m_code << "revert(0, 0)\n";
3148+
return;
3149+
}
3150+
3151+
string signature;
3152+
vector<ASTPointer<Expression const>> errorArguments;
3153+
vector<Type const*> parameterTypes;
3154+
if (usesString)
3155+
{
3156+
signature = "Error(string)";
3157+
errorArguments.push_back(_error);
3158+
parameterTypes.push_back(TypeProvider::stringMemory());
3159+
}
3160+
else
3161+
{
3162+
FunctionCall const* errorCall = dynamic_cast<FunctionCall const*>(_error.get());
3163+
solAssert(errorCall, "");
3164+
solAssert(*errorCall->annotation().kind == FunctionCallKind::FunctionCall, "");
3165+
ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(referencedDeclaration(errorCall->expression()));
3166+
solAssert(error, "");
3167+
signature = error->functionType(true)->externalSignature();
3168+
parameterTypes = error->functionType(true)->parameterTypes();
3169+
errorArguments = errorCall->sortedArguments();
3170+
}
3171+
3172+
Whiskers templ(R"({
3173+
let <pos> := <allocateUnbounded>()
3174+
mstore(<pos>, <hash>)
3175+
let <end> := <encode>(add(<pos>, 4) <argumentVars>)
3176+
revert(<pos>, sub(<end>, <pos>))
3177+
})");
3178+
templ("pos", m_context.newYulVariable());
3179+
templ("end", m_context.newYulVariable());
3180+
templ("hash", util::selectorFromSignature(signature).str());
3181+
templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
3182+
3183+
vector<string> errorArgumentVars;
3184+
vector<Type const*> errorArgumentTypes;
3185+
for (ASTPointer<Expression const> const& arg: errorArguments)
3186+
{
3187+
errorArgumentVars += IRVariable(*arg).stackSlots();
3188+
errorArgumentTypes.push_back(arg->annotation().type);
3189+
}
3190+
templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars));
3191+
templ("encode", m_context.abiFunctions().tupleEncoder(errorArgumentTypes, parameterTypes));
3192+
3193+
m_code << templ.render();
3194+
}
3195+
31103196
bool IRGeneratorForStatements::visit(TryCatchClause const& _clause)
31113197
{
31123198
_clause.block().accept(*this);

libsolidity/codegen/ir/IRGeneratorForStatements.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ class IRGeneratorForStatements: public ASTConstVisitor
106106
/// Generates code to rethrow an exception.
107107
void rethrow();
108108

109+
/// Generates code to revert with an error, which might be a plain string
110+
/// or an error instance. The error arguments and the strings are assumed to
111+
/// be already evaluated and available in local IRVariables, but not yet
112+
/// converted.
113+
/// Honors the revert strings setting.
114+
void revertWithError(ASTPointer<Expression const> const& _error);
115+
109116
void handleVariableReference(
110117
VariableDeclaration const& _variable,
111118
Expression const& _referencingExpression
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error E(uint a);
2+
library L {
3+
error E(uint a, uint b);
4+
}
5+
interface I {
6+
error E(uint a, uint b, uint c);
7+
}
8+
contract C {
9+
function f() public pure {
10+
revert E(1);
11+
}
12+
function g() public pure {
13+
revert L.E(1, 2);
14+
}
15+
function h() public pure {
16+
revert I.E(1, 2, 3);
17+
}
18+
}
19+
// ====
20+
// compileViaYul: also
21+
// ----
22+
// f() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000001"
23+
// g() -> FAILURE, hex"85208890", hex"0000000000000000000000000000000000000000000000000000000000000001", hex"0000000000000000000000000000000000000000000000000000000000000002"
24+
// h() -> FAILURE, hex"7924ea7c", hex"0000000000000000000000000000000000000000000000000000000000000001", hex"0000000000000000000000000000000000000000000000000000000000000002", hex"0000000000000000000000000000000000000000000000000000000000000003"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error E(uint a, uint b);
2+
contract C {
3+
function f() public pure {
4+
revert E({b: 7, a: 2});
5+
}
6+
}
7+
// ====
8+
// compileViaYul: also
9+
// ----
10+
// f() -> FAILURE, hex"85208890", hex"0000000000000000000000000000000000000000000000000000000000000002", hex"0000000000000000000000000000000000000000000000000000000000000007"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
==== Source: s1.sol ====
2+
error E(uint);
3+
==== Source: s2.sol ====
4+
import { E as Panic } from "s1.sol";
5+
contract C {
6+
function a() public pure {
7+
revert Panic(1);
8+
}
9+
}
10+
// ====
11+
// compileViaYul: also
12+
// ----
13+
// a() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000001"

0 commit comments

Comments
 (0)