diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index bc5fe880d..b3872852f 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -104,6 +104,60 @@ string YulUtilFunctions::copyToMemoryFunction(bool _fromCalldata) }); } +string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _messageType) +{ + string functionName = + string(_assert ? "assert_helper" : "require_helper") + + (_messageType ? ("_" + _messageType->identifier()) : ""); + + solAssert(!_assert || !_messageType, "Asserts can't have messages!"); + + return m_functionCollector->createFunction(functionName, [&]() { + if (!_messageType) + return Whiskers(R"( + function (condition) { + if iszero(condition) { } + } + )") + ("invalidOrRevert", _assert ? "invalid()" : "revert(0, 0)") + ("functionName", functionName) + .render(); + + + solUnimplemented("require() with two parameters is not yet implemented."); + // TODO The code below is completely untested as we don't support StringLiterals yet + int const hashHeaderSize = 4; + int const byteSize = 8; + u256 const errorHash = + u256(FixedHash::Arith( + FixedHash(dev::keccak256("Error(string)")) + )) << (256 - hashHeaderSize * byteSize); + + string const encodeFunc = ABIFunctions(m_evmVersion, m_functionCollector) + .tupleEncoder( + {_messageType}, + {TypeProvider::stringMemory()} + ); + + return Whiskers(R"( + function (condition, message) { + if iszero(condition) { + let fmp := mload() + mstore(fmp, ) + let end := (add(fmp, ), message) + revert(fmp, sub(end, fmp)) + } + } + )") + ("functionName", functionName) + ("freeMemPointer", to_string(CompilerUtils::freeMemoryPointer)) + ("errorHash", errorHash.str()) + ("abiEncodeFunc", encodeFunc) + ("hashHeaderSize", to_string(hashHeaderSize)) + .render(); + }); +} + string YulUtilFunctions::leftAlignFunction(Type const& _type) { string functionName = string("leftAlign_") + _type.identifier(); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index b3c29dcfc..729710ac1 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -62,6 +62,10 @@ public: /// Pads with zeros and might write more than exactly length. std::string copyToMemoryFunction(bool _fromCalldata); + // @returns the name of a function that has the equivalent logic of an + // `assert` or `require` call. + std::string requireOrAssertFunction(bool _assert, Type const* _messageType = nullptr); + /// @returns the name of a function that takes a (cleaned) value of the given value type and /// left-aligns it, usually for use in non-padded encoding. std::string leftAlignFunction(Type const& _type); diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index ec5c9cd31..70ce39031 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -89,6 +89,8 @@ public: /// @returns a new copy of the utility function generator (but using the same function set). YulUtilFunctions utils(); + langutil::EVMVersion evmVersion() const { return m_evmVersion; }; + private: langutil::EVMVersion m_evmVersion; OptimiserSettings m_optimiserSettings; diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index c31449239..28a57da67 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -20,9 +20,11 @@ #include +#include #include #include #include +#include #include #include @@ -30,6 +32,8 @@ #include #include +#include +#include using namespace std; using namespace dev; @@ -366,6 +370,24 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) ")\n"; break; } + case FunctionType::Kind::Assert: + case FunctionType::Kind::Require: + { + solAssert(arguments.size() > 0, "Expected at least one parameter for require/assert"); + solAssert(arguments.size() <= 2, "Expected no more than two parameters for require/assert"); + + string requireOrAssertFunction = m_utils.requireOrAssertFunction( + functionType->kind() == FunctionType::Kind::Assert, + arguments.size() > 1 ? arguments[1]->annotation().type : nullptr + ); + + m_code << move(requireOrAssertFunction) << "(" << m_context.variable(*arguments[0]); + if (arguments.size() > 1) + m_code << ", " << m_context.variable(*arguments[1]); + m_code << ")\n"; + + break; + } default: solUnimplemented(""); } @@ -404,7 +426,7 @@ bool IRGeneratorForStatements::visit(Identifier const& _identifier) setLValue(_identifier, move(lvalue)); } - else + else if (!dynamic_cast(declaration)) solUnimplemented(""); return false; } diff --git a/test/libsolidity/semanticTests/viaYul/assert.sol b/test/libsolidity/semanticTests/viaYul/assert.sol new file mode 100644 index 000000000..6b1be606e --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/assert.sol @@ -0,0 +1,22 @@ +contract C { + function f(bool a) public pure returns (bool x) { + bool b = a; + x = b; + assert(b); + } + function fail() public pure returns (bool x) { + x = true; + assert(false); + } + function succeed() public pure returns (bool x) { + x = true; + assert(true); + } +} +// ==== +// compileViaYul: true +// ---- +// f(bool): true -> true +// f(bool): false -> FAILURE +// fail() -> FAILURE +// succeed() -> true diff --git a/test/libsolidity/semanticTests/viaYul/assert_and_require.sol b/test/libsolidity/semanticTests/viaYul/assert_and_require.sol new file mode 100644 index 000000000..4140653e8 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/assert_and_require.sol @@ -0,0 +1,19 @@ +contract C { + function f(bool a) public pure returns (bool x) { + bool b = a; + x = b; + assert(b); + } + function f2(bool a) public pure returns (bool x) { + bool b = a; + x = b; + require(b); + } +} +// ==== +// compileViaYul: true +// ---- +// f(bool): true -> true +// f(bool): false -> FAILURE +// f2(bool): true -> true +// f2(bool): false -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/require.sol b/test/libsolidity/semanticTests/viaYul/require.sol new file mode 100644 index 000000000..f816ace05 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/require.sol @@ -0,0 +1,28 @@ +contract C { + function f(bool a) public pure returns (bool x) { + bool b = a; + x = b; + require(b); + } + function fail() public pure returns (bool x) { + x = true; + require(false); + } + function succeed() public pure returns (bool x) { + x = true; + require(true); + } + /* Not properly supported by test system yet + function f2(bool a) public pure returns (bool x) { + x = a; + string memory message; + require(a, message); + }*/ +} +// ==== +// compileViaYul: true +// ---- +// f(bool): true -> true +// f(bool): false -> FAILURE +// fail() -> FAILURE +// succeed() -> true