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