Merge pull request #8987 from ethereum/sol-yul-bound-functions

[Sol->Yul] Bound functions
This commit is contained in:
chriseth 2020-05-25 16:53:33 +02:00 committed by GitHub
commit 6d64095ccf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 364 additions and 15 deletions

View File

@ -627,14 +627,20 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression());
if (memberAccess)
{
if (auto expressionType = dynamic_cast<TypeType const*>(memberAccess->expression().annotation().type))
{
solAssert(!functionType->bound(), "");
if (auto contractType = dynamic_cast<ContractType const*>(expressionType->actualType()))
solUnimplementedAssert(
!contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal,
"Only internal function calls implemented for libraries"
);
}
}
else
solAssert(!functionType->bound(), "");
solUnimplementedAssert(!functionType->bound(), "");
switch (functionType->kind())
{
case FunctionType::Kind::Declaration:
@ -658,14 +664,18 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
else
solAssert(!functionType->hasDeclaration(), "");
if (memberAccess)
solUnimplementedAssert(!functionType->bound(), "");
else
solAssert(!functionType->bound(), "");
solAssert(!functionType->takesArbitraryParameters(), "");
vector<string> args;
if (functionType->bound())
{
solAssert(memberAccess && functionDef, "");
solAssert(functionDef->parameters().size() == arguments.size() + 1, "");
args += convert(memberAccess->expression(), *functionDef->parameters()[0]->type()).stackSlots();
}
else
solAssert(!functionDef || functionDef->parameters().size() == arguments.size(), "");
for (size_t i = 0; i < arguments.size(); ++i)
args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
@ -1221,13 +1231,27 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
ASTString const& member = _memberAccess.memberName();
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
if (funType->bound())
{
solUnimplementedAssert(false, "");
}
auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
switch (_memberAccess.expression().annotation().type->category())
if (memberFunctionType && memberFunctionType->bound())
{
solAssert((set<Type::Category>{
Type::Category::Contract,
Type::Category::Bool,
Type::Category::Integer,
Type::Category::Address,
Type::Category::Function,
Type::Category::Struct,
Type::Category::Enum,
Type::Category::Mapping,
Type::Category::Array,
Type::Category::FixedBytes,
}).count(objectCategory) > 0, "");
return;
}
switch (objectCategory)
{
case Type::Category::Contract:
{
@ -1460,9 +1484,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
handleVariableReference(*variable, _memberAccess);
else if (auto const* funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
else if (memberFunctionType)
{
switch (funType->kind())
switch (memberFunctionType->kind())
{
case FunctionType::Kind::Declaration:
break;
@ -1481,7 +1505,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
break;
case FunctionType::Kind::DelegateCall:
define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(funType->externalIdentifier()) << "\n";
define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n";
break;
case FunctionType::Kind::External:
case FunctionType::Kind::Creation:

View File

@ -0,0 +1,18 @@
library L {
function equals(address a, address b) internal pure returns (bool) {
return a == b;
}
}
contract C {
using L for address;
function foo(address a, address b) public returns (bool) {
return a.equals(b);
}
}
// ====
// compileViaYul: also
// ----
// foo(address, address): 0x111122223333444455556666777788889999aAaa, 0x111122223333444455556666777788889999aAaa -> true
// foo(address, address): 0x111122223333444455556666777788889999aAaa, 0x0000000000000000000000000000000000000000 -> false

View File

@ -0,0 +1,21 @@
library L {
function transfer(address a) internal {}
function send(address a) internal {}
}
contract C {
using L for address;
function useTransfer(address a) public {
a.transfer();
}
function useSend(address a) public {
a.send();
}
}
// ====
// compileViaYul: also
// ----
// useTransfer(address): 0x111122223333444455556666777788889999aAaa ->
// useSend(address): 0x111122223333444455556666777788889999aAaa ->

View File

@ -0,0 +1,19 @@
library L {
function pop(uint[2] memory a) internal {}
function push(uint[2] memory a) internal {}
}
contract C {
using L for uint[2];
function test() public {
uint[2] memory input;
input.push();
input.pop();
}
}
// ====
// compileViaYul: also
// ----
// test() ->

View File

@ -0,0 +1,20 @@
library L {
function xor(bool a, bool b) internal pure returns (bool) {
return a != b;
}
}
contract C {
using L for bool;
function foo(bool a, bool b) public returns (bool) {
return a.xor(b);
}
}
// ====
// compileViaYul: also
// ----
// foo(bool, bool): true, true -> false
// foo(bool, bool): true, false -> true
// foo(bool, bool): false, true -> true
// foo(bool, bool): false, false -> false

View File

@ -0,0 +1,21 @@
contract E {}
library L {
function foo(E e) internal pure returns (uint) {
return 42;
}
}
contract C {
using L for E;
function test() public returns (uint) {
E e = new E();
return e.foo();
}
}
// ====
// compileViaYul: also
// ----
// test() -> 42

View File

@ -0,0 +1,21 @@
library L {
function at(uint[] memory a, uint i) internal pure returns (uint) {
return a[i];
}
}
contract C {
using L for uint[];
function secondItem() public returns (uint) {
uint[] memory input = new uint[](2);
input[0] = 0x11;
input[1] = 0x22;
return input.at(1);
}
}
// ====
// compileViaYul: also
// ----
// secondItem() -> 0x22

View File

@ -0,0 +1,21 @@
library L {
enum E { A, B }
function equals(E a, E b) internal pure returns (bool) {
return a == b;
}
}
contract C {
using L for L.E;
function equalsA(uint choice) public returns (bool) {
L.E x = L.E.A;
return x.equals(L.E(choice));
}
}
// ====
// compileViaYul: also
// ----
// equalsA(uint256): 0 -> true
// equalsA(uint256): 1 -> false

View File

@ -0,0 +1,23 @@
library L {
// NOTE: External function takes up two stack slots
function double(function(uint) external pure returns (uint) f, uint x) internal pure returns (uint) {
return f(x) * 2;
}
}
contract C {
using L for function(uint) external pure returns (uint);
function identity(uint x) external pure returns (uint) {
return x;
}
function test(uint value) public returns (uint) {
return this.identity.double(value);
}
}
// ====
// compileViaYul: also
// ----
// test(uint256): 5 -> 10

View File

@ -0,0 +1,21 @@
library L {
function at(uint[2] memory a, uint i) internal pure returns (uint) {
return a[i];
}
}
contract C {
using L for uint[2];
function secondItem() public returns (uint) {
uint[2] memory input;
input[0] = 0x11;
input[1] = 0x22;
return input.at(1);
}
}
// ====
// compileViaYul: also
// ----
// secondItem() -> 0x22

View File

@ -0,0 +1,17 @@
library L {
function add(bytes2 a, bytes2 b) internal pure returns (bytes2) {
return bytes2(uint16(a) + uint16(b));
}
}
contract C {
using L for bytes2;
function sum(bytes2 a, bytes2 b) public returns (bytes2) {
return a.add(b);
}
}
// ====
// compileViaYul: also
// ----
// sum(bytes2, bytes2): left(0x1100), left(0x0022) -> left(0x1122)

View File

@ -0,0 +1,22 @@
library L {
function selector(function(uint) internal pure returns (uint) f, uint x) internal pure returns (uint) {
return f(x) * 2;
}
}
contract C {
using L for function(uint) internal pure returns (uint);
function identity(uint x) internal pure returns (uint) {
return x;
}
function test(uint value) public returns (uint) {
return identity.selector(value);
}
}
// ====
// compileViaYul: also
// ----
// test(uint256): 5 -> 10

View File

@ -0,0 +1,17 @@
library L {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
}
contract C {
using L for uint256;
function foo(uint256 a, uint256 b) public returns (uint256) {
return a.add(b);
}
}
// ====
// compileViaYul: also
// ----
// foo(uint256, uint256): 8, 42 -> 50

View File

@ -0,0 +1,22 @@
interface I {}
contract E is I {}
library L {
function foo(I i) internal pure returns (uint) {
return 42;
}
}
contract C {
using L for I;
function test() public returns (uint) {
E e = new E();
return I(e).foo();
}
}
// ====
// compileViaYul: also
// ----
// test() -> 42

View File

@ -0,0 +1,22 @@
library L {
function double(function(uint) internal pure returns (uint) f, uint x) internal pure returns (uint) {
return f(x) * 2;
}
}
contract C {
using L for function(uint) internal pure returns (uint);
function identity(uint x) internal pure returns (uint) {
return x;
}
function test(uint value) public returns (uint) {
return identity.double(value);
}
}
// ====
// compileViaYul: also
// ----
// test(uint256): 5 -> 10

View File

@ -0,0 +1,22 @@
library L {
function at(mapping(uint => uint) storage a, uint i) internal view returns (uint) {
return a[i];
}
}
contract C {
using L for mapping(uint => uint);
mapping(uint => uint) map;
function mapValue(uint a) public returns (uint) {
map[42] = 0x24;
map[66] = 0x66;
return map.at(a);
}
}
// ====
// compileViaYul: also
// ----
// mapValue(uint256): 42 -> 0x24

View File

@ -0,0 +1,18 @@
library L {
function at(string memory a, uint i) internal pure returns (uint8) {
return uint8(bytes(a)[i]);
}
}
contract C {
using L for string;
function secondChar() public returns (uint8) {
string memory input = "abc";
return input.at(1);
}
}
// ====
// compileViaYul: also
// ----
// secondChar() -> 98