Skip to content

Commit 0b36b69

Browse files
committed
Require with custom error for legacy pipeline
1 parent 0f98226 commit 0b36b69

17 files changed

+114
-30
lines changed

libsolidity/codegen/ExpressionCompiler.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
12491249
case FunctionType::Kind::Require:
12501250
{
12511251
acceptAndConvert(*arguments.front(), *function.parameterTypes().front(), false);
1252-
1252+
// stack: <condition>
12531253
bool haveReasonString = arguments.size() > 1 && m_context.revertStrings() != RevertStrings::Strip;
12541254

12551255
if (arguments.size() > 1)
@@ -1263,7 +1263,41 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
12631263
// as require with custom errors is not supported in legacy codegen.
12641264
auto const* magicType = dynamic_cast<MagicType const*>(arguments[1]->annotation().type);
12651265
if (magicType && magicType->kind() == MagicType::Kind::Error)
1266-
solUnimplemented("Require with a custom error is only available using the via-ir pipeline.");
1266+
{
1267+
// Make sure that error constructor arguments are evaluated regardless of the require condition
1268+
auto const& errorConstructorCall = dynamic_cast<FunctionCall const&>(*arguments[1]);
1269+
errorConstructorCall.expression().accept(*this);
1270+
std::vector<Type const*> argumentTypes{};
1271+
unsigned stackDepth{0};
1272+
for (ASTPointer<Expression const> const& arg: errorConstructorCall.sortedArguments())
1273+
{
1274+
arg->accept(*this);
1275+
stackDepth += arg->annotation().type->sizeOnStack();
1276+
argumentTypes.push_back(arg->annotation().type);
1277+
}
1278+
// stack: <condition> <arg0> <arg1> ... <argN>
1279+
// Move condition to the top of the stack
1280+
utils().moveIntoStack(arguments.at(0)->annotation().type->sizeOnStack(), stackDepth);
1281+
// stack: <arg0> <arg1> ... <argN> <condition>
1282+
m_context << Instruction::ISZERO << Instruction::ISZERO;
1283+
auto success = m_context.appendConditionalJump();
1284+
1285+
auto const* errorDefinition = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(errorConstructorCall.expression()));
1286+
solAssert(errorDefinition && errorDefinition->functionType(true));
1287+
1288+
utils().revertWithError(
1289+
errorDefinition->functionType(true)->externalSignature(),
1290+
errorDefinition->functionType(true)->parameterTypes(),
1291+
argumentTypes
1292+
);
1293+
// Here, the argument is consumed, but in the other branch, it is still there.
1294+
m_context.adjustStackOffset(static_cast<int>(stackDepth));
1295+
m_context << success;
1296+
// In case of the success branch i.e. require(true, ...), pop error constructor arguments
1297+
for (ASTPointer<Expression const> const& arg: errorConstructorCall.sortedArguments())
1298+
utils().popStackElement(*arg->annotation().type);
1299+
break;
1300+
}
12671301

12681302
if (m_context.revertStrings() == RevertStrings::Strip)
12691303
{

test/libsolidity/semanticTests/errors/require_different_errors_same_parameters.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ contract C
1414
}
1515
}
1616

17-
// ====
18-
// compileViaYul: true
1917
// ----
2018
// f() -> FAILURE, hex"f55fefe3", hex"0000000000000000000000000000000000000000000000000000000000000001", hex"0000000000000000000000000000000000000000000000000000000000000060", hex"0000000000000000000000000000000000000000000000000000000000000003", hex"0000000000000000000000000000000000000000000000000000000000000003", hex"74776f0000000000000000000000000000000000000000000000000000000000"
2119
// g() -> FAILURE, hex"44a06798", hex"0000000000000000000000000000000000000000000000000000000000000004", hex"0000000000000000000000000000000000000000000000000000000000000060", hex"0000000000000000000000000000000000000000000000000000000000000006", hex"0000000000000000000000000000000000000000000000000000000000000004", hex"6669766500000000000000000000000000000000000000000000000000000000"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
contract C {
2+
uint256 counter = 0;
3+
4+
error CustomError(uint256);
5+
6+
function g() internal returns (bool) {
7+
counter++;
8+
return false;
9+
}
10+
11+
function f() external {
12+
require(g(), CustomError(counter));
13+
}
14+
}
15+
16+
// ----
17+
// f() -> FAILURE, hex"110b3655", hex"0000000000000000000000000000000000000000000000000000000000000001"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
contract C {
2+
uint y;
3+
function g(bool x) internal returns (bool) {
4+
y = 42;
5+
return x;
6+
}
7+
error E(uint256);
8+
function h() internal returns (uint256) { return y; }
9+
function f(bool c) public {
10+
require(g(c), E(h()));
11+
}
12+
}
13+
14+
// ----
15+
// f(bool): 0 -> FAILURE, hex"002ff067", hex"000000000000000000000000000000000000000000000000000000000000002a"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
contract C {
2+
string failureMessage = "Failure Message";
3+
function g(bool x) internal returns (bool) {
4+
failureMessage = "Intercepted failure message";
5+
return x;
6+
}
7+
function h() internal returns (string memory) { return failureMessage; }
8+
function f(bool c) public {
9+
require(g(c), h());
10+
}
11+
}
12+
13+
// ----
14+
// f(bool): 0 -> FAILURE, hex"08c379a0", 0x20, 0x1b, "Intercepted failure message"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
contract C {
2+
uint x = 0;
3+
uint y = 42;
4+
error E(uint);
5+
function f(bool c) public returns (uint256, uint256, uint256) {
6+
uint z = x;
7+
if (y == 42) {
8+
x = 21;
9+
} else {
10+
require(c, E(y));
11+
}
12+
y /= 2;
13+
return (x,y,z);
14+
}
15+
}
16+
// ----
17+
// f(bool): 1 -> 0x15, 0x15, 0
18+
// f(bool): 0 -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000015"

test/libsolidity/semanticTests/errors/require_error_function_pointer_parameter.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,5 @@ contract C
1414
}
1515
}
1616

17-
// ====
18-
// compileViaYul: true
1917
// ----
2018
// f() -> FAILURE, hex"271b1dfa", hex"c06afe3a8444fc0004668591e8306bfb9968e79ef37cdc8e0000000000000000"

test/libsolidity/semanticTests/errors/require_error_multiple_arguments.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ contract C
1313
}
1414
}
1515

16-
// ====
17-
// compileViaYul: true
1816
// ----
1917
// f() -> FAILURE, hex"11a1077e", hex"0000000000000000000000000000000000000000000000000000000000000001", hex"0000000000000000000000000000000000000000000000000000000000000060", hex"0000000000000000000000000000000000000000000000000000000000000003", hex"0000000000000000000000000000000000000000000000000000000000000003", hex"74776f0000000000000000000000000000000000000000000000000000000000"
2018
// g() -> FAILURE, hex"11a1077e", hex"0000000000000000000000000000000000000000000000000000000000000004", hex"0000000000000000000000000000000000000000000000000000000000000060", hex"0000000000000000000000000000000000000000000000000000000000000006", hex"0000000000000000000000000000000000000000000000000000000000000004", hex"6669766500000000000000000000000000000000000000000000000000000000"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// The purpose of this test is to make sure that error constructor call
2+
// stack items are popped from the stack in the success branch, i.e. when
3+
// require condition is true.
4+
contract C {
5+
error E(uint,uint,uint);
6+
uint public x;
7+
function f(bool condition, uint a, uint b, uint c) public {
8+
require(condition,E(a,b,c));
9+
x = b;
10+
}
11+
}
12+
// ----
13+
// f(bool,uint256,uint256,uint256): true, 42, 4242, 424242 ->
14+
// x() -> 4242

test/libsolidity/semanticTests/errors/require_error_string_literal.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ contract C
1313
}
1414
}
1515

16-
// ====
17-
// compileViaYul: true
1816
// ----
1917
// f() -> FAILURE, hex"8d6ea8be", hex"0000000000000000000000000000000000000000000000000000000000000020", hex"000000000000000000000000000000000000000000000000000000000000000b", hex"6572726f72526561736f6e000000000000000000000000000000000000000000"
2018
// g() -> FAILURE, hex"8d6ea8be", hex"0000000000000000000000000000000000000000000000000000000000000020", hex"000000000000000000000000000000000000000000000000000000000000000d", hex"616e6f74686572526561736f6e00000000000000000000000000000000000000"

0 commit comments

Comments
 (0)