mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8987 from ethereum/sol-yul-bound-functions
[Sol->Yul] Bound functions
This commit is contained in:
commit
6d64095ccf
@ -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:
|
||||
|
@ -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
|
@ -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 ->
|
@ -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() ->
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user